From b70de7dbc33c90e91bdeb5fd56621033278eb509 Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Thu, 30 Nov 2023 12:04:56 +0800 Subject: [PATCH 1/5] MDL-78370 course: Allow fetching of only required course summary fields * Add a 'requiredfields' parameter for get_enrolled_courses_by_timeline_classification() * Set default values for the following course_summary_exporter properties: - summary (null) - summaryformat (FORMAT_MOODLE) - pdfexportfont (null) --- .../external/course_summary_exporter.php | 7 +++- course/externallib.php | 37 +++++++++++++------ course/upgrade.txt | 6 +++ 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/course/classes/external/course_summary_exporter.php b/course/classes/external/course_summary_exporter.php index 64260851ba4..1fa2d764761 100644 --- a/course/classes/external/course_summary_exporter.php +++ b/course/classes/external/course_summary_exporter.php @@ -95,10 +95,12 @@ class course_summary_exporter extends \core\external\exporter { ), 'summary' => array( 'type' => PARAM_RAW, - 'null' => NULL_ALLOWED + 'null' => NULL_ALLOWED, + 'default' => null, ), 'summaryformat' => array( 'type' => PARAM_INT, + 'default' => FORMAT_MOODLE, ), 'startdate' => array( 'type' => PARAM_INT, @@ -119,7 +121,8 @@ class course_summary_exporter extends \core\external\exporter { ], 'pdfexportfont' => [ 'type' => PARAM_TEXT, - 'null' => NULL_ALLOWED + 'null' => NULL_ALLOWED, + 'default' => null, ], ); } diff --git a/course/externallib.php b/course/externallib.php index 94af358f8e1..b15dadd0c34 100644 --- a/course/externallib.php +++ b/course/externallib.php @@ -3890,6 +3890,11 @@ class core_course_external extends external_api { VALUE_DEFAULT, null), 'searchvalue' => new external_value(PARAM_RAW, 'The value a user wishes to search against', VALUE_DEFAULT, null), + 'requiredfields' => new core_external\external_multiple_structure( + new external_value(PARAM_ALPHANUMEXT, 'Field name to be included from the results', VALUE_DEFAULT), + 'Array of the only field names that need to be returned. If empty, all fields will be returned.', + VALUE_DEFAULT, [] + ), ) ); } @@ -3908,15 +3913,15 @@ class core_course_external extends external_api { * c1 is skipped (because the offset applies *before* the classification filtering) * and c4 and c5 will be return. * - * @param string $classification past, inprogress, or future - * @param int $limit Result set limit - * @param int $offset Offset the full course set before timeline classification is applied - * @param string $sort SQL sort string for results - * @param string $customfieldname - * @param string $customfieldvalue - * @param string $searchvalue + * @param string $classification past, inprogress, or future + * @param int $limit Result set limit + * @param int $offset Offset the full course set before timeline classification is applied + * @param string|null $sort SQL sort string for results + * @param string|null $customfieldname + * @param string|null $customfieldvalue + * @param string|null $searchvalue + * @param array $requiredfields Array of the only field names that need to be returned. If empty, all fields will be returned. * @return array list of courses and warnings - * @throws invalid_parameter_exception */ public static function get_enrolled_courses_by_timeline_classification( string $classification, @@ -3925,7 +3930,8 @@ class core_course_external extends external_api { string $sort = null, string $customfieldname = null, string $customfieldvalue = null, - string $searchvalue = null + string $searchvalue = null, + array $requiredfields = [] ) { global $CFG, $PAGE, $USER; require_once($CFG->dirroot . '/course/lib.php'); @@ -3938,6 +3944,7 @@ class core_course_external extends external_api { 'sort' => $sort, 'customfieldvalue' => $customfieldvalue, 'searchvalue' => $searchvalue, + 'requiredfields' => $requiredfields, ) ); @@ -3947,6 +3954,7 @@ class core_course_external extends external_api { $sort = $params['sort']; $customfieldvalue = $params['customfieldvalue']; $searchvalue = clean_param($params['searchvalue'], PARAM_TEXT); + $requiredfields = $params['requiredfields']; switch($classification) { case COURSE_TIMELINE_ALLINCLUDINGHIDDEN: @@ -3972,11 +3980,16 @@ class core_course_external extends external_api { } self::validate_context(context_user::instance($USER->id)); + $exporterfields = array_keys(course_summary_exporter::define_properties()); + // Get the required properties from the exporter fields based on the required fields. + $requiredproperties = array_intersect($exporterfields, $requiredfields); + // If the resulting required properties is empty, fall back to the exporter fields. + if (empty($requiredproperties)) { + $requiredproperties = $exporterfields; + } - $requiredproperties = course_summary_exporter::define_properties(); - $fields = join(',', array_keys($requiredproperties)); + $fields = join(',', $requiredproperties); $hiddencourses = get_hidden_courses_on_timeline(); - $courses = []; // If the timeline requires really all courses, get really all courses. if ($classification == COURSE_TIMELINE_ALLINCLUDINGHIDDEN) { diff --git a/course/upgrade.txt b/course/upgrade.txt index 8b906375d1c..0c7cfcaa577 100644 --- a/course/upgrade.txt +++ b/course/upgrade.txt @@ -22,6 +22,12 @@ information provided here is intended especially for developers. * New format actions classes. Those classes will eventually replace all course/lib.php content editing functions. All new methods are distributed in three classes formats can extend. Method can be accessed using static methods (see doc block of core_courseformat\formatactions for more information). +* New parameter 'requiredfields' added to \core_course_external::get_enrolled_courses_by_timeline_classification() to allow + callers of this function to specify only the required course fields. +* Set default values for the following course_summary_exporter properties: + - summary (null) + - summaryformat (FORMAT_MOODLE) + - pdfexportfont (null) === 4.3 === * The `core_course_renderer::course_section_cm_completion` method has been removed, and can no longer be used From 40c329064b5c3774bccb754b1e9bd4c01696b806 Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Thu, 30 Nov 2023 12:07:16 +0800 Subject: [PATCH 2/5] MDL-78370 block_myoverview: Define required fields for views * For list and card views, we only need the course fields ('id', 'fullname', 'shortname', 'showcoursecategory', 'showshortname', 'visible', and 'enddate'). * For summary view, in addition to the fields from list/card views, we only need the 'summary' and 'summaryformat'. --- blocks/myoverview/amd/build/repository.min.js | 2 +- .../amd/build/repository.min.js.map | 2 +- blocks/myoverview/amd/build/view.min.js | 2 +- blocks/myoverview/amd/build/view.min.js.map | 2 +- blocks/myoverview/amd/src/repository.js | 32 +++++++++++++++++++ blocks/myoverview/amd/src/view.js | 24 ++++++++++---- 6 files changed, 54 insertions(+), 10 deletions(-) diff --git a/blocks/myoverview/amd/build/repository.min.js b/blocks/myoverview/amd/build/repository.min.js index f21bccfdd51..5be01abbd8c 100644 --- a/blocks/myoverview/amd/build/repository.min.js +++ b/blocks/myoverview/amd/build/repository.min.js @@ -5,6 +5,6 @@ define("block_myoverview/repository",["exports","core/ajax"],(function(_exports, * @module block_myoverview/repository * @copyright 2018 Bas Brands * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.setFavouriteCourses=_exports.getEnrolledCoursesByTimeline=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.getEnrolledCoursesByTimeline=args=>{const request={methodname:"core_course_get_enrolled_courses_by_timeline_classification",args:args};return _ajax.default.call([request])[0]};_exports.setFavouriteCourses=args=>{const request={methodname:"core_course_set_favourite_courses",args:args};return _ajax.default.call([request])[0]}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.setFavouriteCourses=_exports.getEnrolledCoursesByTimeline=_exports.SUMMARY_REQUIRED_FIELDS=_exports.CARDLIST_REQUIRED_FIELDS=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.getEnrolledCoursesByTimeline=args=>{const request={methodname:"core_course_get_enrolled_courses_by_timeline_classification",args:args};return _ajax.default.call([request])[0]};_exports.setFavouriteCourses=args=>{const request={methodname:"core_course_set_favourite_courses",args:args};return _ajax.default.call([request])[0]};_exports.CARDLIST_REQUIRED_FIELDS=["id","fullname","shortname","showcoursecategory","showshortname","visible","enddate"];_exports.SUMMARY_REQUIRED_FIELDS=["id","fullname","shortname","showcoursecategory","showshortname","visible","enddate","summary","summaryformat"]})); //# sourceMappingURL=repository.min.js.map \ No newline at end of file diff --git a/blocks/myoverview/amd/build/repository.min.js.map b/blocks/myoverview/amd/build/repository.min.js.map index b9f0fbae525..9344d2c2848 100644 --- a/blocks/myoverview/amd/build/repository.min.js.map +++ b/blocks/myoverview/amd/build/repository.min.js.map @@ -1 +1 @@ -{"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 * A javascript module to retrieve enrolled coruses from the server.\n *\n * @module block_myoverview/repository\n * @copyright 2018 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\n\n/**\n * Retrieve a list of enrolled courses.\n *\n * Valid args are:\n * string classification future, inprogress, past\n * int limit number of records to retreive\n * int Offset offset for pagination\n * int sort sort by lastaccess or name\n *\n * @method getEnrolledCoursesByTimeline\n * @param {object} args The request arguments\n * @return {promise} Resolved with an array of courses\n */\nexport const getEnrolledCoursesByTimeline = args => {\n const request = {\n methodname: 'core_course_get_enrolled_courses_by_timeline_classification',\n args: args\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Set the favourite state on a list of courses.\n *\n * Valid args are:\n * Array courses list of course id numbers.\n *\n * @param {Object} args Arguments send to the webservice.\n * @return {Promise} Resolve with warnings.\n */\nexport const setFavouriteCourses = args => {\n const request = {\n methodname: 'core_course_set_favourite_courses',\n args: args\n };\n\n return Ajax.call([request])[0];\n};\n"],"names":["args","request","methodname","Ajax","call"],"mappings":";;;;;;;iOAsC4CA,aAClCC,QAAU,CACZC,WAAY,8DACZF,KAAMA,aAGHG,cAAKC,KAAK,CAACH,UAAU,iCAYGD,aACzBC,QAAU,CACZC,WAAY,oCACZF,KAAMA,aAGHG,cAAKC,KAAK,CAACH,UAAU"} \ No newline at end of file +{"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 * A javascript module to retrieve enrolled coruses from the server.\n *\n * @module block_myoverview/repository\n * @copyright 2018 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\n\n/**\n * Retrieve a list of enrolled courses.\n *\n * Valid args are:\n * string classification future, inprogress, past\n * int limit number of records to retreive\n * int Offset offset for pagination\n * int sort sort by lastaccess or name\n *\n * @method getEnrolledCoursesByTimeline\n * @param {object} args The request arguments\n * @return {promise} Resolved with an array of courses\n */\nexport const getEnrolledCoursesByTimeline = args => {\n const request = {\n methodname: 'core_course_get_enrolled_courses_by_timeline_classification',\n args: args\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Set the favourite state on a list of courses.\n *\n * Valid args are:\n * Array courses list of course id numbers.\n *\n * @param {Object} args Arguments send to the webservice.\n * @return {Promise} Resolve with warnings.\n */\nexport const setFavouriteCourses = args => {\n const request = {\n methodname: 'core_course_set_favourite_courses',\n args: args\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * These course fields are the only ones needed to be included in the results for the card and list views.\n *\n * @type {string[]}\n */\nexport const CARDLIST_REQUIRED_FIELDS = [\n 'id',\n 'fullname',\n 'shortname',\n 'showcoursecategory',\n 'showshortname',\n 'visible',\n 'enddate',\n];\n\n/**\n * These course fields are the only ones needed to be included in the results for the card and list views.\n *\n * @type {string[]}\n */\nexport const SUMMARY_REQUIRED_FIELDS = [\n 'id',\n 'fullname',\n 'shortname',\n 'showcoursecategory',\n 'showshortname',\n 'visible',\n 'enddate',\n 'summary',\n 'summaryformat',\n];\n"],"names":["args","request","methodname","Ajax","call"],"mappings":";;;;;;;oSAsC4CA,aAClCC,QAAU,CACZC,WAAY,8DACZF,KAAMA,aAGHG,cAAKC,KAAK,CAACH,UAAU,iCAYGD,aACzBC,QAAU,CACZC,WAAY,oCACZF,KAAMA,aAGHG,cAAKC,KAAK,CAACH,UAAU,sCAQQ,CACpC,KACA,WACA,YACA,qBACA,gBACA,UACA,4CAQmC,CACnC,KACA,WACA,YACA,qBACA,gBACA,UACA,UACA,UACA"} \ No newline at end of file diff --git a/blocks/myoverview/amd/build/view.min.js b/blocks/myoverview/amd/build/view.min.js index ed080ea6191..375c26cadcb 100644 --- a/blocks/myoverview/amd/build/view.min.js +++ b/blocks/myoverview/amd/build/view.min.js @@ -4,6 +4,6 @@ define("block_myoverview/view",["exports","jquery","block_myoverview/repository" * * @copyright 2018 Bas Brands * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.reset=_exports.init=_exports.clearSearch=void 0,_jquery=_interopRequireDefault(_jquery),Repository=_interopRequireWildcard(Repository),PagedContentFactory=_interopRequireWildcard(PagedContentFactory),PubSub=_interopRequireWildcard(PubSub),CustomEvents=_interopRequireWildcard(CustomEvents),Notification=_interopRequireWildcard(Notification),Templates=_interopRequireWildcard(Templates),CourseEvents=_interopRequireWildcard(CourseEvents),_selectors=_interopRequireDefault(_selectors),PagedContentEvents=_interopRequireWildcard(PagedContentEvents),Aria=_interopRequireWildcard(Aria);const TEMPLATES_COURSES_CARDS="block_myoverview/view-cards",TEMPLATES_COURSES_LIST="block_myoverview/view-list",TEMPLATES_COURSES_SUMMARY="block_myoverview/view-summary",TEMPLATES_NOCOURSES="core_course/no-courses",GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN="allincludinghidden",NUMCOURSES_PERPAGE=[12,24,48,96,0];let loadedPages=[],courseOffset=0,lastPage=0,lastLimit=0,namespace=null;const getFilterValues=root=>{const courseRegion=root.find(_selectors.default.courseView.region);return{display:courseRegion.attr("data-display"),grouping:courseRegion.attr("data-grouping"),sort:courseRegion.attr("data-sort"),displaycategories:courseRegion.attr("data-displaycategories"),customfieldname:courseRegion.attr("data-customfieldname"),customfieldvalue:courseRegion.attr("data-customfieldvalue")}},DEFAULT_PAGED_CONTENT_CONFIG={ignoreControlWhileLoading:!0,controlPlacementBottom:!0,persistentLimitKey:"block_myoverview_user_paging_preference"},getFavouriteIconContainer=(root,courseId)=>root.find(_selectors.default.FAVOURITE_ICON+'[data-course-id="'+courseId+'"]'),getPagedContentContainer=(root,index)=>root.find('[data-region="paged-content-page"][data-page="'+index+'"]'),getCourseId=root=>root.attr("data-course-id"),getAddFavouriteMenuItem=(root,courseId)=>root.find('[data-action="add-favourite"][data-course-id="'+courseId+'"]'),getRemoveFavouriteMenuItem=(root,courseId)=>root.find('[data-action="remove-favourite"][data-course-id="'+courseId+'"]'),addToFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!0).then((success=>{success?(PubSub.publish(CourseEvents.favourited,courseId),removeAction.removeClass("hidden"),addAction.addClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.removeClass("hidden"),Aria.unhide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.addClass("hidden"),Aria.hide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},removeFromFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!1).then((success=>{success?(PubSub.publish(CourseEvents.unfavorited,courseId),removeAction.addClass("hidden"),addAction.removeClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.addClass("hidden"),Aria.hide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.removeClass("hidden"),Aria.unhide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},getHideCourseMenuItem=(root,courseId)=>root.find('[data-action="hide-course"][data-course-id="'+courseId+'"]'),getShowCourseMenuItem=(root,courseId)=>root.find('[data-action="show-course"][data-course-id="'+courseId+'"]'),setCourseHiddenState=(courseId,status)=>(!1===status&&(status=null),(0,_repository2.setUserPreference)("block_myoverview_hidden_course_".concat(courseId),status).catch(Notification.exception)),hideElement=(root,id)=>{const pagingBar=root.find('[data-region="paging-bar"]'),jumpto=parseInt(pagingBar.attr("data-active-page-number"));let reducedCourse=loadedPages[jumpto].courses.reduce(((accumulator,current)=>(+id!=+current.id&&accumulator.push(current),accumulator)),[]);if(void 0!==loadedPages[jumpto+1]){const newElement=loadedPages[jumpto+1].courses.slice(0,1);loadedPages.forEach(((courseList,index)=>{if(index>jumpto){let popElement=[];void 0!==loadedPages[index+1]&&(popElement=loadedPages[index+1].courses.slice(0,1)),loadedPages[index].courses=[...loadedPages[index].courses.slice(1),...popElement]}})),reducedCourse=[...reducedCourse,...newElement]}if(lastPage===jumpto+1&&0===loadedPages[jumpto+1].courses.length){const pagedContentContainer=root.find('[data-region="paged-content-container"]');PagedContentFactory.resetLastPageNumber((0,_jquery.default)(pagedContentContainer).attr("id"),jumpto)}loadedPages[jumpto].courses=reducedCourse,courseOffset--;const pagedContentPage=getPagedContentContainer(root,jumpto);renderCourses(root,loadedPages[jumpto]).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception),loadedPages.forEach(((courseList,index)=>{if(index>jumpto){getPagedContentContainer(root,index).remove()}}))},setCourseFavouriteState=(courseId,status)=>Repository.setFavouriteCourses({courses:[{id:courseId,favourite:status}]}).then((result=>0===result.warnings.length&&(loadedPages.forEach((courseList=>{courseList.courses.forEach(((course,index)=>{course.id==courseId&&(courseList.courses[index].isfavourite=status)}))})),!0))).catch(Notification.exception),noCoursesRender=root=>{const nocoursesimg=root.find(_selectors.default.courseView.region).attr("data-nocoursesimg"),newcourseurl=root.find(_selectors.default.courseView.region).attr("data-newcourseurl");return Templates.render(TEMPLATES_NOCOURSES,{nocoursesimg:nocoursesimg,newcourseurl:newcourseurl})},renderCourses=(root,coursesData)=>{const filters=getFilterValues(root);let currentTemplate="";return currentTemplate="card"===filters.display?TEMPLATES_COURSES_CARDS:"list"===filters.display?TEMPLATES_COURSES_LIST:TEMPLATES_COURSES_SUMMARY,coursesData?(!1===Array.isArray(coursesData.courses)&&(coursesData.courses=Object.values(coursesData.courses)),coursesData.courses=coursesData.courses.map((course=>(course.showcoursecategory="on"===filters.displaycategories,course))),coursesData.courses.length?Templates.render(currentTemplate,{courses:coursesData.courses}):noCoursesRender(root)):noCoursesRender(root)},registerPagedEventHandlers=(root,namespace)=>{const event=namespace+PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;PubSub.subscribe(event,(root=>limit=>root.find(_selectors.default.courseView.region).attr("data-paging",limit))(root))},itemsPerPageFunc=(pagingLimit,root)=>{let itemsPerPage=NUMCOURSES_PERPAGE.map((value=>{let active=!1;return value===pagingLimit&&(active=!0),{value:value,active:active}}));const totalCourseCount=parseInt(root.find(_selectors.default.courseView.region).attr("data-totalcoursecount"),10);return itemsPerPage.filter((pagingOption=>pagingOption.value4&&void 0!==arguments[4]?arguments[4]:null,courses=coursesData.courses?coursesData.courses:coursesData,nextPageStart=0,pageCourses=[];if(void 0!==loadedPages[currentPage]){pageCourses=loadedPages[currentPage].courses;const currentPageLength=pageCourses.length;currentPageLength0?courses.slice(0,pageData.limit):courses;loadedPages[currentPage]={courses:pageCourses};const remainingCourses=!1!==nextPageStart?courses.slice(nextPageStart,courses.length):[];remainingCourses.length&&(loadedPages[currentPage+1]={courses:remainingCourses}),loadedPages[currentPage].courses.length{courseOffset=0,loadedPages=[],lastPage=0,lastLimit=0},standardFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit)=>{const pagePromise=((filters,limit)=>Repository.getEnrolledCoursesByTimeline({offset:courseOffset,limit:limit,classification:filters.grouping,sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue}))(filters,limit).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(pagePromise)}),searchFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit,inputValue)=>{const searchingPromise=((filters,limit,searchValue)=>Repository.getEnrolledCoursesByTimeline({offset:courseOffset,limit:limit,classification:"search",sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue,searchvalue:searchValue}))(filters,limit,inputValue).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(searchingPromise)}),initializePagedContent=function(root,promiseFunction){let inputValue=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const pagingLimit=parseInt(root.find(_selectors.default.courseView.region).attr("data-paging"),10);let itemsPerPage=itemsPerPageFunc(pagingLimit,root);const filters=getFilterValues(root),config={...DEFAULT_PAGED_CONTENT_CONFIG};config.eventNamespace=namespace;const pagedContentPromise=PagedContentFactory.createWithLimit(itemsPerPage,((pagesData,actions)=>{let promises=[];return pagesData.forEach((pageData=>{const currentPage=pageData.pageNumber;let limit=pageData.limit>0?pageData.limit:0;if(+lastLimit!=+limit&&(loadedPages=[],courseOffset=0,lastPage=0),lastPage===currentPage)return actions.allItemsLoaded(lastPage),void promises.push(renderCourses(root,loadedPages[currentPage]));lastLimit=limit,void 0===loadedPages[currentPage+1]&&void 0===loadedPages[currentPage]&&(limit*=2),promiseFunction(filters,currentPage,pageData,actions,root,promises,limit,inputValue)})),promises}),config);pagedContentPromise.then(((html,js)=>(registerPagedEventHandlers(root,namespace),Templates.replaceNodeContents(root.find(_selectors.default.courseView.region),html,js)))).catch(Notification.exception)},registerEventListeners=(root,page)=>{CustomEvents.define(root,[CustomEvents.events.activate]),root.on(CustomEvents.events.activate,_selectors.default.ACTION_ADD_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_ADD_FAVOURITE),courseId=getCourseId(favourite);addToFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_REMOVE_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_REMOVE_FAVOURITE),courseId=getCourseId(favourite);removeFromFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.FAVOURITE_ICON,((e,data)=>{data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_HIDE_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_HIDE_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,!0),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.addClass("hidden"),showAction.removeClass("hidden")})(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_SHOW_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_SHOW_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,null),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.removeClass("hidden"),showAction.addClass("hidden")})(root,courseId),data.originalEvent.preventDefault()}));const input=page.querySelector(_selectors.default.region.searchInput),clearIcon=page.querySelector(_selectors.default.region.clearIcon);clearIcon.addEventListener("click",(()=>{input.value="",input.focus(),clearSearch(clearIcon,root)})),input.addEventListener("input",(0,_utils.debounce)((()=>{""===input.value?clearSearch(clearIcon,root):(activeSearch(clearIcon),initializePagedContent(root,searchFunctionalityCurry(),input.value.trim()))}),1e3))},clearSearch=(clearIcon,root)=>{clearIcon.classList.add("d-none"),init(root)};_exports.clearSearch=clearSearch;const activeSearch=clearIcon=>{clearIcon.classList.remove("d-none")},init=root=>{if(root=(0,_jquery.default)(root),loadedPages=[],lastPage=0,courseOffset=0,!root.attr("data-init")){const page=document.querySelector(_selectors.default.region.selectBlock);registerEventListeners(root,page),namespace="block_myoverview_"+root.attr("id")+"_"+Math.random(),root.attr("data-init",!0)}initializePagedContent(root,standardFunctionalityCurry())};_exports.init=init;_exports.reset=root=>{loadedPages.length>0?loadedPages.forEach(((courseList,index)=>{let pagedContentPage=getPagedContentContainer(root,index);renderCourses(root,courseList).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception)})):init(root)}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.reset=_exports.init=_exports.clearSearch=void 0,_jquery=_interopRequireDefault(_jquery),Repository=_interopRequireWildcard(Repository),PagedContentFactory=_interopRequireWildcard(PagedContentFactory),PubSub=_interopRequireWildcard(PubSub),CustomEvents=_interopRequireWildcard(CustomEvents),Notification=_interopRequireWildcard(Notification),Templates=_interopRequireWildcard(Templates),CourseEvents=_interopRequireWildcard(CourseEvents),_selectors=_interopRequireDefault(_selectors),PagedContentEvents=_interopRequireWildcard(PagedContentEvents),Aria=_interopRequireWildcard(Aria);const TEMPLATES_COURSES_CARDS="block_myoverview/view-cards",TEMPLATES_COURSES_LIST="block_myoverview/view-list",TEMPLATES_COURSES_SUMMARY="block_myoverview/view-summary",TEMPLATES_NOCOURSES="core_course/no-courses",GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN="allincludinghidden",NUMCOURSES_PERPAGE=[12,24,48,96,0];let loadedPages=[],courseOffset=0,lastPage=0,lastLimit=0,namespace=null;const getFilterValues=root=>{const courseRegion=root.find(_selectors.default.courseView.region);return{display:courseRegion.attr("data-display"),grouping:courseRegion.attr("data-grouping"),sort:courseRegion.attr("data-sort"),displaycategories:courseRegion.attr("data-displaycategories"),customfieldname:courseRegion.attr("data-customfieldname"),customfieldvalue:courseRegion.attr("data-customfieldvalue")}},DEFAULT_PAGED_CONTENT_CONFIG={ignoreControlWhileLoading:!0,controlPlacementBottom:!0,persistentLimitKey:"block_myoverview_user_paging_preference"},getFavouriteIconContainer=(root,courseId)=>root.find(_selectors.default.FAVOURITE_ICON+'[data-course-id="'+courseId+'"]'),getPagedContentContainer=(root,index)=>root.find('[data-region="paged-content-page"][data-page="'+index+'"]'),getCourseId=root=>root.attr("data-course-id"),getAddFavouriteMenuItem=(root,courseId)=>root.find('[data-action="add-favourite"][data-course-id="'+courseId+'"]'),getRemoveFavouriteMenuItem=(root,courseId)=>root.find('[data-action="remove-favourite"][data-course-id="'+courseId+'"]'),addToFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!0).then((success=>{success?(PubSub.publish(CourseEvents.favourited,courseId),removeAction.removeClass("hidden"),addAction.addClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.removeClass("hidden"),Aria.unhide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.addClass("hidden"),Aria.hide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},removeFromFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!1).then((success=>{success?(PubSub.publish(CourseEvents.unfavorited,courseId),removeAction.addClass("hidden"),addAction.removeClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.addClass("hidden"),Aria.hide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.removeClass("hidden"),Aria.unhide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},getHideCourseMenuItem=(root,courseId)=>root.find('[data-action="hide-course"][data-course-id="'+courseId+'"]'),getShowCourseMenuItem=(root,courseId)=>root.find('[data-action="show-course"][data-course-id="'+courseId+'"]'),setCourseHiddenState=(courseId,status)=>(!1===status&&(status=null),(0,_repository2.setUserPreference)("block_myoverview_hidden_course_".concat(courseId),status).catch(Notification.exception)),hideElement=(root,id)=>{const pagingBar=root.find('[data-region="paging-bar"]'),jumpto=parseInt(pagingBar.attr("data-active-page-number"));let reducedCourse=loadedPages[jumpto].courses.reduce(((accumulator,current)=>(+id!=+current.id&&accumulator.push(current),accumulator)),[]);if(void 0!==loadedPages[jumpto+1]){const newElement=loadedPages[jumpto+1].courses.slice(0,1);loadedPages.forEach(((courseList,index)=>{if(index>jumpto){let popElement=[];void 0!==loadedPages[index+1]&&(popElement=loadedPages[index+1].courses.slice(0,1)),loadedPages[index].courses=[...loadedPages[index].courses.slice(1),...popElement]}})),reducedCourse=[...reducedCourse,...newElement]}if(lastPage===jumpto+1&&0===loadedPages[jumpto+1].courses.length){const pagedContentContainer=root.find('[data-region="paged-content-container"]');PagedContentFactory.resetLastPageNumber((0,_jquery.default)(pagedContentContainer).attr("id"),jumpto)}loadedPages[jumpto].courses=reducedCourse,courseOffset--;const pagedContentPage=getPagedContentContainer(root,jumpto);renderCourses(root,loadedPages[jumpto]).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception),loadedPages.forEach(((courseList,index)=>{if(index>jumpto){getPagedContentContainer(root,index).remove()}}))},setCourseFavouriteState=(courseId,status)=>Repository.setFavouriteCourses({courses:[{id:courseId,favourite:status}]}).then((result=>0===result.warnings.length&&(loadedPages.forEach((courseList=>{courseList.courses.forEach(((course,index)=>{course.id==courseId&&(courseList.courses[index].isfavourite=status)}))})),!0))).catch(Notification.exception),noCoursesRender=root=>{const nocoursesimg=root.find(_selectors.default.courseView.region).attr("data-nocoursesimg"),newcourseurl=root.find(_selectors.default.courseView.region).attr("data-newcourseurl");return Templates.render(TEMPLATES_NOCOURSES,{nocoursesimg:nocoursesimg,newcourseurl:newcourseurl})},renderCourses=(root,coursesData)=>{const filters=getFilterValues(root);let currentTemplate="";return currentTemplate="card"===filters.display?TEMPLATES_COURSES_CARDS:"list"===filters.display?TEMPLATES_COURSES_LIST:TEMPLATES_COURSES_SUMMARY,coursesData?(!1===Array.isArray(coursesData.courses)&&(coursesData.courses=Object.values(coursesData.courses)),coursesData.courses=coursesData.courses.map((course=>(course.showcoursecategory="on"===filters.displaycategories,course))),coursesData.courses.length?Templates.render(currentTemplate,{courses:coursesData.courses}):noCoursesRender(root)):noCoursesRender(root)},registerPagedEventHandlers=(root,namespace)=>{const event=namespace+PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;PubSub.subscribe(event,(root=>limit=>root.find(_selectors.default.courseView.region).attr("data-paging",limit))(root))},itemsPerPageFunc=(pagingLimit,root)=>{let itemsPerPage=NUMCOURSES_PERPAGE.map((value=>{let active=!1;return value===pagingLimit&&(active=!0),{value:value,active:active}}));const totalCourseCount=parseInt(root.find(_selectors.default.courseView.region).attr("data-totalcoursecount"),10);return itemsPerPage.filter((pagingOption=>pagingOption.value4&&void 0!==arguments[4]?arguments[4]:null,courses=coursesData.courses?coursesData.courses:coursesData,nextPageStart=0,pageCourses=[];if(void 0!==loadedPages[currentPage]){pageCourses=loadedPages[currentPage].courses;const currentPageLength=pageCourses.length;currentPageLength0?courses.slice(0,pageData.limit):courses;loadedPages[currentPage]={courses:pageCourses};const remainingCourses=!1!==nextPageStart?courses.slice(nextPageStart,courses.length):[];remainingCourses.length&&(loadedPages[currentPage+1]={courses:remainingCourses}),loadedPages[currentPage].courses.length{courseOffset=0,loadedPages=[],lastPage=0,lastLimit=0},standardFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit)=>{const pagePromise=((filters,limit)=>{const params={offset:courseOffset,limit:limit,classification:filters.grouping,sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue};return"summary"===filters.display?params.requiredfields=Repository.SUMMARY_REQUIRED_FIELDS:params.requiredfields=Repository.CARDLIST_REQUIRED_FIELDS,Repository.getEnrolledCoursesByTimeline(params)})(filters,limit).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(pagePromise)}),searchFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit,inputValue)=>{const searchingPromise=((filters,limit,searchValue)=>{const params={offset:courseOffset,limit:limit,classification:"search",sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue,searchvalue:searchValue};return"summary"===filters.display?params.requiredfields=Repository.SUMMARY_REQUIRED_FIELDS:params.requiredfields=Repository.CARDLIST_REQUIRED_FIELDS,Repository.getEnrolledCoursesByTimeline(params)})(filters,limit,inputValue).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(searchingPromise)}),initializePagedContent=function(root,promiseFunction){let inputValue=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const pagingLimit=parseInt(root.find(_selectors.default.courseView.region).attr("data-paging"),10);let itemsPerPage=itemsPerPageFunc(pagingLimit,root);const filters=getFilterValues(root),config={...DEFAULT_PAGED_CONTENT_CONFIG};config.eventNamespace=namespace;const pagedContentPromise=PagedContentFactory.createWithLimit(itemsPerPage,((pagesData,actions)=>{let promises=[];return pagesData.forEach((pageData=>{const currentPage=pageData.pageNumber;let limit=pageData.limit>0?pageData.limit:0;if(+lastLimit!=+limit&&(loadedPages=[],courseOffset=0,lastPage=0),lastPage===currentPage)return actions.allItemsLoaded(lastPage),void promises.push(renderCourses(root,loadedPages[currentPage]));lastLimit=limit,void 0===loadedPages[currentPage+1]&&void 0===loadedPages[currentPage]&&(limit*=2),promiseFunction(filters,currentPage,pageData,actions,root,promises,limit,inputValue)})),promises}),config);pagedContentPromise.then(((html,js)=>(registerPagedEventHandlers(root,namespace),Templates.replaceNodeContents(root.find(_selectors.default.courseView.region),html,js)))).catch(Notification.exception)},registerEventListeners=(root,page)=>{CustomEvents.define(root,[CustomEvents.events.activate]),root.on(CustomEvents.events.activate,_selectors.default.ACTION_ADD_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_ADD_FAVOURITE),courseId=getCourseId(favourite);addToFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_REMOVE_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_REMOVE_FAVOURITE),courseId=getCourseId(favourite);removeFromFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.FAVOURITE_ICON,((e,data)=>{data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_HIDE_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_HIDE_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,!0),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.addClass("hidden"),showAction.removeClass("hidden")})(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_SHOW_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_SHOW_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,null),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.removeClass("hidden"),showAction.addClass("hidden")})(root,courseId),data.originalEvent.preventDefault()}));const input=page.querySelector(_selectors.default.region.searchInput),clearIcon=page.querySelector(_selectors.default.region.clearIcon);clearIcon.addEventListener("click",(()=>{input.value="",input.focus(),clearSearch(clearIcon,root)})),input.addEventListener("input",(0,_utils.debounce)((()=>{""===input.value?clearSearch(clearIcon,root):(activeSearch(clearIcon),initializePagedContent(root,searchFunctionalityCurry(),input.value.trim()))}),1e3))},clearSearch=(clearIcon,root)=>{clearIcon.classList.add("d-none"),init(root)};_exports.clearSearch=clearSearch;const activeSearch=clearIcon=>{clearIcon.classList.remove("d-none")},init=root=>{if(root=(0,_jquery.default)(root),loadedPages=[],lastPage=0,courseOffset=0,!root.attr("data-init")){const page=document.querySelector(_selectors.default.region.selectBlock);registerEventListeners(root,page),namespace="block_myoverview_"+root.attr("id")+"_"+Math.random(),root.attr("data-init",!0)}initializePagedContent(root,standardFunctionalityCurry())};_exports.init=init;_exports.reset=root=>{loadedPages.length>0?loadedPages.forEach(((courseList,index)=>{let pagedContentPage=getPagedContentContainer(root,index);renderCourses(root,courseList).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception)})):init(root)}})); //# sourceMappingURL=view.min.js.map \ No newline at end of file diff --git a/blocks/myoverview/amd/build/view.min.js.map b/blocks/myoverview/amd/build/view.min.js.map index 589db6fa12e..db03dafa406 100644 --- a/blocks/myoverview/amd/build/view.min.js.map +++ b/blocks/myoverview/amd/build/view.min.js.map @@ -1 +1 @@ -{"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 courses view for the overview block.\n *\n * @copyright 2018 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as Repository from 'block_myoverview/repository';\nimport * as PagedContentFactory from 'core/paged_content_factory';\nimport * as PubSub from 'core/pubsub';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as Notification from 'core/notification';\nimport * as Templates from 'core/templates';\nimport * as CourseEvents from 'core_course/events';\nimport SELECTORS from 'block_myoverview/selectors';\nimport * as PagedContentEvents from 'core/paged_content_events';\nimport * as Aria from 'core/aria';\nimport {debounce} from 'core/utils';\nimport {setUserPreference} from 'core_user/repository';\n\nconst TEMPLATES = {\n COURSES_CARDS: 'block_myoverview/view-cards',\n COURSES_LIST: 'block_myoverview/view-list',\n COURSES_SUMMARY: 'block_myoverview/view-summary',\n NOCOURSES: 'core_course/no-courses'\n};\n\nconst GROUPINGS = {\n GROUPING_ALLINCLUDINGHIDDEN: 'allincludinghidden',\n GROUPING_ALL: 'all',\n GROUPING_INPROGRESS: 'inprogress',\n GROUPING_FUTURE: 'future',\n GROUPING_PAST: 'past',\n GROUPING_FAVOURITES: 'favourites',\n GROUPING_HIDDEN: 'hidden'\n};\n\nconst NUMCOURSES_PERPAGE = [12, 24, 48, 96, 0];\n\nlet loadedPages = [];\n\nlet courseOffset = 0;\n\nlet lastPage = 0;\n\nlet lastLimit = 0;\n\nlet namespace = null;\n\n/**\n * Get filter values from DOM.\n *\n * @param {object} root The root element for the courses view.\n * @return {filters} Set filters.\n */\nconst getFilterValues = root => {\n const courseRegion = root.find(SELECTORS.courseView.region);\n return {\n display: courseRegion.attr('data-display'),\n grouping: courseRegion.attr('data-grouping'),\n sort: courseRegion.attr('data-sort'),\n displaycategories: courseRegion.attr('data-displaycategories'),\n customfieldname: courseRegion.attr('data-customfieldname'),\n customfieldvalue: courseRegion.attr('data-customfieldvalue'),\n };\n};\n\n// We want the paged content controls below the paged content area.\n// and the controls should be ignored while data is loading.\nconst DEFAULT_PAGED_CONTENT_CONFIG = {\n ignoreControlWhileLoading: true,\n controlPlacementBottom: true,\n persistentLimitKey: 'block_myoverview_user_paging_preference'\n};\n\n/**\n * Get enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @return {promise} Resolved with an array of courses.\n */\nconst getMyCourses = (filters, limit) => {\n return Repository.getEnrolledCoursesByTimeline({\n offset: courseOffset,\n limit: limit,\n classification: filters.grouping,\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue\n });\n};\n\n/**\n * Search for enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @param {string} searchValue What does the user want to search within their courses.\n * @return {promise} Resolved with an array of courses.\n */\nconst getSearchMyCourses = (filters, limit, searchValue) => {\n return Repository.getEnrolledCoursesByTimeline({\n offset: courseOffset,\n limit: limit,\n classification: 'search',\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n searchvalue: searchValue\n });\n};\n\n/**\n * Get the container element for the favourite icon.\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n * @return {Object} The favourite icon container\n */\nconst getFavouriteIconContainer = (root, courseId) => {\n return root.find(SELECTORS.FAVOURITE_ICON + '[data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the paged content container element.\n *\n * @param {Object} root The course overview container\n * @param {Number} index Rendered page index.\n * @return {Object} The rendered paged container.\n */\nconst getPagedContentContainer = (root, index) => {\n return root.find('[data-region=\"paged-content-page\"][data-page=\"' + index + '\"]');\n};\n\n/**\n * Get the course id from a favourite element.\n *\n * @param {Object} root The favourite icon container element.\n * @return {Number} Course id.\n */\nconst getCourseId = root => {\n return root.attr('data-course-id');\n};\n\n/**\n * Hide the favourite icon.\n *\n * @param {Object} root The favourite icon container element.\n * @param {Number} courseId Course id number.\n */\nconst hideFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.addClass('hidden');\n Aria.hide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.removeClass('hidden');\n Aria.unhide(notFavourteIcon);\n};\n\n/**\n * Show the favourite icon.\n *\n * @param {Object} root The course overview container.\n * @param {Number} courseId Course id number.\n */\nconst showFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.removeClass('hidden');\n Aria.unhide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.addClass('hidden');\n Aria.hide(notFavourteIcon);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The add to favourite menu item.\n */\nconst getAddFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"add-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The remove from favourites menu item.\n */\nconst getRemoveFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"remove-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Add course to favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst addToFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, true).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.favourited, courseId);\n removeAction.removeClass('hidden');\n addAction.addClass('hidden');\n showFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Remove course from favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst removeFromFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, false).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.unfavorited, courseId);\n removeAction.addClass('hidden');\n addAction.removeClass('hidden');\n hideFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The hide course menu item.\n */\nconst getHideCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"hide-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The show course menu item.\n */\nconst getShowCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"show-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Hide course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst hideCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, true);\n\n // Remove the course from this view as it is now hidden and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.addClass('hidden');\n showAction.removeClass('hidden');\n};\n\n/**\n * Show course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst showCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, null);\n\n // Remove the course from this view as it is now shown again and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.removeClass('hidden');\n showAction.addClass('hidden');\n};\n\n/**\n * Set the courses hidden status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {Boolean} status new hidden status.\n * @return {Promise} Repository promise.\n */\nconst setCourseHiddenState = (courseId, status) => {\n\n // If the given status is not hidden, the preference has to be deleted with a null value.\n if (status === false) {\n status = null;\n }\n\n return setUserPreference(`block_myoverview_hidden_course_${courseId}`, status)\n .catch(Notification.exception);\n};\n\n/**\n * Reset the loadedPages dataset to take into account the hidden element\n *\n * @param {Object} root The course overview container\n * @param {Number} id The course id number\n */\nconst hideElement = (root, id) => {\n const pagingBar = root.find('[data-region=\"paging-bar\"]');\n const jumpto = parseInt(pagingBar.attr('data-active-page-number'));\n\n // Get a reduced dataset for the current page.\n const courseList = loadedPages[jumpto];\n let reducedCourse = courseList.courses.reduce((accumulator, current) => {\n if (+id !== +current.id) {\n accumulator.push(current);\n }\n return accumulator;\n }, []);\n\n // Get the next page's data if loaded and pop the first element from it.\n if (typeof (loadedPages[jumpto + 1]) !== 'undefined') {\n const newElement = loadedPages[jumpto + 1].courses.slice(0, 1);\n\n // Adjust the dataset for the reset of the pages that are loaded.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n let popElement = [];\n if (typeof (loadedPages[index + 1]) !== 'undefined') {\n popElement = loadedPages[index + 1].courses.slice(0, 1);\n }\n loadedPages[index].courses = [...loadedPages[index].courses.slice(1), ...popElement];\n }\n });\n\n reducedCourse = [...reducedCourse, ...newElement];\n }\n\n // Check if the next page is the last page and if it still has data associated to it.\n if (lastPage === jumpto + 1 && loadedPages[jumpto + 1].courses.length === 0) {\n const pagedContentContainer = root.find('[data-region=\"paged-content-container\"]');\n PagedContentFactory.resetLastPageNumber($(pagedContentContainer).attr('id'), jumpto);\n }\n\n loadedPages[jumpto].courses = reducedCourse;\n\n // Reduce the course offset.\n courseOffset--;\n\n // Render the paged content for the current.\n const pagedContentPage = getPagedContentContainer(root, jumpto);\n renderCourses(root, loadedPages[jumpto]).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n\n // Delete subsequent pages in order to trigger the callback.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n const page = getPagedContentContainer(root, index);\n page.remove();\n }\n });\n};\n\n/**\n * Set the courses favourite status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {boolean} status new favourite status.\n * @return {Promise} Repository promise.\n */\nconst setCourseFavouriteState = (courseId, status) => {\n\n return Repository.setFavouriteCourses({\n courses: [\n {\n 'id': courseId,\n 'favourite': status\n }\n ]\n }).then(result => {\n if (result.warnings.length === 0) {\n loadedPages.forEach(courseList => {\n courseList.courses.forEach((course, index) => {\n if (course.id == courseId) {\n courseList.courses[index].isfavourite = status;\n }\n });\n });\n return true;\n } else {\n return false;\n }\n }).catch(Notification.exception);\n};\n\n/**\n * Given there are no courses to render provide the rendered template.\n *\n * @param {object} root The root element for the courses view.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst noCoursesRender = root => {\n const nocoursesimg = root.find(SELECTORS.courseView.region).attr('data-nocoursesimg');\n const newcourseurl = root.find(SELECTORS.courseView.region).attr('data-newcourseurl');\n return Templates.render(TEMPLATES.NOCOURSES, {\n nocoursesimg: nocoursesimg,\n newcourseurl: newcourseurl\n });\n};\n\n/**\n * Render the dashboard courses.\n *\n * @param {object} root The root element for the courses view.\n * @param {array} coursesData containing array of returned courses.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst renderCourses = (root, coursesData) => {\n\n const filters = getFilterValues(root);\n\n let currentTemplate = '';\n if (filters.display === 'card') {\n currentTemplate = TEMPLATES.COURSES_CARDS;\n } else if (filters.display === 'list') {\n currentTemplate = TEMPLATES.COURSES_LIST;\n } else {\n currentTemplate = TEMPLATES.COURSES_SUMMARY;\n }\n\n if (!coursesData) {\n return noCoursesRender(root);\n } else {\n // Sometimes we get weird objects coming after a failed search, cast to ensure typing functions.\n if (Array.isArray(coursesData.courses) === false) {\n coursesData.courses = Object.values(coursesData.courses);\n }\n // Whether the course category should be displayed in the course item.\n coursesData.courses = coursesData.courses.map(course => {\n course.showcoursecategory = filters.displaycategories === 'on';\n return course;\n });\n if (coursesData.courses.length) {\n return Templates.render(currentTemplate, {\n courses: coursesData.courses,\n });\n } else {\n return noCoursesRender(root);\n }\n }\n};\n\n/**\n * Return the callback to be passed to the subscribe event\n *\n * @param {object} root The root element for the courses view\n * @return {function} Partially applied function that'll execute when passed a limit\n */\nconst setLimit = root => {\n // @param {Number} limit The paged limit that is passed through the event.\n return limit => root.find(SELECTORS.courseView.region).attr('data-paging', limit);\n};\n\n/**\n * Intialise the paged list and cards views on page load.\n * Returns an array of paged contents that we would like to handle here\n *\n * @param {object} root The root element for the courses view\n * @param {string} namespace The namespace for all the events attached\n */\nconst registerPagedEventHandlers = (root, namespace) => {\n const event = namespace + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;\n PubSub.subscribe(event, setLimit(root));\n};\n\n/**\n * Figure out how many items are going to be allowed to be rendered in the block.\n *\n * @param {Number} pagingLimit How many courses to display\n * @param {Object} root The course overview container\n * @return {Number[]} How many courses will be rendered\n */\nconst itemsPerPageFunc = (pagingLimit, root) => {\n let itemsPerPage = NUMCOURSES_PERPAGE.map(value => {\n let active = false;\n if (value === pagingLimit) {\n active = true;\n }\n\n return {\n value: value,\n active: active\n };\n });\n\n // Filter out all pagination options which are too large for the amount of courses user is enrolled in.\n const totalCourseCount = parseInt(root.find(SELECTORS.courseView.region).attr('data-totalcoursecount'), 10);\n return itemsPerPage.filter(pagingOption => {\n return pagingOption.value < totalCourseCount || pagingOption.value === 0;\n });\n};\n\n/**\n * Mutates and controls the loadedPages array and handles the bootstrapping.\n *\n * @param {Array|Object} coursesData Array of all of the courses to start building the page from\n * @param {Number} currentPage What page are we currently on?\n * @param {Object} pageData Any current page information\n * @param {Object} actions Paged content helper\n * @param {null|boolean} activeSearch Are we currently actively searching and building up search results?\n */\nconst pageBuilder = (coursesData, currentPage, pageData, actions, activeSearch = null) => {\n // If the courseData comes in an object then get the value otherwise it is a pure array.\n let courses = coursesData.courses ? coursesData.courses : coursesData;\n let nextPageStart = 0;\n let pageCourses = [];\n\n // If current page's data is loaded make sure we max it to page limit.\n if (typeof (loadedPages[currentPage]) !== 'undefined') {\n pageCourses = loadedPages[currentPage].courses;\n const currentPageLength = pageCourses.length;\n if (currentPageLength < pageData.limit) {\n nextPageStart = pageData.limit - currentPageLength;\n pageCourses = {...loadedPages[currentPage].courses, ...courses.slice(0, nextPageStart)};\n }\n } else {\n // When the page limit is zero, there is only one page of courses, no start for next page.\n nextPageStart = pageData.limit || false;\n pageCourses = (pageData.limit > 0) ? courses.slice(0, pageData.limit) : courses;\n }\n\n // Finished setting up the current page.\n loadedPages[currentPage] = {\n courses: pageCourses\n };\n\n // Set up the next page (if there is more than one page).\n const remainingCourses = nextPageStart !== false ? courses.slice(nextPageStart, courses.length) : [];\n if (remainingCourses.length) {\n loadedPages[currentPage + 1] = {\n courses: remainingCourses\n };\n }\n\n // Set the last page to either the current or next page.\n if (loadedPages[currentPage].courses.length < pageData.limit || !remainingCourses.length) {\n lastPage = currentPage;\n if (activeSearch === null) {\n actions.allItemsLoaded(currentPage);\n }\n } else if (typeof (loadedPages[currentPage + 1]) !== 'undefined'\n && loadedPages[currentPage + 1].courses.length < pageData.limit) {\n lastPage = currentPage + 1;\n }\n\n courseOffset = coursesData.nextoffset;\n};\n\n/**\n * In cases when switching between regular rendering and search rendering we need to reset some variables.\n */\nconst resetGlobals = () => {\n courseOffset = 0;\n loadedPages = [];\n lastPage = 0;\n lastLimit = 0;\n};\n\n/**\n * The default functionality of fetching paginated courses without special handling.\n *\n * @return {function(Object, Object, Object, Object, Object, Promise, Number): void}\n */\nconst standardFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit) => {\n const pagePromise = getMyCourses(\n filters,\n limit\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(pagePromise);\n };\n};\n\n/**\n * Initialize the searching functionality so we can call it when required.\n *\n * @return {function(Object, Number, Object, Object, Object, Promise, Number, String): void}\n */\nconst searchFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit, inputValue) => {\n const searchingPromise = getSearchMyCourses(\n filters,\n limit,\n inputValue\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(searchingPromise);\n };\n};\n\n/**\n * Initialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n * @param {function} promiseFunction How do we fetch the courses and what do we do with them?\n * @param {null | string} inputValue What to search for\n */\nconst initializePagedContent = (root, promiseFunction, inputValue = null) => {\n const pagingLimit = parseInt(root.find(SELECTORS.courseView.region).attr('data-paging'), 10);\n let itemsPerPage = itemsPerPageFunc(pagingLimit, root);\n\n const filters = getFilterValues(root);\n const config = {...{}, ...DEFAULT_PAGED_CONTENT_CONFIG};\n config.eventNamespace = namespace;\n\n const pagedContentPromise = PagedContentFactory.createWithLimit(\n itemsPerPage,\n (pagesData, actions) => {\n let promises = [];\n pagesData.forEach(pageData => {\n const currentPage = pageData.pageNumber;\n let limit = (pageData.limit > 0) ? pageData.limit : 0;\n\n // Reset local variables if limits have changed.\n if (+lastLimit !== +limit) {\n loadedPages = [];\n courseOffset = 0;\n lastPage = 0;\n }\n\n if (lastPage === currentPage) {\n // If we are on the last page and have it's data then load it from cache.\n actions.allItemsLoaded(lastPage);\n promises.push(renderCourses(root, loadedPages[currentPage]));\n return;\n }\n\n lastLimit = limit;\n\n // Get 2 pages worth of data as we will need it for the hidden functionality.\n if (typeof (loadedPages[currentPage + 1]) === 'undefined') {\n if (typeof (loadedPages[currentPage]) === 'undefined') {\n limit *= 2;\n }\n }\n\n // Call the curried function that'll handle the course promise and any manipulation of it.\n promiseFunction(filters, currentPage, pageData, actions, root, promises, limit, inputValue);\n });\n return promises;\n },\n config\n );\n\n pagedContentPromise.then((html, js) => {\n registerPagedEventHandlers(root, namespace);\n return Templates.replaceNodeContents(root.find(SELECTORS.courseView.region), html, js);\n }).catch(Notification.exception);\n};\n\n/**\n * Listen to, and handle events for the myoverview block.\n *\n * @param {Object} root The myoverview block container element.\n * @param {HTMLElement} page The whole HTMLElement for our block.\n */\nconst registerEventListeners = (root, page) => {\n\n CustomEvents.define(root, [\n CustomEvents.events.activate\n ]);\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_ADD_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_ADD_FAVOURITE);\n const courseId = getCourseId(favourite);\n addToFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_REMOVE_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_REMOVE_FAVOURITE);\n const courseId = getCourseId(favourite);\n removeFromFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.FAVOURITE_ICON, (e, data) => {\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_HIDE_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_HIDE_COURSE);\n const courseId = getCourseId(target);\n hideCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_SHOW_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_SHOW_COURSE);\n const courseId = getCourseId(target);\n showCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n // Searching functionality event handlers.\n const input = page.querySelector(SELECTORS.region.searchInput);\n const clearIcon = page.querySelector(SELECTORS.region.clearIcon);\n\n clearIcon.addEventListener('click', () => {\n input.value = '';\n input.focus();\n clearSearch(clearIcon, root);\n });\n\n input.addEventListener('input', debounce(() => {\n if (input.value === '') {\n clearSearch(clearIcon, root);\n } else {\n activeSearch(clearIcon);\n initializePagedContent(root, searchFunctionalityCurry(), input.value.trim());\n }\n }, 1000));\n};\n\n/**\n * Reset the search icon and trigger the init for the block.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n * @param {Object} root The myoverview block container element.\n */\nexport const clearSearch = (clearIcon, root) => {\n clearIcon.classList.add('d-none');\n init(root);\n};\n\n/**\n * Change the searching icon to its' active state.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n */\nconst activeSearch = (clearIcon) => {\n clearIcon.classList.remove('d-none');\n};\n\n/**\n * Intialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n */\nexport const init = root => {\n root = $(root);\n loadedPages = [];\n lastPage = 0;\n courseOffset = 0;\n\n if (!root.attr('data-init')) {\n const page = document.querySelector(SELECTORS.region.selectBlock);\n registerEventListeners(root, page);\n namespace = \"block_myoverview_\" + root.attr('id') + \"_\" + Math.random();\n root.attr('data-init', true);\n }\n\n initializePagedContent(root, standardFunctionalityCurry());\n};\n\n/**\n * Reset the courses views to their original\n * state on first page load.courseOffset\n *\n * This is called when configuration has changed for the event lists\n * to cause them to reload their data.\n *\n * @param {Object} root The root element for the timeline view.\n */\nexport const reset = root => {\n if (loadedPages.length > 0) {\n loadedPages.forEach((courseList, index) => {\n let pagedContentPage = getPagedContentContainer(root, index);\n renderCourses(root, courseList).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n });\n } else {\n init(root);\n }\n};\n"],"names":["TEMPLATES","GROUPINGS","NUMCOURSES_PERPAGE","loadedPages","courseOffset","lastPage","lastLimit","namespace","getFilterValues","root","courseRegion","find","SELECTORS","courseView","region","display","attr","grouping","sort","displaycategories","customfieldname","customfieldvalue","DEFAULT_PAGED_CONTENT_CONFIG","ignoreControlWhileLoading","controlPlacementBottom","persistentLimitKey","getFavouriteIconContainer","courseId","FAVOURITE_ICON","getPagedContentContainer","index","getCourseId","getAddFavouriteMenuItem","getRemoveFavouriteMenuItem","addToFavourites","removeAction","addAction","setCourseFavouriteState","then","success","PubSub","publish","CourseEvents","favourited","removeClass","addClass","iconContainer","isFavouriteIcon","ICON_IS_FAVOURITE","Aria","unhide","notFavourteIcon","ICON_NOT_FAVOURITE","hide","showFavouriteIcon","Notification","alert","catch","exception","removeFromFavourites","unfavorited","hideFavouriteIcon","getHideCourseMenuItem","getShowCourseMenuItem","setCourseHiddenState","status","hideElement","id","pagingBar","jumpto","parseInt","reducedCourse","courses","reduce","accumulator","current","push","newElement","slice","forEach","courseList","popElement","length","pagedContentContainer","PagedContentFactory","resetLastPageNumber","pagedContentPage","renderCourses","html","js","Templates","replaceNodeContents","remove","Repository","setFavouriteCourses","result","warnings","course","isfavourite","noCoursesRender","nocoursesimg","newcourseurl","render","coursesData","filters","currentTemplate","Array","isArray","Object","values","map","showcoursecategory","registerPagedEventHandlers","event","PagedContentEvents","SET_ITEMS_PER_PAGE_LIMIT","subscribe","limit","setLimit","itemsPerPageFunc","pagingLimit","itemsPerPage","value","active","totalCourseCount","filter","pagingOption","pageBuilder","currentPage","pageData","actions","activeSearch","nextPageStart","pageCourses","currentPageLength","remainingCourses","allItemsLoaded","nextoffset","resetGlobals","standardFunctionalityCurry","promises","pagePromise","getEnrolledCoursesByTimeline","offset","classification","getMyCourses","searchFunctionalityCurry","inputValue","searchingPromise","searchValue","searchvalue","getSearchMyCourses","initializePagedContent","promiseFunction","config","eventNamespace","pagedContentPromise","createWithLimit","pagesData","pageNumber","registerEventListeners","page","CustomEvents","define","events","activate","on","ACTION_ADD_FAVOURITE","e","data","favourite","target","closest","originalEvent","preventDefault","ACTION_REMOVE_FAVOURITE","ACTION_HIDE_COURSE","hideAction","showAction","hideCourse","ACTION_SHOW_COURSE","showCourse","input","querySelector","searchInput","clearIcon","addEventListener","focus","clearSearch","trim","classList","add","init","document","selectBlock","Math","random"],"mappings":"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"} \ No newline at end of file +{"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 courses view for the overview block.\n *\n * @copyright 2018 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as Repository from 'block_myoverview/repository';\nimport * as PagedContentFactory from 'core/paged_content_factory';\nimport * as PubSub from 'core/pubsub';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as Notification from 'core/notification';\nimport * as Templates from 'core/templates';\nimport * as CourseEvents from 'core_course/events';\nimport SELECTORS from 'block_myoverview/selectors';\nimport * as PagedContentEvents from 'core/paged_content_events';\nimport * as Aria from 'core/aria';\nimport {debounce} from 'core/utils';\nimport {setUserPreference} from 'core_user/repository';\n\nconst TEMPLATES = {\n COURSES_CARDS: 'block_myoverview/view-cards',\n COURSES_LIST: 'block_myoverview/view-list',\n COURSES_SUMMARY: 'block_myoverview/view-summary',\n NOCOURSES: 'core_course/no-courses'\n};\n\nconst GROUPINGS = {\n GROUPING_ALLINCLUDINGHIDDEN: 'allincludinghidden',\n GROUPING_ALL: 'all',\n GROUPING_INPROGRESS: 'inprogress',\n GROUPING_FUTURE: 'future',\n GROUPING_PAST: 'past',\n GROUPING_FAVOURITES: 'favourites',\n GROUPING_HIDDEN: 'hidden'\n};\n\nconst NUMCOURSES_PERPAGE = [12, 24, 48, 96, 0];\n\nlet loadedPages = [];\n\nlet courseOffset = 0;\n\nlet lastPage = 0;\n\nlet lastLimit = 0;\n\nlet namespace = null;\n\n/**\n * Get filter values from DOM.\n *\n * @param {object} root The root element for the courses view.\n * @return {filters} Set filters.\n */\nconst getFilterValues = root => {\n const courseRegion = root.find(SELECTORS.courseView.region);\n return {\n display: courseRegion.attr('data-display'),\n grouping: courseRegion.attr('data-grouping'),\n sort: courseRegion.attr('data-sort'),\n displaycategories: courseRegion.attr('data-displaycategories'),\n customfieldname: courseRegion.attr('data-customfieldname'),\n customfieldvalue: courseRegion.attr('data-customfieldvalue'),\n };\n};\n\n// We want the paged content controls below the paged content area.\n// and the controls should be ignored while data is loading.\nconst DEFAULT_PAGED_CONTENT_CONFIG = {\n ignoreControlWhileLoading: true,\n controlPlacementBottom: true,\n persistentLimitKey: 'block_myoverview_user_paging_preference'\n};\n\n/**\n * Get enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @return {promise} Resolved with an array of courses.\n */\nconst getMyCourses = (filters, limit) => {\n const params = {\n offset: courseOffset,\n limit: limit,\n classification: filters.grouping,\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n };\n if (filters.display === 'summary') {\n params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS;\n } else {\n params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS;\n }\n return Repository.getEnrolledCoursesByTimeline(params);\n};\n\n/**\n * Search for enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @param {string} searchValue What does the user want to search within their courses.\n * @return {promise} Resolved with an array of courses.\n */\nconst getSearchMyCourses = (filters, limit, searchValue) => {\n const params = {\n offset: courseOffset,\n limit: limit,\n classification: 'search',\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n searchvalue: searchValue,\n };\n if (filters.display === 'summary') {\n params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS;\n } else {\n params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS;\n }\n return Repository.getEnrolledCoursesByTimeline(params);\n};\n\n/**\n * Get the container element for the favourite icon.\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n * @return {Object} The favourite icon container\n */\nconst getFavouriteIconContainer = (root, courseId) => {\n return root.find(SELECTORS.FAVOURITE_ICON + '[data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the paged content container element.\n *\n * @param {Object} root The course overview container\n * @param {Number} index Rendered page index.\n * @return {Object} The rendered paged container.\n */\nconst getPagedContentContainer = (root, index) => {\n return root.find('[data-region=\"paged-content-page\"][data-page=\"' + index + '\"]');\n};\n\n/**\n * Get the course id from a favourite element.\n *\n * @param {Object} root The favourite icon container element.\n * @return {Number} Course id.\n */\nconst getCourseId = root => {\n return root.attr('data-course-id');\n};\n\n/**\n * Hide the favourite icon.\n *\n * @param {Object} root The favourite icon container element.\n * @param {Number} courseId Course id number.\n */\nconst hideFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.addClass('hidden');\n Aria.hide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.removeClass('hidden');\n Aria.unhide(notFavourteIcon);\n};\n\n/**\n * Show the favourite icon.\n *\n * @param {Object} root The course overview container.\n * @param {Number} courseId Course id number.\n */\nconst showFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.removeClass('hidden');\n Aria.unhide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.addClass('hidden');\n Aria.hide(notFavourteIcon);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The add to favourite menu item.\n */\nconst getAddFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"add-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The remove from favourites menu item.\n */\nconst getRemoveFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"remove-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Add course to favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst addToFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, true).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.favourited, courseId);\n removeAction.removeClass('hidden');\n addAction.addClass('hidden');\n showFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Remove course from favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst removeFromFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, false).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.unfavorited, courseId);\n removeAction.addClass('hidden');\n addAction.removeClass('hidden');\n hideFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The hide course menu item.\n */\nconst getHideCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"hide-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The show course menu item.\n */\nconst getShowCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"show-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Hide course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst hideCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, true);\n\n // Remove the course from this view as it is now hidden and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.addClass('hidden');\n showAction.removeClass('hidden');\n};\n\n/**\n * Show course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst showCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, null);\n\n // Remove the course from this view as it is now shown again and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.removeClass('hidden');\n showAction.addClass('hidden');\n};\n\n/**\n * Set the courses hidden status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {Boolean} status new hidden status.\n * @return {Promise} Repository promise.\n */\nconst setCourseHiddenState = (courseId, status) => {\n\n // If the given status is not hidden, the preference has to be deleted with a null value.\n if (status === false) {\n status = null;\n }\n\n return setUserPreference(`block_myoverview_hidden_course_${courseId}`, status)\n .catch(Notification.exception);\n};\n\n/**\n * Reset the loadedPages dataset to take into account the hidden element\n *\n * @param {Object} root The course overview container\n * @param {Number} id The course id number\n */\nconst hideElement = (root, id) => {\n const pagingBar = root.find('[data-region=\"paging-bar\"]');\n const jumpto = parseInt(pagingBar.attr('data-active-page-number'));\n\n // Get a reduced dataset for the current page.\n const courseList = loadedPages[jumpto];\n let reducedCourse = courseList.courses.reduce((accumulator, current) => {\n if (+id !== +current.id) {\n accumulator.push(current);\n }\n return accumulator;\n }, []);\n\n // Get the next page's data if loaded and pop the first element from it.\n if (typeof (loadedPages[jumpto + 1]) !== 'undefined') {\n const newElement = loadedPages[jumpto + 1].courses.slice(0, 1);\n\n // Adjust the dataset for the reset of the pages that are loaded.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n let popElement = [];\n if (typeof (loadedPages[index + 1]) !== 'undefined') {\n popElement = loadedPages[index + 1].courses.slice(0, 1);\n }\n loadedPages[index].courses = [...loadedPages[index].courses.slice(1), ...popElement];\n }\n });\n\n reducedCourse = [...reducedCourse, ...newElement];\n }\n\n // Check if the next page is the last page and if it still has data associated to it.\n if (lastPage === jumpto + 1 && loadedPages[jumpto + 1].courses.length === 0) {\n const pagedContentContainer = root.find('[data-region=\"paged-content-container\"]');\n PagedContentFactory.resetLastPageNumber($(pagedContentContainer).attr('id'), jumpto);\n }\n\n loadedPages[jumpto].courses = reducedCourse;\n\n // Reduce the course offset.\n courseOffset--;\n\n // Render the paged content for the current.\n const pagedContentPage = getPagedContentContainer(root, jumpto);\n renderCourses(root, loadedPages[jumpto]).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n\n // Delete subsequent pages in order to trigger the callback.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n const page = getPagedContentContainer(root, index);\n page.remove();\n }\n });\n};\n\n/**\n * Set the courses favourite status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {boolean} status new favourite status.\n * @return {Promise} Repository promise.\n */\nconst setCourseFavouriteState = (courseId, status) => {\n\n return Repository.setFavouriteCourses({\n courses: [\n {\n 'id': courseId,\n 'favourite': status\n }\n ]\n }).then(result => {\n if (result.warnings.length === 0) {\n loadedPages.forEach(courseList => {\n courseList.courses.forEach((course, index) => {\n if (course.id == courseId) {\n courseList.courses[index].isfavourite = status;\n }\n });\n });\n return true;\n } else {\n return false;\n }\n }).catch(Notification.exception);\n};\n\n/**\n * Given there are no courses to render provide the rendered template.\n *\n * @param {object} root The root element for the courses view.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst noCoursesRender = root => {\n const nocoursesimg = root.find(SELECTORS.courseView.region).attr('data-nocoursesimg');\n const newcourseurl = root.find(SELECTORS.courseView.region).attr('data-newcourseurl');\n return Templates.render(TEMPLATES.NOCOURSES, {\n nocoursesimg: nocoursesimg,\n newcourseurl: newcourseurl\n });\n};\n\n/**\n * Render the dashboard courses.\n *\n * @param {object} root The root element for the courses view.\n * @param {array} coursesData containing array of returned courses.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst renderCourses = (root, coursesData) => {\n\n const filters = getFilterValues(root);\n\n let currentTemplate = '';\n if (filters.display === 'card') {\n currentTemplate = TEMPLATES.COURSES_CARDS;\n } else if (filters.display === 'list') {\n currentTemplate = TEMPLATES.COURSES_LIST;\n } else {\n currentTemplate = TEMPLATES.COURSES_SUMMARY;\n }\n\n if (!coursesData) {\n return noCoursesRender(root);\n } else {\n // Sometimes we get weird objects coming after a failed search, cast to ensure typing functions.\n if (Array.isArray(coursesData.courses) === false) {\n coursesData.courses = Object.values(coursesData.courses);\n }\n // Whether the course category should be displayed in the course item.\n coursesData.courses = coursesData.courses.map(course => {\n course.showcoursecategory = filters.displaycategories === 'on';\n return course;\n });\n if (coursesData.courses.length) {\n return Templates.render(currentTemplate, {\n courses: coursesData.courses,\n });\n } else {\n return noCoursesRender(root);\n }\n }\n};\n\n/**\n * Return the callback to be passed to the subscribe event\n *\n * @param {object} root The root element for the courses view\n * @return {function} Partially applied function that'll execute when passed a limit\n */\nconst setLimit = root => {\n // @param {Number} limit The paged limit that is passed through the event.\n return limit => root.find(SELECTORS.courseView.region).attr('data-paging', limit);\n};\n\n/**\n * Intialise the paged list and cards views on page load.\n * Returns an array of paged contents that we would like to handle here\n *\n * @param {object} root The root element for the courses view\n * @param {string} namespace The namespace for all the events attached\n */\nconst registerPagedEventHandlers = (root, namespace) => {\n const event = namespace + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;\n PubSub.subscribe(event, setLimit(root));\n};\n\n/**\n * Figure out how many items are going to be allowed to be rendered in the block.\n *\n * @param {Number} pagingLimit How many courses to display\n * @param {Object} root The course overview container\n * @return {Number[]} How many courses will be rendered\n */\nconst itemsPerPageFunc = (pagingLimit, root) => {\n let itemsPerPage = NUMCOURSES_PERPAGE.map(value => {\n let active = false;\n if (value === pagingLimit) {\n active = true;\n }\n\n return {\n value: value,\n active: active\n };\n });\n\n // Filter out all pagination options which are too large for the amount of courses user is enrolled in.\n const totalCourseCount = parseInt(root.find(SELECTORS.courseView.region).attr('data-totalcoursecount'), 10);\n return itemsPerPage.filter(pagingOption => {\n return pagingOption.value < totalCourseCount || pagingOption.value === 0;\n });\n};\n\n/**\n * Mutates and controls the loadedPages array and handles the bootstrapping.\n *\n * @param {Array|Object} coursesData Array of all of the courses to start building the page from\n * @param {Number} currentPage What page are we currently on?\n * @param {Object} pageData Any current page information\n * @param {Object} actions Paged content helper\n * @param {null|boolean} activeSearch Are we currently actively searching and building up search results?\n */\nconst pageBuilder = (coursesData, currentPage, pageData, actions, activeSearch = null) => {\n // If the courseData comes in an object then get the value otherwise it is a pure array.\n let courses = coursesData.courses ? coursesData.courses : coursesData;\n let nextPageStart = 0;\n let pageCourses = [];\n\n // If current page's data is loaded make sure we max it to page limit.\n if (typeof (loadedPages[currentPage]) !== 'undefined') {\n pageCourses = loadedPages[currentPage].courses;\n const currentPageLength = pageCourses.length;\n if (currentPageLength < pageData.limit) {\n nextPageStart = pageData.limit - currentPageLength;\n pageCourses = {...loadedPages[currentPage].courses, ...courses.slice(0, nextPageStart)};\n }\n } else {\n // When the page limit is zero, there is only one page of courses, no start for next page.\n nextPageStart = pageData.limit || false;\n pageCourses = (pageData.limit > 0) ? courses.slice(0, pageData.limit) : courses;\n }\n\n // Finished setting up the current page.\n loadedPages[currentPage] = {\n courses: pageCourses\n };\n\n // Set up the next page (if there is more than one page).\n const remainingCourses = nextPageStart !== false ? courses.slice(nextPageStart, courses.length) : [];\n if (remainingCourses.length) {\n loadedPages[currentPage + 1] = {\n courses: remainingCourses\n };\n }\n\n // Set the last page to either the current or next page.\n if (loadedPages[currentPage].courses.length < pageData.limit || !remainingCourses.length) {\n lastPage = currentPage;\n if (activeSearch === null) {\n actions.allItemsLoaded(currentPage);\n }\n } else if (typeof (loadedPages[currentPage + 1]) !== 'undefined'\n && loadedPages[currentPage + 1].courses.length < pageData.limit) {\n lastPage = currentPage + 1;\n }\n\n courseOffset = coursesData.nextoffset;\n};\n\n/**\n * In cases when switching between regular rendering and search rendering we need to reset some variables.\n */\nconst resetGlobals = () => {\n courseOffset = 0;\n loadedPages = [];\n lastPage = 0;\n lastLimit = 0;\n};\n\n/**\n * The default functionality of fetching paginated courses without special handling.\n *\n * @return {function(Object, Object, Object, Object, Object, Promise, Number): void}\n */\nconst standardFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit) => {\n const pagePromise = getMyCourses(\n filters,\n limit\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(pagePromise);\n };\n};\n\n/**\n * Initialize the searching functionality so we can call it when required.\n *\n * @return {function(Object, Number, Object, Object, Object, Promise, Number, String): void}\n */\nconst searchFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit, inputValue) => {\n const searchingPromise = getSearchMyCourses(\n filters,\n limit,\n inputValue\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(searchingPromise);\n };\n};\n\n/**\n * Initialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n * @param {function} promiseFunction How do we fetch the courses and what do we do with them?\n * @param {null | string} inputValue What to search for\n */\nconst initializePagedContent = (root, promiseFunction, inputValue = null) => {\n const pagingLimit = parseInt(root.find(SELECTORS.courseView.region).attr('data-paging'), 10);\n let itemsPerPage = itemsPerPageFunc(pagingLimit, root);\n\n const filters = getFilterValues(root);\n const config = {...{}, ...DEFAULT_PAGED_CONTENT_CONFIG};\n config.eventNamespace = namespace;\n\n const pagedContentPromise = PagedContentFactory.createWithLimit(\n itemsPerPage,\n (pagesData, actions) => {\n let promises = [];\n pagesData.forEach(pageData => {\n const currentPage = pageData.pageNumber;\n let limit = (pageData.limit > 0) ? pageData.limit : 0;\n\n // Reset local variables if limits have changed.\n if (+lastLimit !== +limit) {\n loadedPages = [];\n courseOffset = 0;\n lastPage = 0;\n }\n\n if (lastPage === currentPage) {\n // If we are on the last page and have it's data then load it from cache.\n actions.allItemsLoaded(lastPage);\n promises.push(renderCourses(root, loadedPages[currentPage]));\n return;\n }\n\n lastLimit = limit;\n\n // Get 2 pages worth of data as we will need it for the hidden functionality.\n if (typeof (loadedPages[currentPage + 1]) === 'undefined') {\n if (typeof (loadedPages[currentPage]) === 'undefined') {\n limit *= 2;\n }\n }\n\n // Call the curried function that'll handle the course promise and any manipulation of it.\n promiseFunction(filters, currentPage, pageData, actions, root, promises, limit, inputValue);\n });\n return promises;\n },\n config\n );\n\n pagedContentPromise.then((html, js) => {\n registerPagedEventHandlers(root, namespace);\n return Templates.replaceNodeContents(root.find(SELECTORS.courseView.region), html, js);\n }).catch(Notification.exception);\n};\n\n/**\n * Listen to, and handle events for the myoverview block.\n *\n * @param {Object} root The myoverview block container element.\n * @param {HTMLElement} page The whole HTMLElement for our block.\n */\nconst registerEventListeners = (root, page) => {\n\n CustomEvents.define(root, [\n CustomEvents.events.activate\n ]);\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_ADD_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_ADD_FAVOURITE);\n const courseId = getCourseId(favourite);\n addToFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_REMOVE_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_REMOVE_FAVOURITE);\n const courseId = getCourseId(favourite);\n removeFromFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.FAVOURITE_ICON, (e, data) => {\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_HIDE_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_HIDE_COURSE);\n const courseId = getCourseId(target);\n hideCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_SHOW_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_SHOW_COURSE);\n const courseId = getCourseId(target);\n showCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n // Searching functionality event handlers.\n const input = page.querySelector(SELECTORS.region.searchInput);\n const clearIcon = page.querySelector(SELECTORS.region.clearIcon);\n\n clearIcon.addEventListener('click', () => {\n input.value = '';\n input.focus();\n clearSearch(clearIcon, root);\n });\n\n input.addEventListener('input', debounce(() => {\n if (input.value === '') {\n clearSearch(clearIcon, root);\n } else {\n activeSearch(clearIcon);\n initializePagedContent(root, searchFunctionalityCurry(), input.value.trim());\n }\n }, 1000));\n};\n\n/**\n * Reset the search icon and trigger the init for the block.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n * @param {Object} root The myoverview block container element.\n */\nexport const clearSearch = (clearIcon, root) => {\n clearIcon.classList.add('d-none');\n init(root);\n};\n\n/**\n * Change the searching icon to its' active state.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n */\nconst activeSearch = (clearIcon) => {\n clearIcon.classList.remove('d-none');\n};\n\n/**\n * Intialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n */\nexport const init = root => {\n root = $(root);\n loadedPages = [];\n lastPage = 0;\n courseOffset = 0;\n\n if (!root.attr('data-init')) {\n const page = document.querySelector(SELECTORS.region.selectBlock);\n registerEventListeners(root, page);\n namespace = \"block_myoverview_\" + root.attr('id') + \"_\" + Math.random();\n root.attr('data-init', true);\n }\n\n initializePagedContent(root, standardFunctionalityCurry());\n};\n\n/**\n * Reset the courses views to their original\n * state on first page load.courseOffset\n *\n * This is called when configuration has changed for the event lists\n * to cause them to reload their data.\n *\n * @param {Object} root The root element for the timeline view.\n */\nexport const reset = root => {\n if (loadedPages.length > 0) {\n loadedPages.forEach((courseList, index) => {\n let pagedContentPage = getPagedContentContainer(root, index);\n renderCourses(root, courseList).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n });\n } else {\n init(root);\n }\n};\n"],"names":["TEMPLATES","GROUPINGS","NUMCOURSES_PERPAGE","loadedPages","courseOffset","lastPage","lastLimit","namespace","getFilterValues","root","courseRegion","find","SELECTORS","courseView","region","display","attr","grouping","sort","displaycategories","customfieldname","customfieldvalue","DEFAULT_PAGED_CONTENT_CONFIG","ignoreControlWhileLoading","controlPlacementBottom","persistentLimitKey","getFavouriteIconContainer","courseId","FAVOURITE_ICON","getPagedContentContainer","index","getCourseId","getAddFavouriteMenuItem","getRemoveFavouriteMenuItem","addToFavourites","removeAction","addAction","setCourseFavouriteState","then","success","PubSub","publish","CourseEvents","favourited","removeClass","addClass","iconContainer","isFavouriteIcon","ICON_IS_FAVOURITE","Aria","unhide","notFavourteIcon","ICON_NOT_FAVOURITE","hide","showFavouriteIcon","Notification","alert","catch","exception","removeFromFavourites","unfavorited","hideFavouriteIcon","getHideCourseMenuItem","getShowCourseMenuItem","setCourseHiddenState","status","hideElement","id","pagingBar","jumpto","parseInt","reducedCourse","courses","reduce","accumulator","current","push","newElement","slice","forEach","courseList","popElement","length","pagedContentContainer","PagedContentFactory","resetLastPageNumber","pagedContentPage","renderCourses","html","js","Templates","replaceNodeContents","remove","Repository","setFavouriteCourses","result","warnings","course","isfavourite","noCoursesRender","nocoursesimg","newcourseurl","render","coursesData","filters","currentTemplate","Array","isArray","Object","values","map","showcoursecategory","registerPagedEventHandlers","event","PagedContentEvents","SET_ITEMS_PER_PAGE_LIMIT","subscribe","limit","setLimit","itemsPerPageFunc","pagingLimit","itemsPerPage","value","active","totalCourseCount","filter","pagingOption","pageBuilder","currentPage","pageData","actions","activeSearch","nextPageStart","pageCourses","currentPageLength","remainingCourses","allItemsLoaded","nextoffset","resetGlobals","standardFunctionalityCurry","promises","pagePromise","params","offset","classification","requiredfields","SUMMARY_REQUIRED_FIELDS","CARDLIST_REQUIRED_FIELDS","getEnrolledCoursesByTimeline","getMyCourses","searchFunctionalityCurry","inputValue","searchingPromise","searchValue","searchvalue","getSearchMyCourses","initializePagedContent","promiseFunction","config","eventNamespace","pagedContentPromise","createWithLimit","pagesData","pageNumber","registerEventListeners","page","CustomEvents","define","events","activate","on","ACTION_ADD_FAVOURITE","e","data","favourite","target","closest","originalEvent","preventDefault","ACTION_REMOVE_FAVOURITE","ACTION_HIDE_COURSE","hideAction","showAction","hideCourse","ACTION_SHOW_COURSE","showCourse","input","querySelector","searchInput","clearIcon","addEventListener","focus","clearSearch","trim","classList","add","init","document","selectBlock","Math","random"],"mappings":"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"} \ No newline at end of file diff --git a/blocks/myoverview/amd/src/repository.js b/blocks/myoverview/amd/src/repository.js index 2659c9a9cdd..f6eafb4cdc2 100644 --- a/blocks/myoverview/amd/src/repository.js +++ b/blocks/myoverview/amd/src/repository.js @@ -62,3 +62,35 @@ export const setFavouriteCourses = args => { return Ajax.call([request])[0]; }; + +/** + * These course fields are the only ones needed to be included in the results for the card and list views. + * + * @type {string[]} + */ +export const CARDLIST_REQUIRED_FIELDS = [ + 'id', + 'fullname', + 'shortname', + 'showcoursecategory', + 'showshortname', + 'visible', + 'enddate', +]; + +/** + * These course fields are the only ones needed to be included in the results for the card and list views. + * + * @type {string[]} + */ +export const SUMMARY_REQUIRED_FIELDS = [ + 'id', + 'fullname', + 'shortname', + 'showcoursecategory', + 'showshortname', + 'visible', + 'enddate', + 'summary', + 'summaryformat', +]; diff --git a/blocks/myoverview/amd/src/view.js b/blocks/myoverview/amd/src/view.js index b018846bec0..77cff2e68f0 100644 --- a/blocks/myoverview/amd/src/view.js +++ b/blocks/myoverview/amd/src/view.js @@ -97,14 +97,20 @@ const DEFAULT_PAGED_CONTENT_CONFIG = { * @return {promise} Resolved with an array of courses. */ const getMyCourses = (filters, limit) => { - return Repository.getEnrolledCoursesByTimeline({ + const params = { offset: courseOffset, limit: limit, classification: filters.grouping, sort: filters.sort, customfieldname: filters.customfieldname, - customfieldvalue: filters.customfieldvalue - }); + customfieldvalue: filters.customfieldvalue, + }; + if (filters.display === 'summary') { + params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS; + } else { + params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS; + } + return Repository.getEnrolledCoursesByTimeline(params); }; /** @@ -116,15 +122,21 @@ const getMyCourses = (filters, limit) => { * @return {promise} Resolved with an array of courses. */ const getSearchMyCourses = (filters, limit, searchValue) => { - return Repository.getEnrolledCoursesByTimeline({ + const params = { offset: courseOffset, limit: limit, classification: 'search', sort: filters.sort, customfieldname: filters.customfieldname, customfieldvalue: filters.customfieldvalue, - searchvalue: searchValue - }); + searchvalue: searchValue, + }; + if (filters.display === 'summary') { + params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS; + } else { + params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS; + } + return Repository.getEnrolledCoursesByTimeline(params); }; /** From 3056f6cdbd8777fcdc28c81a4dcf83e9f238e833 Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Sat, 17 Feb 2024 23:37:02 +0800 Subject: [PATCH 3/5] MDL-78370 block_myoverview: Ensure that the summary text will be loaded * With the summary data being excluded in card/list display modes, it means that switching to the summary display mode will render the courses in summary view without the summary text. This patch will ensure that course summary text are included when switching to the summary view. --- blocks/myoverview/amd/build/view.min.js | 2 +- blocks/myoverview/amd/build/view.min.js.map | 2 +- blocks/myoverview/amd/src/view.js | 38 +++++++++++++++++---- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/blocks/myoverview/amd/build/view.min.js b/blocks/myoverview/amd/build/view.min.js index 375c26cadcb..347911a6808 100644 --- a/blocks/myoverview/amd/build/view.min.js +++ b/blocks/myoverview/amd/build/view.min.js @@ -4,6 +4,6 @@ define("block_myoverview/view",["exports","jquery","block_myoverview/repository" * * @copyright 2018 Bas Brands * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.reset=_exports.init=_exports.clearSearch=void 0,_jquery=_interopRequireDefault(_jquery),Repository=_interopRequireWildcard(Repository),PagedContentFactory=_interopRequireWildcard(PagedContentFactory),PubSub=_interopRequireWildcard(PubSub),CustomEvents=_interopRequireWildcard(CustomEvents),Notification=_interopRequireWildcard(Notification),Templates=_interopRequireWildcard(Templates),CourseEvents=_interopRequireWildcard(CourseEvents),_selectors=_interopRequireDefault(_selectors),PagedContentEvents=_interopRequireWildcard(PagedContentEvents),Aria=_interopRequireWildcard(Aria);const TEMPLATES_COURSES_CARDS="block_myoverview/view-cards",TEMPLATES_COURSES_LIST="block_myoverview/view-list",TEMPLATES_COURSES_SUMMARY="block_myoverview/view-summary",TEMPLATES_NOCOURSES="core_course/no-courses",GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN="allincludinghidden",NUMCOURSES_PERPAGE=[12,24,48,96,0];let loadedPages=[],courseOffset=0,lastPage=0,lastLimit=0,namespace=null;const getFilterValues=root=>{const courseRegion=root.find(_selectors.default.courseView.region);return{display:courseRegion.attr("data-display"),grouping:courseRegion.attr("data-grouping"),sort:courseRegion.attr("data-sort"),displaycategories:courseRegion.attr("data-displaycategories"),customfieldname:courseRegion.attr("data-customfieldname"),customfieldvalue:courseRegion.attr("data-customfieldvalue")}},DEFAULT_PAGED_CONTENT_CONFIG={ignoreControlWhileLoading:!0,controlPlacementBottom:!0,persistentLimitKey:"block_myoverview_user_paging_preference"},getFavouriteIconContainer=(root,courseId)=>root.find(_selectors.default.FAVOURITE_ICON+'[data-course-id="'+courseId+'"]'),getPagedContentContainer=(root,index)=>root.find('[data-region="paged-content-page"][data-page="'+index+'"]'),getCourseId=root=>root.attr("data-course-id"),getAddFavouriteMenuItem=(root,courseId)=>root.find('[data-action="add-favourite"][data-course-id="'+courseId+'"]'),getRemoveFavouriteMenuItem=(root,courseId)=>root.find('[data-action="remove-favourite"][data-course-id="'+courseId+'"]'),addToFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!0).then((success=>{success?(PubSub.publish(CourseEvents.favourited,courseId),removeAction.removeClass("hidden"),addAction.addClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.removeClass("hidden"),Aria.unhide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.addClass("hidden"),Aria.hide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},removeFromFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!1).then((success=>{success?(PubSub.publish(CourseEvents.unfavorited,courseId),removeAction.addClass("hidden"),addAction.removeClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.addClass("hidden"),Aria.hide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.removeClass("hidden"),Aria.unhide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},getHideCourseMenuItem=(root,courseId)=>root.find('[data-action="hide-course"][data-course-id="'+courseId+'"]'),getShowCourseMenuItem=(root,courseId)=>root.find('[data-action="show-course"][data-course-id="'+courseId+'"]'),setCourseHiddenState=(courseId,status)=>(!1===status&&(status=null),(0,_repository2.setUserPreference)("block_myoverview_hidden_course_".concat(courseId),status).catch(Notification.exception)),hideElement=(root,id)=>{const pagingBar=root.find('[data-region="paging-bar"]'),jumpto=parseInt(pagingBar.attr("data-active-page-number"));let reducedCourse=loadedPages[jumpto].courses.reduce(((accumulator,current)=>(+id!=+current.id&&accumulator.push(current),accumulator)),[]);if(void 0!==loadedPages[jumpto+1]){const newElement=loadedPages[jumpto+1].courses.slice(0,1);loadedPages.forEach(((courseList,index)=>{if(index>jumpto){let popElement=[];void 0!==loadedPages[index+1]&&(popElement=loadedPages[index+1].courses.slice(0,1)),loadedPages[index].courses=[...loadedPages[index].courses.slice(1),...popElement]}})),reducedCourse=[...reducedCourse,...newElement]}if(lastPage===jumpto+1&&0===loadedPages[jumpto+1].courses.length){const pagedContentContainer=root.find('[data-region="paged-content-container"]');PagedContentFactory.resetLastPageNumber((0,_jquery.default)(pagedContentContainer).attr("id"),jumpto)}loadedPages[jumpto].courses=reducedCourse,courseOffset--;const pagedContentPage=getPagedContentContainer(root,jumpto);renderCourses(root,loadedPages[jumpto]).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception),loadedPages.forEach(((courseList,index)=>{if(index>jumpto){getPagedContentContainer(root,index).remove()}}))},setCourseFavouriteState=(courseId,status)=>Repository.setFavouriteCourses({courses:[{id:courseId,favourite:status}]}).then((result=>0===result.warnings.length&&(loadedPages.forEach((courseList=>{courseList.courses.forEach(((course,index)=>{course.id==courseId&&(courseList.courses[index].isfavourite=status)}))})),!0))).catch(Notification.exception),noCoursesRender=root=>{const nocoursesimg=root.find(_selectors.default.courseView.region).attr("data-nocoursesimg"),newcourseurl=root.find(_selectors.default.courseView.region).attr("data-newcourseurl");return Templates.render(TEMPLATES_NOCOURSES,{nocoursesimg:nocoursesimg,newcourseurl:newcourseurl})},renderCourses=(root,coursesData)=>{const filters=getFilterValues(root);let currentTemplate="";return currentTemplate="card"===filters.display?TEMPLATES_COURSES_CARDS:"list"===filters.display?TEMPLATES_COURSES_LIST:TEMPLATES_COURSES_SUMMARY,coursesData?(!1===Array.isArray(coursesData.courses)&&(coursesData.courses=Object.values(coursesData.courses)),coursesData.courses=coursesData.courses.map((course=>(course.showcoursecategory="on"===filters.displaycategories,course))),coursesData.courses.length?Templates.render(currentTemplate,{courses:coursesData.courses}):noCoursesRender(root)):noCoursesRender(root)},registerPagedEventHandlers=(root,namespace)=>{const event=namespace+PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;PubSub.subscribe(event,(root=>limit=>root.find(_selectors.default.courseView.region).attr("data-paging",limit))(root))},itemsPerPageFunc=(pagingLimit,root)=>{let itemsPerPage=NUMCOURSES_PERPAGE.map((value=>{let active=!1;return value===pagingLimit&&(active=!0),{value:value,active:active}}));const totalCourseCount=parseInt(root.find(_selectors.default.courseView.region).attr("data-totalcoursecount"),10);return itemsPerPage.filter((pagingOption=>pagingOption.value4&&void 0!==arguments[4]?arguments[4]:null,courses=coursesData.courses?coursesData.courses:coursesData,nextPageStart=0,pageCourses=[];if(void 0!==loadedPages[currentPage]){pageCourses=loadedPages[currentPage].courses;const currentPageLength=pageCourses.length;currentPageLength0?courses.slice(0,pageData.limit):courses;loadedPages[currentPage]={courses:pageCourses};const remainingCourses=!1!==nextPageStart?courses.slice(nextPageStart,courses.length):[];remainingCourses.length&&(loadedPages[currentPage+1]={courses:remainingCourses}),loadedPages[currentPage].courses.length{courseOffset=0,loadedPages=[],lastPage=0,lastLimit=0},standardFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit)=>{const pagePromise=((filters,limit)=>{const params={offset:courseOffset,limit:limit,classification:filters.grouping,sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue};return"summary"===filters.display?params.requiredfields=Repository.SUMMARY_REQUIRED_FIELDS:params.requiredfields=Repository.CARDLIST_REQUIRED_FIELDS,Repository.getEnrolledCoursesByTimeline(params)})(filters,limit).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(pagePromise)}),searchFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit,inputValue)=>{const searchingPromise=((filters,limit,searchValue)=>{const params={offset:courseOffset,limit:limit,classification:"search",sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue,searchvalue:searchValue};return"summary"===filters.display?params.requiredfields=Repository.SUMMARY_REQUIRED_FIELDS:params.requiredfields=Repository.CARDLIST_REQUIRED_FIELDS,Repository.getEnrolledCoursesByTimeline(params)})(filters,limit,inputValue).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(searchingPromise)}),initializePagedContent=function(root,promiseFunction){let inputValue=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const pagingLimit=parseInt(root.find(_selectors.default.courseView.region).attr("data-paging"),10);let itemsPerPage=itemsPerPageFunc(pagingLimit,root);const filters=getFilterValues(root),config={...DEFAULT_PAGED_CONTENT_CONFIG};config.eventNamespace=namespace;const pagedContentPromise=PagedContentFactory.createWithLimit(itemsPerPage,((pagesData,actions)=>{let promises=[];return pagesData.forEach((pageData=>{const currentPage=pageData.pageNumber;let limit=pageData.limit>0?pageData.limit:0;if(+lastLimit!=+limit&&(loadedPages=[],courseOffset=0,lastPage=0),lastPage===currentPage)return actions.allItemsLoaded(lastPage),void promises.push(renderCourses(root,loadedPages[currentPage]));lastLimit=limit,void 0===loadedPages[currentPage+1]&&void 0===loadedPages[currentPage]&&(limit*=2),promiseFunction(filters,currentPage,pageData,actions,root,promises,limit,inputValue)})),promises}),config);pagedContentPromise.then(((html,js)=>(registerPagedEventHandlers(root,namespace),Templates.replaceNodeContents(root.find(_selectors.default.courseView.region),html,js)))).catch(Notification.exception)},registerEventListeners=(root,page)=>{CustomEvents.define(root,[CustomEvents.events.activate]),root.on(CustomEvents.events.activate,_selectors.default.ACTION_ADD_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_ADD_FAVOURITE),courseId=getCourseId(favourite);addToFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_REMOVE_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_REMOVE_FAVOURITE),courseId=getCourseId(favourite);removeFromFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.FAVOURITE_ICON,((e,data)=>{data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_HIDE_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_HIDE_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,!0),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.addClass("hidden"),showAction.removeClass("hidden")})(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_SHOW_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_SHOW_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,null),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.removeClass("hidden"),showAction.addClass("hidden")})(root,courseId),data.originalEvent.preventDefault()}));const input=page.querySelector(_selectors.default.region.searchInput),clearIcon=page.querySelector(_selectors.default.region.clearIcon);clearIcon.addEventListener("click",(()=>{input.value="",input.focus(),clearSearch(clearIcon,root)})),input.addEventListener("input",(0,_utils.debounce)((()=>{""===input.value?clearSearch(clearIcon,root):(activeSearch(clearIcon),initializePagedContent(root,searchFunctionalityCurry(),input.value.trim()))}),1e3))},clearSearch=(clearIcon,root)=>{clearIcon.classList.add("d-none"),init(root)};_exports.clearSearch=clearSearch;const activeSearch=clearIcon=>{clearIcon.classList.remove("d-none")},init=root=>{if(root=(0,_jquery.default)(root),loadedPages=[],lastPage=0,courseOffset=0,!root.attr("data-init")){const page=document.querySelector(_selectors.default.region.selectBlock);registerEventListeners(root,page),namespace="block_myoverview_"+root.attr("id")+"_"+Math.random(),root.attr("data-init",!0)}initializePagedContent(root,standardFunctionalityCurry())};_exports.init=init;_exports.reset=root=>{loadedPages.length>0?loadedPages.forEach(((courseList,index)=>{let pagedContentPage=getPagedContentContainer(root,index);renderCourses(root,courseList).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception)})):init(root)}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.reset=_exports.init=_exports.clearSearch=void 0,_jquery=_interopRequireDefault(_jquery),Repository=_interopRequireWildcard(Repository),PagedContentFactory=_interopRequireWildcard(PagedContentFactory),PubSub=_interopRequireWildcard(PubSub),CustomEvents=_interopRequireWildcard(CustomEvents),Notification=_interopRequireWildcard(Notification),Templates=_interopRequireWildcard(Templates),CourseEvents=_interopRequireWildcard(CourseEvents),_selectors=_interopRequireDefault(_selectors),PagedContentEvents=_interopRequireWildcard(PagedContentEvents),Aria=_interopRequireWildcard(Aria);const TEMPLATES_COURSES_CARDS="block_myoverview/view-cards",TEMPLATES_COURSES_LIST="block_myoverview/view-list",TEMPLATES_COURSES_SUMMARY="block_myoverview/view-summary",TEMPLATES_NOCOURSES="core_course/no-courses",GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN="allincludinghidden",NUMCOURSES_PERPAGE=[12,24,48,96,0];let loadedPages=[],courseOffset=0,lastPage=0,lastLimit=0,namespace=null,summaryDisplayLoaded=!1;const getFilterValues=root=>{const courseRegion=root.find(_selectors.default.courseView.region);return{display:courseRegion.attr("data-display"),grouping:courseRegion.attr("data-grouping"),sort:courseRegion.attr("data-sort"),displaycategories:courseRegion.attr("data-displaycategories"),customfieldname:courseRegion.attr("data-customfieldname"),customfieldvalue:courseRegion.attr("data-customfieldvalue")}},DEFAULT_PAGED_CONTENT_CONFIG={ignoreControlWhileLoading:!0,controlPlacementBottom:!0,persistentLimitKey:"block_myoverview_user_paging_preference"},getFavouriteIconContainer=(root,courseId)=>root.find(_selectors.default.FAVOURITE_ICON+'[data-course-id="'+courseId+'"]'),getPagedContentContainer=(root,index)=>root.find('[data-region="paged-content-page"][data-page="'+index+'"]'),getCourseId=root=>root.attr("data-course-id"),getAddFavouriteMenuItem=(root,courseId)=>root.find('[data-action="add-favourite"][data-course-id="'+courseId+'"]'),getRemoveFavouriteMenuItem=(root,courseId)=>root.find('[data-action="remove-favourite"][data-course-id="'+courseId+'"]'),addToFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!0).then((success=>{success?(PubSub.publish(CourseEvents.favourited,courseId),removeAction.removeClass("hidden"),addAction.addClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.removeClass("hidden"),Aria.unhide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.addClass("hidden"),Aria.hide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},removeFromFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!1).then((success=>{success?(PubSub.publish(CourseEvents.unfavorited,courseId),removeAction.addClass("hidden"),addAction.removeClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.addClass("hidden"),Aria.hide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.removeClass("hidden"),Aria.unhide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},getHideCourseMenuItem=(root,courseId)=>root.find('[data-action="hide-course"][data-course-id="'+courseId+'"]'),getShowCourseMenuItem=(root,courseId)=>root.find('[data-action="show-course"][data-course-id="'+courseId+'"]'),setCourseHiddenState=(courseId,status)=>(!1===status&&(status=null),(0,_repository2.setUserPreference)("block_myoverview_hidden_course_".concat(courseId),status).catch(Notification.exception)),hideElement=(root,id)=>{const pagingBar=root.find('[data-region="paging-bar"]'),jumpto=parseInt(pagingBar.attr("data-active-page-number"));let reducedCourse=loadedPages[jumpto].courses.reduce(((accumulator,current)=>(+id!=+current.id&&accumulator.push(current),accumulator)),[]);if(void 0!==loadedPages[jumpto+1]){const newElement=loadedPages[jumpto+1].courses.slice(0,1);loadedPages.forEach(((courseList,index)=>{if(index>jumpto){let popElement=[];void 0!==loadedPages[index+1]&&(popElement=loadedPages[index+1].courses.slice(0,1)),loadedPages[index].courses=[...loadedPages[index].courses.slice(1),...popElement]}})),reducedCourse=[...reducedCourse,...newElement]}if(lastPage===jumpto+1&&0===loadedPages[jumpto+1].courses.length){const pagedContentContainer=root.find('[data-region="paged-content-container"]');PagedContentFactory.resetLastPageNumber((0,_jquery.default)(pagedContentContainer).attr("id"),jumpto)}loadedPages[jumpto].courses=reducedCourse,courseOffset--;const pagedContentPage=getPagedContentContainer(root,jumpto);renderCourses(root,loadedPages[jumpto]).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception),loadedPages.forEach(((courseList,index)=>{if(index>jumpto){getPagedContentContainer(root,index).remove()}}))},setCourseFavouriteState=(courseId,status)=>Repository.setFavouriteCourses({courses:[{id:courseId,favourite:status}]}).then((result=>0===result.warnings.length&&(loadedPages.forEach((courseList=>{courseList.courses.forEach(((course,index)=>{course.id==courseId&&(courseList.courses[index].isfavourite=status)}))})),!0))).catch(Notification.exception),noCoursesRender=root=>{const nocoursesimg=root.find(_selectors.default.courseView.region).attr("data-nocoursesimg"),newcourseurl=root.find(_selectors.default.courseView.region).attr("data-newcourseurl");return Templates.render(TEMPLATES_NOCOURSES,{nocoursesimg:nocoursesimg,newcourseurl:newcourseurl})},renderCourses=(root,coursesData)=>{const filters=getFilterValues(root);let currentTemplate="";return currentTemplate="card"===filters.display?TEMPLATES_COURSES_CARDS:"list"===filters.display?TEMPLATES_COURSES_LIST:TEMPLATES_COURSES_SUMMARY,coursesData?(!1===Array.isArray(coursesData.courses)&&(coursesData.courses=Object.values(coursesData.courses)),coursesData.courses=coursesData.courses.map((course=>(course.showcoursecategory="on"===filters.displaycategories,course))),coursesData.courses.length?Templates.render(currentTemplate,{courses:coursesData.courses}):noCoursesRender(root)):noCoursesRender(root)},registerPagedEventHandlers=(root,namespace)=>{const event=namespace+PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;PubSub.subscribe(event,(root=>limit=>root.find(_selectors.default.courseView.region).attr("data-paging",limit))(root))},itemsPerPageFunc=(pagingLimit,root)=>{let itemsPerPage=NUMCOURSES_PERPAGE.map((value=>{let active=!1;return value===pagingLimit&&(active=!0),{value:value,active:active}}));const totalCourseCount=parseInt(root.find(_selectors.default.courseView.region).attr("data-totalcoursecount"),10);return itemsPerPage.filter((pagingOption=>pagingOption.value4&&void 0!==arguments[4]?arguments[4]:null,courses=coursesData.courses?coursesData.courses:coursesData,nextPageStart=0,pageCourses=[];if(void 0!==loadedPages[currentPage]){pageCourses=loadedPages[currentPage].courses;const currentPageLength=pageCourses.length;currentPageLength0?courses.slice(0,pageData.limit):courses;loadedPages[currentPage]={courses:pageCourses};const remainingCourses=!1!==nextPageStart?courses.slice(nextPageStart,courses.length):[];remainingCourses.length&&(loadedPages[currentPage+1]={courses:remainingCourses}),loadedPages[currentPage].courses.length{courseOffset=0,loadedPages=[],lastPage=0,lastLimit=0},standardFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit)=>{const pagePromise=((filters,limit)=>{const params={offset:courseOffset,limit:limit,classification:filters.grouping,sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue};return"summary"===filters.display?(params.requiredfields=Repository.SUMMARY_REQUIRED_FIELDS,summaryDisplayLoaded=!0):params.requiredfields=Repository.CARDLIST_REQUIRED_FIELDS,Repository.getEnrolledCoursesByTimeline(params)})(filters,limit).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(pagePromise)}),searchFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit,inputValue)=>{const searchingPromise=((filters,limit,searchValue)=>{const params={offset:courseOffset,limit:limit,classification:"search",sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue,searchvalue:searchValue};return"summary"===filters.display?(params.requiredfields=Repository.SUMMARY_REQUIRED_FIELDS,summaryDisplayLoaded=!0):(params.requiredfields=Repository.CARDLIST_REQUIRED_FIELDS,summaryDisplayLoaded=!1),Repository.getEnrolledCoursesByTimeline(params)})(filters,limit,inputValue).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(searchingPromise)}),initializePagedContent=function(root,promiseFunction){let inputValue=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const pagingLimit=parseInt(root.find(_selectors.default.courseView.region).attr("data-paging"),10);let itemsPerPage=itemsPerPageFunc(pagingLimit,root);const filters=getFilterValues(root),config={...DEFAULT_PAGED_CONTENT_CONFIG};config.eventNamespace=namespace;const pagedContentPromise=PagedContentFactory.createWithLimit(itemsPerPage,((pagesData,actions)=>{let promises=[];return pagesData.forEach((pageData=>{const currentPage=pageData.pageNumber;let limit=pageData.limit>0?pageData.limit:0;if(+lastLimit!=+limit&&(loadedPages=[],courseOffset=0,lastPage=0),lastPage===currentPage)return actions.allItemsLoaded(lastPage),void promises.push(renderCourses(root,loadedPages[currentPage]));lastLimit=limit,void 0===loadedPages[currentPage+1]&&void 0===loadedPages[currentPage]&&(limit*=2),promiseFunction(filters,currentPage,pageData,actions,root,promises,limit,inputValue)})),promises}),config);pagedContentPromise.then(((html,js)=>(registerPagedEventHandlers(root,namespace),Templates.replaceNodeContents(root.find(_selectors.default.courseView.region),html,js)))).catch(Notification.exception)},registerEventListeners=(root,page)=>{CustomEvents.define(root,[CustomEvents.events.activate]),root.on(CustomEvents.events.activate,_selectors.default.ACTION_ADD_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_ADD_FAVOURITE),courseId=getCourseId(favourite);addToFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_REMOVE_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_REMOVE_FAVOURITE),courseId=getCourseId(favourite);removeFromFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.FAVOURITE_ICON,((e,data)=>{data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_HIDE_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_HIDE_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,!0),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.addClass("hidden"),showAction.removeClass("hidden")})(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_SHOW_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_SHOW_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,null),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.removeClass("hidden"),showAction.addClass("hidden")})(root,courseId),data.originalEvent.preventDefault()}));const input=page.querySelector(_selectors.default.region.searchInput),clearIcon=page.querySelector(_selectors.default.region.clearIcon);clearIcon.addEventListener("click",(()=>{input.value="",input.focus(),clearSearch(clearIcon,root)})),input.addEventListener("input",(0,_utils.debounce)((()=>{""===input.value?clearSearch(clearIcon,root):(activeSearch(clearIcon),initializePagedContent(root,searchFunctionalityCurry(),input.value.trim()))}),1e3))},clearSearch=(clearIcon,root)=>{clearIcon.classList.add("d-none"),init(root)};_exports.clearSearch=clearSearch;const activeSearch=clearIcon=>{clearIcon.classList.remove("d-none")},init=root=>{if(root=(0,_jquery.default)(root),loadedPages=[],lastPage=0,courseOffset=0,!root.attr("data-init")){const page=document.querySelector(_selectors.default.region.selectBlock);registerEventListeners(root,page),namespace="block_myoverview_"+root.attr("id")+"_"+Math.random(),root.attr("data-init",!0)}initializePagedContent(root,standardFunctionalityCurry())};_exports.init=init;_exports.reset=root=>{if(loadedPages.length>0){if("summary"!==getFilterValues(root).display||summaryDisplayLoaded)loadedPages.forEach(((courseList,index)=>{let pagedContentPage=getPagedContentContainer(root,index);renderCourses(root,courseList).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception)}));else{const input=document.querySelector(_selectors.default.region.selectBlock).querySelector(_selectors.default.region.searchInput);""!==input.value?initializePagedContent(root,searchFunctionalityCurry(),input.value.trim()):initializePagedContent(root,standardFunctionalityCurry())}}else init(root)}})); //# sourceMappingURL=view.min.js.map \ No newline at end of file diff --git a/blocks/myoverview/amd/build/view.min.js.map b/blocks/myoverview/amd/build/view.min.js.map index db03dafa406..3031516e38d 100644 --- a/blocks/myoverview/amd/build/view.min.js.map +++ b/blocks/myoverview/amd/build/view.min.js.map @@ -1 +1 @@ -{"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 courses view for the overview block.\n *\n * @copyright 2018 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as Repository from 'block_myoverview/repository';\nimport * as PagedContentFactory from 'core/paged_content_factory';\nimport * as PubSub from 'core/pubsub';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as Notification from 'core/notification';\nimport * as Templates from 'core/templates';\nimport * as CourseEvents from 'core_course/events';\nimport SELECTORS from 'block_myoverview/selectors';\nimport * as PagedContentEvents from 'core/paged_content_events';\nimport * as Aria from 'core/aria';\nimport {debounce} from 'core/utils';\nimport {setUserPreference} from 'core_user/repository';\n\nconst TEMPLATES = {\n COURSES_CARDS: 'block_myoverview/view-cards',\n COURSES_LIST: 'block_myoverview/view-list',\n COURSES_SUMMARY: 'block_myoverview/view-summary',\n NOCOURSES: 'core_course/no-courses'\n};\n\nconst GROUPINGS = {\n GROUPING_ALLINCLUDINGHIDDEN: 'allincludinghidden',\n GROUPING_ALL: 'all',\n GROUPING_INPROGRESS: 'inprogress',\n GROUPING_FUTURE: 'future',\n GROUPING_PAST: 'past',\n GROUPING_FAVOURITES: 'favourites',\n GROUPING_HIDDEN: 'hidden'\n};\n\nconst NUMCOURSES_PERPAGE = [12, 24, 48, 96, 0];\n\nlet loadedPages = [];\n\nlet courseOffset = 0;\n\nlet lastPage = 0;\n\nlet lastLimit = 0;\n\nlet namespace = null;\n\n/**\n * Get filter values from DOM.\n *\n * @param {object} root The root element for the courses view.\n * @return {filters} Set filters.\n */\nconst getFilterValues = root => {\n const courseRegion = root.find(SELECTORS.courseView.region);\n return {\n display: courseRegion.attr('data-display'),\n grouping: courseRegion.attr('data-grouping'),\n sort: courseRegion.attr('data-sort'),\n displaycategories: courseRegion.attr('data-displaycategories'),\n customfieldname: courseRegion.attr('data-customfieldname'),\n customfieldvalue: courseRegion.attr('data-customfieldvalue'),\n };\n};\n\n// We want the paged content controls below the paged content area.\n// and the controls should be ignored while data is loading.\nconst DEFAULT_PAGED_CONTENT_CONFIG = {\n ignoreControlWhileLoading: true,\n controlPlacementBottom: true,\n persistentLimitKey: 'block_myoverview_user_paging_preference'\n};\n\n/**\n * Get enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @return {promise} Resolved with an array of courses.\n */\nconst getMyCourses = (filters, limit) => {\n const params = {\n offset: courseOffset,\n limit: limit,\n classification: filters.grouping,\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n };\n if (filters.display === 'summary') {\n params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS;\n } else {\n params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS;\n }\n return Repository.getEnrolledCoursesByTimeline(params);\n};\n\n/**\n * Search for enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @param {string} searchValue What does the user want to search within their courses.\n * @return {promise} Resolved with an array of courses.\n */\nconst getSearchMyCourses = (filters, limit, searchValue) => {\n const params = {\n offset: courseOffset,\n limit: limit,\n classification: 'search',\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n searchvalue: searchValue,\n };\n if (filters.display === 'summary') {\n params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS;\n } else {\n params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS;\n }\n return Repository.getEnrolledCoursesByTimeline(params);\n};\n\n/**\n * Get the container element for the favourite icon.\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n * @return {Object} The favourite icon container\n */\nconst getFavouriteIconContainer = (root, courseId) => {\n return root.find(SELECTORS.FAVOURITE_ICON + '[data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the paged content container element.\n *\n * @param {Object} root The course overview container\n * @param {Number} index Rendered page index.\n * @return {Object} The rendered paged container.\n */\nconst getPagedContentContainer = (root, index) => {\n return root.find('[data-region=\"paged-content-page\"][data-page=\"' + index + '\"]');\n};\n\n/**\n * Get the course id from a favourite element.\n *\n * @param {Object} root The favourite icon container element.\n * @return {Number} Course id.\n */\nconst getCourseId = root => {\n return root.attr('data-course-id');\n};\n\n/**\n * Hide the favourite icon.\n *\n * @param {Object} root The favourite icon container element.\n * @param {Number} courseId Course id number.\n */\nconst hideFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.addClass('hidden');\n Aria.hide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.removeClass('hidden');\n Aria.unhide(notFavourteIcon);\n};\n\n/**\n * Show the favourite icon.\n *\n * @param {Object} root The course overview container.\n * @param {Number} courseId Course id number.\n */\nconst showFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.removeClass('hidden');\n Aria.unhide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.addClass('hidden');\n Aria.hide(notFavourteIcon);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The add to favourite menu item.\n */\nconst getAddFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"add-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The remove from favourites menu item.\n */\nconst getRemoveFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"remove-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Add course to favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst addToFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, true).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.favourited, courseId);\n removeAction.removeClass('hidden');\n addAction.addClass('hidden');\n showFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Remove course from favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst removeFromFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, false).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.unfavorited, courseId);\n removeAction.addClass('hidden');\n addAction.removeClass('hidden');\n hideFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The hide course menu item.\n */\nconst getHideCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"hide-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The show course menu item.\n */\nconst getShowCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"show-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Hide course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst hideCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, true);\n\n // Remove the course from this view as it is now hidden and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.addClass('hidden');\n showAction.removeClass('hidden');\n};\n\n/**\n * Show course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst showCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, null);\n\n // Remove the course from this view as it is now shown again and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.removeClass('hidden');\n showAction.addClass('hidden');\n};\n\n/**\n * Set the courses hidden status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {Boolean} status new hidden status.\n * @return {Promise} Repository promise.\n */\nconst setCourseHiddenState = (courseId, status) => {\n\n // If the given status is not hidden, the preference has to be deleted with a null value.\n if (status === false) {\n status = null;\n }\n\n return setUserPreference(`block_myoverview_hidden_course_${courseId}`, status)\n .catch(Notification.exception);\n};\n\n/**\n * Reset the loadedPages dataset to take into account the hidden element\n *\n * @param {Object} root The course overview container\n * @param {Number} id The course id number\n */\nconst hideElement = (root, id) => {\n const pagingBar = root.find('[data-region=\"paging-bar\"]');\n const jumpto = parseInt(pagingBar.attr('data-active-page-number'));\n\n // Get a reduced dataset for the current page.\n const courseList = loadedPages[jumpto];\n let reducedCourse = courseList.courses.reduce((accumulator, current) => {\n if (+id !== +current.id) {\n accumulator.push(current);\n }\n return accumulator;\n }, []);\n\n // Get the next page's data if loaded and pop the first element from it.\n if (typeof (loadedPages[jumpto + 1]) !== 'undefined') {\n const newElement = loadedPages[jumpto + 1].courses.slice(0, 1);\n\n // Adjust the dataset for the reset of the pages that are loaded.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n let popElement = [];\n if (typeof (loadedPages[index + 1]) !== 'undefined') {\n popElement = loadedPages[index + 1].courses.slice(0, 1);\n }\n loadedPages[index].courses = [...loadedPages[index].courses.slice(1), ...popElement];\n }\n });\n\n reducedCourse = [...reducedCourse, ...newElement];\n }\n\n // Check if the next page is the last page and if it still has data associated to it.\n if (lastPage === jumpto + 1 && loadedPages[jumpto + 1].courses.length === 0) {\n const pagedContentContainer = root.find('[data-region=\"paged-content-container\"]');\n PagedContentFactory.resetLastPageNumber($(pagedContentContainer).attr('id'), jumpto);\n }\n\n loadedPages[jumpto].courses = reducedCourse;\n\n // Reduce the course offset.\n courseOffset--;\n\n // Render the paged content for the current.\n const pagedContentPage = getPagedContentContainer(root, jumpto);\n renderCourses(root, loadedPages[jumpto]).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n\n // Delete subsequent pages in order to trigger the callback.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n const page = getPagedContentContainer(root, index);\n page.remove();\n }\n });\n};\n\n/**\n * Set the courses favourite status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {boolean} status new favourite status.\n * @return {Promise} Repository promise.\n */\nconst setCourseFavouriteState = (courseId, status) => {\n\n return Repository.setFavouriteCourses({\n courses: [\n {\n 'id': courseId,\n 'favourite': status\n }\n ]\n }).then(result => {\n if (result.warnings.length === 0) {\n loadedPages.forEach(courseList => {\n courseList.courses.forEach((course, index) => {\n if (course.id == courseId) {\n courseList.courses[index].isfavourite = status;\n }\n });\n });\n return true;\n } else {\n return false;\n }\n }).catch(Notification.exception);\n};\n\n/**\n * Given there are no courses to render provide the rendered template.\n *\n * @param {object} root The root element for the courses view.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst noCoursesRender = root => {\n const nocoursesimg = root.find(SELECTORS.courseView.region).attr('data-nocoursesimg');\n const newcourseurl = root.find(SELECTORS.courseView.region).attr('data-newcourseurl');\n return Templates.render(TEMPLATES.NOCOURSES, {\n nocoursesimg: nocoursesimg,\n newcourseurl: newcourseurl\n });\n};\n\n/**\n * Render the dashboard courses.\n *\n * @param {object} root The root element for the courses view.\n * @param {array} coursesData containing array of returned courses.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst renderCourses = (root, coursesData) => {\n\n const filters = getFilterValues(root);\n\n let currentTemplate = '';\n if (filters.display === 'card') {\n currentTemplate = TEMPLATES.COURSES_CARDS;\n } else if (filters.display === 'list') {\n currentTemplate = TEMPLATES.COURSES_LIST;\n } else {\n currentTemplate = TEMPLATES.COURSES_SUMMARY;\n }\n\n if (!coursesData) {\n return noCoursesRender(root);\n } else {\n // Sometimes we get weird objects coming after a failed search, cast to ensure typing functions.\n if (Array.isArray(coursesData.courses) === false) {\n coursesData.courses = Object.values(coursesData.courses);\n }\n // Whether the course category should be displayed in the course item.\n coursesData.courses = coursesData.courses.map(course => {\n course.showcoursecategory = filters.displaycategories === 'on';\n return course;\n });\n if (coursesData.courses.length) {\n return Templates.render(currentTemplate, {\n courses: coursesData.courses,\n });\n } else {\n return noCoursesRender(root);\n }\n }\n};\n\n/**\n * Return the callback to be passed to the subscribe event\n *\n * @param {object} root The root element for the courses view\n * @return {function} Partially applied function that'll execute when passed a limit\n */\nconst setLimit = root => {\n // @param {Number} limit The paged limit that is passed through the event.\n return limit => root.find(SELECTORS.courseView.region).attr('data-paging', limit);\n};\n\n/**\n * Intialise the paged list and cards views on page load.\n * Returns an array of paged contents that we would like to handle here\n *\n * @param {object} root The root element for the courses view\n * @param {string} namespace The namespace for all the events attached\n */\nconst registerPagedEventHandlers = (root, namespace) => {\n const event = namespace + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;\n PubSub.subscribe(event, setLimit(root));\n};\n\n/**\n * Figure out how many items are going to be allowed to be rendered in the block.\n *\n * @param {Number} pagingLimit How many courses to display\n * @param {Object} root The course overview container\n * @return {Number[]} How many courses will be rendered\n */\nconst itemsPerPageFunc = (pagingLimit, root) => {\n let itemsPerPage = NUMCOURSES_PERPAGE.map(value => {\n let active = false;\n if (value === pagingLimit) {\n active = true;\n }\n\n return {\n value: value,\n active: active\n };\n });\n\n // Filter out all pagination options which are too large for the amount of courses user is enrolled in.\n const totalCourseCount = parseInt(root.find(SELECTORS.courseView.region).attr('data-totalcoursecount'), 10);\n return itemsPerPage.filter(pagingOption => {\n return pagingOption.value < totalCourseCount || pagingOption.value === 0;\n });\n};\n\n/**\n * Mutates and controls the loadedPages array and handles the bootstrapping.\n *\n * @param {Array|Object} coursesData Array of all of the courses to start building the page from\n * @param {Number} currentPage What page are we currently on?\n * @param {Object} pageData Any current page information\n * @param {Object} actions Paged content helper\n * @param {null|boolean} activeSearch Are we currently actively searching and building up search results?\n */\nconst pageBuilder = (coursesData, currentPage, pageData, actions, activeSearch = null) => {\n // If the courseData comes in an object then get the value otherwise it is a pure array.\n let courses = coursesData.courses ? coursesData.courses : coursesData;\n let nextPageStart = 0;\n let pageCourses = [];\n\n // If current page's data is loaded make sure we max it to page limit.\n if (typeof (loadedPages[currentPage]) !== 'undefined') {\n pageCourses = loadedPages[currentPage].courses;\n const currentPageLength = pageCourses.length;\n if (currentPageLength < pageData.limit) {\n nextPageStart = pageData.limit - currentPageLength;\n pageCourses = {...loadedPages[currentPage].courses, ...courses.slice(0, nextPageStart)};\n }\n } else {\n // When the page limit is zero, there is only one page of courses, no start for next page.\n nextPageStart = pageData.limit || false;\n pageCourses = (pageData.limit > 0) ? courses.slice(0, pageData.limit) : courses;\n }\n\n // Finished setting up the current page.\n loadedPages[currentPage] = {\n courses: pageCourses\n };\n\n // Set up the next page (if there is more than one page).\n const remainingCourses = nextPageStart !== false ? courses.slice(nextPageStart, courses.length) : [];\n if (remainingCourses.length) {\n loadedPages[currentPage + 1] = {\n courses: remainingCourses\n };\n }\n\n // Set the last page to either the current or next page.\n if (loadedPages[currentPage].courses.length < pageData.limit || !remainingCourses.length) {\n lastPage = currentPage;\n if (activeSearch === null) {\n actions.allItemsLoaded(currentPage);\n }\n } else if (typeof (loadedPages[currentPage + 1]) !== 'undefined'\n && loadedPages[currentPage + 1].courses.length < pageData.limit) {\n lastPage = currentPage + 1;\n }\n\n courseOffset = coursesData.nextoffset;\n};\n\n/**\n * In cases when switching between regular rendering and search rendering we need to reset some variables.\n */\nconst resetGlobals = () => {\n courseOffset = 0;\n loadedPages = [];\n lastPage = 0;\n lastLimit = 0;\n};\n\n/**\n * The default functionality of fetching paginated courses without special handling.\n *\n * @return {function(Object, Object, Object, Object, Object, Promise, Number): void}\n */\nconst standardFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit) => {\n const pagePromise = getMyCourses(\n filters,\n limit\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(pagePromise);\n };\n};\n\n/**\n * Initialize the searching functionality so we can call it when required.\n *\n * @return {function(Object, Number, Object, Object, Object, Promise, Number, String): void}\n */\nconst searchFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit, inputValue) => {\n const searchingPromise = getSearchMyCourses(\n filters,\n limit,\n inputValue\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(searchingPromise);\n };\n};\n\n/**\n * Initialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n * @param {function} promiseFunction How do we fetch the courses and what do we do with them?\n * @param {null | string} inputValue What to search for\n */\nconst initializePagedContent = (root, promiseFunction, inputValue = null) => {\n const pagingLimit = parseInt(root.find(SELECTORS.courseView.region).attr('data-paging'), 10);\n let itemsPerPage = itemsPerPageFunc(pagingLimit, root);\n\n const filters = getFilterValues(root);\n const config = {...{}, ...DEFAULT_PAGED_CONTENT_CONFIG};\n config.eventNamespace = namespace;\n\n const pagedContentPromise = PagedContentFactory.createWithLimit(\n itemsPerPage,\n (pagesData, actions) => {\n let promises = [];\n pagesData.forEach(pageData => {\n const currentPage = pageData.pageNumber;\n let limit = (pageData.limit > 0) ? pageData.limit : 0;\n\n // Reset local variables if limits have changed.\n if (+lastLimit !== +limit) {\n loadedPages = [];\n courseOffset = 0;\n lastPage = 0;\n }\n\n if (lastPage === currentPage) {\n // If we are on the last page and have it's data then load it from cache.\n actions.allItemsLoaded(lastPage);\n promises.push(renderCourses(root, loadedPages[currentPage]));\n return;\n }\n\n lastLimit = limit;\n\n // Get 2 pages worth of data as we will need it for the hidden functionality.\n if (typeof (loadedPages[currentPage + 1]) === 'undefined') {\n if (typeof (loadedPages[currentPage]) === 'undefined') {\n limit *= 2;\n }\n }\n\n // Call the curried function that'll handle the course promise and any manipulation of it.\n promiseFunction(filters, currentPage, pageData, actions, root, promises, limit, inputValue);\n });\n return promises;\n },\n config\n );\n\n pagedContentPromise.then((html, js) => {\n registerPagedEventHandlers(root, namespace);\n return Templates.replaceNodeContents(root.find(SELECTORS.courseView.region), html, js);\n }).catch(Notification.exception);\n};\n\n/**\n * Listen to, and handle events for the myoverview block.\n *\n * @param {Object} root The myoverview block container element.\n * @param {HTMLElement} page The whole HTMLElement for our block.\n */\nconst registerEventListeners = (root, page) => {\n\n CustomEvents.define(root, [\n CustomEvents.events.activate\n ]);\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_ADD_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_ADD_FAVOURITE);\n const courseId = getCourseId(favourite);\n addToFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_REMOVE_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_REMOVE_FAVOURITE);\n const courseId = getCourseId(favourite);\n removeFromFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.FAVOURITE_ICON, (e, data) => {\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_HIDE_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_HIDE_COURSE);\n const courseId = getCourseId(target);\n hideCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_SHOW_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_SHOW_COURSE);\n const courseId = getCourseId(target);\n showCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n // Searching functionality event handlers.\n const input = page.querySelector(SELECTORS.region.searchInput);\n const clearIcon = page.querySelector(SELECTORS.region.clearIcon);\n\n clearIcon.addEventListener('click', () => {\n input.value = '';\n input.focus();\n clearSearch(clearIcon, root);\n });\n\n input.addEventListener('input', debounce(() => {\n if (input.value === '') {\n clearSearch(clearIcon, root);\n } else {\n activeSearch(clearIcon);\n initializePagedContent(root, searchFunctionalityCurry(), input.value.trim());\n }\n }, 1000));\n};\n\n/**\n * Reset the search icon and trigger the init for the block.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n * @param {Object} root The myoverview block container element.\n */\nexport const clearSearch = (clearIcon, root) => {\n clearIcon.classList.add('d-none');\n init(root);\n};\n\n/**\n * Change the searching icon to its' active state.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n */\nconst activeSearch = (clearIcon) => {\n clearIcon.classList.remove('d-none');\n};\n\n/**\n * Intialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n */\nexport const init = root => {\n root = $(root);\n loadedPages = [];\n lastPage = 0;\n courseOffset = 0;\n\n if (!root.attr('data-init')) {\n const page = document.querySelector(SELECTORS.region.selectBlock);\n registerEventListeners(root, page);\n namespace = \"block_myoverview_\" + root.attr('id') + \"_\" + Math.random();\n root.attr('data-init', true);\n }\n\n initializePagedContent(root, standardFunctionalityCurry());\n};\n\n/**\n * Reset the courses views to their original\n * state on first page load.courseOffset\n *\n * This is called when configuration has changed for the event lists\n * to cause them to reload their data.\n *\n * @param {Object} root The root element for the timeline view.\n */\nexport const reset = root => {\n if (loadedPages.length > 0) {\n loadedPages.forEach((courseList, index) => {\n let pagedContentPage = getPagedContentContainer(root, index);\n renderCourses(root, courseList).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n });\n } else {\n init(root);\n }\n};\n"],"names":["TEMPLATES","GROUPINGS","NUMCOURSES_PERPAGE","loadedPages","courseOffset","lastPage","lastLimit","namespace","getFilterValues","root","courseRegion","find","SELECTORS","courseView","region","display","attr","grouping","sort","displaycategories","customfieldname","customfieldvalue","DEFAULT_PAGED_CONTENT_CONFIG","ignoreControlWhileLoading","controlPlacementBottom","persistentLimitKey","getFavouriteIconContainer","courseId","FAVOURITE_ICON","getPagedContentContainer","index","getCourseId","getAddFavouriteMenuItem","getRemoveFavouriteMenuItem","addToFavourites","removeAction","addAction","setCourseFavouriteState","then","success","PubSub","publish","CourseEvents","favourited","removeClass","addClass","iconContainer","isFavouriteIcon","ICON_IS_FAVOURITE","Aria","unhide","notFavourteIcon","ICON_NOT_FAVOURITE","hide","showFavouriteIcon","Notification","alert","catch","exception","removeFromFavourites","unfavorited","hideFavouriteIcon","getHideCourseMenuItem","getShowCourseMenuItem","setCourseHiddenState","status","hideElement","id","pagingBar","jumpto","parseInt","reducedCourse","courses","reduce","accumulator","current","push","newElement","slice","forEach","courseList","popElement","length","pagedContentContainer","PagedContentFactory","resetLastPageNumber","pagedContentPage","renderCourses","html","js","Templates","replaceNodeContents","remove","Repository","setFavouriteCourses","result","warnings","course","isfavourite","noCoursesRender","nocoursesimg","newcourseurl","render","coursesData","filters","currentTemplate","Array","isArray","Object","values","map","showcoursecategory","registerPagedEventHandlers","event","PagedContentEvents","SET_ITEMS_PER_PAGE_LIMIT","subscribe","limit","setLimit","itemsPerPageFunc","pagingLimit","itemsPerPage","value","active","totalCourseCount","filter","pagingOption","pageBuilder","currentPage","pageData","actions","activeSearch","nextPageStart","pageCourses","currentPageLength","remainingCourses","allItemsLoaded","nextoffset","resetGlobals","standardFunctionalityCurry","promises","pagePromise","params","offset","classification","requiredfields","SUMMARY_REQUIRED_FIELDS","CARDLIST_REQUIRED_FIELDS","getEnrolledCoursesByTimeline","getMyCourses","searchFunctionalityCurry","inputValue","searchingPromise","searchValue","searchvalue","getSearchMyCourses","initializePagedContent","promiseFunction","config","eventNamespace","pagedContentPromise","createWithLimit","pagesData","pageNumber","registerEventListeners","page","CustomEvents","define","events","activate","on","ACTION_ADD_FAVOURITE","e","data","favourite","target","closest","originalEvent","preventDefault","ACTION_REMOVE_FAVOURITE","ACTION_HIDE_COURSE","hideAction","showAction","hideCourse","ACTION_SHOW_COURSE","showCourse","input","querySelector","searchInput","clearIcon","addEventListener","focus","clearSearch","trim","classList","add","init","document","selectBlock","Math","random"],"mappings":"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"} \ No newline at end of file +{"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 courses view for the overview block.\n *\n * @copyright 2018 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as Repository from 'block_myoverview/repository';\nimport * as PagedContentFactory from 'core/paged_content_factory';\nimport * as PubSub from 'core/pubsub';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as Notification from 'core/notification';\nimport * as Templates from 'core/templates';\nimport * as CourseEvents from 'core_course/events';\nimport SELECTORS from 'block_myoverview/selectors';\nimport * as PagedContentEvents from 'core/paged_content_events';\nimport * as Aria from 'core/aria';\nimport {debounce} from 'core/utils';\nimport {setUserPreference} from 'core_user/repository';\n\nconst TEMPLATES = {\n COURSES_CARDS: 'block_myoverview/view-cards',\n COURSES_LIST: 'block_myoverview/view-list',\n COURSES_SUMMARY: 'block_myoverview/view-summary',\n NOCOURSES: 'core_course/no-courses'\n};\n\nconst GROUPINGS = {\n GROUPING_ALLINCLUDINGHIDDEN: 'allincludinghidden',\n GROUPING_ALL: 'all',\n GROUPING_INPROGRESS: 'inprogress',\n GROUPING_FUTURE: 'future',\n GROUPING_PAST: 'past',\n GROUPING_FAVOURITES: 'favourites',\n GROUPING_HIDDEN: 'hidden'\n};\n\nconst NUMCOURSES_PERPAGE = [12, 24, 48, 96, 0];\n\nlet loadedPages = [];\n\nlet courseOffset = 0;\n\nlet lastPage = 0;\n\nlet lastLimit = 0;\n\nlet namespace = null;\n\n/**\n * Whether the summary display has been loaded.\n *\n * If true, this means that courses have been loaded with the summary text.\n * Otherwise, switching to the summary display mode will require course data to be fetched with the summary text.\n *\n * @type {boolean}\n */\nlet summaryDisplayLoaded = false;\n\n/**\n * Get filter values from DOM.\n *\n * @param {object} root The root element for the courses view.\n * @return {filters} Set filters.\n */\nconst getFilterValues = root => {\n const courseRegion = root.find(SELECTORS.courseView.region);\n return {\n display: courseRegion.attr('data-display'),\n grouping: courseRegion.attr('data-grouping'),\n sort: courseRegion.attr('data-sort'),\n displaycategories: courseRegion.attr('data-displaycategories'),\n customfieldname: courseRegion.attr('data-customfieldname'),\n customfieldvalue: courseRegion.attr('data-customfieldvalue'),\n };\n};\n\n// We want the paged content controls below the paged content area.\n// and the controls should be ignored while data is loading.\nconst DEFAULT_PAGED_CONTENT_CONFIG = {\n ignoreControlWhileLoading: true,\n controlPlacementBottom: true,\n persistentLimitKey: 'block_myoverview_user_paging_preference'\n};\n\n/**\n * Get enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @return {promise} Resolved with an array of courses.\n */\nconst getMyCourses = (filters, limit) => {\n const params = {\n offset: courseOffset,\n limit: limit,\n classification: filters.grouping,\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n };\n if (filters.display === 'summary') {\n params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS;\n summaryDisplayLoaded = true;\n } else {\n params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS;\n }\n return Repository.getEnrolledCoursesByTimeline(params);\n};\n\n/**\n * Search for enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @param {string} searchValue What does the user want to search within their courses.\n * @return {promise} Resolved with an array of courses.\n */\nconst getSearchMyCourses = (filters, limit, searchValue) => {\n const params = {\n offset: courseOffset,\n limit: limit,\n classification: 'search',\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n searchvalue: searchValue,\n };\n if (filters.display === 'summary') {\n params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS;\n summaryDisplayLoaded = true;\n } else {\n params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS;\n summaryDisplayLoaded = false;\n }\n return Repository.getEnrolledCoursesByTimeline(params);\n};\n\n/**\n * Get the container element for the favourite icon.\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n * @return {Object} The favourite icon container\n */\nconst getFavouriteIconContainer = (root, courseId) => {\n return root.find(SELECTORS.FAVOURITE_ICON + '[data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the paged content container element.\n *\n * @param {Object} root The course overview container\n * @param {Number} index Rendered page index.\n * @return {Object} The rendered paged container.\n */\nconst getPagedContentContainer = (root, index) => {\n return root.find('[data-region=\"paged-content-page\"][data-page=\"' + index + '\"]');\n};\n\n/**\n * Get the course id from a favourite element.\n *\n * @param {Object} root The favourite icon container element.\n * @return {Number} Course id.\n */\nconst getCourseId = root => {\n return root.attr('data-course-id');\n};\n\n/**\n * Hide the favourite icon.\n *\n * @param {Object} root The favourite icon container element.\n * @param {Number} courseId Course id number.\n */\nconst hideFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.addClass('hidden');\n Aria.hide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.removeClass('hidden');\n Aria.unhide(notFavourteIcon);\n};\n\n/**\n * Show the favourite icon.\n *\n * @param {Object} root The course overview container.\n * @param {Number} courseId Course id number.\n */\nconst showFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.removeClass('hidden');\n Aria.unhide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.addClass('hidden');\n Aria.hide(notFavourteIcon);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The add to favourite menu item.\n */\nconst getAddFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"add-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The remove from favourites menu item.\n */\nconst getRemoveFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"remove-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Add course to favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst addToFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, true).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.favourited, courseId);\n removeAction.removeClass('hidden');\n addAction.addClass('hidden');\n showFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Remove course from favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst removeFromFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, false).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.unfavorited, courseId);\n removeAction.addClass('hidden');\n addAction.removeClass('hidden');\n hideFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The hide course menu item.\n */\nconst getHideCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"hide-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The show course menu item.\n */\nconst getShowCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"show-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Hide course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst hideCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, true);\n\n // Remove the course from this view as it is now hidden and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.addClass('hidden');\n showAction.removeClass('hidden');\n};\n\n/**\n * Show course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst showCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, null);\n\n // Remove the course from this view as it is now shown again and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.removeClass('hidden');\n showAction.addClass('hidden');\n};\n\n/**\n * Set the courses hidden status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {Boolean} status new hidden status.\n * @return {Promise} Repository promise.\n */\nconst setCourseHiddenState = (courseId, status) => {\n\n // If the given status is not hidden, the preference has to be deleted with a null value.\n if (status === false) {\n status = null;\n }\n\n return setUserPreference(`block_myoverview_hidden_course_${courseId}`, status)\n .catch(Notification.exception);\n};\n\n/**\n * Reset the loadedPages dataset to take into account the hidden element\n *\n * @param {Object} root The course overview container\n * @param {Number} id The course id number\n */\nconst hideElement = (root, id) => {\n const pagingBar = root.find('[data-region=\"paging-bar\"]');\n const jumpto = parseInt(pagingBar.attr('data-active-page-number'));\n\n // Get a reduced dataset for the current page.\n const courseList = loadedPages[jumpto];\n let reducedCourse = courseList.courses.reduce((accumulator, current) => {\n if (+id !== +current.id) {\n accumulator.push(current);\n }\n return accumulator;\n }, []);\n\n // Get the next page's data if loaded and pop the first element from it.\n if (typeof (loadedPages[jumpto + 1]) !== 'undefined') {\n const newElement = loadedPages[jumpto + 1].courses.slice(0, 1);\n\n // Adjust the dataset for the reset of the pages that are loaded.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n let popElement = [];\n if (typeof (loadedPages[index + 1]) !== 'undefined') {\n popElement = loadedPages[index + 1].courses.slice(0, 1);\n }\n loadedPages[index].courses = [...loadedPages[index].courses.slice(1), ...popElement];\n }\n });\n\n reducedCourse = [...reducedCourse, ...newElement];\n }\n\n // Check if the next page is the last page and if it still has data associated to it.\n if (lastPage === jumpto + 1 && loadedPages[jumpto + 1].courses.length === 0) {\n const pagedContentContainer = root.find('[data-region=\"paged-content-container\"]');\n PagedContentFactory.resetLastPageNumber($(pagedContentContainer).attr('id'), jumpto);\n }\n\n loadedPages[jumpto].courses = reducedCourse;\n\n // Reduce the course offset.\n courseOffset--;\n\n // Render the paged content for the current.\n const pagedContentPage = getPagedContentContainer(root, jumpto);\n renderCourses(root, loadedPages[jumpto]).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n\n // Delete subsequent pages in order to trigger the callback.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n const page = getPagedContentContainer(root, index);\n page.remove();\n }\n });\n};\n\n/**\n * Set the courses favourite status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {boolean} status new favourite status.\n * @return {Promise} Repository promise.\n */\nconst setCourseFavouriteState = (courseId, status) => {\n\n return Repository.setFavouriteCourses({\n courses: [\n {\n 'id': courseId,\n 'favourite': status\n }\n ]\n }).then(result => {\n if (result.warnings.length === 0) {\n loadedPages.forEach(courseList => {\n courseList.courses.forEach((course, index) => {\n if (course.id == courseId) {\n courseList.courses[index].isfavourite = status;\n }\n });\n });\n return true;\n } else {\n return false;\n }\n }).catch(Notification.exception);\n};\n\n/**\n * Given there are no courses to render provide the rendered template.\n *\n * @param {object} root The root element for the courses view.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst noCoursesRender = root => {\n const nocoursesimg = root.find(SELECTORS.courseView.region).attr('data-nocoursesimg');\n const newcourseurl = root.find(SELECTORS.courseView.region).attr('data-newcourseurl');\n return Templates.render(TEMPLATES.NOCOURSES, {\n nocoursesimg: nocoursesimg,\n newcourseurl: newcourseurl\n });\n};\n\n/**\n * Render the dashboard courses.\n *\n * @param {object} root The root element for the courses view.\n * @param {array} coursesData containing array of returned courses.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst renderCourses = (root, coursesData) => {\n\n const filters = getFilterValues(root);\n\n let currentTemplate = '';\n if (filters.display === 'card') {\n currentTemplate = TEMPLATES.COURSES_CARDS;\n } else if (filters.display === 'list') {\n currentTemplate = TEMPLATES.COURSES_LIST;\n } else {\n currentTemplate = TEMPLATES.COURSES_SUMMARY;\n }\n\n if (!coursesData) {\n return noCoursesRender(root);\n } else {\n // Sometimes we get weird objects coming after a failed search, cast to ensure typing functions.\n if (Array.isArray(coursesData.courses) === false) {\n coursesData.courses = Object.values(coursesData.courses);\n }\n // Whether the course category should be displayed in the course item.\n coursesData.courses = coursesData.courses.map(course => {\n course.showcoursecategory = filters.displaycategories === 'on';\n return course;\n });\n if (coursesData.courses.length) {\n return Templates.render(currentTemplate, {\n courses: coursesData.courses,\n });\n } else {\n return noCoursesRender(root);\n }\n }\n};\n\n/**\n * Return the callback to be passed to the subscribe event\n *\n * @param {object} root The root element for the courses view\n * @return {function} Partially applied function that'll execute when passed a limit\n */\nconst setLimit = root => {\n // @param {Number} limit The paged limit that is passed through the event.\n return limit => root.find(SELECTORS.courseView.region).attr('data-paging', limit);\n};\n\n/**\n * Intialise the paged list and cards views on page load.\n * Returns an array of paged contents that we would like to handle here\n *\n * @param {object} root The root element for the courses view\n * @param {string} namespace The namespace for all the events attached\n */\nconst registerPagedEventHandlers = (root, namespace) => {\n const event = namespace + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;\n PubSub.subscribe(event, setLimit(root));\n};\n\n/**\n * Figure out how many items are going to be allowed to be rendered in the block.\n *\n * @param {Number} pagingLimit How many courses to display\n * @param {Object} root The course overview container\n * @return {Number[]} How many courses will be rendered\n */\nconst itemsPerPageFunc = (pagingLimit, root) => {\n let itemsPerPage = NUMCOURSES_PERPAGE.map(value => {\n let active = false;\n if (value === pagingLimit) {\n active = true;\n }\n\n return {\n value: value,\n active: active\n };\n });\n\n // Filter out all pagination options which are too large for the amount of courses user is enrolled in.\n const totalCourseCount = parseInt(root.find(SELECTORS.courseView.region).attr('data-totalcoursecount'), 10);\n return itemsPerPage.filter(pagingOption => {\n return pagingOption.value < totalCourseCount || pagingOption.value === 0;\n });\n};\n\n/**\n * Mutates and controls the loadedPages array and handles the bootstrapping.\n *\n * @param {Array|Object} coursesData Array of all of the courses to start building the page from\n * @param {Number} currentPage What page are we currently on?\n * @param {Object} pageData Any current page information\n * @param {Object} actions Paged content helper\n * @param {null|boolean} activeSearch Are we currently actively searching and building up search results?\n */\nconst pageBuilder = (coursesData, currentPage, pageData, actions, activeSearch = null) => {\n // If the courseData comes in an object then get the value otherwise it is a pure array.\n let courses = coursesData.courses ? coursesData.courses : coursesData;\n let nextPageStart = 0;\n let pageCourses = [];\n\n // If current page's data is loaded make sure we max it to page limit.\n if (typeof (loadedPages[currentPage]) !== 'undefined') {\n pageCourses = loadedPages[currentPage].courses;\n const currentPageLength = pageCourses.length;\n if (currentPageLength < pageData.limit) {\n nextPageStart = pageData.limit - currentPageLength;\n pageCourses = {...loadedPages[currentPage].courses, ...courses.slice(0, nextPageStart)};\n }\n } else {\n // When the page limit is zero, there is only one page of courses, no start for next page.\n nextPageStart = pageData.limit || false;\n pageCourses = (pageData.limit > 0) ? courses.slice(0, pageData.limit) : courses;\n }\n\n // Finished setting up the current page.\n loadedPages[currentPage] = {\n courses: pageCourses\n };\n\n // Set up the next page (if there is more than one page).\n const remainingCourses = nextPageStart !== false ? courses.slice(nextPageStart, courses.length) : [];\n if (remainingCourses.length) {\n loadedPages[currentPage + 1] = {\n courses: remainingCourses\n };\n }\n\n // Set the last page to either the current or next page.\n if (loadedPages[currentPage].courses.length < pageData.limit || !remainingCourses.length) {\n lastPage = currentPage;\n if (activeSearch === null) {\n actions.allItemsLoaded(currentPage);\n }\n } else if (typeof (loadedPages[currentPage + 1]) !== 'undefined'\n && loadedPages[currentPage + 1].courses.length < pageData.limit) {\n lastPage = currentPage + 1;\n }\n\n courseOffset = coursesData.nextoffset;\n};\n\n/**\n * In cases when switching between regular rendering and search rendering we need to reset some variables.\n */\nconst resetGlobals = () => {\n courseOffset = 0;\n loadedPages = [];\n lastPage = 0;\n lastLimit = 0;\n};\n\n/**\n * The default functionality of fetching paginated courses without special handling.\n *\n * @return {function(Object, Object, Object, Object, Object, Promise, Number): void}\n */\nconst standardFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit) => {\n const pagePromise = getMyCourses(\n filters,\n limit\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(pagePromise);\n };\n};\n\n/**\n * Initialize the searching functionality so we can call it when required.\n *\n * @return {function(Object, Number, Object, Object, Object, Promise, Number, String): void}\n */\nconst searchFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit, inputValue) => {\n const searchingPromise = getSearchMyCourses(\n filters,\n limit,\n inputValue\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(searchingPromise);\n };\n};\n\n/**\n * Initialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n * @param {function} promiseFunction How do we fetch the courses and what do we do with them?\n * @param {null | string} inputValue What to search for\n */\nconst initializePagedContent = (root, promiseFunction, inputValue = null) => {\n const pagingLimit = parseInt(root.find(SELECTORS.courseView.region).attr('data-paging'), 10);\n let itemsPerPage = itemsPerPageFunc(pagingLimit, root);\n\n const filters = getFilterValues(root);\n const config = {...{}, ...DEFAULT_PAGED_CONTENT_CONFIG};\n config.eventNamespace = namespace;\n\n const pagedContentPromise = PagedContentFactory.createWithLimit(\n itemsPerPage,\n (pagesData, actions) => {\n let promises = [];\n pagesData.forEach(pageData => {\n const currentPage = pageData.pageNumber;\n let limit = (pageData.limit > 0) ? pageData.limit : 0;\n\n // Reset local variables if limits have changed.\n if (+lastLimit !== +limit) {\n loadedPages = [];\n courseOffset = 0;\n lastPage = 0;\n }\n\n if (lastPage === currentPage) {\n // If we are on the last page and have it's data then load it from cache.\n actions.allItemsLoaded(lastPage);\n promises.push(renderCourses(root, loadedPages[currentPage]));\n return;\n }\n\n lastLimit = limit;\n\n // Get 2 pages worth of data as we will need it for the hidden functionality.\n if (typeof (loadedPages[currentPage + 1]) === 'undefined') {\n if (typeof (loadedPages[currentPage]) === 'undefined') {\n limit *= 2;\n }\n }\n\n // Call the curried function that'll handle the course promise and any manipulation of it.\n promiseFunction(filters, currentPage, pageData, actions, root, promises, limit, inputValue);\n });\n return promises;\n },\n config\n );\n\n pagedContentPromise.then((html, js) => {\n registerPagedEventHandlers(root, namespace);\n return Templates.replaceNodeContents(root.find(SELECTORS.courseView.region), html, js);\n }).catch(Notification.exception);\n};\n\n/**\n * Listen to, and handle events for the myoverview block.\n *\n * @param {Object} root The myoverview block container element.\n * @param {HTMLElement} page The whole HTMLElement for our block.\n */\nconst registerEventListeners = (root, page) => {\n\n CustomEvents.define(root, [\n CustomEvents.events.activate\n ]);\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_ADD_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_ADD_FAVOURITE);\n const courseId = getCourseId(favourite);\n addToFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_REMOVE_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_REMOVE_FAVOURITE);\n const courseId = getCourseId(favourite);\n removeFromFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.FAVOURITE_ICON, (e, data) => {\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_HIDE_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_HIDE_COURSE);\n const courseId = getCourseId(target);\n hideCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_SHOW_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_SHOW_COURSE);\n const courseId = getCourseId(target);\n showCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n // Searching functionality event handlers.\n const input = page.querySelector(SELECTORS.region.searchInput);\n const clearIcon = page.querySelector(SELECTORS.region.clearIcon);\n\n clearIcon.addEventListener('click', () => {\n input.value = '';\n input.focus();\n clearSearch(clearIcon, root);\n });\n\n input.addEventListener('input', debounce(() => {\n if (input.value === '') {\n clearSearch(clearIcon, root);\n } else {\n activeSearch(clearIcon);\n initializePagedContent(root, searchFunctionalityCurry(), input.value.trim());\n }\n }, 1000));\n};\n\n/**\n * Reset the search icon and trigger the init for the block.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n * @param {Object} root The myoverview block container element.\n */\nexport const clearSearch = (clearIcon, root) => {\n clearIcon.classList.add('d-none');\n init(root);\n};\n\n/**\n * Change the searching icon to its' active state.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n */\nconst activeSearch = (clearIcon) => {\n clearIcon.classList.remove('d-none');\n};\n\n/**\n * Intialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n */\nexport const init = root => {\n root = $(root);\n loadedPages = [];\n lastPage = 0;\n courseOffset = 0;\n\n if (!root.attr('data-init')) {\n const page = document.querySelector(SELECTORS.region.selectBlock);\n registerEventListeners(root, page);\n namespace = \"block_myoverview_\" + root.attr('id') + \"_\" + Math.random();\n root.attr('data-init', true);\n }\n\n initializePagedContent(root, standardFunctionalityCurry());\n};\n\n/**\n * Reset the courses views to their original\n * state on first page load.courseOffset\n *\n * This is called when configuration has changed for the event lists\n * to cause them to reload their data.\n *\n * @param {Object} root The root element for the timeline view.\n */\nexport const reset = root => {\n if (loadedPages.length > 0) {\n const filters = getFilterValues(root);\n // If the display mode is changed to 'summary' but the summary display has not been loaded yet,\n // we need to re-fetch the courses to include the course summary text.\n if (filters.display === 'summary' && !summaryDisplayLoaded) {\n const page = document.querySelector(SELECTORS.region.selectBlock);\n const input = page.querySelector(SELECTORS.region.searchInput);\n if (input.value !== '') {\n initializePagedContent(root, searchFunctionalityCurry(), input.value.trim());\n } else {\n initializePagedContent(root, standardFunctionalityCurry());\n }\n } else {\n loadedPages.forEach((courseList, index) => {\n let pagedContentPage = getPagedContentContainer(root, index);\n renderCourses(root, courseList).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n });\n }\n } else {\n init(root);\n }\n};\n"],"names":["TEMPLATES","GROUPINGS","NUMCOURSES_PERPAGE","loadedPages","courseOffset","lastPage","lastLimit","namespace","summaryDisplayLoaded","getFilterValues","root","courseRegion","find","SELECTORS","courseView","region","display","attr","grouping","sort","displaycategories","customfieldname","customfieldvalue","DEFAULT_PAGED_CONTENT_CONFIG","ignoreControlWhileLoading","controlPlacementBottom","persistentLimitKey","getFavouriteIconContainer","courseId","FAVOURITE_ICON","getPagedContentContainer","index","getCourseId","getAddFavouriteMenuItem","getRemoveFavouriteMenuItem","addToFavourites","removeAction","addAction","setCourseFavouriteState","then","success","PubSub","publish","CourseEvents","favourited","removeClass","addClass","iconContainer","isFavouriteIcon","ICON_IS_FAVOURITE","Aria","unhide","notFavourteIcon","ICON_NOT_FAVOURITE","hide","showFavouriteIcon","Notification","alert","catch","exception","removeFromFavourites","unfavorited","hideFavouriteIcon","getHideCourseMenuItem","getShowCourseMenuItem","setCourseHiddenState","status","hideElement","id","pagingBar","jumpto","parseInt","reducedCourse","courses","reduce","accumulator","current","push","newElement","slice","forEach","courseList","popElement","length","pagedContentContainer","PagedContentFactory","resetLastPageNumber","pagedContentPage","renderCourses","html","js","Templates","replaceNodeContents","remove","Repository","setFavouriteCourses","result","warnings","course","isfavourite","noCoursesRender","nocoursesimg","newcourseurl","render","coursesData","filters","currentTemplate","Array","isArray","Object","values","map","showcoursecategory","registerPagedEventHandlers","event","PagedContentEvents","SET_ITEMS_PER_PAGE_LIMIT","subscribe","limit","setLimit","itemsPerPageFunc","pagingLimit","itemsPerPage","value","active","totalCourseCount","filter","pagingOption","pageBuilder","currentPage","pageData","actions","activeSearch","nextPageStart","pageCourses","currentPageLength","remainingCourses","allItemsLoaded","nextoffset","resetGlobals","standardFunctionalityCurry","promises","pagePromise","params","offset","classification","requiredfields","SUMMARY_REQUIRED_FIELDS","CARDLIST_REQUIRED_FIELDS","getEnrolledCoursesByTimeline","getMyCourses","searchFunctionalityCurry","inputValue","searchingPromise","searchValue","searchvalue","getSearchMyCourses","initializePagedContent","promiseFunction","config","eventNamespace","pagedContentPromise","createWithLimit","pagesData","pageNumber","registerEventListeners","page","CustomEvents","define","events","activate","on","ACTION_ADD_FAVOURITE","e","data","favourite","target","closest","originalEvent","preventDefault","ACTION_REMOVE_FAVOURITE","ACTION_HIDE_COURSE","hideAction","showAction","hideCourse","ACTION_SHOW_COURSE","showCourse","input","querySelector","searchInput","clearIcon","addEventListener","focus","clearSearch","trim","classList","add","init","document","selectBlock","Math","random"],"mappings":"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"} \ No newline at end of file diff --git a/blocks/myoverview/amd/src/view.js b/blocks/myoverview/amd/src/view.js index 77cff2e68f0..08cdbb6abb2 100644 --- a/blocks/myoverview/amd/src/view.js +++ b/blocks/myoverview/amd/src/view.js @@ -63,6 +63,16 @@ let lastLimit = 0; let namespace = null; +/** + * Whether the summary display has been loaded. + * + * If true, this means that courses have been loaded with the summary text. + * Otherwise, switching to the summary display mode will require course data to be fetched with the summary text. + * + * @type {boolean} + */ +let summaryDisplayLoaded = false; + /** * Get filter values from DOM. * @@ -107,6 +117,7 @@ const getMyCourses = (filters, limit) => { }; if (filters.display === 'summary') { params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS; + summaryDisplayLoaded = true; } else { params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS; } @@ -133,8 +144,10 @@ const getSearchMyCourses = (filters, limit, searchValue) => { }; if (filters.display === 'summary') { params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS; + summaryDisplayLoaded = true; } else { params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS; + summaryDisplayLoaded = false; } return Repository.getEnrolledCoursesByTimeline(params); }; @@ -846,12 +859,25 @@ export const init = root => { */ export const reset = root => { if (loadedPages.length > 0) { - loadedPages.forEach((courseList, index) => { - let pagedContentPage = getPagedContentContainer(root, index); - renderCourses(root, courseList).then((html, js) => { - return Templates.replaceNodeContents(pagedContentPage, html, js); - }).catch(Notification.exception); - }); + const filters = getFilterValues(root); + // If the display mode is changed to 'summary' but the summary display has not been loaded yet, + // we need to re-fetch the courses to include the course summary text. + if (filters.display === 'summary' && !summaryDisplayLoaded) { + const page = document.querySelector(SELECTORS.region.selectBlock); + const input = page.querySelector(SELECTORS.region.searchInput); + if (input.value !== '') { + initializePagedContent(root, searchFunctionalityCurry(), input.value.trim()); + } else { + initializePagedContent(root, standardFunctionalityCurry()); + } + } else { + loadedPages.forEach((courseList, index) => { + let pagedContentPage = getPagedContentContainer(root, index); + renderCourses(root, courseList).then((html, js) => { + return Templates.replaceNodeContents(pagedContentPage, html, js); + }).catch(Notification.exception); + }); + } } else { init(root); } From 8c26445d8ded1bd709393e2052a1a44db5163623 Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Mon, 19 Feb 2024 12:07:43 +0800 Subject: [PATCH 4/5] MDL-78370 block_myoverview: Conditionally show "All" paging option To minimise performance issues when displaying courses in Summary view, we need to conditionally show the "All" paging option. This commit will prevent the "All" paging option to be shown if there are more than 100 courses to be shown. --- blocks/myoverview/amd/build/view.min.js | 2 +- blocks/myoverview/amd/build/view.min.js.map | 2 +- blocks/myoverview/amd/src/view.js | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/blocks/myoverview/amd/build/view.min.js b/blocks/myoverview/amd/build/view.min.js index 347911a6808..af66365f27a 100644 --- a/blocks/myoverview/amd/build/view.min.js +++ b/blocks/myoverview/amd/build/view.min.js @@ -4,6 +4,6 @@ define("block_myoverview/view",["exports","jquery","block_myoverview/repository" * * @copyright 2018 Bas Brands * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.reset=_exports.init=_exports.clearSearch=void 0,_jquery=_interopRequireDefault(_jquery),Repository=_interopRequireWildcard(Repository),PagedContentFactory=_interopRequireWildcard(PagedContentFactory),PubSub=_interopRequireWildcard(PubSub),CustomEvents=_interopRequireWildcard(CustomEvents),Notification=_interopRequireWildcard(Notification),Templates=_interopRequireWildcard(Templates),CourseEvents=_interopRequireWildcard(CourseEvents),_selectors=_interopRequireDefault(_selectors),PagedContentEvents=_interopRequireWildcard(PagedContentEvents),Aria=_interopRequireWildcard(Aria);const TEMPLATES_COURSES_CARDS="block_myoverview/view-cards",TEMPLATES_COURSES_LIST="block_myoverview/view-list",TEMPLATES_COURSES_SUMMARY="block_myoverview/view-summary",TEMPLATES_NOCOURSES="core_course/no-courses",GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN="allincludinghidden",NUMCOURSES_PERPAGE=[12,24,48,96,0];let loadedPages=[],courseOffset=0,lastPage=0,lastLimit=0,namespace=null,summaryDisplayLoaded=!1;const getFilterValues=root=>{const courseRegion=root.find(_selectors.default.courseView.region);return{display:courseRegion.attr("data-display"),grouping:courseRegion.attr("data-grouping"),sort:courseRegion.attr("data-sort"),displaycategories:courseRegion.attr("data-displaycategories"),customfieldname:courseRegion.attr("data-customfieldname"),customfieldvalue:courseRegion.attr("data-customfieldvalue")}},DEFAULT_PAGED_CONTENT_CONFIG={ignoreControlWhileLoading:!0,controlPlacementBottom:!0,persistentLimitKey:"block_myoverview_user_paging_preference"},getFavouriteIconContainer=(root,courseId)=>root.find(_selectors.default.FAVOURITE_ICON+'[data-course-id="'+courseId+'"]'),getPagedContentContainer=(root,index)=>root.find('[data-region="paged-content-page"][data-page="'+index+'"]'),getCourseId=root=>root.attr("data-course-id"),getAddFavouriteMenuItem=(root,courseId)=>root.find('[data-action="add-favourite"][data-course-id="'+courseId+'"]'),getRemoveFavouriteMenuItem=(root,courseId)=>root.find('[data-action="remove-favourite"][data-course-id="'+courseId+'"]'),addToFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!0).then((success=>{success?(PubSub.publish(CourseEvents.favourited,courseId),removeAction.removeClass("hidden"),addAction.addClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.removeClass("hidden"),Aria.unhide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.addClass("hidden"),Aria.hide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},removeFromFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!1).then((success=>{success?(PubSub.publish(CourseEvents.unfavorited,courseId),removeAction.addClass("hidden"),addAction.removeClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.addClass("hidden"),Aria.hide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.removeClass("hidden"),Aria.unhide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},getHideCourseMenuItem=(root,courseId)=>root.find('[data-action="hide-course"][data-course-id="'+courseId+'"]'),getShowCourseMenuItem=(root,courseId)=>root.find('[data-action="show-course"][data-course-id="'+courseId+'"]'),setCourseHiddenState=(courseId,status)=>(!1===status&&(status=null),(0,_repository2.setUserPreference)("block_myoverview_hidden_course_".concat(courseId),status).catch(Notification.exception)),hideElement=(root,id)=>{const pagingBar=root.find('[data-region="paging-bar"]'),jumpto=parseInt(pagingBar.attr("data-active-page-number"));let reducedCourse=loadedPages[jumpto].courses.reduce(((accumulator,current)=>(+id!=+current.id&&accumulator.push(current),accumulator)),[]);if(void 0!==loadedPages[jumpto+1]){const newElement=loadedPages[jumpto+1].courses.slice(0,1);loadedPages.forEach(((courseList,index)=>{if(index>jumpto){let popElement=[];void 0!==loadedPages[index+1]&&(popElement=loadedPages[index+1].courses.slice(0,1)),loadedPages[index].courses=[...loadedPages[index].courses.slice(1),...popElement]}})),reducedCourse=[...reducedCourse,...newElement]}if(lastPage===jumpto+1&&0===loadedPages[jumpto+1].courses.length){const pagedContentContainer=root.find('[data-region="paged-content-container"]');PagedContentFactory.resetLastPageNumber((0,_jquery.default)(pagedContentContainer).attr("id"),jumpto)}loadedPages[jumpto].courses=reducedCourse,courseOffset--;const pagedContentPage=getPagedContentContainer(root,jumpto);renderCourses(root,loadedPages[jumpto]).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception),loadedPages.forEach(((courseList,index)=>{if(index>jumpto){getPagedContentContainer(root,index).remove()}}))},setCourseFavouriteState=(courseId,status)=>Repository.setFavouriteCourses({courses:[{id:courseId,favourite:status}]}).then((result=>0===result.warnings.length&&(loadedPages.forEach((courseList=>{courseList.courses.forEach(((course,index)=>{course.id==courseId&&(courseList.courses[index].isfavourite=status)}))})),!0))).catch(Notification.exception),noCoursesRender=root=>{const nocoursesimg=root.find(_selectors.default.courseView.region).attr("data-nocoursesimg"),newcourseurl=root.find(_selectors.default.courseView.region).attr("data-newcourseurl");return Templates.render(TEMPLATES_NOCOURSES,{nocoursesimg:nocoursesimg,newcourseurl:newcourseurl})},renderCourses=(root,coursesData)=>{const filters=getFilterValues(root);let currentTemplate="";return currentTemplate="card"===filters.display?TEMPLATES_COURSES_CARDS:"list"===filters.display?TEMPLATES_COURSES_LIST:TEMPLATES_COURSES_SUMMARY,coursesData?(!1===Array.isArray(coursesData.courses)&&(coursesData.courses=Object.values(coursesData.courses)),coursesData.courses=coursesData.courses.map((course=>(course.showcoursecategory="on"===filters.displaycategories,course))),coursesData.courses.length?Templates.render(currentTemplate,{courses:coursesData.courses}):noCoursesRender(root)):noCoursesRender(root)},registerPagedEventHandlers=(root,namespace)=>{const event=namespace+PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;PubSub.subscribe(event,(root=>limit=>root.find(_selectors.default.courseView.region).attr("data-paging",limit))(root))},itemsPerPageFunc=(pagingLimit,root)=>{let itemsPerPage=NUMCOURSES_PERPAGE.map((value=>{let active=!1;return value===pagingLimit&&(active=!0),{value:value,active:active}}));const totalCourseCount=parseInt(root.find(_selectors.default.courseView.region).attr("data-totalcoursecount"),10);return itemsPerPage.filter((pagingOption=>pagingOption.value4&&void 0!==arguments[4]?arguments[4]:null,courses=coursesData.courses?coursesData.courses:coursesData,nextPageStart=0,pageCourses=[];if(void 0!==loadedPages[currentPage]){pageCourses=loadedPages[currentPage].courses;const currentPageLength=pageCourses.length;currentPageLength0?courses.slice(0,pageData.limit):courses;loadedPages[currentPage]={courses:pageCourses};const remainingCourses=!1!==nextPageStart?courses.slice(nextPageStart,courses.length):[];remainingCourses.length&&(loadedPages[currentPage+1]={courses:remainingCourses}),loadedPages[currentPage].courses.length{courseOffset=0,loadedPages=[],lastPage=0,lastLimit=0},standardFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit)=>{const pagePromise=((filters,limit)=>{const params={offset:courseOffset,limit:limit,classification:filters.grouping,sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue};return"summary"===filters.display?(params.requiredfields=Repository.SUMMARY_REQUIRED_FIELDS,summaryDisplayLoaded=!0):params.requiredfields=Repository.CARDLIST_REQUIRED_FIELDS,Repository.getEnrolledCoursesByTimeline(params)})(filters,limit).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(pagePromise)}),searchFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit,inputValue)=>{const searchingPromise=((filters,limit,searchValue)=>{const params={offset:courseOffset,limit:limit,classification:"search",sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue,searchvalue:searchValue};return"summary"===filters.display?(params.requiredfields=Repository.SUMMARY_REQUIRED_FIELDS,summaryDisplayLoaded=!0):(params.requiredfields=Repository.CARDLIST_REQUIRED_FIELDS,summaryDisplayLoaded=!1),Repository.getEnrolledCoursesByTimeline(params)})(filters,limit,inputValue).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(searchingPromise)}),initializePagedContent=function(root,promiseFunction){let inputValue=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const pagingLimit=parseInt(root.find(_selectors.default.courseView.region).attr("data-paging"),10);let itemsPerPage=itemsPerPageFunc(pagingLimit,root);const filters=getFilterValues(root),config={...DEFAULT_PAGED_CONTENT_CONFIG};config.eventNamespace=namespace;const pagedContentPromise=PagedContentFactory.createWithLimit(itemsPerPage,((pagesData,actions)=>{let promises=[];return pagesData.forEach((pageData=>{const currentPage=pageData.pageNumber;let limit=pageData.limit>0?pageData.limit:0;if(+lastLimit!=+limit&&(loadedPages=[],courseOffset=0,lastPage=0),lastPage===currentPage)return actions.allItemsLoaded(lastPage),void promises.push(renderCourses(root,loadedPages[currentPage]));lastLimit=limit,void 0===loadedPages[currentPage+1]&&void 0===loadedPages[currentPage]&&(limit*=2),promiseFunction(filters,currentPage,pageData,actions,root,promises,limit,inputValue)})),promises}),config);pagedContentPromise.then(((html,js)=>(registerPagedEventHandlers(root,namespace),Templates.replaceNodeContents(root.find(_selectors.default.courseView.region),html,js)))).catch(Notification.exception)},registerEventListeners=(root,page)=>{CustomEvents.define(root,[CustomEvents.events.activate]),root.on(CustomEvents.events.activate,_selectors.default.ACTION_ADD_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_ADD_FAVOURITE),courseId=getCourseId(favourite);addToFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_REMOVE_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_REMOVE_FAVOURITE),courseId=getCourseId(favourite);removeFromFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.FAVOURITE_ICON,((e,data)=>{data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_HIDE_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_HIDE_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,!0),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.addClass("hidden"),showAction.removeClass("hidden")})(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_SHOW_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_SHOW_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,null),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.removeClass("hidden"),showAction.addClass("hidden")})(root,courseId),data.originalEvent.preventDefault()}));const input=page.querySelector(_selectors.default.region.searchInput),clearIcon=page.querySelector(_selectors.default.region.clearIcon);clearIcon.addEventListener("click",(()=>{input.value="",input.focus(),clearSearch(clearIcon,root)})),input.addEventListener("input",(0,_utils.debounce)((()=>{""===input.value?clearSearch(clearIcon,root):(activeSearch(clearIcon),initializePagedContent(root,searchFunctionalityCurry(),input.value.trim()))}),1e3))},clearSearch=(clearIcon,root)=>{clearIcon.classList.add("d-none"),init(root)};_exports.clearSearch=clearSearch;const activeSearch=clearIcon=>{clearIcon.classList.remove("d-none")},init=root=>{if(root=(0,_jquery.default)(root),loadedPages=[],lastPage=0,courseOffset=0,!root.attr("data-init")){const page=document.querySelector(_selectors.default.region.selectBlock);registerEventListeners(root,page),namespace="block_myoverview_"+root.attr("id")+"_"+Math.random(),root.attr("data-init",!0)}initializePagedContent(root,standardFunctionalityCurry())};_exports.init=init;_exports.reset=root=>{if(loadedPages.length>0){if("summary"!==getFilterValues(root).display||summaryDisplayLoaded)loadedPages.forEach(((courseList,index)=>{let pagedContentPage=getPagedContentContainer(root,index);renderCourses(root,courseList).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception)}));else{const input=document.querySelector(_selectors.default.region.selectBlock).querySelector(_selectors.default.region.searchInput);""!==input.value?initializePagedContent(root,searchFunctionalityCurry(),input.value.trim()):initializePagedContent(root,standardFunctionalityCurry())}}else init(root)}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.reset=_exports.init=_exports.clearSearch=void 0,_jquery=_interopRequireDefault(_jquery),Repository=_interopRequireWildcard(Repository),PagedContentFactory=_interopRequireWildcard(PagedContentFactory),PubSub=_interopRequireWildcard(PubSub),CustomEvents=_interopRequireWildcard(CustomEvents),Notification=_interopRequireWildcard(Notification),Templates=_interopRequireWildcard(Templates),CourseEvents=_interopRequireWildcard(CourseEvents),_selectors=_interopRequireDefault(_selectors),PagedContentEvents=_interopRequireWildcard(PagedContentEvents),Aria=_interopRequireWildcard(Aria);const TEMPLATES_COURSES_CARDS="block_myoverview/view-cards",TEMPLATES_COURSES_LIST="block_myoverview/view-list",TEMPLATES_COURSES_SUMMARY="block_myoverview/view-summary",TEMPLATES_NOCOURSES="core_course/no-courses",GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN="allincludinghidden",NUMCOURSES_PERPAGE=[12,24,48,96,0];let loadedPages=[],courseOffset=0,lastPage=0,lastLimit=0,namespace=null,summaryDisplayLoaded=!1;const getFilterValues=root=>{const courseRegion=root.find(_selectors.default.courseView.region);return{display:courseRegion.attr("data-display"),grouping:courseRegion.attr("data-grouping"),sort:courseRegion.attr("data-sort"),displaycategories:courseRegion.attr("data-displaycategories"),customfieldname:courseRegion.attr("data-customfieldname"),customfieldvalue:courseRegion.attr("data-customfieldvalue")}},DEFAULT_PAGED_CONTENT_CONFIG={ignoreControlWhileLoading:!0,controlPlacementBottom:!0,persistentLimitKey:"block_myoverview_user_paging_preference"},getFavouriteIconContainer=(root,courseId)=>root.find(_selectors.default.FAVOURITE_ICON+'[data-course-id="'+courseId+'"]'),getPagedContentContainer=(root,index)=>root.find('[data-region="paged-content-page"][data-page="'+index+'"]'),getCourseId=root=>root.attr("data-course-id"),getAddFavouriteMenuItem=(root,courseId)=>root.find('[data-action="add-favourite"][data-course-id="'+courseId+'"]'),getRemoveFavouriteMenuItem=(root,courseId)=>root.find('[data-action="remove-favourite"][data-course-id="'+courseId+'"]'),addToFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!0).then((success=>{success?(PubSub.publish(CourseEvents.favourited,courseId),removeAction.removeClass("hidden"),addAction.addClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.removeClass("hidden"),Aria.unhide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.addClass("hidden"),Aria.hide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},removeFromFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!1).then((success=>{success?(PubSub.publish(CourseEvents.unfavorited,courseId),removeAction.addClass("hidden"),addAction.removeClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.addClass("hidden"),Aria.hide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.removeClass("hidden"),Aria.unhide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},getHideCourseMenuItem=(root,courseId)=>root.find('[data-action="hide-course"][data-course-id="'+courseId+'"]'),getShowCourseMenuItem=(root,courseId)=>root.find('[data-action="show-course"][data-course-id="'+courseId+'"]'),setCourseHiddenState=(courseId,status)=>(!1===status&&(status=null),(0,_repository2.setUserPreference)("block_myoverview_hidden_course_".concat(courseId),status).catch(Notification.exception)),hideElement=(root,id)=>{const pagingBar=root.find('[data-region="paging-bar"]'),jumpto=parseInt(pagingBar.attr("data-active-page-number"));let reducedCourse=loadedPages[jumpto].courses.reduce(((accumulator,current)=>(+id!=+current.id&&accumulator.push(current),accumulator)),[]);if(void 0!==loadedPages[jumpto+1]){const newElement=loadedPages[jumpto+1].courses.slice(0,1);loadedPages.forEach(((courseList,index)=>{if(index>jumpto){let popElement=[];void 0!==loadedPages[index+1]&&(popElement=loadedPages[index+1].courses.slice(0,1)),loadedPages[index].courses=[...loadedPages[index].courses.slice(1),...popElement]}})),reducedCourse=[...reducedCourse,...newElement]}if(lastPage===jumpto+1&&0===loadedPages[jumpto+1].courses.length){const pagedContentContainer=root.find('[data-region="paged-content-container"]');PagedContentFactory.resetLastPageNumber((0,_jquery.default)(pagedContentContainer).attr("id"),jumpto)}loadedPages[jumpto].courses=reducedCourse,courseOffset--;const pagedContentPage=getPagedContentContainer(root,jumpto);renderCourses(root,loadedPages[jumpto]).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception),loadedPages.forEach(((courseList,index)=>{if(index>jumpto){getPagedContentContainer(root,index).remove()}}))},setCourseFavouriteState=(courseId,status)=>Repository.setFavouriteCourses({courses:[{id:courseId,favourite:status}]}).then((result=>0===result.warnings.length&&(loadedPages.forEach((courseList=>{courseList.courses.forEach(((course,index)=>{course.id==courseId&&(courseList.courses[index].isfavourite=status)}))})),!0))).catch(Notification.exception),noCoursesRender=root=>{const nocoursesimg=root.find(_selectors.default.courseView.region).attr("data-nocoursesimg"),newcourseurl=root.find(_selectors.default.courseView.region).attr("data-newcourseurl");return Templates.render(TEMPLATES_NOCOURSES,{nocoursesimg:nocoursesimg,newcourseurl:newcourseurl})},renderCourses=(root,coursesData)=>{const filters=getFilterValues(root);let currentTemplate="";return currentTemplate="card"===filters.display?TEMPLATES_COURSES_CARDS:"list"===filters.display?TEMPLATES_COURSES_LIST:TEMPLATES_COURSES_SUMMARY,coursesData?(!1===Array.isArray(coursesData.courses)&&(coursesData.courses=Object.values(coursesData.courses)),coursesData.courses=coursesData.courses.map((course=>(course.showcoursecategory="on"===filters.displaycategories,course))),coursesData.courses.length?Templates.render(currentTemplate,{courses:coursesData.courses}):noCoursesRender(root)):noCoursesRender(root)},registerPagedEventHandlers=(root,namespace)=>{const event=namespace+PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;PubSub.subscribe(event,(root=>limit=>root.find(_selectors.default.courseView.region).attr("data-paging",limit))(root))},itemsPerPageFunc=(pagingLimit,root)=>{let itemsPerPage=NUMCOURSES_PERPAGE.map((value=>{let active=!1;return value===pagingLimit&&(active=!0),{value:value,active:active}}));const totalCourseCount=parseInt(root.find(_selectors.default.courseView.region).attr("data-totalcoursecount"),10);return itemsPerPage.filter((pagingOption=>!(0===pagingOption.value&&totalCourseCount>100)&&pagingOption.value4&&void 0!==arguments[4]?arguments[4]:null,courses=coursesData.courses?coursesData.courses:coursesData,nextPageStart=0,pageCourses=[];if(void 0!==loadedPages[currentPage]){pageCourses=loadedPages[currentPage].courses;const currentPageLength=pageCourses.length;currentPageLength0?courses.slice(0,pageData.limit):courses;loadedPages[currentPage]={courses:pageCourses};const remainingCourses=!1!==nextPageStart?courses.slice(nextPageStart,courses.length):[];remainingCourses.length&&(loadedPages[currentPage+1]={courses:remainingCourses}),loadedPages[currentPage].courses.length{courseOffset=0,loadedPages=[],lastPage=0,lastLimit=0},standardFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit)=>{const pagePromise=((filters,limit)=>{const params={offset:courseOffset,limit:limit,classification:filters.grouping,sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue};return"summary"===filters.display?(params.requiredfields=Repository.SUMMARY_REQUIRED_FIELDS,summaryDisplayLoaded=!0):params.requiredfields=Repository.CARDLIST_REQUIRED_FIELDS,Repository.getEnrolledCoursesByTimeline(params)})(filters,limit).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(pagePromise)}),searchFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit,inputValue)=>{const searchingPromise=((filters,limit,searchValue)=>{const params={offset:courseOffset,limit:limit,classification:"search",sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue,searchvalue:searchValue};return"summary"===filters.display?(params.requiredfields=Repository.SUMMARY_REQUIRED_FIELDS,summaryDisplayLoaded=!0):(params.requiredfields=Repository.CARDLIST_REQUIRED_FIELDS,summaryDisplayLoaded=!1),Repository.getEnrolledCoursesByTimeline(params)})(filters,limit,inputValue).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(searchingPromise)}),initializePagedContent=function(root,promiseFunction){let inputValue=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const pagingLimit=parseInt(root.find(_selectors.default.courseView.region).attr("data-paging"),10);let itemsPerPage=itemsPerPageFunc(pagingLimit,root);const filters=getFilterValues(root),config={...DEFAULT_PAGED_CONTENT_CONFIG};config.eventNamespace=namespace;const pagedContentPromise=PagedContentFactory.createWithLimit(itemsPerPage,((pagesData,actions)=>{let promises=[];return pagesData.forEach((pageData=>{const currentPage=pageData.pageNumber;let limit=pageData.limit>0?pageData.limit:0;if(+lastLimit!=+limit&&(loadedPages=[],courseOffset=0,lastPage=0),lastPage===currentPage)return actions.allItemsLoaded(lastPage),void promises.push(renderCourses(root,loadedPages[currentPage]));lastLimit=limit,void 0===loadedPages[currentPage+1]&&void 0===loadedPages[currentPage]&&(limit*=2),promiseFunction(filters,currentPage,pageData,actions,root,promises,limit,inputValue)})),promises}),config);pagedContentPromise.then(((html,js)=>(registerPagedEventHandlers(root,namespace),Templates.replaceNodeContents(root.find(_selectors.default.courseView.region),html,js)))).catch(Notification.exception)},registerEventListeners=(root,page)=>{CustomEvents.define(root,[CustomEvents.events.activate]),root.on(CustomEvents.events.activate,_selectors.default.ACTION_ADD_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_ADD_FAVOURITE),courseId=getCourseId(favourite);addToFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_REMOVE_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_REMOVE_FAVOURITE),courseId=getCourseId(favourite);removeFromFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.FAVOURITE_ICON,((e,data)=>{data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_HIDE_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_HIDE_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,!0),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.addClass("hidden"),showAction.removeClass("hidden")})(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_SHOW_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_SHOW_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,null),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.removeClass("hidden"),showAction.addClass("hidden")})(root,courseId),data.originalEvent.preventDefault()}));const input=page.querySelector(_selectors.default.region.searchInput),clearIcon=page.querySelector(_selectors.default.region.clearIcon);clearIcon.addEventListener("click",(()=>{input.value="",input.focus(),clearSearch(clearIcon,root)})),input.addEventListener("input",(0,_utils.debounce)((()=>{""===input.value?clearSearch(clearIcon,root):(activeSearch(clearIcon),initializePagedContent(root,searchFunctionalityCurry(),input.value.trim()))}),1e3))},clearSearch=(clearIcon,root)=>{clearIcon.classList.add("d-none"),init(root)};_exports.clearSearch=clearSearch;const activeSearch=clearIcon=>{clearIcon.classList.remove("d-none")},init=root=>{if(root=(0,_jquery.default)(root),loadedPages=[],lastPage=0,courseOffset=0,!root.attr("data-init")){const page=document.querySelector(_selectors.default.region.selectBlock);registerEventListeners(root,page),namespace="block_myoverview_"+root.attr("id")+"_"+Math.random(),root.attr("data-init",!0)}initializePagedContent(root,standardFunctionalityCurry())};_exports.init=init;_exports.reset=root=>{if(loadedPages.length>0){if("summary"!==getFilterValues(root).display||summaryDisplayLoaded)loadedPages.forEach(((courseList,index)=>{let pagedContentPage=getPagedContentContainer(root,index);renderCourses(root,courseList).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception)}));else{const input=document.querySelector(_selectors.default.region.selectBlock).querySelector(_selectors.default.region.searchInput);""!==input.value?initializePagedContent(root,searchFunctionalityCurry(),input.value.trim()):initializePagedContent(root,standardFunctionalityCurry())}}else init(root)}})); //# sourceMappingURL=view.min.js.map \ No newline at end of file diff --git a/blocks/myoverview/amd/build/view.min.js.map b/blocks/myoverview/amd/build/view.min.js.map index 3031516e38d..d9cffc5deac 100644 --- a/blocks/myoverview/amd/build/view.min.js.map +++ b/blocks/myoverview/amd/build/view.min.js.map @@ -1 +1 @@ -{"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 courses view for the overview block.\n *\n * @copyright 2018 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as Repository from 'block_myoverview/repository';\nimport * as PagedContentFactory from 'core/paged_content_factory';\nimport * as PubSub from 'core/pubsub';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as Notification from 'core/notification';\nimport * as Templates from 'core/templates';\nimport * as CourseEvents from 'core_course/events';\nimport SELECTORS from 'block_myoverview/selectors';\nimport * as PagedContentEvents from 'core/paged_content_events';\nimport * as Aria from 'core/aria';\nimport {debounce} from 'core/utils';\nimport {setUserPreference} from 'core_user/repository';\n\nconst TEMPLATES = {\n COURSES_CARDS: 'block_myoverview/view-cards',\n COURSES_LIST: 'block_myoverview/view-list',\n COURSES_SUMMARY: 'block_myoverview/view-summary',\n NOCOURSES: 'core_course/no-courses'\n};\n\nconst GROUPINGS = {\n GROUPING_ALLINCLUDINGHIDDEN: 'allincludinghidden',\n GROUPING_ALL: 'all',\n GROUPING_INPROGRESS: 'inprogress',\n GROUPING_FUTURE: 'future',\n GROUPING_PAST: 'past',\n GROUPING_FAVOURITES: 'favourites',\n GROUPING_HIDDEN: 'hidden'\n};\n\nconst NUMCOURSES_PERPAGE = [12, 24, 48, 96, 0];\n\nlet loadedPages = [];\n\nlet courseOffset = 0;\n\nlet lastPage = 0;\n\nlet lastLimit = 0;\n\nlet namespace = null;\n\n/**\n * Whether the summary display has been loaded.\n *\n * If true, this means that courses have been loaded with the summary text.\n * Otherwise, switching to the summary display mode will require course data to be fetched with the summary text.\n *\n * @type {boolean}\n */\nlet summaryDisplayLoaded = false;\n\n/**\n * Get filter values from DOM.\n *\n * @param {object} root The root element for the courses view.\n * @return {filters} Set filters.\n */\nconst getFilterValues = root => {\n const courseRegion = root.find(SELECTORS.courseView.region);\n return {\n display: courseRegion.attr('data-display'),\n grouping: courseRegion.attr('data-grouping'),\n sort: courseRegion.attr('data-sort'),\n displaycategories: courseRegion.attr('data-displaycategories'),\n customfieldname: courseRegion.attr('data-customfieldname'),\n customfieldvalue: courseRegion.attr('data-customfieldvalue'),\n };\n};\n\n// We want the paged content controls below the paged content area.\n// and the controls should be ignored while data is loading.\nconst DEFAULT_PAGED_CONTENT_CONFIG = {\n ignoreControlWhileLoading: true,\n controlPlacementBottom: true,\n persistentLimitKey: 'block_myoverview_user_paging_preference'\n};\n\n/**\n * Get enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @return {promise} Resolved with an array of courses.\n */\nconst getMyCourses = (filters, limit) => {\n const params = {\n offset: courseOffset,\n limit: limit,\n classification: filters.grouping,\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n };\n if (filters.display === 'summary') {\n params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS;\n summaryDisplayLoaded = true;\n } else {\n params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS;\n }\n return Repository.getEnrolledCoursesByTimeline(params);\n};\n\n/**\n * Search for enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @param {string} searchValue What does the user want to search within their courses.\n * @return {promise} Resolved with an array of courses.\n */\nconst getSearchMyCourses = (filters, limit, searchValue) => {\n const params = {\n offset: courseOffset,\n limit: limit,\n classification: 'search',\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n searchvalue: searchValue,\n };\n if (filters.display === 'summary') {\n params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS;\n summaryDisplayLoaded = true;\n } else {\n params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS;\n summaryDisplayLoaded = false;\n }\n return Repository.getEnrolledCoursesByTimeline(params);\n};\n\n/**\n * Get the container element for the favourite icon.\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n * @return {Object} The favourite icon container\n */\nconst getFavouriteIconContainer = (root, courseId) => {\n return root.find(SELECTORS.FAVOURITE_ICON + '[data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the paged content container element.\n *\n * @param {Object} root The course overview container\n * @param {Number} index Rendered page index.\n * @return {Object} The rendered paged container.\n */\nconst getPagedContentContainer = (root, index) => {\n return root.find('[data-region=\"paged-content-page\"][data-page=\"' + index + '\"]');\n};\n\n/**\n * Get the course id from a favourite element.\n *\n * @param {Object} root The favourite icon container element.\n * @return {Number} Course id.\n */\nconst getCourseId = root => {\n return root.attr('data-course-id');\n};\n\n/**\n * Hide the favourite icon.\n *\n * @param {Object} root The favourite icon container element.\n * @param {Number} courseId Course id number.\n */\nconst hideFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.addClass('hidden');\n Aria.hide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.removeClass('hidden');\n Aria.unhide(notFavourteIcon);\n};\n\n/**\n * Show the favourite icon.\n *\n * @param {Object} root The course overview container.\n * @param {Number} courseId Course id number.\n */\nconst showFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.removeClass('hidden');\n Aria.unhide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.addClass('hidden');\n Aria.hide(notFavourteIcon);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The add to favourite menu item.\n */\nconst getAddFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"add-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The remove from favourites menu item.\n */\nconst getRemoveFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"remove-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Add course to favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst addToFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, true).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.favourited, courseId);\n removeAction.removeClass('hidden');\n addAction.addClass('hidden');\n showFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Remove course from favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst removeFromFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, false).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.unfavorited, courseId);\n removeAction.addClass('hidden');\n addAction.removeClass('hidden');\n hideFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The hide course menu item.\n */\nconst getHideCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"hide-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The show course menu item.\n */\nconst getShowCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"show-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Hide course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst hideCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, true);\n\n // Remove the course from this view as it is now hidden and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.addClass('hidden');\n showAction.removeClass('hidden');\n};\n\n/**\n * Show course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst showCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, null);\n\n // Remove the course from this view as it is now shown again and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.removeClass('hidden');\n showAction.addClass('hidden');\n};\n\n/**\n * Set the courses hidden status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {Boolean} status new hidden status.\n * @return {Promise} Repository promise.\n */\nconst setCourseHiddenState = (courseId, status) => {\n\n // If the given status is not hidden, the preference has to be deleted with a null value.\n if (status === false) {\n status = null;\n }\n\n return setUserPreference(`block_myoverview_hidden_course_${courseId}`, status)\n .catch(Notification.exception);\n};\n\n/**\n * Reset the loadedPages dataset to take into account the hidden element\n *\n * @param {Object} root The course overview container\n * @param {Number} id The course id number\n */\nconst hideElement = (root, id) => {\n const pagingBar = root.find('[data-region=\"paging-bar\"]');\n const jumpto = parseInt(pagingBar.attr('data-active-page-number'));\n\n // Get a reduced dataset for the current page.\n const courseList = loadedPages[jumpto];\n let reducedCourse = courseList.courses.reduce((accumulator, current) => {\n if (+id !== +current.id) {\n accumulator.push(current);\n }\n return accumulator;\n }, []);\n\n // Get the next page's data if loaded and pop the first element from it.\n if (typeof (loadedPages[jumpto + 1]) !== 'undefined') {\n const newElement = loadedPages[jumpto + 1].courses.slice(0, 1);\n\n // Adjust the dataset for the reset of the pages that are loaded.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n let popElement = [];\n if (typeof (loadedPages[index + 1]) !== 'undefined') {\n popElement = loadedPages[index + 1].courses.slice(0, 1);\n }\n loadedPages[index].courses = [...loadedPages[index].courses.slice(1), ...popElement];\n }\n });\n\n reducedCourse = [...reducedCourse, ...newElement];\n }\n\n // Check if the next page is the last page and if it still has data associated to it.\n if (lastPage === jumpto + 1 && loadedPages[jumpto + 1].courses.length === 0) {\n const pagedContentContainer = root.find('[data-region=\"paged-content-container\"]');\n PagedContentFactory.resetLastPageNumber($(pagedContentContainer).attr('id'), jumpto);\n }\n\n loadedPages[jumpto].courses = reducedCourse;\n\n // Reduce the course offset.\n courseOffset--;\n\n // Render the paged content for the current.\n const pagedContentPage = getPagedContentContainer(root, jumpto);\n renderCourses(root, loadedPages[jumpto]).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n\n // Delete subsequent pages in order to trigger the callback.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n const page = getPagedContentContainer(root, index);\n page.remove();\n }\n });\n};\n\n/**\n * Set the courses favourite status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {boolean} status new favourite status.\n * @return {Promise} Repository promise.\n */\nconst setCourseFavouriteState = (courseId, status) => {\n\n return Repository.setFavouriteCourses({\n courses: [\n {\n 'id': courseId,\n 'favourite': status\n }\n ]\n }).then(result => {\n if (result.warnings.length === 0) {\n loadedPages.forEach(courseList => {\n courseList.courses.forEach((course, index) => {\n if (course.id == courseId) {\n courseList.courses[index].isfavourite = status;\n }\n });\n });\n return true;\n } else {\n return false;\n }\n }).catch(Notification.exception);\n};\n\n/**\n * Given there are no courses to render provide the rendered template.\n *\n * @param {object} root The root element for the courses view.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst noCoursesRender = root => {\n const nocoursesimg = root.find(SELECTORS.courseView.region).attr('data-nocoursesimg');\n const newcourseurl = root.find(SELECTORS.courseView.region).attr('data-newcourseurl');\n return Templates.render(TEMPLATES.NOCOURSES, {\n nocoursesimg: nocoursesimg,\n newcourseurl: newcourseurl\n });\n};\n\n/**\n * Render the dashboard courses.\n *\n * @param {object} root The root element for the courses view.\n * @param {array} coursesData containing array of returned courses.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst renderCourses = (root, coursesData) => {\n\n const filters = getFilterValues(root);\n\n let currentTemplate = '';\n if (filters.display === 'card') {\n currentTemplate = TEMPLATES.COURSES_CARDS;\n } else if (filters.display === 'list') {\n currentTemplate = TEMPLATES.COURSES_LIST;\n } else {\n currentTemplate = TEMPLATES.COURSES_SUMMARY;\n }\n\n if (!coursesData) {\n return noCoursesRender(root);\n } else {\n // Sometimes we get weird objects coming after a failed search, cast to ensure typing functions.\n if (Array.isArray(coursesData.courses) === false) {\n coursesData.courses = Object.values(coursesData.courses);\n }\n // Whether the course category should be displayed in the course item.\n coursesData.courses = coursesData.courses.map(course => {\n course.showcoursecategory = filters.displaycategories === 'on';\n return course;\n });\n if (coursesData.courses.length) {\n return Templates.render(currentTemplate, {\n courses: coursesData.courses,\n });\n } else {\n return noCoursesRender(root);\n }\n }\n};\n\n/**\n * Return the callback to be passed to the subscribe event\n *\n * @param {object} root The root element for the courses view\n * @return {function} Partially applied function that'll execute when passed a limit\n */\nconst setLimit = root => {\n // @param {Number} limit The paged limit that is passed through the event.\n return limit => root.find(SELECTORS.courseView.region).attr('data-paging', limit);\n};\n\n/**\n * Intialise the paged list and cards views on page load.\n * Returns an array of paged contents that we would like to handle here\n *\n * @param {object} root The root element for the courses view\n * @param {string} namespace The namespace for all the events attached\n */\nconst registerPagedEventHandlers = (root, namespace) => {\n const event = namespace + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;\n PubSub.subscribe(event, setLimit(root));\n};\n\n/**\n * Figure out how many items are going to be allowed to be rendered in the block.\n *\n * @param {Number} pagingLimit How many courses to display\n * @param {Object} root The course overview container\n * @return {Number[]} How many courses will be rendered\n */\nconst itemsPerPageFunc = (pagingLimit, root) => {\n let itemsPerPage = NUMCOURSES_PERPAGE.map(value => {\n let active = false;\n if (value === pagingLimit) {\n active = true;\n }\n\n return {\n value: value,\n active: active\n };\n });\n\n // Filter out all pagination options which are too large for the amount of courses user is enrolled in.\n const totalCourseCount = parseInt(root.find(SELECTORS.courseView.region).attr('data-totalcoursecount'), 10);\n return itemsPerPage.filter(pagingOption => {\n return pagingOption.value < totalCourseCount || pagingOption.value === 0;\n });\n};\n\n/**\n * Mutates and controls the loadedPages array and handles the bootstrapping.\n *\n * @param {Array|Object} coursesData Array of all of the courses to start building the page from\n * @param {Number} currentPage What page are we currently on?\n * @param {Object} pageData Any current page information\n * @param {Object} actions Paged content helper\n * @param {null|boolean} activeSearch Are we currently actively searching and building up search results?\n */\nconst pageBuilder = (coursesData, currentPage, pageData, actions, activeSearch = null) => {\n // If the courseData comes in an object then get the value otherwise it is a pure array.\n let courses = coursesData.courses ? coursesData.courses : coursesData;\n let nextPageStart = 0;\n let pageCourses = [];\n\n // If current page's data is loaded make sure we max it to page limit.\n if (typeof (loadedPages[currentPage]) !== 'undefined') {\n pageCourses = loadedPages[currentPage].courses;\n const currentPageLength = pageCourses.length;\n if (currentPageLength < pageData.limit) {\n nextPageStart = pageData.limit - currentPageLength;\n pageCourses = {...loadedPages[currentPage].courses, ...courses.slice(0, nextPageStart)};\n }\n } else {\n // When the page limit is zero, there is only one page of courses, no start for next page.\n nextPageStart = pageData.limit || false;\n pageCourses = (pageData.limit > 0) ? courses.slice(0, pageData.limit) : courses;\n }\n\n // Finished setting up the current page.\n loadedPages[currentPage] = {\n courses: pageCourses\n };\n\n // Set up the next page (if there is more than one page).\n const remainingCourses = nextPageStart !== false ? courses.slice(nextPageStart, courses.length) : [];\n if (remainingCourses.length) {\n loadedPages[currentPage + 1] = {\n courses: remainingCourses\n };\n }\n\n // Set the last page to either the current or next page.\n if (loadedPages[currentPage].courses.length < pageData.limit || !remainingCourses.length) {\n lastPage = currentPage;\n if (activeSearch === null) {\n actions.allItemsLoaded(currentPage);\n }\n } else if (typeof (loadedPages[currentPage + 1]) !== 'undefined'\n && loadedPages[currentPage + 1].courses.length < pageData.limit) {\n lastPage = currentPage + 1;\n }\n\n courseOffset = coursesData.nextoffset;\n};\n\n/**\n * In cases when switching between regular rendering and search rendering we need to reset some variables.\n */\nconst resetGlobals = () => {\n courseOffset = 0;\n loadedPages = [];\n lastPage = 0;\n lastLimit = 0;\n};\n\n/**\n * The default functionality of fetching paginated courses without special handling.\n *\n * @return {function(Object, Object, Object, Object, Object, Promise, Number): void}\n */\nconst standardFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit) => {\n const pagePromise = getMyCourses(\n filters,\n limit\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(pagePromise);\n };\n};\n\n/**\n * Initialize the searching functionality so we can call it when required.\n *\n * @return {function(Object, Number, Object, Object, Object, Promise, Number, String): void}\n */\nconst searchFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit, inputValue) => {\n const searchingPromise = getSearchMyCourses(\n filters,\n limit,\n inputValue\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(searchingPromise);\n };\n};\n\n/**\n * Initialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n * @param {function} promiseFunction How do we fetch the courses and what do we do with them?\n * @param {null | string} inputValue What to search for\n */\nconst initializePagedContent = (root, promiseFunction, inputValue = null) => {\n const pagingLimit = parseInt(root.find(SELECTORS.courseView.region).attr('data-paging'), 10);\n let itemsPerPage = itemsPerPageFunc(pagingLimit, root);\n\n const filters = getFilterValues(root);\n const config = {...{}, ...DEFAULT_PAGED_CONTENT_CONFIG};\n config.eventNamespace = namespace;\n\n const pagedContentPromise = PagedContentFactory.createWithLimit(\n itemsPerPage,\n (pagesData, actions) => {\n let promises = [];\n pagesData.forEach(pageData => {\n const currentPage = pageData.pageNumber;\n let limit = (pageData.limit > 0) ? pageData.limit : 0;\n\n // Reset local variables if limits have changed.\n if (+lastLimit !== +limit) {\n loadedPages = [];\n courseOffset = 0;\n lastPage = 0;\n }\n\n if (lastPage === currentPage) {\n // If we are on the last page and have it's data then load it from cache.\n actions.allItemsLoaded(lastPage);\n promises.push(renderCourses(root, loadedPages[currentPage]));\n return;\n }\n\n lastLimit = limit;\n\n // Get 2 pages worth of data as we will need it for the hidden functionality.\n if (typeof (loadedPages[currentPage + 1]) === 'undefined') {\n if (typeof (loadedPages[currentPage]) === 'undefined') {\n limit *= 2;\n }\n }\n\n // Call the curried function that'll handle the course promise and any manipulation of it.\n promiseFunction(filters, currentPage, pageData, actions, root, promises, limit, inputValue);\n });\n return promises;\n },\n config\n );\n\n pagedContentPromise.then((html, js) => {\n registerPagedEventHandlers(root, namespace);\n return Templates.replaceNodeContents(root.find(SELECTORS.courseView.region), html, js);\n }).catch(Notification.exception);\n};\n\n/**\n * Listen to, and handle events for the myoverview block.\n *\n * @param {Object} root The myoverview block container element.\n * @param {HTMLElement} page The whole HTMLElement for our block.\n */\nconst registerEventListeners = (root, page) => {\n\n CustomEvents.define(root, [\n CustomEvents.events.activate\n ]);\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_ADD_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_ADD_FAVOURITE);\n const courseId = getCourseId(favourite);\n addToFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_REMOVE_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_REMOVE_FAVOURITE);\n const courseId = getCourseId(favourite);\n removeFromFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.FAVOURITE_ICON, (e, data) => {\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_HIDE_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_HIDE_COURSE);\n const courseId = getCourseId(target);\n hideCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_SHOW_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_SHOW_COURSE);\n const courseId = getCourseId(target);\n showCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n // Searching functionality event handlers.\n const input = page.querySelector(SELECTORS.region.searchInput);\n const clearIcon = page.querySelector(SELECTORS.region.clearIcon);\n\n clearIcon.addEventListener('click', () => {\n input.value = '';\n input.focus();\n clearSearch(clearIcon, root);\n });\n\n input.addEventListener('input', debounce(() => {\n if (input.value === '') {\n clearSearch(clearIcon, root);\n } else {\n activeSearch(clearIcon);\n initializePagedContent(root, searchFunctionalityCurry(), input.value.trim());\n }\n }, 1000));\n};\n\n/**\n * Reset the search icon and trigger the init for the block.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n * @param {Object} root The myoverview block container element.\n */\nexport const clearSearch = (clearIcon, root) => {\n clearIcon.classList.add('d-none');\n init(root);\n};\n\n/**\n * Change the searching icon to its' active state.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n */\nconst activeSearch = (clearIcon) => {\n clearIcon.classList.remove('d-none');\n};\n\n/**\n * Intialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n */\nexport const init = root => {\n root = $(root);\n loadedPages = [];\n lastPage = 0;\n courseOffset = 0;\n\n if (!root.attr('data-init')) {\n const page = document.querySelector(SELECTORS.region.selectBlock);\n registerEventListeners(root, page);\n namespace = \"block_myoverview_\" + root.attr('id') + \"_\" + Math.random();\n root.attr('data-init', true);\n }\n\n initializePagedContent(root, standardFunctionalityCurry());\n};\n\n/**\n * Reset the courses views to their original\n * state on first page load.courseOffset\n *\n * This is called when configuration has changed for the event lists\n * to cause them to reload their data.\n *\n * @param {Object} root The root element for the timeline view.\n */\nexport const reset = root => {\n if (loadedPages.length > 0) {\n const filters = getFilterValues(root);\n // If the display mode is changed to 'summary' but the summary display has not been loaded yet,\n // we need to re-fetch the courses to include the course summary text.\n if (filters.display === 'summary' && !summaryDisplayLoaded) {\n const page = document.querySelector(SELECTORS.region.selectBlock);\n const input = page.querySelector(SELECTORS.region.searchInput);\n if (input.value !== '') {\n initializePagedContent(root, searchFunctionalityCurry(), input.value.trim());\n } else {\n initializePagedContent(root, standardFunctionalityCurry());\n }\n } else {\n loadedPages.forEach((courseList, index) => {\n let pagedContentPage = getPagedContentContainer(root, index);\n renderCourses(root, courseList).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n });\n }\n } else {\n init(root);\n }\n};\n"],"names":["TEMPLATES","GROUPINGS","NUMCOURSES_PERPAGE","loadedPages","courseOffset","lastPage","lastLimit","namespace","summaryDisplayLoaded","getFilterValues","root","courseRegion","find","SELECTORS","courseView","region","display","attr","grouping","sort","displaycategories","customfieldname","customfieldvalue","DEFAULT_PAGED_CONTENT_CONFIG","ignoreControlWhileLoading","controlPlacementBottom","persistentLimitKey","getFavouriteIconContainer","courseId","FAVOURITE_ICON","getPagedContentContainer","index","getCourseId","getAddFavouriteMenuItem","getRemoveFavouriteMenuItem","addToFavourites","removeAction","addAction","setCourseFavouriteState","then","success","PubSub","publish","CourseEvents","favourited","removeClass","addClass","iconContainer","isFavouriteIcon","ICON_IS_FAVOURITE","Aria","unhide","notFavourteIcon","ICON_NOT_FAVOURITE","hide","showFavouriteIcon","Notification","alert","catch","exception","removeFromFavourites","unfavorited","hideFavouriteIcon","getHideCourseMenuItem","getShowCourseMenuItem","setCourseHiddenState","status","hideElement","id","pagingBar","jumpto","parseInt","reducedCourse","courses","reduce","accumulator","current","push","newElement","slice","forEach","courseList","popElement","length","pagedContentContainer","PagedContentFactory","resetLastPageNumber","pagedContentPage","renderCourses","html","js","Templates","replaceNodeContents","remove","Repository","setFavouriteCourses","result","warnings","course","isfavourite","noCoursesRender","nocoursesimg","newcourseurl","render","coursesData","filters","currentTemplate","Array","isArray","Object","values","map","showcoursecategory","registerPagedEventHandlers","event","PagedContentEvents","SET_ITEMS_PER_PAGE_LIMIT","subscribe","limit","setLimit","itemsPerPageFunc","pagingLimit","itemsPerPage","value","active","totalCourseCount","filter","pagingOption","pageBuilder","currentPage","pageData","actions","activeSearch","nextPageStart","pageCourses","currentPageLength","remainingCourses","allItemsLoaded","nextoffset","resetGlobals","standardFunctionalityCurry","promises","pagePromise","params","offset","classification","requiredfields","SUMMARY_REQUIRED_FIELDS","CARDLIST_REQUIRED_FIELDS","getEnrolledCoursesByTimeline","getMyCourses","searchFunctionalityCurry","inputValue","searchingPromise","searchValue","searchvalue","getSearchMyCourses","initializePagedContent","promiseFunction","config","eventNamespace","pagedContentPromise","createWithLimit","pagesData","pageNumber","registerEventListeners","page","CustomEvents","define","events","activate","on","ACTION_ADD_FAVOURITE","e","data","favourite","target","closest","originalEvent","preventDefault","ACTION_REMOVE_FAVOURITE","ACTION_HIDE_COURSE","hideAction","showAction","hideCourse","ACTION_SHOW_COURSE","showCourse","input","querySelector","searchInput","clearIcon","addEventListener","focus","clearSearch","trim","classList","add","init","document","selectBlock","Math","random"],"mappings":"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"} \ No newline at end of file +{"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 courses view for the overview block.\n *\n * @copyright 2018 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as Repository from 'block_myoverview/repository';\nimport * as PagedContentFactory from 'core/paged_content_factory';\nimport * as PubSub from 'core/pubsub';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as Notification from 'core/notification';\nimport * as Templates from 'core/templates';\nimport * as CourseEvents from 'core_course/events';\nimport SELECTORS from 'block_myoverview/selectors';\nimport * as PagedContentEvents from 'core/paged_content_events';\nimport * as Aria from 'core/aria';\nimport {debounce} from 'core/utils';\nimport {setUserPreference} from 'core_user/repository';\n\nconst TEMPLATES = {\n COURSES_CARDS: 'block_myoverview/view-cards',\n COURSES_LIST: 'block_myoverview/view-list',\n COURSES_SUMMARY: 'block_myoverview/view-summary',\n NOCOURSES: 'core_course/no-courses'\n};\n\nconst GROUPINGS = {\n GROUPING_ALLINCLUDINGHIDDEN: 'allincludinghidden',\n GROUPING_ALL: 'all',\n GROUPING_INPROGRESS: 'inprogress',\n GROUPING_FUTURE: 'future',\n GROUPING_PAST: 'past',\n GROUPING_FAVOURITES: 'favourites',\n GROUPING_HIDDEN: 'hidden'\n};\n\nconst NUMCOURSES_PERPAGE = [12, 24, 48, 96, 0];\n\nlet loadedPages = [];\n\nlet courseOffset = 0;\n\nlet lastPage = 0;\n\nlet lastLimit = 0;\n\nlet namespace = null;\n\n/**\n * Whether the summary display has been loaded.\n *\n * If true, this means that courses have been loaded with the summary text.\n * Otherwise, switching to the summary display mode will require course data to be fetched with the summary text.\n *\n * @type {boolean}\n */\nlet summaryDisplayLoaded = false;\n\n/**\n * Get filter values from DOM.\n *\n * @param {object} root The root element for the courses view.\n * @return {filters} Set filters.\n */\nconst getFilterValues = root => {\n const courseRegion = root.find(SELECTORS.courseView.region);\n return {\n display: courseRegion.attr('data-display'),\n grouping: courseRegion.attr('data-grouping'),\n sort: courseRegion.attr('data-sort'),\n displaycategories: courseRegion.attr('data-displaycategories'),\n customfieldname: courseRegion.attr('data-customfieldname'),\n customfieldvalue: courseRegion.attr('data-customfieldvalue'),\n };\n};\n\n// We want the paged content controls below the paged content area.\n// and the controls should be ignored while data is loading.\nconst DEFAULT_PAGED_CONTENT_CONFIG = {\n ignoreControlWhileLoading: true,\n controlPlacementBottom: true,\n persistentLimitKey: 'block_myoverview_user_paging_preference'\n};\n\n/**\n * Get enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @return {promise} Resolved with an array of courses.\n */\nconst getMyCourses = (filters, limit) => {\n const params = {\n offset: courseOffset,\n limit: limit,\n classification: filters.grouping,\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n };\n if (filters.display === 'summary') {\n params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS;\n summaryDisplayLoaded = true;\n } else {\n params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS;\n }\n return Repository.getEnrolledCoursesByTimeline(params);\n};\n\n/**\n * Search for enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @param {string} searchValue What does the user want to search within their courses.\n * @return {promise} Resolved with an array of courses.\n */\nconst getSearchMyCourses = (filters, limit, searchValue) => {\n const params = {\n offset: courseOffset,\n limit: limit,\n classification: 'search',\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n searchvalue: searchValue,\n };\n if (filters.display === 'summary') {\n params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS;\n summaryDisplayLoaded = true;\n } else {\n params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS;\n summaryDisplayLoaded = false;\n }\n return Repository.getEnrolledCoursesByTimeline(params);\n};\n\n/**\n * Get the container element for the favourite icon.\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n * @return {Object} The favourite icon container\n */\nconst getFavouriteIconContainer = (root, courseId) => {\n return root.find(SELECTORS.FAVOURITE_ICON + '[data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the paged content container element.\n *\n * @param {Object} root The course overview container\n * @param {Number} index Rendered page index.\n * @return {Object} The rendered paged container.\n */\nconst getPagedContentContainer = (root, index) => {\n return root.find('[data-region=\"paged-content-page\"][data-page=\"' + index + '\"]');\n};\n\n/**\n * Get the course id from a favourite element.\n *\n * @param {Object} root The favourite icon container element.\n * @return {Number} Course id.\n */\nconst getCourseId = root => {\n return root.attr('data-course-id');\n};\n\n/**\n * Hide the favourite icon.\n *\n * @param {Object} root The favourite icon container element.\n * @param {Number} courseId Course id number.\n */\nconst hideFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.addClass('hidden');\n Aria.hide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.removeClass('hidden');\n Aria.unhide(notFavourteIcon);\n};\n\n/**\n * Show the favourite icon.\n *\n * @param {Object} root The course overview container.\n * @param {Number} courseId Course id number.\n */\nconst showFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.removeClass('hidden');\n Aria.unhide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.addClass('hidden');\n Aria.hide(notFavourteIcon);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The add to favourite menu item.\n */\nconst getAddFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"add-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The remove from favourites menu item.\n */\nconst getRemoveFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"remove-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Add course to favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst addToFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, true).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.favourited, courseId);\n removeAction.removeClass('hidden');\n addAction.addClass('hidden');\n showFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Remove course from favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst removeFromFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, false).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.unfavorited, courseId);\n removeAction.addClass('hidden');\n addAction.removeClass('hidden');\n hideFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The hide course menu item.\n */\nconst getHideCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"hide-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The show course menu item.\n */\nconst getShowCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"show-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Hide course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst hideCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, true);\n\n // Remove the course from this view as it is now hidden and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.addClass('hidden');\n showAction.removeClass('hidden');\n};\n\n/**\n * Show course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst showCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, null);\n\n // Remove the course from this view as it is now shown again and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.removeClass('hidden');\n showAction.addClass('hidden');\n};\n\n/**\n * Set the courses hidden status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {Boolean} status new hidden status.\n * @return {Promise} Repository promise.\n */\nconst setCourseHiddenState = (courseId, status) => {\n\n // If the given status is not hidden, the preference has to be deleted with a null value.\n if (status === false) {\n status = null;\n }\n\n return setUserPreference(`block_myoverview_hidden_course_${courseId}`, status)\n .catch(Notification.exception);\n};\n\n/**\n * Reset the loadedPages dataset to take into account the hidden element\n *\n * @param {Object} root The course overview container\n * @param {Number} id The course id number\n */\nconst hideElement = (root, id) => {\n const pagingBar = root.find('[data-region=\"paging-bar\"]');\n const jumpto = parseInt(pagingBar.attr('data-active-page-number'));\n\n // Get a reduced dataset for the current page.\n const courseList = loadedPages[jumpto];\n let reducedCourse = courseList.courses.reduce((accumulator, current) => {\n if (+id !== +current.id) {\n accumulator.push(current);\n }\n return accumulator;\n }, []);\n\n // Get the next page's data if loaded and pop the first element from it.\n if (typeof (loadedPages[jumpto + 1]) !== 'undefined') {\n const newElement = loadedPages[jumpto + 1].courses.slice(0, 1);\n\n // Adjust the dataset for the reset of the pages that are loaded.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n let popElement = [];\n if (typeof (loadedPages[index + 1]) !== 'undefined') {\n popElement = loadedPages[index + 1].courses.slice(0, 1);\n }\n loadedPages[index].courses = [...loadedPages[index].courses.slice(1), ...popElement];\n }\n });\n\n reducedCourse = [...reducedCourse, ...newElement];\n }\n\n // Check if the next page is the last page and if it still has data associated to it.\n if (lastPage === jumpto + 1 && loadedPages[jumpto + 1].courses.length === 0) {\n const pagedContentContainer = root.find('[data-region=\"paged-content-container\"]');\n PagedContentFactory.resetLastPageNumber($(pagedContentContainer).attr('id'), jumpto);\n }\n\n loadedPages[jumpto].courses = reducedCourse;\n\n // Reduce the course offset.\n courseOffset--;\n\n // Render the paged content for the current.\n const pagedContentPage = getPagedContentContainer(root, jumpto);\n renderCourses(root, loadedPages[jumpto]).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n\n // Delete subsequent pages in order to trigger the callback.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n const page = getPagedContentContainer(root, index);\n page.remove();\n }\n });\n};\n\n/**\n * Set the courses favourite status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {boolean} status new favourite status.\n * @return {Promise} Repository promise.\n */\nconst setCourseFavouriteState = (courseId, status) => {\n\n return Repository.setFavouriteCourses({\n courses: [\n {\n 'id': courseId,\n 'favourite': status\n }\n ]\n }).then(result => {\n if (result.warnings.length === 0) {\n loadedPages.forEach(courseList => {\n courseList.courses.forEach((course, index) => {\n if (course.id == courseId) {\n courseList.courses[index].isfavourite = status;\n }\n });\n });\n return true;\n } else {\n return false;\n }\n }).catch(Notification.exception);\n};\n\n/**\n * Given there are no courses to render provide the rendered template.\n *\n * @param {object} root The root element for the courses view.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst noCoursesRender = root => {\n const nocoursesimg = root.find(SELECTORS.courseView.region).attr('data-nocoursesimg');\n const newcourseurl = root.find(SELECTORS.courseView.region).attr('data-newcourseurl');\n return Templates.render(TEMPLATES.NOCOURSES, {\n nocoursesimg: nocoursesimg,\n newcourseurl: newcourseurl\n });\n};\n\n/**\n * Render the dashboard courses.\n *\n * @param {object} root The root element for the courses view.\n * @param {array} coursesData containing array of returned courses.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst renderCourses = (root, coursesData) => {\n\n const filters = getFilterValues(root);\n\n let currentTemplate = '';\n if (filters.display === 'card') {\n currentTemplate = TEMPLATES.COURSES_CARDS;\n } else if (filters.display === 'list') {\n currentTemplate = TEMPLATES.COURSES_LIST;\n } else {\n currentTemplate = TEMPLATES.COURSES_SUMMARY;\n }\n\n if (!coursesData) {\n return noCoursesRender(root);\n } else {\n // Sometimes we get weird objects coming after a failed search, cast to ensure typing functions.\n if (Array.isArray(coursesData.courses) === false) {\n coursesData.courses = Object.values(coursesData.courses);\n }\n // Whether the course category should be displayed in the course item.\n coursesData.courses = coursesData.courses.map(course => {\n course.showcoursecategory = filters.displaycategories === 'on';\n return course;\n });\n if (coursesData.courses.length) {\n return Templates.render(currentTemplate, {\n courses: coursesData.courses,\n });\n } else {\n return noCoursesRender(root);\n }\n }\n};\n\n/**\n * Return the callback to be passed to the subscribe event\n *\n * @param {object} root The root element for the courses view\n * @return {function} Partially applied function that'll execute when passed a limit\n */\nconst setLimit = root => {\n // @param {Number} limit The paged limit that is passed through the event.\n return limit => root.find(SELECTORS.courseView.region).attr('data-paging', limit);\n};\n\n/**\n * Intialise the paged list and cards views on page load.\n * Returns an array of paged contents that we would like to handle here\n *\n * @param {object} root The root element for the courses view\n * @param {string} namespace The namespace for all the events attached\n */\nconst registerPagedEventHandlers = (root, namespace) => {\n const event = namespace + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;\n PubSub.subscribe(event, setLimit(root));\n};\n\n/**\n * Figure out how many items are going to be allowed to be rendered in the block.\n *\n * @param {Number} pagingLimit How many courses to display\n * @param {Object} root The course overview container\n * @return {Number[]} How many courses will be rendered\n */\nconst itemsPerPageFunc = (pagingLimit, root) => {\n let itemsPerPage = NUMCOURSES_PERPAGE.map(value => {\n let active = false;\n if (value === pagingLimit) {\n active = true;\n }\n\n return {\n value: value,\n active: active\n };\n });\n\n // Filter out all pagination options which are too large for the amount of courses user is enrolled in.\n const totalCourseCount = parseInt(root.find(SELECTORS.courseView.region).attr('data-totalcoursecount'), 10);\n return itemsPerPage.filter(pagingOption => {\n if (pagingOption.value === 0 && totalCourseCount > 100) {\n // To minimise performance issues, do not show the \"All\" option if the user is enrolled in more than 100 courses.\n return false;\n }\n return pagingOption.value < totalCourseCount;\n });\n};\n\n/**\n * Mutates and controls the loadedPages array and handles the bootstrapping.\n *\n * @param {Array|Object} coursesData Array of all of the courses to start building the page from\n * @param {Number} currentPage What page are we currently on?\n * @param {Object} pageData Any current page information\n * @param {Object} actions Paged content helper\n * @param {null|boolean} activeSearch Are we currently actively searching and building up search results?\n */\nconst pageBuilder = (coursesData, currentPage, pageData, actions, activeSearch = null) => {\n // If the courseData comes in an object then get the value otherwise it is a pure array.\n let courses = coursesData.courses ? coursesData.courses : coursesData;\n let nextPageStart = 0;\n let pageCourses = [];\n\n // If current page's data is loaded make sure we max it to page limit.\n if (typeof (loadedPages[currentPage]) !== 'undefined') {\n pageCourses = loadedPages[currentPage].courses;\n const currentPageLength = pageCourses.length;\n if (currentPageLength < pageData.limit) {\n nextPageStart = pageData.limit - currentPageLength;\n pageCourses = {...loadedPages[currentPage].courses, ...courses.slice(0, nextPageStart)};\n }\n } else {\n // When the page limit is zero, there is only one page of courses, no start for next page.\n nextPageStart = pageData.limit || false;\n pageCourses = (pageData.limit > 0) ? courses.slice(0, pageData.limit) : courses;\n }\n\n // Finished setting up the current page.\n loadedPages[currentPage] = {\n courses: pageCourses\n };\n\n // Set up the next page (if there is more than one page).\n const remainingCourses = nextPageStart !== false ? courses.slice(nextPageStart, courses.length) : [];\n if (remainingCourses.length) {\n loadedPages[currentPage + 1] = {\n courses: remainingCourses\n };\n }\n\n // Set the last page to either the current or next page.\n if (loadedPages[currentPage].courses.length < pageData.limit || !remainingCourses.length) {\n lastPage = currentPage;\n if (activeSearch === null) {\n actions.allItemsLoaded(currentPage);\n }\n } else if (typeof (loadedPages[currentPage + 1]) !== 'undefined'\n && loadedPages[currentPage + 1].courses.length < pageData.limit) {\n lastPage = currentPage + 1;\n }\n\n courseOffset = coursesData.nextoffset;\n};\n\n/**\n * In cases when switching between regular rendering and search rendering we need to reset some variables.\n */\nconst resetGlobals = () => {\n courseOffset = 0;\n loadedPages = [];\n lastPage = 0;\n lastLimit = 0;\n};\n\n/**\n * The default functionality of fetching paginated courses without special handling.\n *\n * @return {function(Object, Object, Object, Object, Object, Promise, Number): void}\n */\nconst standardFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit) => {\n const pagePromise = getMyCourses(\n filters,\n limit\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(pagePromise);\n };\n};\n\n/**\n * Initialize the searching functionality so we can call it when required.\n *\n * @return {function(Object, Number, Object, Object, Object, Promise, Number, String): void}\n */\nconst searchFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit, inputValue) => {\n const searchingPromise = getSearchMyCourses(\n filters,\n limit,\n inputValue\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(searchingPromise);\n };\n};\n\n/**\n * Initialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n * @param {function} promiseFunction How do we fetch the courses and what do we do with them?\n * @param {null | string} inputValue What to search for\n */\nconst initializePagedContent = (root, promiseFunction, inputValue = null) => {\n const pagingLimit = parseInt(root.find(SELECTORS.courseView.region).attr('data-paging'), 10);\n let itemsPerPage = itemsPerPageFunc(pagingLimit, root);\n\n const filters = getFilterValues(root);\n const config = {...{}, ...DEFAULT_PAGED_CONTENT_CONFIG};\n config.eventNamespace = namespace;\n\n const pagedContentPromise = PagedContentFactory.createWithLimit(\n itemsPerPage,\n (pagesData, actions) => {\n let promises = [];\n pagesData.forEach(pageData => {\n const currentPage = pageData.pageNumber;\n let limit = (pageData.limit > 0) ? pageData.limit : 0;\n\n // Reset local variables if limits have changed.\n if (+lastLimit !== +limit) {\n loadedPages = [];\n courseOffset = 0;\n lastPage = 0;\n }\n\n if (lastPage === currentPage) {\n // If we are on the last page and have it's data then load it from cache.\n actions.allItemsLoaded(lastPage);\n promises.push(renderCourses(root, loadedPages[currentPage]));\n return;\n }\n\n lastLimit = limit;\n\n // Get 2 pages worth of data as we will need it for the hidden functionality.\n if (typeof (loadedPages[currentPage + 1]) === 'undefined') {\n if (typeof (loadedPages[currentPage]) === 'undefined') {\n limit *= 2;\n }\n }\n\n // Call the curried function that'll handle the course promise and any manipulation of it.\n promiseFunction(filters, currentPage, pageData, actions, root, promises, limit, inputValue);\n });\n return promises;\n },\n config\n );\n\n pagedContentPromise.then((html, js) => {\n registerPagedEventHandlers(root, namespace);\n return Templates.replaceNodeContents(root.find(SELECTORS.courseView.region), html, js);\n }).catch(Notification.exception);\n};\n\n/**\n * Listen to, and handle events for the myoverview block.\n *\n * @param {Object} root The myoverview block container element.\n * @param {HTMLElement} page The whole HTMLElement for our block.\n */\nconst registerEventListeners = (root, page) => {\n\n CustomEvents.define(root, [\n CustomEvents.events.activate\n ]);\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_ADD_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_ADD_FAVOURITE);\n const courseId = getCourseId(favourite);\n addToFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_REMOVE_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_REMOVE_FAVOURITE);\n const courseId = getCourseId(favourite);\n removeFromFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.FAVOURITE_ICON, (e, data) => {\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_HIDE_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_HIDE_COURSE);\n const courseId = getCourseId(target);\n hideCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_SHOW_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_SHOW_COURSE);\n const courseId = getCourseId(target);\n showCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n // Searching functionality event handlers.\n const input = page.querySelector(SELECTORS.region.searchInput);\n const clearIcon = page.querySelector(SELECTORS.region.clearIcon);\n\n clearIcon.addEventListener('click', () => {\n input.value = '';\n input.focus();\n clearSearch(clearIcon, root);\n });\n\n input.addEventListener('input', debounce(() => {\n if (input.value === '') {\n clearSearch(clearIcon, root);\n } else {\n activeSearch(clearIcon);\n initializePagedContent(root, searchFunctionalityCurry(), input.value.trim());\n }\n }, 1000));\n};\n\n/**\n * Reset the search icon and trigger the init for the block.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n * @param {Object} root The myoverview block container element.\n */\nexport const clearSearch = (clearIcon, root) => {\n clearIcon.classList.add('d-none');\n init(root);\n};\n\n/**\n * Change the searching icon to its' active state.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n */\nconst activeSearch = (clearIcon) => {\n clearIcon.classList.remove('d-none');\n};\n\n/**\n * Intialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n */\nexport const init = root => {\n root = $(root);\n loadedPages = [];\n lastPage = 0;\n courseOffset = 0;\n\n if (!root.attr('data-init')) {\n const page = document.querySelector(SELECTORS.region.selectBlock);\n registerEventListeners(root, page);\n namespace = \"block_myoverview_\" + root.attr('id') + \"_\" + Math.random();\n root.attr('data-init', true);\n }\n\n initializePagedContent(root, standardFunctionalityCurry());\n};\n\n/**\n * Reset the courses views to their original\n * state on first page load.courseOffset\n *\n * This is called when configuration has changed for the event lists\n * to cause them to reload their data.\n *\n * @param {Object} root The root element for the timeline view.\n */\nexport const reset = root => {\n if (loadedPages.length > 0) {\n const filters = getFilterValues(root);\n // If the display mode is changed to 'summary' but the summary display has not been loaded yet,\n // we need to re-fetch the courses to include the course summary text.\n if (filters.display === 'summary' && !summaryDisplayLoaded) {\n const page = document.querySelector(SELECTORS.region.selectBlock);\n const input = page.querySelector(SELECTORS.region.searchInput);\n if (input.value !== '') {\n initializePagedContent(root, searchFunctionalityCurry(), input.value.trim());\n } else {\n initializePagedContent(root, standardFunctionalityCurry());\n }\n } else {\n loadedPages.forEach((courseList, index) => {\n let pagedContentPage = getPagedContentContainer(root, index);\n renderCourses(root, courseList).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n });\n }\n } else {\n init(root);\n }\n};\n"],"names":["TEMPLATES","GROUPINGS","NUMCOURSES_PERPAGE","loadedPages","courseOffset","lastPage","lastLimit","namespace","summaryDisplayLoaded","getFilterValues","root","courseRegion","find","SELECTORS","courseView","region","display","attr","grouping","sort","displaycategories","customfieldname","customfieldvalue","DEFAULT_PAGED_CONTENT_CONFIG","ignoreControlWhileLoading","controlPlacementBottom","persistentLimitKey","getFavouriteIconContainer","courseId","FAVOURITE_ICON","getPagedContentContainer","index","getCourseId","getAddFavouriteMenuItem","getRemoveFavouriteMenuItem","addToFavourites","removeAction","addAction","setCourseFavouriteState","then","success","PubSub","publish","CourseEvents","favourited","removeClass","addClass","iconContainer","isFavouriteIcon","ICON_IS_FAVOURITE","Aria","unhide","notFavourteIcon","ICON_NOT_FAVOURITE","hide","showFavouriteIcon","Notification","alert","catch","exception","removeFromFavourites","unfavorited","hideFavouriteIcon","getHideCourseMenuItem","getShowCourseMenuItem","setCourseHiddenState","status","hideElement","id","pagingBar","jumpto","parseInt","reducedCourse","courses","reduce","accumulator","current","push","newElement","slice","forEach","courseList","popElement","length","pagedContentContainer","PagedContentFactory","resetLastPageNumber","pagedContentPage","renderCourses","html","js","Templates","replaceNodeContents","remove","Repository","setFavouriteCourses","result","warnings","course","isfavourite","noCoursesRender","nocoursesimg","newcourseurl","render","coursesData","filters","currentTemplate","Array","isArray","Object","values","map","showcoursecategory","registerPagedEventHandlers","event","PagedContentEvents","SET_ITEMS_PER_PAGE_LIMIT","subscribe","limit","setLimit","itemsPerPageFunc","pagingLimit","itemsPerPage","value","active","totalCourseCount","filter","pagingOption","pageBuilder","currentPage","pageData","actions","activeSearch","nextPageStart","pageCourses","currentPageLength","remainingCourses","allItemsLoaded","nextoffset","resetGlobals","standardFunctionalityCurry","promises","pagePromise","params","offset","classification","requiredfields","SUMMARY_REQUIRED_FIELDS","CARDLIST_REQUIRED_FIELDS","getEnrolledCoursesByTimeline","getMyCourses","searchFunctionalityCurry","inputValue","searchingPromise","searchValue","searchvalue","getSearchMyCourses","initializePagedContent","promiseFunction","config","eventNamespace","pagedContentPromise","createWithLimit","pagesData","pageNumber","registerEventListeners","page","CustomEvents","define","events","activate","on","ACTION_ADD_FAVOURITE","e","data","favourite","target","closest","originalEvent","preventDefault","ACTION_REMOVE_FAVOURITE","ACTION_HIDE_COURSE","hideAction","showAction","hideCourse","ACTION_SHOW_COURSE","showCourse","input","querySelector","searchInput","clearIcon","addEventListener","focus","clearSearch","trim","classList","add","init","document","selectBlock","Math","random"],"mappings":"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"} \ No newline at end of file diff --git a/blocks/myoverview/amd/src/view.js b/blocks/myoverview/amd/src/view.js index 08cdbb6abb2..a6e7dfc0e14 100644 --- a/blocks/myoverview/amd/src/view.js +++ b/blocks/myoverview/amd/src/view.js @@ -572,7 +572,11 @@ const itemsPerPageFunc = (pagingLimit, root) => { // Filter out all pagination options which are too large for the amount of courses user is enrolled in. const totalCourseCount = parseInt(root.find(SELECTORS.courseView.region).attr('data-totalcoursecount'), 10); return itemsPerPage.filter(pagingOption => { - return pagingOption.value < totalCourseCount || pagingOption.value === 0; + if (pagingOption.value === 0 && totalCourseCount > 100) { + // To minimise performance issues, do not show the "All" option if the user is enrolled in more than 100 courses. + return false; + } + return pagingOption.value < totalCourseCount; }); }; From e0ef590b00769d6b0a464e40828e443b0936e534 Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Tue, 20 Feb 2024 13:43:42 +0800 Subject: [PATCH 5/5] MDL-78370 block_myoverview: Ensure curry functions use current filters --- blocks/myoverview/amd/build/view.min.js | 2 +- blocks/myoverview/amd/build/view.min.js.map | 2 +- blocks/myoverview/amd/src/view.js | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/blocks/myoverview/amd/build/view.min.js b/blocks/myoverview/amd/build/view.min.js index af66365f27a..291d67a2d2a 100644 --- a/blocks/myoverview/amd/build/view.min.js +++ b/blocks/myoverview/amd/build/view.min.js @@ -4,6 +4,6 @@ define("block_myoverview/view",["exports","jquery","block_myoverview/repository" * * @copyright 2018 Bas Brands * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.reset=_exports.init=_exports.clearSearch=void 0,_jquery=_interopRequireDefault(_jquery),Repository=_interopRequireWildcard(Repository),PagedContentFactory=_interopRequireWildcard(PagedContentFactory),PubSub=_interopRequireWildcard(PubSub),CustomEvents=_interopRequireWildcard(CustomEvents),Notification=_interopRequireWildcard(Notification),Templates=_interopRequireWildcard(Templates),CourseEvents=_interopRequireWildcard(CourseEvents),_selectors=_interopRequireDefault(_selectors),PagedContentEvents=_interopRequireWildcard(PagedContentEvents),Aria=_interopRequireWildcard(Aria);const TEMPLATES_COURSES_CARDS="block_myoverview/view-cards",TEMPLATES_COURSES_LIST="block_myoverview/view-list",TEMPLATES_COURSES_SUMMARY="block_myoverview/view-summary",TEMPLATES_NOCOURSES="core_course/no-courses",GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN="allincludinghidden",NUMCOURSES_PERPAGE=[12,24,48,96,0];let loadedPages=[],courseOffset=0,lastPage=0,lastLimit=0,namespace=null,summaryDisplayLoaded=!1;const getFilterValues=root=>{const courseRegion=root.find(_selectors.default.courseView.region);return{display:courseRegion.attr("data-display"),grouping:courseRegion.attr("data-grouping"),sort:courseRegion.attr("data-sort"),displaycategories:courseRegion.attr("data-displaycategories"),customfieldname:courseRegion.attr("data-customfieldname"),customfieldvalue:courseRegion.attr("data-customfieldvalue")}},DEFAULT_PAGED_CONTENT_CONFIG={ignoreControlWhileLoading:!0,controlPlacementBottom:!0,persistentLimitKey:"block_myoverview_user_paging_preference"},getFavouriteIconContainer=(root,courseId)=>root.find(_selectors.default.FAVOURITE_ICON+'[data-course-id="'+courseId+'"]'),getPagedContentContainer=(root,index)=>root.find('[data-region="paged-content-page"][data-page="'+index+'"]'),getCourseId=root=>root.attr("data-course-id"),getAddFavouriteMenuItem=(root,courseId)=>root.find('[data-action="add-favourite"][data-course-id="'+courseId+'"]'),getRemoveFavouriteMenuItem=(root,courseId)=>root.find('[data-action="remove-favourite"][data-course-id="'+courseId+'"]'),addToFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!0).then((success=>{success?(PubSub.publish(CourseEvents.favourited,courseId),removeAction.removeClass("hidden"),addAction.addClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.removeClass("hidden"),Aria.unhide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.addClass("hidden"),Aria.hide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},removeFromFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!1).then((success=>{success?(PubSub.publish(CourseEvents.unfavorited,courseId),removeAction.addClass("hidden"),addAction.removeClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.addClass("hidden"),Aria.hide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.removeClass("hidden"),Aria.unhide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},getHideCourseMenuItem=(root,courseId)=>root.find('[data-action="hide-course"][data-course-id="'+courseId+'"]'),getShowCourseMenuItem=(root,courseId)=>root.find('[data-action="show-course"][data-course-id="'+courseId+'"]'),setCourseHiddenState=(courseId,status)=>(!1===status&&(status=null),(0,_repository2.setUserPreference)("block_myoverview_hidden_course_".concat(courseId),status).catch(Notification.exception)),hideElement=(root,id)=>{const pagingBar=root.find('[data-region="paging-bar"]'),jumpto=parseInt(pagingBar.attr("data-active-page-number"));let reducedCourse=loadedPages[jumpto].courses.reduce(((accumulator,current)=>(+id!=+current.id&&accumulator.push(current),accumulator)),[]);if(void 0!==loadedPages[jumpto+1]){const newElement=loadedPages[jumpto+1].courses.slice(0,1);loadedPages.forEach(((courseList,index)=>{if(index>jumpto){let popElement=[];void 0!==loadedPages[index+1]&&(popElement=loadedPages[index+1].courses.slice(0,1)),loadedPages[index].courses=[...loadedPages[index].courses.slice(1),...popElement]}})),reducedCourse=[...reducedCourse,...newElement]}if(lastPage===jumpto+1&&0===loadedPages[jumpto+1].courses.length){const pagedContentContainer=root.find('[data-region="paged-content-container"]');PagedContentFactory.resetLastPageNumber((0,_jquery.default)(pagedContentContainer).attr("id"),jumpto)}loadedPages[jumpto].courses=reducedCourse,courseOffset--;const pagedContentPage=getPagedContentContainer(root,jumpto);renderCourses(root,loadedPages[jumpto]).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception),loadedPages.forEach(((courseList,index)=>{if(index>jumpto){getPagedContentContainer(root,index).remove()}}))},setCourseFavouriteState=(courseId,status)=>Repository.setFavouriteCourses({courses:[{id:courseId,favourite:status}]}).then((result=>0===result.warnings.length&&(loadedPages.forEach((courseList=>{courseList.courses.forEach(((course,index)=>{course.id==courseId&&(courseList.courses[index].isfavourite=status)}))})),!0))).catch(Notification.exception),noCoursesRender=root=>{const nocoursesimg=root.find(_selectors.default.courseView.region).attr("data-nocoursesimg"),newcourseurl=root.find(_selectors.default.courseView.region).attr("data-newcourseurl");return Templates.render(TEMPLATES_NOCOURSES,{nocoursesimg:nocoursesimg,newcourseurl:newcourseurl})},renderCourses=(root,coursesData)=>{const filters=getFilterValues(root);let currentTemplate="";return currentTemplate="card"===filters.display?TEMPLATES_COURSES_CARDS:"list"===filters.display?TEMPLATES_COURSES_LIST:TEMPLATES_COURSES_SUMMARY,coursesData?(!1===Array.isArray(coursesData.courses)&&(coursesData.courses=Object.values(coursesData.courses)),coursesData.courses=coursesData.courses.map((course=>(course.showcoursecategory="on"===filters.displaycategories,course))),coursesData.courses.length?Templates.render(currentTemplate,{courses:coursesData.courses}):noCoursesRender(root)):noCoursesRender(root)},registerPagedEventHandlers=(root,namespace)=>{const event=namespace+PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;PubSub.subscribe(event,(root=>limit=>root.find(_selectors.default.courseView.region).attr("data-paging",limit))(root))},itemsPerPageFunc=(pagingLimit,root)=>{let itemsPerPage=NUMCOURSES_PERPAGE.map((value=>{let active=!1;return value===pagingLimit&&(active=!0),{value:value,active:active}}));const totalCourseCount=parseInt(root.find(_selectors.default.courseView.region).attr("data-totalcoursecount"),10);return itemsPerPage.filter((pagingOption=>!(0===pagingOption.value&&totalCourseCount>100)&&pagingOption.value4&&void 0!==arguments[4]?arguments[4]:null,courses=coursesData.courses?coursesData.courses:coursesData,nextPageStart=0,pageCourses=[];if(void 0!==loadedPages[currentPage]){pageCourses=loadedPages[currentPage].courses;const currentPageLength=pageCourses.length;currentPageLength0?courses.slice(0,pageData.limit):courses;loadedPages[currentPage]={courses:pageCourses};const remainingCourses=!1!==nextPageStart?courses.slice(nextPageStart,courses.length):[];remainingCourses.length&&(loadedPages[currentPage+1]={courses:remainingCourses}),loadedPages[currentPage].courses.length{courseOffset=0,loadedPages=[],lastPage=0,lastLimit=0},standardFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit)=>{const pagePromise=((filters,limit)=>{const params={offset:courseOffset,limit:limit,classification:filters.grouping,sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue};return"summary"===filters.display?(params.requiredfields=Repository.SUMMARY_REQUIRED_FIELDS,summaryDisplayLoaded=!0):params.requiredfields=Repository.CARDLIST_REQUIRED_FIELDS,Repository.getEnrolledCoursesByTimeline(params)})(filters,limit).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(pagePromise)}),searchFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit,inputValue)=>{const searchingPromise=((filters,limit,searchValue)=>{const params={offset:courseOffset,limit:limit,classification:"search",sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue,searchvalue:searchValue};return"summary"===filters.display?(params.requiredfields=Repository.SUMMARY_REQUIRED_FIELDS,summaryDisplayLoaded=!0):(params.requiredfields=Repository.CARDLIST_REQUIRED_FIELDS,summaryDisplayLoaded=!1),Repository.getEnrolledCoursesByTimeline(params)})(filters,limit,inputValue).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(searchingPromise)}),initializePagedContent=function(root,promiseFunction){let inputValue=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const pagingLimit=parseInt(root.find(_selectors.default.courseView.region).attr("data-paging"),10);let itemsPerPage=itemsPerPageFunc(pagingLimit,root);const filters=getFilterValues(root),config={...DEFAULT_PAGED_CONTENT_CONFIG};config.eventNamespace=namespace;const pagedContentPromise=PagedContentFactory.createWithLimit(itemsPerPage,((pagesData,actions)=>{let promises=[];return pagesData.forEach((pageData=>{const currentPage=pageData.pageNumber;let limit=pageData.limit>0?pageData.limit:0;if(+lastLimit!=+limit&&(loadedPages=[],courseOffset=0,lastPage=0),lastPage===currentPage)return actions.allItemsLoaded(lastPage),void promises.push(renderCourses(root,loadedPages[currentPage]));lastLimit=limit,void 0===loadedPages[currentPage+1]&&void 0===loadedPages[currentPage]&&(limit*=2),promiseFunction(filters,currentPage,pageData,actions,root,promises,limit,inputValue)})),promises}),config);pagedContentPromise.then(((html,js)=>(registerPagedEventHandlers(root,namespace),Templates.replaceNodeContents(root.find(_selectors.default.courseView.region),html,js)))).catch(Notification.exception)},registerEventListeners=(root,page)=>{CustomEvents.define(root,[CustomEvents.events.activate]),root.on(CustomEvents.events.activate,_selectors.default.ACTION_ADD_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_ADD_FAVOURITE),courseId=getCourseId(favourite);addToFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_REMOVE_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_REMOVE_FAVOURITE),courseId=getCourseId(favourite);removeFromFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.FAVOURITE_ICON,((e,data)=>{data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_HIDE_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_HIDE_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,!0),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.addClass("hidden"),showAction.removeClass("hidden")})(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_SHOW_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_SHOW_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,null),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.removeClass("hidden"),showAction.addClass("hidden")})(root,courseId),data.originalEvent.preventDefault()}));const input=page.querySelector(_selectors.default.region.searchInput),clearIcon=page.querySelector(_selectors.default.region.clearIcon);clearIcon.addEventListener("click",(()=>{input.value="",input.focus(),clearSearch(clearIcon,root)})),input.addEventListener("input",(0,_utils.debounce)((()=>{""===input.value?clearSearch(clearIcon,root):(activeSearch(clearIcon),initializePagedContent(root,searchFunctionalityCurry(),input.value.trim()))}),1e3))},clearSearch=(clearIcon,root)=>{clearIcon.classList.add("d-none"),init(root)};_exports.clearSearch=clearSearch;const activeSearch=clearIcon=>{clearIcon.classList.remove("d-none")},init=root=>{if(root=(0,_jquery.default)(root),loadedPages=[],lastPage=0,courseOffset=0,!root.attr("data-init")){const page=document.querySelector(_selectors.default.region.selectBlock);registerEventListeners(root,page),namespace="block_myoverview_"+root.attr("id")+"_"+Math.random(),root.attr("data-init",!0)}initializePagedContent(root,standardFunctionalityCurry())};_exports.init=init;_exports.reset=root=>{if(loadedPages.length>0){if("summary"!==getFilterValues(root).display||summaryDisplayLoaded)loadedPages.forEach(((courseList,index)=>{let pagedContentPage=getPagedContentContainer(root,index);renderCourses(root,courseList).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception)}));else{const input=document.querySelector(_selectors.default.region.selectBlock).querySelector(_selectors.default.region.searchInput);""!==input.value?initializePagedContent(root,searchFunctionalityCurry(),input.value.trim()):initializePagedContent(root,standardFunctionalityCurry())}}else init(root)}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.reset=_exports.init=_exports.clearSearch=void 0,_jquery=_interopRequireDefault(_jquery),Repository=_interopRequireWildcard(Repository),PagedContentFactory=_interopRequireWildcard(PagedContentFactory),PubSub=_interopRequireWildcard(PubSub),CustomEvents=_interopRequireWildcard(CustomEvents),Notification=_interopRequireWildcard(Notification),Templates=_interopRequireWildcard(Templates),CourseEvents=_interopRequireWildcard(CourseEvents),_selectors=_interopRequireDefault(_selectors),PagedContentEvents=_interopRequireWildcard(PagedContentEvents),Aria=_interopRequireWildcard(Aria);const TEMPLATES_COURSES_CARDS="block_myoverview/view-cards",TEMPLATES_COURSES_LIST="block_myoverview/view-list",TEMPLATES_COURSES_SUMMARY="block_myoverview/view-summary",TEMPLATES_NOCOURSES="core_course/no-courses",GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN="allincludinghidden",NUMCOURSES_PERPAGE=[12,24,48,96,0];let loadedPages=[],courseOffset=0,lastPage=0,lastLimit=0,namespace=null,summaryDisplayLoaded=!1;const getFilterValues=root=>{const courseRegion=root.find(_selectors.default.courseView.region);return{display:courseRegion.attr("data-display"),grouping:courseRegion.attr("data-grouping"),sort:courseRegion.attr("data-sort"),displaycategories:courseRegion.attr("data-displaycategories"),customfieldname:courseRegion.attr("data-customfieldname"),customfieldvalue:courseRegion.attr("data-customfieldvalue")}},DEFAULT_PAGED_CONTENT_CONFIG={ignoreControlWhileLoading:!0,controlPlacementBottom:!0,persistentLimitKey:"block_myoverview_user_paging_preference"},getFavouriteIconContainer=(root,courseId)=>root.find(_selectors.default.FAVOURITE_ICON+'[data-course-id="'+courseId+'"]'),getPagedContentContainer=(root,index)=>root.find('[data-region="paged-content-page"][data-page="'+index+'"]'),getCourseId=root=>root.attr("data-course-id"),getAddFavouriteMenuItem=(root,courseId)=>root.find('[data-action="add-favourite"][data-course-id="'+courseId+'"]'),getRemoveFavouriteMenuItem=(root,courseId)=>root.find('[data-action="remove-favourite"][data-course-id="'+courseId+'"]'),addToFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!0).then((success=>{success?(PubSub.publish(CourseEvents.favourited,courseId),removeAction.removeClass("hidden"),addAction.addClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.removeClass("hidden"),Aria.unhide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.addClass("hidden"),Aria.hide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},removeFromFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!1).then((success=>{success?(PubSub.publish(CourseEvents.unfavorited,courseId),removeAction.addClass("hidden"),addAction.removeClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.addClass("hidden"),Aria.hide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.removeClass("hidden"),Aria.unhide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},getHideCourseMenuItem=(root,courseId)=>root.find('[data-action="hide-course"][data-course-id="'+courseId+'"]'),getShowCourseMenuItem=(root,courseId)=>root.find('[data-action="show-course"][data-course-id="'+courseId+'"]'),setCourseHiddenState=(courseId,status)=>(!1===status&&(status=null),(0,_repository2.setUserPreference)("block_myoverview_hidden_course_".concat(courseId),status).catch(Notification.exception)),hideElement=(root,id)=>{const pagingBar=root.find('[data-region="paging-bar"]'),jumpto=parseInt(pagingBar.attr("data-active-page-number"));let reducedCourse=loadedPages[jumpto].courses.reduce(((accumulator,current)=>(+id!=+current.id&&accumulator.push(current),accumulator)),[]);if(void 0!==loadedPages[jumpto+1]){const newElement=loadedPages[jumpto+1].courses.slice(0,1);loadedPages.forEach(((courseList,index)=>{if(index>jumpto){let popElement=[];void 0!==loadedPages[index+1]&&(popElement=loadedPages[index+1].courses.slice(0,1)),loadedPages[index].courses=[...loadedPages[index].courses.slice(1),...popElement]}})),reducedCourse=[...reducedCourse,...newElement]}if(lastPage===jumpto+1&&0===loadedPages[jumpto+1].courses.length){const pagedContentContainer=root.find('[data-region="paged-content-container"]');PagedContentFactory.resetLastPageNumber((0,_jquery.default)(pagedContentContainer).attr("id"),jumpto)}loadedPages[jumpto].courses=reducedCourse,courseOffset--;const pagedContentPage=getPagedContentContainer(root,jumpto);renderCourses(root,loadedPages[jumpto]).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception),loadedPages.forEach(((courseList,index)=>{if(index>jumpto){getPagedContentContainer(root,index).remove()}}))},setCourseFavouriteState=(courseId,status)=>Repository.setFavouriteCourses({courses:[{id:courseId,favourite:status}]}).then((result=>0===result.warnings.length&&(loadedPages.forEach((courseList=>{courseList.courses.forEach(((course,index)=>{course.id==courseId&&(courseList.courses[index].isfavourite=status)}))})),!0))).catch(Notification.exception),noCoursesRender=root=>{const nocoursesimg=root.find(_selectors.default.courseView.region).attr("data-nocoursesimg"),newcourseurl=root.find(_selectors.default.courseView.region).attr("data-newcourseurl");return Templates.render(TEMPLATES_NOCOURSES,{nocoursesimg:nocoursesimg,newcourseurl:newcourseurl})},renderCourses=(root,coursesData)=>{const filters=getFilterValues(root);let currentTemplate="";return currentTemplate="card"===filters.display?TEMPLATES_COURSES_CARDS:"list"===filters.display?TEMPLATES_COURSES_LIST:TEMPLATES_COURSES_SUMMARY,coursesData?(!1===Array.isArray(coursesData.courses)&&(coursesData.courses=Object.values(coursesData.courses)),coursesData.courses=coursesData.courses.map((course=>(course.showcoursecategory="on"===filters.displaycategories,course))),coursesData.courses.length?Templates.render(currentTemplate,{courses:coursesData.courses}):noCoursesRender(root)):noCoursesRender(root)},registerPagedEventHandlers=(root,namespace)=>{const event=namespace+PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;PubSub.subscribe(event,(root=>limit=>root.find(_selectors.default.courseView.region).attr("data-paging",limit))(root))},itemsPerPageFunc=(pagingLimit,root)=>{let itemsPerPage=NUMCOURSES_PERPAGE.map((value=>{let active=!1;return value===pagingLimit&&(active=!0),{value:value,active:active}}));const totalCourseCount=parseInt(root.find(_selectors.default.courseView.region).attr("data-totalcoursecount"),10);return itemsPerPage.filter((pagingOption=>!(0===pagingOption.value&&totalCourseCount>100)&&pagingOption.value4&&void 0!==arguments[4]?arguments[4]:null,courses=coursesData.courses?coursesData.courses:coursesData,nextPageStart=0,pageCourses=[];if(void 0!==loadedPages[currentPage]){pageCourses=loadedPages[currentPage].courses;const currentPageLength=pageCourses.length;currentPageLength0?courses.slice(0,pageData.limit):courses;loadedPages[currentPage]={courses:pageCourses};const remainingCourses=!1!==nextPageStart?courses.slice(nextPageStart,courses.length):[];remainingCourses.length&&(loadedPages[currentPage+1]={courses:remainingCourses}),loadedPages[currentPage].courses.length{courseOffset=0,loadedPages=[],lastPage=0,lastLimit=0},standardFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit)=>{const pagePromise=((filters,limit)=>{const params={offset:courseOffset,limit:limit,classification:filters.grouping,sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue};return"summary"===filters.display?(params.requiredfields=Repository.SUMMARY_REQUIRED_FIELDS,summaryDisplayLoaded=!0):params.requiredfields=Repository.CARDLIST_REQUIRED_FIELDS,Repository.getEnrolledCoursesByTimeline(params)})(filters,limit).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(pagePromise)}),searchFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit,inputValue)=>{const searchingPromise=((filters,limit,searchValue)=>{const params={offset:courseOffset,limit:limit,classification:"search",sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue,searchvalue:searchValue};return"summary"===filters.display?(params.requiredfields=Repository.SUMMARY_REQUIRED_FIELDS,summaryDisplayLoaded=!0):(params.requiredfields=Repository.CARDLIST_REQUIRED_FIELDS,summaryDisplayLoaded=!1),Repository.getEnrolledCoursesByTimeline(params)})(filters,limit,inputValue).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(searchingPromise)}),initializePagedContent=function(root,promiseFunction){let inputValue=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const pagingLimit=parseInt(root.find(_selectors.default.courseView.region).attr("data-paging"),10);let itemsPerPage=itemsPerPageFunc(pagingLimit,root);const config={...DEFAULT_PAGED_CONTENT_CONFIG};config.eventNamespace=namespace;const pagedContentPromise=PagedContentFactory.createWithLimit(itemsPerPage,((pagesData,actions)=>{let promises=[];return pagesData.forEach((pageData=>{const currentPage=pageData.pageNumber;let limit=pageData.limit>0?pageData.limit:0;if(+lastLimit!=+limit&&(loadedPages=[],courseOffset=0,lastPage=0),lastPage===currentPage)return actions.allItemsLoaded(lastPage),void promises.push(renderCourses(root,loadedPages[currentPage]));lastLimit=limit,void 0===loadedPages[currentPage+1]&&void 0===loadedPages[currentPage]&&(limit*=2);const filters=getFilterValues(root);promiseFunction(filters,currentPage,pageData,actions,root,promises,limit,inputValue)})),promises}),config);pagedContentPromise.then(((html,js)=>(registerPagedEventHandlers(root,namespace),Templates.replaceNodeContents(root.find(_selectors.default.courseView.region),html,js)))).catch(Notification.exception)},registerEventListeners=(root,page)=>{CustomEvents.define(root,[CustomEvents.events.activate]),root.on(CustomEvents.events.activate,_selectors.default.ACTION_ADD_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_ADD_FAVOURITE),courseId=getCourseId(favourite);addToFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_REMOVE_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_REMOVE_FAVOURITE),courseId=getCourseId(favourite);removeFromFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.FAVOURITE_ICON,((e,data)=>{data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_HIDE_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_HIDE_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,!0),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.addClass("hidden"),showAction.removeClass("hidden")})(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_SHOW_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_SHOW_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,null),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.removeClass("hidden"),showAction.addClass("hidden")})(root,courseId),data.originalEvent.preventDefault()}));const input=page.querySelector(_selectors.default.region.searchInput),clearIcon=page.querySelector(_selectors.default.region.clearIcon);clearIcon.addEventListener("click",(()=>{input.value="",input.focus(),clearSearch(clearIcon,root)})),input.addEventListener("input",(0,_utils.debounce)((()=>{""===input.value?clearSearch(clearIcon,root):(activeSearch(clearIcon),initializePagedContent(root,searchFunctionalityCurry(),input.value.trim()))}),1e3))},clearSearch=(clearIcon,root)=>{clearIcon.classList.add("d-none"),init(root)};_exports.clearSearch=clearSearch;const activeSearch=clearIcon=>{clearIcon.classList.remove("d-none")},init=root=>{if(root=(0,_jquery.default)(root),loadedPages=[],lastPage=0,courseOffset=0,!root.attr("data-init")){const page=document.querySelector(_selectors.default.region.selectBlock);registerEventListeners(root,page),namespace="block_myoverview_"+root.attr("id")+"_"+Math.random(),root.attr("data-init",!0)}initializePagedContent(root,standardFunctionalityCurry())};_exports.init=init;_exports.reset=root=>{if(loadedPages.length>0){if("summary"!==getFilterValues(root).display||summaryDisplayLoaded)loadedPages.forEach(((courseList,index)=>{let pagedContentPage=getPagedContentContainer(root,index);renderCourses(root,courseList).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception)}));else{const input=document.querySelector(_selectors.default.region.selectBlock).querySelector(_selectors.default.region.searchInput);""!==input.value?initializePagedContent(root,searchFunctionalityCurry(),input.value.trim()):initializePagedContent(root,standardFunctionalityCurry())}}else init(root)}})); //# sourceMappingURL=view.min.js.map \ No newline at end of file diff --git a/blocks/myoverview/amd/build/view.min.js.map b/blocks/myoverview/amd/build/view.min.js.map index d9cffc5deac..e6220ab9eb1 100644 --- a/blocks/myoverview/amd/build/view.min.js.map +++ b/blocks/myoverview/amd/build/view.min.js.map @@ -1 +1 @@ -{"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 courses view for the overview block.\n *\n * @copyright 2018 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as Repository from 'block_myoverview/repository';\nimport * as PagedContentFactory from 'core/paged_content_factory';\nimport * as PubSub from 'core/pubsub';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as Notification from 'core/notification';\nimport * as Templates from 'core/templates';\nimport * as CourseEvents from 'core_course/events';\nimport SELECTORS from 'block_myoverview/selectors';\nimport * as PagedContentEvents from 'core/paged_content_events';\nimport * as Aria from 'core/aria';\nimport {debounce} from 'core/utils';\nimport {setUserPreference} from 'core_user/repository';\n\nconst TEMPLATES = {\n COURSES_CARDS: 'block_myoverview/view-cards',\n COURSES_LIST: 'block_myoverview/view-list',\n COURSES_SUMMARY: 'block_myoverview/view-summary',\n NOCOURSES: 'core_course/no-courses'\n};\n\nconst GROUPINGS = {\n GROUPING_ALLINCLUDINGHIDDEN: 'allincludinghidden',\n GROUPING_ALL: 'all',\n GROUPING_INPROGRESS: 'inprogress',\n GROUPING_FUTURE: 'future',\n GROUPING_PAST: 'past',\n GROUPING_FAVOURITES: 'favourites',\n GROUPING_HIDDEN: 'hidden'\n};\n\nconst NUMCOURSES_PERPAGE = [12, 24, 48, 96, 0];\n\nlet loadedPages = [];\n\nlet courseOffset = 0;\n\nlet lastPage = 0;\n\nlet lastLimit = 0;\n\nlet namespace = null;\n\n/**\n * Whether the summary display has been loaded.\n *\n * If true, this means that courses have been loaded with the summary text.\n * Otherwise, switching to the summary display mode will require course data to be fetched with the summary text.\n *\n * @type {boolean}\n */\nlet summaryDisplayLoaded = false;\n\n/**\n * Get filter values from DOM.\n *\n * @param {object} root The root element for the courses view.\n * @return {filters} Set filters.\n */\nconst getFilterValues = root => {\n const courseRegion = root.find(SELECTORS.courseView.region);\n return {\n display: courseRegion.attr('data-display'),\n grouping: courseRegion.attr('data-grouping'),\n sort: courseRegion.attr('data-sort'),\n displaycategories: courseRegion.attr('data-displaycategories'),\n customfieldname: courseRegion.attr('data-customfieldname'),\n customfieldvalue: courseRegion.attr('data-customfieldvalue'),\n };\n};\n\n// We want the paged content controls below the paged content area.\n// and the controls should be ignored while data is loading.\nconst DEFAULT_PAGED_CONTENT_CONFIG = {\n ignoreControlWhileLoading: true,\n controlPlacementBottom: true,\n persistentLimitKey: 'block_myoverview_user_paging_preference'\n};\n\n/**\n * Get enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @return {promise} Resolved with an array of courses.\n */\nconst getMyCourses = (filters, limit) => {\n const params = {\n offset: courseOffset,\n limit: limit,\n classification: filters.grouping,\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n };\n if (filters.display === 'summary') {\n params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS;\n summaryDisplayLoaded = true;\n } else {\n params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS;\n }\n return Repository.getEnrolledCoursesByTimeline(params);\n};\n\n/**\n * Search for enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @param {string} searchValue What does the user want to search within their courses.\n * @return {promise} Resolved with an array of courses.\n */\nconst getSearchMyCourses = (filters, limit, searchValue) => {\n const params = {\n offset: courseOffset,\n limit: limit,\n classification: 'search',\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n searchvalue: searchValue,\n };\n if (filters.display === 'summary') {\n params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS;\n summaryDisplayLoaded = true;\n } else {\n params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS;\n summaryDisplayLoaded = false;\n }\n return Repository.getEnrolledCoursesByTimeline(params);\n};\n\n/**\n * Get the container element for the favourite icon.\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n * @return {Object} The favourite icon container\n */\nconst getFavouriteIconContainer = (root, courseId) => {\n return root.find(SELECTORS.FAVOURITE_ICON + '[data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the paged content container element.\n *\n * @param {Object} root The course overview container\n * @param {Number} index Rendered page index.\n * @return {Object} The rendered paged container.\n */\nconst getPagedContentContainer = (root, index) => {\n return root.find('[data-region=\"paged-content-page\"][data-page=\"' + index + '\"]');\n};\n\n/**\n * Get the course id from a favourite element.\n *\n * @param {Object} root The favourite icon container element.\n * @return {Number} Course id.\n */\nconst getCourseId = root => {\n return root.attr('data-course-id');\n};\n\n/**\n * Hide the favourite icon.\n *\n * @param {Object} root The favourite icon container element.\n * @param {Number} courseId Course id number.\n */\nconst hideFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.addClass('hidden');\n Aria.hide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.removeClass('hidden');\n Aria.unhide(notFavourteIcon);\n};\n\n/**\n * Show the favourite icon.\n *\n * @param {Object} root The course overview container.\n * @param {Number} courseId Course id number.\n */\nconst showFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.removeClass('hidden');\n Aria.unhide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.addClass('hidden');\n Aria.hide(notFavourteIcon);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The add to favourite menu item.\n */\nconst getAddFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"add-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The remove from favourites menu item.\n */\nconst getRemoveFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"remove-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Add course to favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst addToFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, true).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.favourited, courseId);\n removeAction.removeClass('hidden');\n addAction.addClass('hidden');\n showFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Remove course from favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst removeFromFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, false).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.unfavorited, courseId);\n removeAction.addClass('hidden');\n addAction.removeClass('hidden');\n hideFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The hide course menu item.\n */\nconst getHideCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"hide-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The show course menu item.\n */\nconst getShowCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"show-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Hide course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst hideCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, true);\n\n // Remove the course from this view as it is now hidden and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.addClass('hidden');\n showAction.removeClass('hidden');\n};\n\n/**\n * Show course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst showCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, null);\n\n // Remove the course from this view as it is now shown again and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.removeClass('hidden');\n showAction.addClass('hidden');\n};\n\n/**\n * Set the courses hidden status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {Boolean} status new hidden status.\n * @return {Promise} Repository promise.\n */\nconst setCourseHiddenState = (courseId, status) => {\n\n // If the given status is not hidden, the preference has to be deleted with a null value.\n if (status === false) {\n status = null;\n }\n\n return setUserPreference(`block_myoverview_hidden_course_${courseId}`, status)\n .catch(Notification.exception);\n};\n\n/**\n * Reset the loadedPages dataset to take into account the hidden element\n *\n * @param {Object} root The course overview container\n * @param {Number} id The course id number\n */\nconst hideElement = (root, id) => {\n const pagingBar = root.find('[data-region=\"paging-bar\"]');\n const jumpto = parseInt(pagingBar.attr('data-active-page-number'));\n\n // Get a reduced dataset for the current page.\n const courseList = loadedPages[jumpto];\n let reducedCourse = courseList.courses.reduce((accumulator, current) => {\n if (+id !== +current.id) {\n accumulator.push(current);\n }\n return accumulator;\n }, []);\n\n // Get the next page's data if loaded and pop the first element from it.\n if (typeof (loadedPages[jumpto + 1]) !== 'undefined') {\n const newElement = loadedPages[jumpto + 1].courses.slice(0, 1);\n\n // Adjust the dataset for the reset of the pages that are loaded.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n let popElement = [];\n if (typeof (loadedPages[index + 1]) !== 'undefined') {\n popElement = loadedPages[index + 1].courses.slice(0, 1);\n }\n loadedPages[index].courses = [...loadedPages[index].courses.slice(1), ...popElement];\n }\n });\n\n reducedCourse = [...reducedCourse, ...newElement];\n }\n\n // Check if the next page is the last page and if it still has data associated to it.\n if (lastPage === jumpto + 1 && loadedPages[jumpto + 1].courses.length === 0) {\n const pagedContentContainer = root.find('[data-region=\"paged-content-container\"]');\n PagedContentFactory.resetLastPageNumber($(pagedContentContainer).attr('id'), jumpto);\n }\n\n loadedPages[jumpto].courses = reducedCourse;\n\n // Reduce the course offset.\n courseOffset--;\n\n // Render the paged content for the current.\n const pagedContentPage = getPagedContentContainer(root, jumpto);\n renderCourses(root, loadedPages[jumpto]).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n\n // Delete subsequent pages in order to trigger the callback.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n const page = getPagedContentContainer(root, index);\n page.remove();\n }\n });\n};\n\n/**\n * Set the courses favourite status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {boolean} status new favourite status.\n * @return {Promise} Repository promise.\n */\nconst setCourseFavouriteState = (courseId, status) => {\n\n return Repository.setFavouriteCourses({\n courses: [\n {\n 'id': courseId,\n 'favourite': status\n }\n ]\n }).then(result => {\n if (result.warnings.length === 0) {\n loadedPages.forEach(courseList => {\n courseList.courses.forEach((course, index) => {\n if (course.id == courseId) {\n courseList.courses[index].isfavourite = status;\n }\n });\n });\n return true;\n } else {\n return false;\n }\n }).catch(Notification.exception);\n};\n\n/**\n * Given there are no courses to render provide the rendered template.\n *\n * @param {object} root The root element for the courses view.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst noCoursesRender = root => {\n const nocoursesimg = root.find(SELECTORS.courseView.region).attr('data-nocoursesimg');\n const newcourseurl = root.find(SELECTORS.courseView.region).attr('data-newcourseurl');\n return Templates.render(TEMPLATES.NOCOURSES, {\n nocoursesimg: nocoursesimg,\n newcourseurl: newcourseurl\n });\n};\n\n/**\n * Render the dashboard courses.\n *\n * @param {object} root The root element for the courses view.\n * @param {array} coursesData containing array of returned courses.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst renderCourses = (root, coursesData) => {\n\n const filters = getFilterValues(root);\n\n let currentTemplate = '';\n if (filters.display === 'card') {\n currentTemplate = TEMPLATES.COURSES_CARDS;\n } else if (filters.display === 'list') {\n currentTemplate = TEMPLATES.COURSES_LIST;\n } else {\n currentTemplate = TEMPLATES.COURSES_SUMMARY;\n }\n\n if (!coursesData) {\n return noCoursesRender(root);\n } else {\n // Sometimes we get weird objects coming after a failed search, cast to ensure typing functions.\n if (Array.isArray(coursesData.courses) === false) {\n coursesData.courses = Object.values(coursesData.courses);\n }\n // Whether the course category should be displayed in the course item.\n coursesData.courses = coursesData.courses.map(course => {\n course.showcoursecategory = filters.displaycategories === 'on';\n return course;\n });\n if (coursesData.courses.length) {\n return Templates.render(currentTemplate, {\n courses: coursesData.courses,\n });\n } else {\n return noCoursesRender(root);\n }\n }\n};\n\n/**\n * Return the callback to be passed to the subscribe event\n *\n * @param {object} root The root element for the courses view\n * @return {function} Partially applied function that'll execute when passed a limit\n */\nconst setLimit = root => {\n // @param {Number} limit The paged limit that is passed through the event.\n return limit => root.find(SELECTORS.courseView.region).attr('data-paging', limit);\n};\n\n/**\n * Intialise the paged list and cards views on page load.\n * Returns an array of paged contents that we would like to handle here\n *\n * @param {object} root The root element for the courses view\n * @param {string} namespace The namespace for all the events attached\n */\nconst registerPagedEventHandlers = (root, namespace) => {\n const event = namespace + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;\n PubSub.subscribe(event, setLimit(root));\n};\n\n/**\n * Figure out how many items are going to be allowed to be rendered in the block.\n *\n * @param {Number} pagingLimit How many courses to display\n * @param {Object} root The course overview container\n * @return {Number[]} How many courses will be rendered\n */\nconst itemsPerPageFunc = (pagingLimit, root) => {\n let itemsPerPage = NUMCOURSES_PERPAGE.map(value => {\n let active = false;\n if (value === pagingLimit) {\n active = true;\n }\n\n return {\n value: value,\n active: active\n };\n });\n\n // Filter out all pagination options which are too large for the amount of courses user is enrolled in.\n const totalCourseCount = parseInt(root.find(SELECTORS.courseView.region).attr('data-totalcoursecount'), 10);\n return itemsPerPage.filter(pagingOption => {\n if (pagingOption.value === 0 && totalCourseCount > 100) {\n // To minimise performance issues, do not show the \"All\" option if the user is enrolled in more than 100 courses.\n return false;\n }\n return pagingOption.value < totalCourseCount;\n });\n};\n\n/**\n * Mutates and controls the loadedPages array and handles the bootstrapping.\n *\n * @param {Array|Object} coursesData Array of all of the courses to start building the page from\n * @param {Number} currentPage What page are we currently on?\n * @param {Object} pageData Any current page information\n * @param {Object} actions Paged content helper\n * @param {null|boolean} activeSearch Are we currently actively searching and building up search results?\n */\nconst pageBuilder = (coursesData, currentPage, pageData, actions, activeSearch = null) => {\n // If the courseData comes in an object then get the value otherwise it is a pure array.\n let courses = coursesData.courses ? coursesData.courses : coursesData;\n let nextPageStart = 0;\n let pageCourses = [];\n\n // If current page's data is loaded make sure we max it to page limit.\n if (typeof (loadedPages[currentPage]) !== 'undefined') {\n pageCourses = loadedPages[currentPage].courses;\n const currentPageLength = pageCourses.length;\n if (currentPageLength < pageData.limit) {\n nextPageStart = pageData.limit - currentPageLength;\n pageCourses = {...loadedPages[currentPage].courses, ...courses.slice(0, nextPageStart)};\n }\n } else {\n // When the page limit is zero, there is only one page of courses, no start for next page.\n nextPageStart = pageData.limit || false;\n pageCourses = (pageData.limit > 0) ? courses.slice(0, pageData.limit) : courses;\n }\n\n // Finished setting up the current page.\n loadedPages[currentPage] = {\n courses: pageCourses\n };\n\n // Set up the next page (if there is more than one page).\n const remainingCourses = nextPageStart !== false ? courses.slice(nextPageStart, courses.length) : [];\n if (remainingCourses.length) {\n loadedPages[currentPage + 1] = {\n courses: remainingCourses\n };\n }\n\n // Set the last page to either the current or next page.\n if (loadedPages[currentPage].courses.length < pageData.limit || !remainingCourses.length) {\n lastPage = currentPage;\n if (activeSearch === null) {\n actions.allItemsLoaded(currentPage);\n }\n } else if (typeof (loadedPages[currentPage + 1]) !== 'undefined'\n && loadedPages[currentPage + 1].courses.length < pageData.limit) {\n lastPage = currentPage + 1;\n }\n\n courseOffset = coursesData.nextoffset;\n};\n\n/**\n * In cases when switching between regular rendering and search rendering we need to reset some variables.\n */\nconst resetGlobals = () => {\n courseOffset = 0;\n loadedPages = [];\n lastPage = 0;\n lastLimit = 0;\n};\n\n/**\n * The default functionality of fetching paginated courses without special handling.\n *\n * @return {function(Object, Object, Object, Object, Object, Promise, Number): void}\n */\nconst standardFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit) => {\n const pagePromise = getMyCourses(\n filters,\n limit\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(pagePromise);\n };\n};\n\n/**\n * Initialize the searching functionality so we can call it when required.\n *\n * @return {function(Object, Number, Object, Object, Object, Promise, Number, String): void}\n */\nconst searchFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit, inputValue) => {\n const searchingPromise = getSearchMyCourses(\n filters,\n limit,\n inputValue\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(searchingPromise);\n };\n};\n\n/**\n * Initialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n * @param {function} promiseFunction How do we fetch the courses and what do we do with them?\n * @param {null | string} inputValue What to search for\n */\nconst initializePagedContent = (root, promiseFunction, inputValue = null) => {\n const pagingLimit = parseInt(root.find(SELECTORS.courseView.region).attr('data-paging'), 10);\n let itemsPerPage = itemsPerPageFunc(pagingLimit, root);\n\n const filters = getFilterValues(root);\n const config = {...{}, ...DEFAULT_PAGED_CONTENT_CONFIG};\n config.eventNamespace = namespace;\n\n const pagedContentPromise = PagedContentFactory.createWithLimit(\n itemsPerPage,\n (pagesData, actions) => {\n let promises = [];\n pagesData.forEach(pageData => {\n const currentPage = pageData.pageNumber;\n let limit = (pageData.limit > 0) ? pageData.limit : 0;\n\n // Reset local variables if limits have changed.\n if (+lastLimit !== +limit) {\n loadedPages = [];\n courseOffset = 0;\n lastPage = 0;\n }\n\n if (lastPage === currentPage) {\n // If we are on the last page and have it's data then load it from cache.\n actions.allItemsLoaded(lastPage);\n promises.push(renderCourses(root, loadedPages[currentPage]));\n return;\n }\n\n lastLimit = limit;\n\n // Get 2 pages worth of data as we will need it for the hidden functionality.\n if (typeof (loadedPages[currentPage + 1]) === 'undefined') {\n if (typeof (loadedPages[currentPage]) === 'undefined') {\n limit *= 2;\n }\n }\n\n // Call the curried function that'll handle the course promise and any manipulation of it.\n promiseFunction(filters, currentPage, pageData, actions, root, promises, limit, inputValue);\n });\n return promises;\n },\n config\n );\n\n pagedContentPromise.then((html, js) => {\n registerPagedEventHandlers(root, namespace);\n return Templates.replaceNodeContents(root.find(SELECTORS.courseView.region), html, js);\n }).catch(Notification.exception);\n};\n\n/**\n * Listen to, and handle events for the myoverview block.\n *\n * @param {Object} root The myoverview block container element.\n * @param {HTMLElement} page The whole HTMLElement for our block.\n */\nconst registerEventListeners = (root, page) => {\n\n CustomEvents.define(root, [\n CustomEvents.events.activate\n ]);\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_ADD_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_ADD_FAVOURITE);\n const courseId = getCourseId(favourite);\n addToFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_REMOVE_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_REMOVE_FAVOURITE);\n const courseId = getCourseId(favourite);\n removeFromFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.FAVOURITE_ICON, (e, data) => {\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_HIDE_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_HIDE_COURSE);\n const courseId = getCourseId(target);\n hideCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_SHOW_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_SHOW_COURSE);\n const courseId = getCourseId(target);\n showCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n // Searching functionality event handlers.\n const input = page.querySelector(SELECTORS.region.searchInput);\n const clearIcon = page.querySelector(SELECTORS.region.clearIcon);\n\n clearIcon.addEventListener('click', () => {\n input.value = '';\n input.focus();\n clearSearch(clearIcon, root);\n });\n\n input.addEventListener('input', debounce(() => {\n if (input.value === '') {\n clearSearch(clearIcon, root);\n } else {\n activeSearch(clearIcon);\n initializePagedContent(root, searchFunctionalityCurry(), input.value.trim());\n }\n }, 1000));\n};\n\n/**\n * Reset the search icon and trigger the init for the block.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n * @param {Object} root The myoverview block container element.\n */\nexport const clearSearch = (clearIcon, root) => {\n clearIcon.classList.add('d-none');\n init(root);\n};\n\n/**\n * Change the searching icon to its' active state.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n */\nconst activeSearch = (clearIcon) => {\n clearIcon.classList.remove('d-none');\n};\n\n/**\n * Intialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n */\nexport const init = root => {\n root = $(root);\n loadedPages = [];\n lastPage = 0;\n courseOffset = 0;\n\n if (!root.attr('data-init')) {\n const page = document.querySelector(SELECTORS.region.selectBlock);\n registerEventListeners(root, page);\n namespace = \"block_myoverview_\" + root.attr('id') + \"_\" + Math.random();\n root.attr('data-init', true);\n }\n\n initializePagedContent(root, standardFunctionalityCurry());\n};\n\n/**\n * Reset the courses views to their original\n * state on first page load.courseOffset\n *\n * This is called when configuration has changed for the event lists\n * to cause them to reload their data.\n *\n * @param {Object} root The root element for the timeline view.\n */\nexport const reset = root => {\n if (loadedPages.length > 0) {\n const filters = getFilterValues(root);\n // If the display mode is changed to 'summary' but the summary display has not been loaded yet,\n // we need to re-fetch the courses to include the course summary text.\n if (filters.display === 'summary' && !summaryDisplayLoaded) {\n const page = document.querySelector(SELECTORS.region.selectBlock);\n const input = page.querySelector(SELECTORS.region.searchInput);\n if (input.value !== '') {\n initializePagedContent(root, searchFunctionalityCurry(), input.value.trim());\n } else {\n initializePagedContent(root, standardFunctionalityCurry());\n }\n } else {\n loadedPages.forEach((courseList, index) => {\n let pagedContentPage = getPagedContentContainer(root, index);\n renderCourses(root, courseList).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n });\n }\n } else {\n init(root);\n }\n};\n"],"names":["TEMPLATES","GROUPINGS","NUMCOURSES_PERPAGE","loadedPages","courseOffset","lastPage","lastLimit","namespace","summaryDisplayLoaded","getFilterValues","root","courseRegion","find","SELECTORS","courseView","region","display","attr","grouping","sort","displaycategories","customfieldname","customfieldvalue","DEFAULT_PAGED_CONTENT_CONFIG","ignoreControlWhileLoading","controlPlacementBottom","persistentLimitKey","getFavouriteIconContainer","courseId","FAVOURITE_ICON","getPagedContentContainer","index","getCourseId","getAddFavouriteMenuItem","getRemoveFavouriteMenuItem","addToFavourites","removeAction","addAction","setCourseFavouriteState","then","success","PubSub","publish","CourseEvents","favourited","removeClass","addClass","iconContainer","isFavouriteIcon","ICON_IS_FAVOURITE","Aria","unhide","notFavourteIcon","ICON_NOT_FAVOURITE","hide","showFavouriteIcon","Notification","alert","catch","exception","removeFromFavourites","unfavorited","hideFavouriteIcon","getHideCourseMenuItem","getShowCourseMenuItem","setCourseHiddenState","status","hideElement","id","pagingBar","jumpto","parseInt","reducedCourse","courses","reduce","accumulator","current","push","newElement","slice","forEach","courseList","popElement","length","pagedContentContainer","PagedContentFactory","resetLastPageNumber","pagedContentPage","renderCourses","html","js","Templates","replaceNodeContents","remove","Repository","setFavouriteCourses","result","warnings","course","isfavourite","noCoursesRender","nocoursesimg","newcourseurl","render","coursesData","filters","currentTemplate","Array","isArray","Object","values","map","showcoursecategory","registerPagedEventHandlers","event","PagedContentEvents","SET_ITEMS_PER_PAGE_LIMIT","subscribe","limit","setLimit","itemsPerPageFunc","pagingLimit","itemsPerPage","value","active","totalCourseCount","filter","pagingOption","pageBuilder","currentPage","pageData","actions","activeSearch","nextPageStart","pageCourses","currentPageLength","remainingCourses","allItemsLoaded","nextoffset","resetGlobals","standardFunctionalityCurry","promises","pagePromise","params","offset","classification","requiredfields","SUMMARY_REQUIRED_FIELDS","CARDLIST_REQUIRED_FIELDS","getEnrolledCoursesByTimeline","getMyCourses","searchFunctionalityCurry","inputValue","searchingPromise","searchValue","searchvalue","getSearchMyCourses","initializePagedContent","promiseFunction","config","eventNamespace","pagedContentPromise","createWithLimit","pagesData","pageNumber","registerEventListeners","page","CustomEvents","define","events","activate","on","ACTION_ADD_FAVOURITE","e","data","favourite","target","closest","originalEvent","preventDefault","ACTION_REMOVE_FAVOURITE","ACTION_HIDE_COURSE","hideAction","showAction","hideCourse","ACTION_SHOW_COURSE","showCourse","input","querySelector","searchInput","clearIcon","addEventListener","focus","clearSearch","trim","classList","add","init","document","selectBlock","Math","random"],"mappings":"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"} \ No newline at end of file +{"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 courses view for the overview block.\n *\n * @copyright 2018 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as Repository from 'block_myoverview/repository';\nimport * as PagedContentFactory from 'core/paged_content_factory';\nimport * as PubSub from 'core/pubsub';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as Notification from 'core/notification';\nimport * as Templates from 'core/templates';\nimport * as CourseEvents from 'core_course/events';\nimport SELECTORS from 'block_myoverview/selectors';\nimport * as PagedContentEvents from 'core/paged_content_events';\nimport * as Aria from 'core/aria';\nimport {debounce} from 'core/utils';\nimport {setUserPreference} from 'core_user/repository';\n\nconst TEMPLATES = {\n COURSES_CARDS: 'block_myoverview/view-cards',\n COURSES_LIST: 'block_myoverview/view-list',\n COURSES_SUMMARY: 'block_myoverview/view-summary',\n NOCOURSES: 'core_course/no-courses'\n};\n\nconst GROUPINGS = {\n GROUPING_ALLINCLUDINGHIDDEN: 'allincludinghidden',\n GROUPING_ALL: 'all',\n GROUPING_INPROGRESS: 'inprogress',\n GROUPING_FUTURE: 'future',\n GROUPING_PAST: 'past',\n GROUPING_FAVOURITES: 'favourites',\n GROUPING_HIDDEN: 'hidden'\n};\n\nconst NUMCOURSES_PERPAGE = [12, 24, 48, 96, 0];\n\nlet loadedPages = [];\n\nlet courseOffset = 0;\n\nlet lastPage = 0;\n\nlet lastLimit = 0;\n\nlet namespace = null;\n\n/**\n * Whether the summary display has been loaded.\n *\n * If true, this means that courses have been loaded with the summary text.\n * Otherwise, switching to the summary display mode will require course data to be fetched with the summary text.\n *\n * @type {boolean}\n */\nlet summaryDisplayLoaded = false;\n\n/**\n * Get filter values from DOM.\n *\n * @param {object} root The root element for the courses view.\n * @return {filters} Set filters.\n */\nconst getFilterValues = root => {\n const courseRegion = root.find(SELECTORS.courseView.region);\n return {\n display: courseRegion.attr('data-display'),\n grouping: courseRegion.attr('data-grouping'),\n sort: courseRegion.attr('data-sort'),\n displaycategories: courseRegion.attr('data-displaycategories'),\n customfieldname: courseRegion.attr('data-customfieldname'),\n customfieldvalue: courseRegion.attr('data-customfieldvalue'),\n };\n};\n\n// We want the paged content controls below the paged content area.\n// and the controls should be ignored while data is loading.\nconst DEFAULT_PAGED_CONTENT_CONFIG = {\n ignoreControlWhileLoading: true,\n controlPlacementBottom: true,\n persistentLimitKey: 'block_myoverview_user_paging_preference'\n};\n\n/**\n * Get enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @return {promise} Resolved with an array of courses.\n */\nconst getMyCourses = (filters, limit) => {\n const params = {\n offset: courseOffset,\n limit: limit,\n classification: filters.grouping,\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n };\n if (filters.display === 'summary') {\n params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS;\n summaryDisplayLoaded = true;\n } else {\n params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS;\n }\n return Repository.getEnrolledCoursesByTimeline(params);\n};\n\n/**\n * Search for enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @param {string} searchValue What does the user want to search within their courses.\n * @return {promise} Resolved with an array of courses.\n */\nconst getSearchMyCourses = (filters, limit, searchValue) => {\n const params = {\n offset: courseOffset,\n limit: limit,\n classification: 'search',\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n searchvalue: searchValue,\n };\n if (filters.display === 'summary') {\n params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS;\n summaryDisplayLoaded = true;\n } else {\n params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS;\n summaryDisplayLoaded = false;\n }\n return Repository.getEnrolledCoursesByTimeline(params);\n};\n\n/**\n * Get the container element for the favourite icon.\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n * @return {Object} The favourite icon container\n */\nconst getFavouriteIconContainer = (root, courseId) => {\n return root.find(SELECTORS.FAVOURITE_ICON + '[data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the paged content container element.\n *\n * @param {Object} root The course overview container\n * @param {Number} index Rendered page index.\n * @return {Object} The rendered paged container.\n */\nconst getPagedContentContainer = (root, index) => {\n return root.find('[data-region=\"paged-content-page\"][data-page=\"' + index + '\"]');\n};\n\n/**\n * Get the course id from a favourite element.\n *\n * @param {Object} root The favourite icon container element.\n * @return {Number} Course id.\n */\nconst getCourseId = root => {\n return root.attr('data-course-id');\n};\n\n/**\n * Hide the favourite icon.\n *\n * @param {Object} root The favourite icon container element.\n * @param {Number} courseId Course id number.\n */\nconst hideFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.addClass('hidden');\n Aria.hide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.removeClass('hidden');\n Aria.unhide(notFavourteIcon);\n};\n\n/**\n * Show the favourite icon.\n *\n * @param {Object} root The course overview container.\n * @param {Number} courseId Course id number.\n */\nconst showFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.removeClass('hidden');\n Aria.unhide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.addClass('hidden');\n Aria.hide(notFavourteIcon);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The add to favourite menu item.\n */\nconst getAddFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"add-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The remove from favourites menu item.\n */\nconst getRemoveFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"remove-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Add course to favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst addToFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, true).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.favourited, courseId);\n removeAction.removeClass('hidden');\n addAction.addClass('hidden');\n showFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Remove course from favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst removeFromFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, false).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.unfavorited, courseId);\n removeAction.addClass('hidden');\n addAction.removeClass('hidden');\n hideFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The hide course menu item.\n */\nconst getHideCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"hide-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The show course menu item.\n */\nconst getShowCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"show-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Hide course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst hideCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, true);\n\n // Remove the course from this view as it is now hidden and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.addClass('hidden');\n showAction.removeClass('hidden');\n};\n\n/**\n * Show course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst showCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, null);\n\n // Remove the course from this view as it is now shown again and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.removeClass('hidden');\n showAction.addClass('hidden');\n};\n\n/**\n * Set the courses hidden status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {Boolean} status new hidden status.\n * @return {Promise} Repository promise.\n */\nconst setCourseHiddenState = (courseId, status) => {\n\n // If the given status is not hidden, the preference has to be deleted with a null value.\n if (status === false) {\n status = null;\n }\n\n return setUserPreference(`block_myoverview_hidden_course_${courseId}`, status)\n .catch(Notification.exception);\n};\n\n/**\n * Reset the loadedPages dataset to take into account the hidden element\n *\n * @param {Object} root The course overview container\n * @param {Number} id The course id number\n */\nconst hideElement = (root, id) => {\n const pagingBar = root.find('[data-region=\"paging-bar\"]');\n const jumpto = parseInt(pagingBar.attr('data-active-page-number'));\n\n // Get a reduced dataset for the current page.\n const courseList = loadedPages[jumpto];\n let reducedCourse = courseList.courses.reduce((accumulator, current) => {\n if (+id !== +current.id) {\n accumulator.push(current);\n }\n return accumulator;\n }, []);\n\n // Get the next page's data if loaded and pop the first element from it.\n if (typeof (loadedPages[jumpto + 1]) !== 'undefined') {\n const newElement = loadedPages[jumpto + 1].courses.slice(0, 1);\n\n // Adjust the dataset for the reset of the pages that are loaded.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n let popElement = [];\n if (typeof (loadedPages[index + 1]) !== 'undefined') {\n popElement = loadedPages[index + 1].courses.slice(0, 1);\n }\n loadedPages[index].courses = [...loadedPages[index].courses.slice(1), ...popElement];\n }\n });\n\n reducedCourse = [...reducedCourse, ...newElement];\n }\n\n // Check if the next page is the last page and if it still has data associated to it.\n if (lastPage === jumpto + 1 && loadedPages[jumpto + 1].courses.length === 0) {\n const pagedContentContainer = root.find('[data-region=\"paged-content-container\"]');\n PagedContentFactory.resetLastPageNumber($(pagedContentContainer).attr('id'), jumpto);\n }\n\n loadedPages[jumpto].courses = reducedCourse;\n\n // Reduce the course offset.\n courseOffset--;\n\n // Render the paged content for the current.\n const pagedContentPage = getPagedContentContainer(root, jumpto);\n renderCourses(root, loadedPages[jumpto]).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n\n // Delete subsequent pages in order to trigger the callback.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n const page = getPagedContentContainer(root, index);\n page.remove();\n }\n });\n};\n\n/**\n * Set the courses favourite status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {boolean} status new favourite status.\n * @return {Promise} Repository promise.\n */\nconst setCourseFavouriteState = (courseId, status) => {\n\n return Repository.setFavouriteCourses({\n courses: [\n {\n 'id': courseId,\n 'favourite': status\n }\n ]\n }).then(result => {\n if (result.warnings.length === 0) {\n loadedPages.forEach(courseList => {\n courseList.courses.forEach((course, index) => {\n if (course.id == courseId) {\n courseList.courses[index].isfavourite = status;\n }\n });\n });\n return true;\n } else {\n return false;\n }\n }).catch(Notification.exception);\n};\n\n/**\n * Given there are no courses to render provide the rendered template.\n *\n * @param {object} root The root element for the courses view.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst noCoursesRender = root => {\n const nocoursesimg = root.find(SELECTORS.courseView.region).attr('data-nocoursesimg');\n const newcourseurl = root.find(SELECTORS.courseView.region).attr('data-newcourseurl');\n return Templates.render(TEMPLATES.NOCOURSES, {\n nocoursesimg: nocoursesimg,\n newcourseurl: newcourseurl\n });\n};\n\n/**\n * Render the dashboard courses.\n *\n * @param {object} root The root element for the courses view.\n * @param {array} coursesData containing array of returned courses.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst renderCourses = (root, coursesData) => {\n\n const filters = getFilterValues(root);\n\n let currentTemplate = '';\n if (filters.display === 'card') {\n currentTemplate = TEMPLATES.COURSES_CARDS;\n } else if (filters.display === 'list') {\n currentTemplate = TEMPLATES.COURSES_LIST;\n } else {\n currentTemplate = TEMPLATES.COURSES_SUMMARY;\n }\n\n if (!coursesData) {\n return noCoursesRender(root);\n } else {\n // Sometimes we get weird objects coming after a failed search, cast to ensure typing functions.\n if (Array.isArray(coursesData.courses) === false) {\n coursesData.courses = Object.values(coursesData.courses);\n }\n // Whether the course category should be displayed in the course item.\n coursesData.courses = coursesData.courses.map(course => {\n course.showcoursecategory = filters.displaycategories === 'on';\n return course;\n });\n if (coursesData.courses.length) {\n return Templates.render(currentTemplate, {\n courses: coursesData.courses,\n });\n } else {\n return noCoursesRender(root);\n }\n }\n};\n\n/**\n * Return the callback to be passed to the subscribe event\n *\n * @param {object} root The root element for the courses view\n * @return {function} Partially applied function that'll execute when passed a limit\n */\nconst setLimit = root => {\n // @param {Number} limit The paged limit that is passed through the event.\n return limit => root.find(SELECTORS.courseView.region).attr('data-paging', limit);\n};\n\n/**\n * Intialise the paged list and cards views on page load.\n * Returns an array of paged contents that we would like to handle here\n *\n * @param {object} root The root element for the courses view\n * @param {string} namespace The namespace for all the events attached\n */\nconst registerPagedEventHandlers = (root, namespace) => {\n const event = namespace + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;\n PubSub.subscribe(event, setLimit(root));\n};\n\n/**\n * Figure out how many items are going to be allowed to be rendered in the block.\n *\n * @param {Number} pagingLimit How many courses to display\n * @param {Object} root The course overview container\n * @return {Number[]} How many courses will be rendered\n */\nconst itemsPerPageFunc = (pagingLimit, root) => {\n let itemsPerPage = NUMCOURSES_PERPAGE.map(value => {\n let active = false;\n if (value === pagingLimit) {\n active = true;\n }\n\n return {\n value: value,\n active: active\n };\n });\n\n // Filter out all pagination options which are too large for the amount of courses user is enrolled in.\n const totalCourseCount = parseInt(root.find(SELECTORS.courseView.region).attr('data-totalcoursecount'), 10);\n return itemsPerPage.filter(pagingOption => {\n if (pagingOption.value === 0 && totalCourseCount > 100) {\n // To minimise performance issues, do not show the \"All\" option if the user is enrolled in more than 100 courses.\n return false;\n }\n return pagingOption.value < totalCourseCount;\n });\n};\n\n/**\n * Mutates and controls the loadedPages array and handles the bootstrapping.\n *\n * @param {Array|Object} coursesData Array of all of the courses to start building the page from\n * @param {Number} currentPage What page are we currently on?\n * @param {Object} pageData Any current page information\n * @param {Object} actions Paged content helper\n * @param {null|boolean} activeSearch Are we currently actively searching and building up search results?\n */\nconst pageBuilder = (coursesData, currentPage, pageData, actions, activeSearch = null) => {\n // If the courseData comes in an object then get the value otherwise it is a pure array.\n let courses = coursesData.courses ? coursesData.courses : coursesData;\n let nextPageStart = 0;\n let pageCourses = [];\n\n // If current page's data is loaded make sure we max it to page limit.\n if (typeof (loadedPages[currentPage]) !== 'undefined') {\n pageCourses = loadedPages[currentPage].courses;\n const currentPageLength = pageCourses.length;\n if (currentPageLength < pageData.limit) {\n nextPageStart = pageData.limit - currentPageLength;\n pageCourses = {...loadedPages[currentPage].courses, ...courses.slice(0, nextPageStart)};\n }\n } else {\n // When the page limit is zero, there is only one page of courses, no start for next page.\n nextPageStart = pageData.limit || false;\n pageCourses = (pageData.limit > 0) ? courses.slice(0, pageData.limit) : courses;\n }\n\n // Finished setting up the current page.\n loadedPages[currentPage] = {\n courses: pageCourses\n };\n\n // Set up the next page (if there is more than one page).\n const remainingCourses = nextPageStart !== false ? courses.slice(nextPageStart, courses.length) : [];\n if (remainingCourses.length) {\n loadedPages[currentPage + 1] = {\n courses: remainingCourses\n };\n }\n\n // Set the last page to either the current or next page.\n if (loadedPages[currentPage].courses.length < pageData.limit || !remainingCourses.length) {\n lastPage = currentPage;\n if (activeSearch === null) {\n actions.allItemsLoaded(currentPage);\n }\n } else if (typeof (loadedPages[currentPage + 1]) !== 'undefined'\n && loadedPages[currentPage + 1].courses.length < pageData.limit) {\n lastPage = currentPage + 1;\n }\n\n courseOffset = coursesData.nextoffset;\n};\n\n/**\n * In cases when switching between regular rendering and search rendering we need to reset some variables.\n */\nconst resetGlobals = () => {\n courseOffset = 0;\n loadedPages = [];\n lastPage = 0;\n lastLimit = 0;\n};\n\n/**\n * The default functionality of fetching paginated courses without special handling.\n *\n * @return {function(Object, Object, Object, Object, Object, Promise, Number): void}\n */\nconst standardFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit) => {\n const pagePromise = getMyCourses(\n filters,\n limit\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(pagePromise);\n };\n};\n\n/**\n * Initialize the searching functionality so we can call it when required.\n *\n * @return {function(Object, Number, Object, Object, Object, Promise, Number, String): void}\n */\nconst searchFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit, inputValue) => {\n const searchingPromise = getSearchMyCourses(\n filters,\n limit,\n inputValue\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(searchingPromise);\n };\n};\n\n/**\n * Initialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n * @param {function} promiseFunction How do we fetch the courses and what do we do with them?\n * @param {null | string} inputValue What to search for\n */\nconst initializePagedContent = (root, promiseFunction, inputValue = null) => {\n const pagingLimit = parseInt(root.find(SELECTORS.courseView.region).attr('data-paging'), 10);\n let itemsPerPage = itemsPerPageFunc(pagingLimit, root);\n\n const config = {...{}, ...DEFAULT_PAGED_CONTENT_CONFIG};\n config.eventNamespace = namespace;\n\n const pagedContentPromise = PagedContentFactory.createWithLimit(\n itemsPerPage,\n (pagesData, actions) => {\n let promises = [];\n pagesData.forEach(pageData => {\n const currentPage = pageData.pageNumber;\n let limit = (pageData.limit > 0) ? pageData.limit : 0;\n\n // Reset local variables if limits have changed.\n if (+lastLimit !== +limit) {\n loadedPages = [];\n courseOffset = 0;\n lastPage = 0;\n }\n\n if (lastPage === currentPage) {\n // If we are on the last page and have it's data then load it from cache.\n actions.allItemsLoaded(lastPage);\n promises.push(renderCourses(root, loadedPages[currentPage]));\n return;\n }\n\n lastLimit = limit;\n\n // Get 2 pages worth of data as we will need it for the hidden functionality.\n if (typeof (loadedPages[currentPage + 1]) === 'undefined') {\n if (typeof (loadedPages[currentPage]) === 'undefined') {\n limit *= 2;\n }\n }\n\n // Get the current applied filters.\n const filters = getFilterValues(root);\n\n // Call the curried function that'll handle the course promise and any manipulation of it.\n promiseFunction(filters, currentPage, pageData, actions, root, promises, limit, inputValue);\n });\n return promises;\n },\n config\n );\n\n pagedContentPromise.then((html, js) => {\n registerPagedEventHandlers(root, namespace);\n return Templates.replaceNodeContents(root.find(SELECTORS.courseView.region), html, js);\n }).catch(Notification.exception);\n};\n\n/**\n * Listen to, and handle events for the myoverview block.\n *\n * @param {Object} root The myoverview block container element.\n * @param {HTMLElement} page The whole HTMLElement for our block.\n */\nconst registerEventListeners = (root, page) => {\n\n CustomEvents.define(root, [\n CustomEvents.events.activate\n ]);\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_ADD_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_ADD_FAVOURITE);\n const courseId = getCourseId(favourite);\n addToFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_REMOVE_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_REMOVE_FAVOURITE);\n const courseId = getCourseId(favourite);\n removeFromFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.FAVOURITE_ICON, (e, data) => {\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_HIDE_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_HIDE_COURSE);\n const courseId = getCourseId(target);\n hideCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_SHOW_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_SHOW_COURSE);\n const courseId = getCourseId(target);\n showCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n // Searching functionality event handlers.\n const input = page.querySelector(SELECTORS.region.searchInput);\n const clearIcon = page.querySelector(SELECTORS.region.clearIcon);\n\n clearIcon.addEventListener('click', () => {\n input.value = '';\n input.focus();\n clearSearch(clearIcon, root);\n });\n\n input.addEventListener('input', debounce(() => {\n if (input.value === '') {\n clearSearch(clearIcon, root);\n } else {\n activeSearch(clearIcon);\n initializePagedContent(root, searchFunctionalityCurry(), input.value.trim());\n }\n }, 1000));\n};\n\n/**\n * Reset the search icon and trigger the init for the block.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n * @param {Object} root The myoverview block container element.\n */\nexport const clearSearch = (clearIcon, root) => {\n clearIcon.classList.add('d-none');\n init(root);\n};\n\n/**\n * Change the searching icon to its' active state.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n */\nconst activeSearch = (clearIcon) => {\n clearIcon.classList.remove('d-none');\n};\n\n/**\n * Intialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n */\nexport const init = root => {\n root = $(root);\n loadedPages = [];\n lastPage = 0;\n courseOffset = 0;\n\n if (!root.attr('data-init')) {\n const page = document.querySelector(SELECTORS.region.selectBlock);\n registerEventListeners(root, page);\n namespace = \"block_myoverview_\" + root.attr('id') + \"_\" + Math.random();\n root.attr('data-init', true);\n }\n\n initializePagedContent(root, standardFunctionalityCurry());\n};\n\n/**\n * Reset the courses views to their original\n * state on first page load.courseOffset\n *\n * This is called when configuration has changed for the event lists\n * to cause them to reload their data.\n *\n * @param {Object} root The root element for the timeline view.\n */\nexport const reset = root => {\n if (loadedPages.length > 0) {\n const filters = getFilterValues(root);\n // If the display mode is changed to 'summary' but the summary display has not been loaded yet,\n // we need to re-fetch the courses to include the course summary text.\n if (filters.display === 'summary' && !summaryDisplayLoaded) {\n const page = document.querySelector(SELECTORS.region.selectBlock);\n const input = page.querySelector(SELECTORS.region.searchInput);\n if (input.value !== '') {\n initializePagedContent(root, searchFunctionalityCurry(), input.value.trim());\n } else {\n initializePagedContent(root, standardFunctionalityCurry());\n }\n } else {\n loadedPages.forEach((courseList, index) => {\n let pagedContentPage = getPagedContentContainer(root, index);\n renderCourses(root, courseList).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n });\n }\n } else {\n init(root);\n }\n};\n"],"names":["TEMPLATES","GROUPINGS","NUMCOURSES_PERPAGE","loadedPages","courseOffset","lastPage","lastLimit","namespace","summaryDisplayLoaded","getFilterValues","root","courseRegion","find","SELECTORS","courseView","region","display","attr","grouping","sort","displaycategories","customfieldname","customfieldvalue","DEFAULT_PAGED_CONTENT_CONFIG","ignoreControlWhileLoading","controlPlacementBottom","persistentLimitKey","getFavouriteIconContainer","courseId","FAVOURITE_ICON","getPagedContentContainer","index","getCourseId","getAddFavouriteMenuItem","getRemoveFavouriteMenuItem","addToFavourites","removeAction","addAction","setCourseFavouriteState","then","success","PubSub","publish","CourseEvents","favourited","removeClass","addClass","iconContainer","isFavouriteIcon","ICON_IS_FAVOURITE","Aria","unhide","notFavourteIcon","ICON_NOT_FAVOURITE","hide","showFavouriteIcon","Notification","alert","catch","exception","removeFromFavourites","unfavorited","hideFavouriteIcon","getHideCourseMenuItem","getShowCourseMenuItem","setCourseHiddenState","status","hideElement","id","pagingBar","jumpto","parseInt","reducedCourse","courses","reduce","accumulator","current","push","newElement","slice","forEach","courseList","popElement","length","pagedContentContainer","PagedContentFactory","resetLastPageNumber","pagedContentPage","renderCourses","html","js","Templates","replaceNodeContents","remove","Repository","setFavouriteCourses","result","warnings","course","isfavourite","noCoursesRender","nocoursesimg","newcourseurl","render","coursesData","filters","currentTemplate","Array","isArray","Object","values","map","showcoursecategory","registerPagedEventHandlers","event","PagedContentEvents","SET_ITEMS_PER_PAGE_LIMIT","subscribe","limit","setLimit","itemsPerPageFunc","pagingLimit","itemsPerPage","value","active","totalCourseCount","filter","pagingOption","pageBuilder","currentPage","pageData","actions","activeSearch","nextPageStart","pageCourses","currentPageLength","remainingCourses","allItemsLoaded","nextoffset","resetGlobals","standardFunctionalityCurry","promises","pagePromise","params","offset","classification","requiredfields","SUMMARY_REQUIRED_FIELDS","CARDLIST_REQUIRED_FIELDS","getEnrolledCoursesByTimeline","getMyCourses","searchFunctionalityCurry","inputValue","searchingPromise","searchValue","searchvalue","getSearchMyCourses","initializePagedContent","promiseFunction","config","eventNamespace","pagedContentPromise","createWithLimit","pagesData","pageNumber","registerEventListeners","page","CustomEvents","define","events","activate","on","ACTION_ADD_FAVOURITE","e","data","favourite","target","closest","originalEvent","preventDefault","ACTION_REMOVE_FAVOURITE","ACTION_HIDE_COURSE","hideAction","showAction","hideCourse","ACTION_SHOW_COURSE","showCourse","input","querySelector","searchInput","clearIcon","addEventListener","focus","clearSearch","trim","classList","add","init","document","selectBlock","Math","random"],"mappings":"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"} \ No newline at end of file diff --git a/blocks/myoverview/amd/src/view.js b/blocks/myoverview/amd/src/view.js index a6e7dfc0e14..2c02c7c0f3d 100644 --- a/blocks/myoverview/amd/src/view.js +++ b/blocks/myoverview/amd/src/view.js @@ -698,7 +698,6 @@ const initializePagedContent = (root, promiseFunction, inputValue = null) => { const pagingLimit = parseInt(root.find(SELECTORS.courseView.region).attr('data-paging'), 10); let itemsPerPage = itemsPerPageFunc(pagingLimit, root); - const filters = getFilterValues(root); const config = {...{}, ...DEFAULT_PAGED_CONTENT_CONFIG}; config.eventNamespace = namespace; @@ -733,6 +732,9 @@ const initializePagedContent = (root, promiseFunction, inputValue = null) => { } } + // Get the current applied filters. + const filters = getFilterValues(root); + // Call the curried function that'll handle the course promise and any manipulation of it. promiseFunction(filters, currentPage, pageData, actions, root, promises, limit, inputValue); });