From 10b6f979c5d369c70976c642cb7ca10f5d366fad Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Wed, 11 Jan 2023 13:59:04 +0000 Subject: [PATCH 1/4] MDL-62859 user: export methods for manipulating user preferences in JS. --- user/amd/build/repository.min.js | 2 +- user/amd/build/repository.min.js.map | 2 +- user/amd/src/repository.js | 52 ++++++++++++++++++++++++++++ user/externallib.php | 15 ++++---- user/upgrade.txt | 5 +++ 5 files changed, 68 insertions(+), 8 deletions(-) diff --git a/user/amd/build/repository.min.js b/user/amd/build/repository.min.js index 3fbd72df7f6..19b649ff48b 100644 --- a/user/amd/build/repository.min.js +++ b/user/amd/build/repository.min.js @@ -1,3 +1,3 @@ -define("core_user/repository",["exports","core/ajax"],(function(_exports,_ajax){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.unenrolUser=_exports.submitUserEnrolmentForm=_exports.sendMessagesToUsers=_exports.createNotesForUsers=void 0;_exports.unenrolUser=userEnrolmentId=>(0,_ajax.call)([{methodname:"core_enrol_unenrol_user_enrolment",args:{ueid:userEnrolmentId}}])[0];_exports.submitUserEnrolmentForm=formdata=>(0,_ajax.call)([{methodname:"core_enrol_submit_user_enrolment_form",args:{formdata:formdata}}])[0];_exports.createNotesForUsers=notes=>(0,_ajax.call)([{methodname:"core_notes_create_notes",args:{notes:notes}}])[0];_exports.sendMessagesToUsers=messages=>(0,_ajax.call)([{methodname:"core_message_send_instant_messages",args:{messages:messages}}])[0]})); +define("core_user/repository",["exports","core/ajax"],(function(_exports,_ajax){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.unenrolUser=_exports.submitUserEnrolmentForm=_exports.setUserPreferences=_exports.setUserPreference=_exports.sendMessagesToUsers=_exports.getUserPreferences=_exports.getUserPreference=_exports.createNotesForUsers=void 0;_exports.getUserPreference=function(name){let userid=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return getUserPreferences(name,userid).then((response=>response.preferences[0].value))};const getUserPreferences=function(){let name=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,userid=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return(0,_ajax.call)([{methodname:"core_user_get_user_preferences",args:{name:name,userid:userid}}])[0]};_exports.getUserPreferences=getUserPreferences;_exports.setUserPreference=function(name){let value=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,userid=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;return setUserPreferences([{name:name,value:value,userid:userid}])};const setUserPreferences=preferences=>(0,_ajax.call)([{methodname:"core_user_set_user_preferences",args:{preferences:preferences}}])[0];_exports.setUserPreferences=setUserPreferences;_exports.unenrolUser=userEnrolmentId=>(0,_ajax.call)([{methodname:"core_enrol_unenrol_user_enrolment",args:{ueid:userEnrolmentId}}])[0];_exports.submitUserEnrolmentForm=formdata=>(0,_ajax.call)([{methodname:"core_enrol_submit_user_enrolment_form",args:{formdata:formdata}}])[0];_exports.createNotesForUsers=notes=>(0,_ajax.call)([{methodname:"core_notes_create_notes",args:{notes:notes}}])[0];_exports.sendMessagesToUsers=messages=>(0,_ajax.call)([{methodname:"core_message_send_instant_messages",args:{messages:messages}}])[0]})); //# sourceMappingURL=repository.min.js.map \ No newline at end of file diff --git a/user/amd/build/repository.min.js.map b/user/amd/build/repository.min.js.map index 3a9d10a16db..b59633bfe53 100644 --- a/user/amd/build/repository.min.js.map +++ b/user/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 * Module to handle AJAX interactions.\n *\n * @module core_user/repository\n * @copyright 2020 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {call as fetchMany} from 'core/ajax';\n\n/**\n * Unenrol the user with the specified user enrolmentid ID.\n *\n * @param {Number} userEnrolmentId\n * @return {Promise}\n */\nexport const unenrolUser = userEnrolmentId => {\n return fetchMany([{\n methodname: 'core_enrol_unenrol_user_enrolment',\n args: {\n ueid: userEnrolmentId,\n },\n }])[0];\n};\n\n/**\n * Submit the user enrolment form with the specified form data.\n *\n * @param {String} formdata\n * @return {Promise}\n */\nexport const submitUserEnrolmentForm = formdata => {\n return fetchMany([{\n methodname: 'core_enrol_submit_user_enrolment_form',\n args: {\n formdata,\n },\n }])[0];\n};\n\nexport const createNotesForUsers = notes => {\n return fetchMany([{\n methodname: 'core_notes_create_notes',\n args: {\n notes\n }\n }])[0];\n};\n\nexport const sendMessagesToUsers = messages => {\n return fetchMany([{\n methodname: 'core_message_send_instant_messages',\n args: {messages}\n }])[0];\n};\n"],"names":["userEnrolmentId","methodname","args","ueid","formdata","notes","messages"],"mappings":"oRA8B2BA,kBAChB,cAAU,CAAC,CACdC,WAAY,oCACZC,KAAM,CACFC,KAAMH,oBAEV,oCAS+BI,WAC5B,cAAU,CAAC,CACdH,WAAY,wCACZC,KAAM,CACFE,SAAAA,aAEJ,gCAG2BC,QACxB,cAAU,CAAC,CACdJ,WAAY,0BACZC,KAAM,CACFG,MAAAA,UAEJ,gCAG2BC,WACxB,cAAU,CAAC,CACdL,WAAY,qCACZC,KAAM,CAACI,SAAAA,aACP"} \ 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 * Module to handle AJAX interactions.\n *\n * @module core_user/repository\n * @copyright 2020 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {call as fetchMany} from 'core/ajax';\n\n/**\n * Get single user preference\n *\n * @param {String} name Name of the preference\n * @param {Number} userid User ID (defaults to current user)\n * @return {Promise}\n */\nexport const getUserPreference = (name, userid = 0) => {\n return getUserPreferences(name, userid)\n .then(response => response.preferences[0].value);\n};\n\n/**\n * Get multiple user preferences\n *\n * @param {String|null} name Name of the preference (omit if you want to retrieve all)\n * @param {Number} userid User ID (defaults to current user)\n * @return {Promise}\n */\nexport const getUserPreferences = (name = null, userid = 0) => {\n return fetchMany([{\n methodname: 'core_user_get_user_preferences',\n args: {name, userid}\n }])[0];\n};\n\n/**\n * Set single user preference\n *\n * @param {String} name Name of the preference\n * @param {String|null} value Value of the preference (omit if you want to remove the current value)\n * @param {Number} userid User ID (defaults to current user)\n * @return {Promise}\n */\nexport const setUserPreference = (name, value = null, userid = 0) => {\n return setUserPreferences([{name, value, userid}]);\n};\n\n/**\n * Set multiple user preferences\n *\n * @param {Object[]} preferences Array of preferences containing name/value/userid attributes\n * @return {Promise}\n */\nexport const setUserPreferences = (preferences) => {\n return fetchMany([{\n methodname: 'core_user_set_user_preferences',\n args: {preferences}\n }])[0];\n};\n\n/**\n * Unenrol the user with the specified user enrolmentid ID.\n *\n * @param {Number} userEnrolmentId\n * @return {Promise}\n */\nexport const unenrolUser = userEnrolmentId => {\n return fetchMany([{\n methodname: 'core_enrol_unenrol_user_enrolment',\n args: {\n ueid: userEnrolmentId,\n },\n }])[0];\n};\n\n/**\n * Submit the user enrolment form with the specified form data.\n *\n * @param {String} formdata\n * @return {Promise}\n */\nexport const submitUserEnrolmentForm = formdata => {\n return fetchMany([{\n methodname: 'core_enrol_submit_user_enrolment_form',\n args: {\n formdata,\n },\n }])[0];\n};\n\nexport const createNotesForUsers = notes => {\n return fetchMany([{\n methodname: 'core_notes_create_notes',\n args: {\n notes\n }\n }])[0];\n};\n\nexport const sendMessagesToUsers = messages => {\n return fetchMany([{\n methodname: 'core_message_send_instant_messages',\n args: {messages}\n }])[0];\n};\n"],"names":["name","userid","getUserPreferences","then","response","preferences","value","methodname","args","setUserPreferences","userEnrolmentId","ueid","formdata","notes","messages"],"mappings":"wYAgCiC,SAACA,UAAMC,8DAAS,SACtCC,mBAAmBF,KAAMC,QAC3BE,MAAKC,UAAYA,SAASC,YAAY,GAAGC,eAUrCJ,mBAAqB,eAACF,4DAAO,KAAMC,8DAAS,SAC9C,cAAU,CAAC,CACdM,WAAY,iCACZC,KAAM,CAACR,KAAAA,KAAMC,OAAAA,WACb,8EAWyB,SAACD,UAAMM,6DAAQ,KAAML,8DAAS,SACpDQ,mBAAmB,CAAC,CAACT,KAAAA,KAAMM,MAAAA,MAAOL,OAAAA,iBAShCQ,mBAAsBJ,cACxB,cAAU,CAAC,CACdE,WAAY,iCACZC,KAAM,CAACH,YAAAA,gBACP,uEASmBK,kBAChB,cAAU,CAAC,CACdH,WAAY,oCACZC,KAAM,CACFG,KAAMD,oBAEV,oCAS+BE,WAC5B,cAAU,CAAC,CACdL,WAAY,wCACZC,KAAM,CACFI,SAAAA,aAEJ,gCAG2BC,QACxB,cAAU,CAAC,CACdN,WAAY,0BACZC,KAAM,CACFK,MAAAA,UAEJ,gCAG2BC,WACxB,cAAU,CAAC,CACdP,WAAY,qCACZC,KAAM,CAACM,SAAAA,aACP"} \ No newline at end of file diff --git a/user/amd/src/repository.js b/user/amd/src/repository.js index c719bc12046..f6c078539a1 100644 --- a/user/amd/src/repository.js +++ b/user/amd/src/repository.js @@ -20,8 +20,60 @@ * @copyright 2020 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + import {call as fetchMany} from 'core/ajax'; +/** + * Get single user preference + * + * @param {String} name Name of the preference + * @param {Number} userid User ID (defaults to current user) + * @return {Promise} + */ +export const getUserPreference = (name, userid = 0) => { + return getUserPreferences(name, userid) + .then(response => response.preferences[0].value); +}; + +/** + * Get multiple user preferences + * + * @param {String|null} name Name of the preference (omit if you want to retrieve all) + * @param {Number} userid User ID (defaults to current user) + * @return {Promise} + */ +export const getUserPreferences = (name = null, userid = 0) => { + return fetchMany([{ + methodname: 'core_user_get_user_preferences', + args: {name, userid} + }])[0]; +}; + +/** + * Set single user preference + * + * @param {String} name Name of the preference + * @param {String|null} value Value of the preference (omit if you want to remove the current value) + * @param {Number} userid User ID (defaults to current user) + * @return {Promise} + */ +export const setUserPreference = (name, value = null, userid = 0) => { + return setUserPreferences([{name, value, userid}]); +}; + +/** + * Set multiple user preferences + * + * @param {Object[]} preferences Array of preferences containing name/value/userid attributes + * @return {Promise} + */ +export const setUserPreferences = (preferences) => { + return fetchMany([{ + methodname: 'core_user_set_user_preferences', + args: {preferences} + }])[0]; +}; + /** * Unenrol the user with the specified user enrolmentid ID. * diff --git a/user/externallib.php b/user/externallib.php index 54dfb0e032e..540d9e3fd13 100644 --- a/user/externallib.php +++ b/user/externallib.php @@ -1802,7 +1802,8 @@ class core_user_external extends \core_external\external_api { array( 'name' => new external_value(PARAM_RAW, 'The name of the preference'), 'value' => new external_value(PARAM_RAW, 'The value of the preference'), - 'userid' => new external_value(PARAM_INT, 'Id of the user to set the preference'), + 'userid' => new external_value(PARAM_INT, + 'Id of the user to set the preference (default to current user)', VALUE_DEFAULT, 0), ) ) ) @@ -1830,18 +1831,20 @@ class core_user_external extends \core_external\external_api { $userscache = array(); foreach ($params['preferences'] as $pref) { + $userid = $pref['userid'] ?: $USER->id; + // Check to which user set the preference. - if (!empty($userscache[$pref['userid']])) { - $user = $userscache[$pref['userid']]; + if (!empty($userscache[$userid])) { + $user = $userscache[$userid]; } else { try { - $user = core_user::get_user($pref['userid'], '*', MUST_EXIST); + $user = core_user::get_user($userid, '*', MUST_EXIST); core_user::require_active_user($user); - $userscache[$pref['userid']] = $user; + $userscache[$userid] = $user; } catch (Exception $e) { $warnings[] = array( 'item' => 'user', - 'itemid' => $pref['userid'], + 'itemid' => $userid, 'warningcode' => 'invaliduser', 'message' => $e->getMessage() ); diff --git a/user/upgrade.txt b/user/upgrade.txt index 2edf699e1c8..0ab98abaa7b 100644 --- a/user/upgrade.txt +++ b/user/upgrade.txt @@ -2,6 +2,11 @@ This files describes API changes for code that uses the user API. === 4.3 === +* The `core_user/repository` Javascript module now exports new methods for manipulating user preferences: + - `[get|set]UserPreference` + - `[get|set]UserPreferences` +* The external `core_user_set_user_preferences` method will now default the `userid` property of each preference to + that of the current user, if omitted * The following previously deprecated methods have been removed and can no longer be used: - `profile_display_fields` - `profile_edit_category` From c5d1f72dcaa525233e6bce8ef0046c4d20af4eaf Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Thu, 12 Jan 2023 14:37:35 +0000 Subject: [PATCH 2/4] MDL-62859 javascript: replace user preference calls with new methods. --- blocks/myoverview/amd/build/repository.min.js | 4 +- .../amd/build/repository.min.js.map | 2 +- blocks/myoverview/amd/build/view.min.js | 4 +- blocks/myoverview/amd/build/view.min.js.map | 2 +- blocks/myoverview/amd/build/view_nav.min.js | 4 +- .../myoverview/amd/build/view_nav.min.js.map | 2 +- blocks/myoverview/amd/src/repository.js | 26 ------------- blocks/myoverview/amd/src/view.js | 12 ++---- blocks/myoverview/amd/src/view_nav.js | 14 +++---- .../amd/build/change_user_visibility.min.js | 2 +- .../build/change_user_visibility.min.js.map | 2 +- .../amd/src/change_user_visibility.js | 18 ++------- blocks/timeline/amd/build/view_nav.min.js | 2 +- blocks/timeline/amd/build/view_nav.min.js.map | 2 +- blocks/timeline/amd/src/view_nav.js | 37 ++++--------------- contentbank/amd/build/sort.min.js | 4 +- contentbank/amd/build/sort.min.js.map | 2 +- contentbank/amd/src/sort.js | 17 ++------- lib/amd/build/paged_content_factory.min.js | 2 +- .../build/paged_content_factory.min.js.map | 2 +- lib/amd/src/paged_content_factory.js | 24 ++---------- .../amd/build/grading_navigation.min.js | 2 +- .../amd/build/grading_navigation.min.js.map | 2 +- mod/assign/amd/src/grading_navigation.js | 11 ++---- 24 files changed, 51 insertions(+), 148 deletions(-) diff --git a/blocks/myoverview/amd/build/repository.min.js b/blocks/myoverview/amd/build/repository.min.js index 04658725f08..f21bccfdd51 100644 --- a/blocks/myoverview/amd/build/repository.min.js +++ b/blocks/myoverview/amd/build/repository.min.js @@ -1,10 +1,10 @@ -define("block_myoverview/repository",["exports","core/ajax","core/notification"],(function(_exports,_ajax,Notification){var obj; +define("block_myoverview/repository",["exports","core/ajax"],(function(_exports,_ajax){var obj; /** * A javascript module to retrieve enrolled coruses from the server. * * @module block_myoverview/repository * @copyright 2018 Bas Brands * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.updateUserPreferences=_exports.setFavouriteCourses=_exports.getEnrolledCoursesByTimeline=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj},Notification=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Notification);_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.updateUserPreferences=args=>{const request={methodname:"core_user_update_user_preferences",args:args};_ajax.default.call([request])[0].fail(Notification.exception)}})); + */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]}})); //# 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 06850a439a5..b9f0fbae525 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';\nimport * as Notification from 'core/notification';\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 * Update the user preferences.\n *\n * @param {Object} args Arguments send to the webservice.\n *\n * Sample args:\n * {\n * preferences: [\n * {\n * type: 'block_example_user_sort_preference'\n * value: 'title'\n * }\n * ]\n * }\n */\nexport const updateUserPreferences = args => {\n const request = {\n methodname: 'core_user_update_user_preferences',\n args: args\n };\n\n Ajax.call([request])[0]\n .fail(Notification.exception);\n};\n"],"names":["args","request","methodname","Ajax","call","fail","Notification","exception"],"mappings":";;;;;;;2rCAuC4CA,aAClCC,QAAU,CACZC,WAAY,8DACZF,KAAMA,aAGHG,cAAKC,KAAK,CAACH,UAAU,iCAYGD,aACzBC,QAAU,CACZC,WAAY,oCACZF,KAAMA,aAGHG,cAAKC,KAAK,CAACH,UAAU,mCAkBKD,aAC3BC,QAAU,CACZC,WAAY,oCACZF,KAAMA,oBAGLI,KAAK,CAACH,UAAU,GAChBI,KAAKC,aAAaC"} \ 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"],"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 diff --git a/blocks/myoverview/amd/build/view.min.js b/blocks/myoverview/amd/build/view.min.js index d63f0422495..ed080ea6191 100644 --- a/blocks/myoverview/amd/build/view.min.js +++ b/blocks/myoverview/amd/build/view.min.js @@ -1,9 +1,9 @@ -define("block_myoverview/view",["exports","jquery","block_myoverview/repository","core/paged_content_factory","core/pubsub","core/custom_interaction_events","core/notification","core/templates","core_course/events","block_myoverview/selectors","core/paged_content_events","core/aria","core/utils"],(function(_exports,_jquery,Repository,PagedContentFactory,PubSub,CustomEvents,Notification,Templates,CourseEvents,_selectors,PagedContentEvents,Aria,_utils){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +define("block_myoverview/view",["exports","jquery","block_myoverview/repository","core/paged_content_factory","core/pubsub","core/custom_interaction_events","core/notification","core/templates","core_course/events","block_myoverview/selectors","core/paged_content_events","core/aria","core/utils","core_user/repository"],(function(_exports,_jquery,Repository,PagedContentFactory,PubSub,CustomEvents,Notification,Templates,CourseEvents,_selectors,PagedContentEvents,Aria,_utils,_repository2){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Manage the courses view for the overview block. * * @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),Repository.updateUserPreferences({preferences:[{type:"block_myoverview_hidden_course_"+courseId,value:status}]})),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)=>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)}})); //# 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 03a2f786126..589db6fa12e 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';\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 return Repository.updateUserPreferences({\n preferences: [\n {\n type: 'block_myoverview_hidden_course_' + courseId,\n value: status\n }\n ]\n });\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","Repository","updateUserPreferences","preferences","type","value","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","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","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 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 diff --git a/blocks/myoverview/amd/build/view_nav.min.js b/blocks/myoverview/amd/build/view_nav.min.js index 16301eaad6f..8a346c85b9d 100644 --- a/blocks/myoverview/amd/build/view_nav.min.js +++ b/blocks/myoverview/amd/build/view_nav.min.js @@ -1,9 +1,9 @@ -define("block_myoverview/view_nav",["exports","jquery","core/custom_interaction_events","block_myoverview/repository","block_myoverview/view","block_myoverview/selectors"],(function(_exports,_jquery,CustomEvents,Repository,View,_selectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +define("block_myoverview/view_nav",["exports","jquery","core/custom_interaction_events","core/notification","core_user/repository","block_myoverview/view","block_myoverview/selectors"],(function(_exports,_jquery,CustomEvents,_notification,_repository,View,_selectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Manage the timeline view navigation for the overview block. * * @copyright 2018 Bas Brands * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),CustomEvents=_interopRequireWildcard(CustomEvents),Repository=_interopRequireWildcard(Repository),View=_interopRequireWildcard(View),_selectors=_interopRequireDefault(_selectors);const updatePreferences=(filter,value)=>{let type=null;type="display"===filter?"block_myoverview_user_view_preference":"sort"===filter?"block_myoverview_user_sort_preference":"customfieldvalue"===filter?"block_myoverview_user_grouping_customfieldvalue_preference":"block_myoverview_user_grouping_preference",Repository.updateUserPreferences({preferences:[{type:type,value:value}]})};_exports.init=root=>{(root=>{const Selector=root.find(_selectors.default.FILTERS);CustomEvents.define(Selector,[CustomEvents.events.activate]),Selector.on(CustomEvents.events.activate,_selectors.default.FILTER_OPTION,((e,data)=>{const option=(0,_jquery.default)(e.target);if(option.hasClass("active"))return;const filter=option.attr("data-filter"),pref=option.attr("data-pref"),customfieldvalue=option.attr("data-customfieldvalue");root.find(_selectors.default.courseView.region).attr("data-"+filter,option.attr("data-value")),updatePreferences(filter,pref),customfieldvalue&&(root.find(_selectors.default.courseView.region).attr("data-customfieldvalue",customfieldvalue),updatePreferences("customfieldvalue",customfieldvalue));const page=document.querySelector(_selectors.default.region.selectBlock),input=page.querySelector(_selectors.default.region.searchInput);if(""!==input.value){const clearIcon=page.querySelector(_selectors.default.region.clearIcon);input.value="",View.clearSearch(clearIcon,root)}else View.init(root);data.originalEvent.preventDefault()})),Selector.on(CustomEvents.events.activate,_selectors.default.DISPLAY_OPTION,((e,data)=>{const option=(0,_jquery.default)(e.target);if(option.hasClass("active"))return;const filter=option.attr("data-display-option"),pref=option.attr("data-pref");root.find(_selectors.default.courseView.region).attr("data-display",option.attr("data-value")),updatePreferences(filter,pref),View.reset(root),data.originalEvent.preventDefault()}))})(root=(0,_jquery.default)(root))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),CustomEvents=_interopRequireWildcard(CustomEvents),_notification=_interopRequireDefault(_notification),View=_interopRequireWildcard(View),_selectors=_interopRequireDefault(_selectors);const updatePreferences=(filter,value)=>{let type=null;return type="display"===filter?"block_myoverview_user_view_preference":"sort"===filter?"block_myoverview_user_sort_preference":"customfieldvalue"===filter?"block_myoverview_user_grouping_customfieldvalue_preference":"block_myoverview_user_grouping_preference",(0,_repository.setUserPreference)(type,value).catch(_notification.default.exception)};_exports.init=root=>{(root=>{const Selector=root.find(_selectors.default.FILTERS);CustomEvents.define(Selector,[CustomEvents.events.activate]),Selector.on(CustomEvents.events.activate,_selectors.default.FILTER_OPTION,((e,data)=>{const option=(0,_jquery.default)(e.target);if(option.hasClass("active"))return;const filter=option.attr("data-filter"),pref=option.attr("data-pref"),customfieldvalue=option.attr("data-customfieldvalue");root.find(_selectors.default.courseView.region).attr("data-"+filter,option.attr("data-value")),updatePreferences(filter,pref),customfieldvalue&&(root.find(_selectors.default.courseView.region).attr("data-customfieldvalue",customfieldvalue),updatePreferences("customfieldvalue",customfieldvalue));const page=document.querySelector(_selectors.default.region.selectBlock),input=page.querySelector(_selectors.default.region.searchInput);if(""!==input.value){const clearIcon=page.querySelector(_selectors.default.region.clearIcon);input.value="",View.clearSearch(clearIcon,root)}else View.init(root);data.originalEvent.preventDefault()})),Selector.on(CustomEvents.events.activate,_selectors.default.DISPLAY_OPTION,((e,data)=>{const option=(0,_jquery.default)(e.target);if(option.hasClass("active"))return;const filter=option.attr("data-display-option"),pref=option.attr("data-pref");root.find(_selectors.default.courseView.region).attr("data-display",option.attr("data-value")),updatePreferences(filter,pref),View.reset(root),data.originalEvent.preventDefault()}))})(root=(0,_jquery.default)(root))}})); //# sourceMappingURL=view_nav.min.js.map \ No newline at end of file diff --git a/blocks/myoverview/amd/build/view_nav.min.js.map b/blocks/myoverview/amd/build/view_nav.min.js.map index c2c822eac0f..171a48da979 100644 --- a/blocks/myoverview/amd/build/view_nav.min.js.map +++ b/blocks/myoverview/amd/build/view_nav.min.js.map @@ -1 +1 @@ -{"version":3,"file":"view_nav.min.js","sources":["../src/view_nav.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 timeline view navigation 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 CustomEvents from 'core/custom_interaction_events';\nimport * as Repository from 'block_myoverview/repository';\nimport * as View from 'block_myoverview/view';\nimport SELECTORS from 'block_myoverview/selectors';\n\n/**\n * Update the user preference for the block.\n *\n * @param {String} filter The type of filter: display/sort/grouping.\n * @param {String} value The current preferred value.\n */\nconst updatePreferences = (filter, value) => {\n let type = null;\n if (filter === 'display') {\n type = 'block_myoverview_user_view_preference';\n } else if (filter === 'sort') {\n type = 'block_myoverview_user_sort_preference';\n } else if (filter === 'customfieldvalue') {\n type = 'block_myoverview_user_grouping_customfieldvalue_preference';\n } else {\n type = 'block_myoverview_user_grouping_preference';\n }\n\n Repository.updateUserPreferences({\n preferences: [\n {\n type: type,\n value: value\n }\n ]\n });\n};\n\n/**\n * Event listener for the Display filter (cards, list).\n *\n * @param {object} root The root element for the overview block\n */\nconst registerSelector = root => {\n\n const Selector = root.find(SELECTORS.FILTERS);\n\n CustomEvents.define(Selector, [CustomEvents.events.activate]);\n Selector.on(\n CustomEvents.events.activate,\n SELECTORS.FILTER_OPTION,\n (e, data) => {\n const option = $(e.target);\n\n if (option.hasClass('active')) {\n // If it's already active then we don't need to do anything.\n return;\n }\n\n const filter = option.attr('data-filter');\n const pref = option.attr('data-pref');\n const customfieldvalue = option.attr('data-customfieldvalue');\n\n root.find(SELECTORS.courseView.region).attr('data-' + filter, option.attr('data-value'));\n updatePreferences(filter, pref);\n\n if (customfieldvalue) {\n root.find(SELECTORS.courseView.region).attr('data-customfieldvalue', customfieldvalue);\n updatePreferences('customfieldvalue', customfieldvalue);\n }\n\n // Reset the views.\n\n // Check if the user is currently in a searching state, if so we'll reset it.\n const page = document.querySelector(SELECTORS.region.selectBlock);\n const input = page.querySelector(SELECTORS.region.searchInput);\n if (input.value !== '') {\n const clearIcon = page.querySelector(SELECTORS.region.clearIcon);\n input.value = '';\n // Triggers the init so wont need to call it again.\n View.clearSearch(clearIcon, root);\n } else {\n View.init(root);\n }\n\n data.originalEvent.preventDefault();\n }\n );\n\n Selector.on(\n CustomEvents.events.activate,\n SELECTORS.DISPLAY_OPTION,\n (e, data) => {\n const option = $(e.target);\n\n if (option.hasClass('active')) {\n return;\n }\n\n const filter = option.attr('data-display-option');\n const pref = option.attr('data-pref');\n\n root.find(SELECTORS.courseView.region).attr('data-display', option.attr('data-value'));\n updatePreferences(filter, pref);\n View.reset(root);\n data.originalEvent.preventDefault();\n }\n );\n};\n\n/**\n * Initialise the timeline view navigation by adding event listeners to\n * the navigation elements.\n *\n * @param {object} root The root element for the myoverview block\n */\nexport const init = root => {\n root = $(root);\n registerSelector(root);\n};\n"],"names":["updatePreferences","filter","value","type","Repository","updateUserPreferences","preferences","root","Selector","find","SELECTORS","FILTERS","CustomEvents","define","events","activate","on","FILTER_OPTION","e","data","option","target","hasClass","attr","pref","customfieldvalue","courseView","region","page","document","querySelector","selectBlock","input","searchInput","clearIcon","View","clearSearch","init","originalEvent","preventDefault","DISPLAY_OPTION","reset","registerSelector"],"mappings":";;;;;;mTAkCMA,kBAAoB,CAACC,OAAQC,aAC3BC,KAAO,KAEPA,KADW,YAAXF,OACO,wCACW,SAAXA,OACA,wCACW,qBAAXA,OACA,6DAEA,4CAGXG,WAAWC,sBAAsB,CAC7BC,YAAa,CACT,CACIH,KAAMA,KACND,MAAOA,yBAoFHK,OAzEKA,CAAAA,aAEfC,SAAWD,KAAKE,KAAKC,mBAAUC,SAErCC,aAAaC,OAAOL,SAAU,CAACI,aAAaE,OAAOC,WACnDP,SAASQ,GACLJ,aAAaE,OAAOC,SACpBL,mBAAUO,eACV,CAACC,EAAGC,cACMC,QAAS,mBAAEF,EAAEG,WAEfD,OAAOE,SAAS,uBAKdrB,OAASmB,OAAOG,KAAK,eACrBC,KAAOJ,OAAOG,KAAK,aACnBE,iBAAmBL,OAAOG,KAAK,yBAErChB,KAAKE,KAAKC,mBAAUgB,WAAWC,QAAQJ,KAAK,QAAUtB,OAAQmB,OAAOG,KAAK,eAC1EvB,kBAAkBC,OAAQuB,MAEtBC,mBACAlB,KAAKE,KAAKC,mBAAUgB,WAAWC,QAAQJ,KAAK,wBAAyBE,kBACrEzB,kBAAkB,mBAAoByB,yBAMpCG,KAAOC,SAASC,cAAcpB,mBAAUiB,OAAOI,aAC/CC,MAAQJ,KAAKE,cAAcpB,mBAAUiB,OAAOM,gBAC9B,KAAhBD,MAAM9B,MAAc,OACdgC,UAAYN,KAAKE,cAAcpB,mBAAUiB,OAAOO,WACtDF,MAAM9B,MAAQ,GAEdiC,KAAKC,YAAYF,UAAW3B,WAE5B4B,KAAKE,KAAK9B,MAGdY,KAAKmB,cAAcC,oBAI3B/B,SAASQ,GACLJ,aAAaE,OAAOC,SACpBL,mBAAU8B,gBACV,CAACtB,EAAGC,cACMC,QAAS,mBAAEF,EAAEG,WAEfD,OAAOE,SAAS,uBAIdrB,OAASmB,OAAOG,KAAK,uBACrBC,KAAOJ,OAAOG,KAAK,aAEzBhB,KAAKE,KAAKC,mBAAUgB,WAAWC,QAAQJ,KAAK,eAAgBH,OAAOG,KAAK,eACxEvB,kBAAkBC,OAAQuB,MAC1BW,KAAKM,MAAMlC,MACXY,KAAKmB,cAAcC,qBAa3BG,CADAnC,MAAO,mBAAEA"} \ No newline at end of file +{"version":3,"file":"view_nav.min.js","sources":["../src/view_nav.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 timeline view navigation 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 CustomEvents from 'core/custom_interaction_events';\nimport Notification from 'core/notification';\nimport {setUserPreference} from 'core_user/repository';\nimport * as View from 'block_myoverview/view';\nimport SELECTORS from 'block_myoverview/selectors';\n\n/**\n * Update the user preference for the block.\n *\n * @param {String} filter The type of filter: display/sort/grouping.\n * @param {String} value The current preferred value.\n * @return {Promise}\n */\nconst updatePreferences = (filter, value) => {\n let type = null;\n if (filter === 'display') {\n type = 'block_myoverview_user_view_preference';\n } else if (filter === 'sort') {\n type = 'block_myoverview_user_sort_preference';\n } else if (filter === 'customfieldvalue') {\n type = 'block_myoverview_user_grouping_customfieldvalue_preference';\n } else {\n type = 'block_myoverview_user_grouping_preference';\n }\n\n return setUserPreference(type, value)\n .catch(Notification.exception);\n};\n\n/**\n * Event listener for the Display filter (cards, list).\n *\n * @param {object} root The root element for the overview block\n */\nconst registerSelector = root => {\n\n const Selector = root.find(SELECTORS.FILTERS);\n\n CustomEvents.define(Selector, [CustomEvents.events.activate]);\n Selector.on(\n CustomEvents.events.activate,\n SELECTORS.FILTER_OPTION,\n (e, data) => {\n const option = $(e.target);\n\n if (option.hasClass('active')) {\n // If it's already active then we don't need to do anything.\n return;\n }\n\n const filter = option.attr('data-filter');\n const pref = option.attr('data-pref');\n const customfieldvalue = option.attr('data-customfieldvalue');\n\n root.find(SELECTORS.courseView.region).attr('data-' + filter, option.attr('data-value'));\n updatePreferences(filter, pref);\n\n if (customfieldvalue) {\n root.find(SELECTORS.courseView.region).attr('data-customfieldvalue', customfieldvalue);\n updatePreferences('customfieldvalue', customfieldvalue);\n }\n\n // Reset the views.\n\n // Check if the user is currently in a searching state, if so we'll reset it.\n const page = document.querySelector(SELECTORS.region.selectBlock);\n const input = page.querySelector(SELECTORS.region.searchInput);\n if (input.value !== '') {\n const clearIcon = page.querySelector(SELECTORS.region.clearIcon);\n input.value = '';\n // Triggers the init so wont need to call it again.\n View.clearSearch(clearIcon, root);\n } else {\n View.init(root);\n }\n\n data.originalEvent.preventDefault();\n }\n );\n\n Selector.on(\n CustomEvents.events.activate,\n SELECTORS.DISPLAY_OPTION,\n (e, data) => {\n const option = $(e.target);\n\n if (option.hasClass('active')) {\n return;\n }\n\n const filter = option.attr('data-display-option');\n const pref = option.attr('data-pref');\n\n root.find(SELECTORS.courseView.region).attr('data-display', option.attr('data-value'));\n updatePreferences(filter, pref);\n View.reset(root);\n data.originalEvent.preventDefault();\n }\n );\n};\n\n/**\n * Initialise the timeline view navigation by adding event listeners to\n * the navigation elements.\n *\n * @param {object} root The root element for the myoverview block\n */\nexport const init = root => {\n root = $(root);\n registerSelector(root);\n};\n"],"names":["updatePreferences","filter","value","type","catch","Notification","exception","root","Selector","find","SELECTORS","FILTERS","CustomEvents","define","events","activate","on","FILTER_OPTION","e","data","option","target","hasClass","attr","pref","customfieldvalue","courseView","region","page","document","querySelector","selectBlock","input","searchInput","clearIcon","View","clearSearch","init","originalEvent","preventDefault","DISPLAY_OPTION","reset","registerSelector"],"mappings":";;;;;;wTAoCMA,kBAAoB,CAACC,OAAQC,aAC3BC,KAAO,YAEPA,KADW,YAAXF,OACO,wCACW,SAAXA,OACA,wCACW,qBAAXA,OACA,6DAEA,6CAGJ,iCAAkBE,KAAMD,OAC1BE,MAAMC,sBAAaC,0BAiFRC,OAzEKA,CAAAA,aAEfC,SAAWD,KAAKE,KAAKC,mBAAUC,SAErCC,aAAaC,OAAOL,SAAU,CAACI,aAAaE,OAAOC,WACnDP,SAASQ,GACLJ,aAAaE,OAAOC,SACpBL,mBAAUO,eACV,CAACC,EAAGC,cACMC,QAAS,mBAAEF,EAAEG,WAEfD,OAAOE,SAAS,uBAKdrB,OAASmB,OAAOG,KAAK,eACrBC,KAAOJ,OAAOG,KAAK,aACnBE,iBAAmBL,OAAOG,KAAK,yBAErChB,KAAKE,KAAKC,mBAAUgB,WAAWC,QAAQJ,KAAK,QAAUtB,OAAQmB,OAAOG,KAAK,eAC1EvB,kBAAkBC,OAAQuB,MAEtBC,mBACAlB,KAAKE,KAAKC,mBAAUgB,WAAWC,QAAQJ,KAAK,wBAAyBE,kBACrEzB,kBAAkB,mBAAoByB,yBAMpCG,KAAOC,SAASC,cAAcpB,mBAAUiB,OAAOI,aAC/CC,MAAQJ,KAAKE,cAAcpB,mBAAUiB,OAAOM,gBAC9B,KAAhBD,MAAM9B,MAAc,OACdgC,UAAYN,KAAKE,cAAcpB,mBAAUiB,OAAOO,WACtDF,MAAM9B,MAAQ,GAEdiC,KAAKC,YAAYF,UAAW3B,WAE5B4B,KAAKE,KAAK9B,MAGdY,KAAKmB,cAAcC,oBAI3B/B,SAASQ,GACLJ,aAAaE,OAAOC,SACpBL,mBAAU8B,gBACV,CAACtB,EAAGC,cACMC,QAAS,mBAAEF,EAAEG,WAEfD,OAAOE,SAAS,uBAIdrB,OAASmB,OAAOG,KAAK,uBACrBC,KAAOJ,OAAOG,KAAK,aAEzBhB,KAAKE,KAAKC,mBAAUgB,WAAWC,QAAQJ,KAAK,eAAgBH,OAAOG,KAAK,eACxEvB,kBAAkBC,OAAQuB,MAC1BW,KAAKM,MAAMlC,MACXY,KAAKmB,cAAcC,qBAa3BG,CADAnC,MAAO,mBAAEA"} \ No newline at end of file diff --git a/blocks/myoverview/amd/src/repository.js b/blocks/myoverview/amd/src/repository.js index 8fab742be89..2659c9a9cdd 100644 --- a/blocks/myoverview/amd/src/repository.js +++ b/blocks/myoverview/amd/src/repository.js @@ -22,7 +22,6 @@ */ import Ajax from 'core/ajax'; -import * as Notification from 'core/notification'; /** * Retrieve a list of enrolled courses. @@ -63,28 +62,3 @@ export const setFavouriteCourses = args => { return Ajax.call([request])[0]; }; - -/** - * Update the user preferences. - * - * @param {Object} args Arguments send to the webservice. - * - * Sample args: - * { - * preferences: [ - * { - * type: 'block_example_user_sort_preference' - * value: 'title' - * } - * ] - * } - */ -export const updateUserPreferences = args => { - const request = { - methodname: 'core_user_update_user_preferences', - args: args - }; - - Ajax.call([request])[0] - .fail(Notification.exception); -}; diff --git a/blocks/myoverview/amd/src/view.js b/blocks/myoverview/amd/src/view.js index e510be41261..b018846bec0 100644 --- a/blocks/myoverview/amd/src/view.js +++ b/blocks/myoverview/amd/src/view.js @@ -32,6 +32,7 @@ import SELECTORS from 'block_myoverview/selectors'; import * as PagedContentEvents from 'core/paged_content_events'; import * as Aria from 'core/aria'; import {debounce} from 'core/utils'; +import {setUserPreference} from 'core_user/repository'; const TEMPLATES = { COURSES_CARDS: 'block_myoverview/view-cards', @@ -343,14 +344,9 @@ const setCourseHiddenState = (courseId, status) => { if (status === false) { status = null; } - return Repository.updateUserPreferences({ - preferences: [ - { - type: 'block_myoverview_hidden_course_' + courseId, - value: status - } - ] - }); + + return setUserPreference(`block_myoverview_hidden_course_${courseId}`, status) + .catch(Notification.exception); }; /** diff --git a/blocks/myoverview/amd/src/view_nav.js b/blocks/myoverview/amd/src/view_nav.js index 1f8bfe10661..02db054f083 100644 --- a/blocks/myoverview/amd/src/view_nav.js +++ b/blocks/myoverview/amd/src/view_nav.js @@ -22,7 +22,8 @@ import $ from 'jquery'; import * as CustomEvents from 'core/custom_interaction_events'; -import * as Repository from 'block_myoverview/repository'; +import Notification from 'core/notification'; +import {setUserPreference} from 'core_user/repository'; import * as View from 'block_myoverview/view'; import SELECTORS from 'block_myoverview/selectors'; @@ -31,6 +32,7 @@ import SELECTORS from 'block_myoverview/selectors'; * * @param {String} filter The type of filter: display/sort/grouping. * @param {String} value The current preferred value. + * @return {Promise} */ const updatePreferences = (filter, value) => { let type = null; @@ -44,14 +46,8 @@ const updatePreferences = (filter, value) => { type = 'block_myoverview_user_grouping_preference'; } - Repository.updateUserPreferences({ - preferences: [ - { - type: type, - value: value - } - ] - }); + return setUserPreference(type, value) + .catch(Notification.exception); }; /** diff --git a/blocks/online_users/amd/build/change_user_visibility.min.js b/blocks/online_users/amd/build/change_user_visibility.min.js index 4a00a330256..88459892fb9 100644 --- a/blocks/online_users/amd/build/change_user_visibility.min.js +++ b/blocks/online_users/amd/build/change_user_visibility.min.js @@ -6,6 +6,6 @@ * @copyright 2018 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("block_online_users/change_user_visibility",["jquery","core/ajax","core/str","core/notification"],(function($,Ajax,Str,Notification){var SELECTORS_CHANGE_VISIBILITY_LINK="#change-user-visibility",SELECTORS_CHANGE_VISIBILITY_ICON="#change-user-visibility .icon",oppositeAction=function(action){return"show"==action?"hide":"show"},changeVisibilityLinkAttr=function(action){getTitle(action).then((function(title){$(SELECTORS_CHANGE_VISIBILITY_LINK).attr({"data-action":action,title:title})})).catch(Notification.exception)},changeVisibilityIconAttr=function(action){var icon=$(SELECTORS_CHANGE_VISIBILITY_ICON);getTitle(action).then((function(title){$(icon).attr({title:title,"aria-label":title}),icon.is("img")?$(icon).attr({src:M.util.image_url("t/"+action),alt:title}):($(icon).addClass(getIconClass(action)),$(icon).removeClass(getIconClass(oppositeAction(action))))})).catch(Notification.exception)},getIconClass=function(action){return"show"==action?"fa-eye-slash":"fa-eye"},getTitle=function(action){return Str.get_string("online_status:"+action,"block_online_users")};return{init:function(){$(SELECTORS_CHANGE_VISIBILITY_LINK).on("click",(function(e){e.preventDefault(),function(action,userid){var request={methodname:"core_user_set_user_preferences",args:{preferences:[{name:"block_online_users_uservisibility",value:"show"==action?1:0,userid:userid}]}};Ajax.call([request])[0].then((function(data){if(data.saved){var newAction=oppositeAction(action);changeVisibilityLinkAttr(newAction),changeVisibilityIconAttr(newAction)}})).catch(Notification.exception)}($(this).attr("data-action"),$(this).attr("data-userid"))}))}}})); +define("block_online_users/change_user_visibility",["jquery","core/str","core/notification","core_user/repository"],(function($,Str,Notification,UserRepository){var SELECTORS_CHANGE_VISIBILITY_LINK="#change-user-visibility",SELECTORS_CHANGE_VISIBILITY_ICON="#change-user-visibility .icon",oppositeAction=function(action){return"show"==action?"hide":"show"},changeVisibilityLinkAttr=function(action){getTitle(action).then((function(title){$(SELECTORS_CHANGE_VISIBILITY_LINK).attr({"data-action":action,title:title})})).catch(Notification.exception)},changeVisibilityIconAttr=function(action){var icon=$(SELECTORS_CHANGE_VISIBILITY_ICON);getTitle(action).then((function(title){$(icon).attr({title:title,"aria-label":title}),icon.is("img")?$(icon).attr({src:M.util.image_url("t/"+action),alt:title}):($(icon).addClass(getIconClass(action)),$(icon).removeClass(getIconClass(oppositeAction(action))))})).catch(Notification.exception)},getIconClass=function(action){return"show"==action?"fa-eye-slash":"fa-eye"},getTitle=function(action){return Str.get_string("online_status:"+action,"block_online_users")};return{init:function(){$(SELECTORS_CHANGE_VISIBILITY_LINK).on("click",(function(e){e.preventDefault(),function(action,userid){var value="show"==action?1:0;UserRepository.setUserPreference("block_online_users_uservisibility",value,userid).then((data=>{if(data.saved){var newAction=oppositeAction(action);changeVisibilityLinkAttr(newAction),changeVisibilityIconAttr(newAction)}})).catch(Notification.exception)}($(this).attr("data-action"),$(this).attr("data-userid"))}))}}})); //# sourceMappingURL=change_user_visibility.min.js.map \ No newline at end of file diff --git a/blocks/online_users/amd/build/change_user_visibility.min.js.map b/blocks/online_users/amd/build/change_user_visibility.min.js.map index 683db00dfa2..892cbeca5b8 100644 --- a/blocks/online_users/amd/build/change_user_visibility.min.js.map +++ b/blocks/online_users/amd/build/change_user_visibility.min.js.map @@ -1 +1 @@ -{"version":3,"file":"change_user_visibility.min.js","sources":["../src/change_user_visibility.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 that handles the change of the user's visibility in the\n * online users block.\n *\n * @module block_online_users/change_user_visibility\n * @copyright 2018 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax', 'core/str', 'core/notification'],\n function($, Ajax, Str, Notification) {\n\n /**\n * Selectors.\n *\n * @access private\n * @type {Object}\n */\n var SELECTORS = {\n CHANGE_VISIBILITY_LINK: '#change-user-visibility',\n CHANGE_VISIBILITY_ICON: '#change-user-visibility .icon'\n };\n\n /**\n * Change user visibility in the online users block.\n *\n * @method changeVisibility\n * @param {String} action\n * @param {String} userid\n * @private\n */\n var changeVisibility = function(action, userid) {\n\n var value = action == \"show\" ? 1 : 0;\n var preferences = [{\n 'name': 'block_online_users_uservisibility',\n 'value': value,\n 'userid': userid\n }];\n\n var request = {\n methodname: 'core_user_set_user_preferences',\n args: {\n preferences: preferences\n }\n };\n Ajax.call([request])[0].then(function(data) {\n if (data.saved) {\n var newAction = oppositeAction(action);\n changeVisibilityLinkAttr(newAction);\n changeVisibilityIconAttr(newAction);\n }\n return;\n }).catch(Notification.exception);\n };\n\n /**\n * Get the opposite action.\n *\n * @method oppositeAction\n * @param {String} action\n * @return {String}\n * @private\n */\n var oppositeAction = function(action) {\n return action == 'show' ? 'hide' : 'show';\n };\n\n /**\n * Change the attribute values of the user visibility link in the online users block.\n *\n * @method changeVisibilityLinkAttr\n * @param {String} action\n * @private\n */\n var changeVisibilityLinkAttr = function(action) {\n getTitle(action).then(function(title) {\n $(SELECTORS.CHANGE_VISIBILITY_LINK).attr({\n 'data-action': action,\n 'title': title\n });\n return;\n }).catch(Notification.exception);\n };\n\n /**\n * Change the attribute values of the user visibility icon in the online users block.\n *\n * @method changeVisibilityIconAttr\n * @param {String} action\n * @private\n */\n var changeVisibilityIconAttr = function(action) {\n var icon = $(SELECTORS.CHANGE_VISIBILITY_ICON);\n getTitle(action).then(function(title) {\n // Add the proper title to the icon.\n $(icon).attr({\n 'title': title,\n 'aria-label': title\n });\n // If the icon is an image.\n if (icon.is(\"img\")) {\n $(icon).attr({\n 'src': M.util.image_url('t/' + action),\n 'alt': title\n });\n } else {\n // Add the new icon class and remove the old one.\n $(icon).addClass(getIconClass(action));\n $(icon).removeClass(getIconClass(oppositeAction(action)));\n }\n return;\n }).catch(Notification.exception);\n };\n\n /**\n * Get the proper class for the user visibility icon in the online users block.\n *\n * @method getIconClass\n * @param {String} action\n * @return {String}\n * @private\n */\n var getIconClass = function(action) {\n return action == 'show' ? 'fa-eye-slash' : 'fa-eye';\n };\n\n /**\n * Get the title description of the user visibility link in the online users block.\n *\n * @method getTitle\n * @param {String} action\n * @return {object} jQuery promise\n * @private\n */\n var getTitle = function(action) {\n return Str.get_string('online_status:' + action, 'block_online_users');\n };\n\n return {\n // Public variables and functions.\n /**\n * Initialise change user visibility function.\n *\n * @method init\n */\n init: function() {\n $(SELECTORS.CHANGE_VISIBILITY_LINK).on('click', function(e) {\n e.preventDefault();\n var action = ($(this).attr('data-action'));\n var userid = ($(this).attr('data-userid'));\n changeVisibility(action, userid);\n });\n }\n };\n});\n"],"names":["define","$","Ajax","Str","Notification","SELECTORS","oppositeAction","action","changeVisibilityLinkAttr","getTitle","then","title","attr","catch","exception","changeVisibilityIconAttr","icon","is","M","util","image_url","addClass","getIconClass","removeClass","get_string","init","on","e","preventDefault","userid","request","methodname","args","preferences","call","data","saved","newAction","changeVisibility","this"],"mappings":";;;;;;;;AAuBAA,mDAAO,CAAC,SAAU,YAAa,WAAY,sBACnC,SAASC,EAAGC,KAAMC,IAAKC,kBAQvBC,iCACwB,0BADxBA,iCAEwB,gCA4CxBC,eAAiB,SAASC,cACT,QAAVA,OAAmB,OAAS,QAUnCC,yBAA2B,SAASD,QACpCE,SAASF,QAAQG,MAAK,SAASC,OAC3BV,EAAEI,kCAAkCO,KAAK,eACtBL,aACNI,WAGdE,MAAMT,aAAaU,YAUtBC,yBAA2B,SAASR,YAChCS,KAAOf,EAAEI,kCACbI,SAASF,QAAQG,MAAK,SAASC,OAE3BV,EAAEe,MAAMJ,KAAK,OACAD,mBACKA,QAGdK,KAAKC,GAAG,OACRhB,EAAEe,MAAMJ,KAAK,KACFM,EAAEC,KAAKC,UAAU,KAAOb,YACxBI,SAIXV,EAAEe,MAAMK,SAASC,aAAaf,SAC9BN,EAAEe,MAAMO,YAAYD,aAAahB,eAAeC,cAGrDM,MAAMT,aAAaU,YAWtBQ,aAAe,SAASf,cACP,QAAVA,OAAmB,eAAiB,UAW3CE,SAAW,SAASF,eACbJ,IAAIqB,WAAW,iBAAmBjB,OAAQ,6BAG9C,CAOHkB,KAAM,WACFxB,EAAEI,kCAAkCqB,GAAG,SAAS,SAASC,GACrDA,EAAEC,iBArHS,SAASrB,OAAQsB,YAShCC,QAAU,CACVC,WAAY,iCACZC,KAAM,CACFC,YATU,CAAC,MACP,0CAFU,QAAV1B,OAAmB,EAAI,SAIrBsB,WASd3B,KAAKgC,KAAK,CAACJ,UAAU,GAAGpB,MAAK,SAASyB,SAC9BA,KAAKC,MAAO,KACRC,UAAY/B,eAAeC,QAC/BC,yBAAyB6B,WACzBtB,yBAAyBsB,eAG9BxB,MAAMT,aAAaU,WAkGdwB,CAFcrC,EAAEsC,MAAM3B,KAAK,eACbX,EAAEsC,MAAM3B,KAAK"} \ No newline at end of file +{"version":3,"file":"change_user_visibility.min.js","sources":["../src/change_user_visibility.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 that handles the change of the user's visibility in the\n * online users block.\n *\n * @module block_online_users/change_user_visibility\n * @copyright 2018 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/str', 'core/notification', 'core_user/repository'],\n function($, Str, Notification, UserRepository) {\n\n /**\n * Selectors.\n *\n * @access private\n * @type {Object}\n */\n var SELECTORS = {\n CHANGE_VISIBILITY_LINK: '#change-user-visibility',\n CHANGE_VISIBILITY_ICON: '#change-user-visibility .icon'\n };\n\n /**\n * Change user visibility in the online users block.\n *\n * @method changeVisibility\n * @param {String} action\n * @param {String} userid\n * @private\n */\n var changeVisibility = function(action, userid) {\n\n var value = action == \"show\" ? 1 : 0;\n\n UserRepository.setUserPreference('block_online_users_uservisibility', value, userid)\n .then(data => {\n if (data.saved) {\n var newAction = oppositeAction(action);\n changeVisibilityLinkAttr(newAction);\n changeVisibilityIconAttr(newAction);\n }\n return;\n }).catch(Notification.exception);\n };\n\n /**\n * Get the opposite action.\n *\n * @method oppositeAction\n * @param {String} action\n * @return {String}\n * @private\n */\n var oppositeAction = function(action) {\n return action == 'show' ? 'hide' : 'show';\n };\n\n /**\n * Change the attribute values of the user visibility link in the online users block.\n *\n * @method changeVisibilityLinkAttr\n * @param {String} action\n * @private\n */\n var changeVisibilityLinkAttr = function(action) {\n getTitle(action).then(function(title) {\n $(SELECTORS.CHANGE_VISIBILITY_LINK).attr({\n 'data-action': action,\n 'title': title\n });\n return;\n }).catch(Notification.exception);\n };\n\n /**\n * Change the attribute values of the user visibility icon in the online users block.\n *\n * @method changeVisibilityIconAttr\n * @param {String} action\n * @private\n */\n var changeVisibilityIconAttr = function(action) {\n var icon = $(SELECTORS.CHANGE_VISIBILITY_ICON);\n getTitle(action).then(function(title) {\n // Add the proper title to the icon.\n $(icon).attr({\n 'title': title,\n 'aria-label': title\n });\n // If the icon is an image.\n if (icon.is(\"img\")) {\n $(icon).attr({\n 'src': M.util.image_url('t/' + action),\n 'alt': title\n });\n } else {\n // Add the new icon class and remove the old one.\n $(icon).addClass(getIconClass(action));\n $(icon).removeClass(getIconClass(oppositeAction(action)));\n }\n return;\n }).catch(Notification.exception);\n };\n\n /**\n * Get the proper class for the user visibility icon in the online users block.\n *\n * @method getIconClass\n * @param {String} action\n * @return {String}\n * @private\n */\n var getIconClass = function(action) {\n return action == 'show' ? 'fa-eye-slash' : 'fa-eye';\n };\n\n /**\n * Get the title description of the user visibility link in the online users block.\n *\n * @method getTitle\n * @param {String} action\n * @return {object} jQuery promise\n * @private\n */\n var getTitle = function(action) {\n return Str.get_string('online_status:' + action, 'block_online_users');\n };\n\n return {\n // Public variables and functions.\n /**\n * Initialise change user visibility function.\n *\n * @method init\n */\n init: function() {\n $(SELECTORS.CHANGE_VISIBILITY_LINK).on('click', function(e) {\n e.preventDefault();\n var action = ($(this).attr('data-action'));\n var userid = ($(this).attr('data-userid'));\n changeVisibility(action, userid);\n });\n }\n };\n});\n"],"names":["define","$","Str","Notification","UserRepository","SELECTORS","oppositeAction","action","changeVisibilityLinkAttr","getTitle","then","title","attr","catch","exception","changeVisibilityIconAttr","icon","is","M","util","image_url","addClass","getIconClass","removeClass","get_string","init","on","e","preventDefault","userid","value","setUserPreference","data","saved","newAction","changeVisibility","this"],"mappings":";;;;;;;;AAuBAA,mDAAO,CAAC,SAAU,WAAY,oBAAqB,yBAC3C,SAASC,EAAGC,IAAKC,aAAcC,oBAQ/BC,iCACwB,0BADxBA,iCAEwB,gCAkCxBC,eAAiB,SAASC,cACT,QAAVA,OAAmB,OAAS,QAUnCC,yBAA2B,SAASD,QACpCE,SAASF,QAAQG,MAAK,SAASC,OAC3BV,EAAEI,kCAAkCO,KAAK,eACtBL,aACNI,WAGdE,MAAMV,aAAaW,YAUtBC,yBAA2B,SAASR,YAChCS,KAAOf,EAAEI,kCACbI,SAASF,QAAQG,MAAK,SAASC,OAE3BV,EAAEe,MAAMJ,KAAK,OACAD,mBACKA,QAGdK,KAAKC,GAAG,OACRhB,EAAEe,MAAMJ,KAAK,KACFM,EAAEC,KAAKC,UAAU,KAAOb,YACxBI,SAIXV,EAAEe,MAAMK,SAASC,aAAaf,SAC9BN,EAAEe,MAAMO,YAAYD,aAAahB,eAAeC,cAGrDM,MAAMV,aAAaW,YAWtBQ,aAAe,SAASf,cACP,QAAVA,OAAmB,eAAiB,UAW3CE,SAAW,SAASF,eACbL,IAAIsB,WAAW,iBAAmBjB,OAAQ,6BAG9C,CAOHkB,KAAM,WACFxB,EAAEI,kCAAkCqB,GAAG,SAAS,SAASC,GACrDA,EAAEC,iBA3GS,SAASrB,OAAQsB,YAEhCC,MAAkB,QAAVvB,OAAmB,EAAI,EAEnCH,eAAe2B,kBAAkB,oCAAqCD,MAAOD,QAC5EnB,MAAKsB,UACEA,KAAKC,MAAO,KACRC,UAAY5B,eAAeC,QAC/BC,yBAAyB0B,WACzBnB,yBAAyBmB,eAG9BrB,MAAMV,aAAaW,WAkGdqB,CAFclC,EAAEmC,MAAMxB,KAAK,eACbX,EAAEmC,MAAMxB,KAAK"} \ No newline at end of file diff --git a/blocks/online_users/amd/src/change_user_visibility.js b/blocks/online_users/amd/src/change_user_visibility.js index 253e6ad7856..452c28c1df0 100644 --- a/blocks/online_users/amd/src/change_user_visibility.js +++ b/blocks/online_users/amd/src/change_user_visibility.js @@ -21,8 +21,8 @@ * @copyright 2018 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define(['jquery', 'core/ajax', 'core/str', 'core/notification'], - function($, Ajax, Str, Notification) { +define(['jquery', 'core/str', 'core/notification', 'core_user/repository'], + function($, Str, Notification, UserRepository) { /** * Selectors. @@ -46,19 +46,9 @@ define(['jquery', 'core/ajax', 'core/str', 'core/notification'], var changeVisibility = function(action, userid) { var value = action == "show" ? 1 : 0; - var preferences = [{ - 'name': 'block_online_users_uservisibility', - 'value': value, - 'userid': userid - }]; - var request = { - methodname: 'core_user_set_user_preferences', - args: { - preferences: preferences - } - }; - Ajax.call([request])[0].then(function(data) { + UserRepository.setUserPreference('block_online_users_uservisibility', value, userid) + .then(data => { if (data.saved) { var newAction = oppositeAction(action); changeVisibilityLinkAttr(newAction); diff --git a/blocks/timeline/amd/build/view_nav.min.js b/blocks/timeline/amd/build/view_nav.min.js index 0f3c7aadc04..d144b7022c2 100644 --- a/blocks/timeline/amd/build/view_nav.min.js +++ b/blocks/timeline/amd/build/view_nav.min.js @@ -4,6 +4,6 @@ * @copyright 2018 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("block_timeline/view_nav",["jquery","core/custom_interaction_events","block_timeline/view","core/ajax","core/notification","core/utils"],(function($,CustomEvents,View,Ajax,Notification,Utils){var SELECTORS_TIMELINE_DAY_FILTER='[data-region="day-filter"]',SELECTORS_TIMELINE_DAY_FILTER_OPTION="[data-from]",SELECTORS_TIMELINE_VIEW_SELECTOR='[data-region="view-selector"]',SELECTORS_DATA_DAYS_OFFSET="[data-days-offset]",SELECTORS_TIMELINE_SEARCH_INPUT='[data-action="search"]',SELECTORS_TIMELINE_SEARCH_CLEAR_ICON='[data-action="clearsearch"]',SELECTORS_NO_COURSES_EMPTY_MESSAGE='[data-region="no-courses-empty-message"]',updateUserPreferences=function(type,value){var request={methodname:"core_user_update_user_preferences",args:{preferences:[{type:type,value:value}]}};Ajax.call([request])[0].fail(Notification.exception)};const activeSearchState=(clearSearchIcon,timelineViewRoot)=>{clearSearchIcon.removeClass("d-none"),View.reset(timelineViewRoot)},clearSearchState=(clearSearchIcon,timelineViewRoot)=>{clearSearchIcon.addClass("d-none"),View.reset(timelineViewRoot)};return{init:function(root,timelineViewRoot){(function(root,timelineViewRoot){var viewSelector=root.find(SELECTORS_TIMELINE_VIEW_SELECTOR);viewSelector.on("shown shown.bs.tab",(function(e){View.shown(timelineViewRoot),$(e.target).removeClass("active")})),CustomEvents.define(viewSelector,[CustomEvents.events.activate]),viewSelector.on(CustomEvents.events.activate,"[data-toggle='tab']",(function(e){var filtername=$(e.currentTarget).data("filtername");updateUserPreferences("block_timeline_user_sort_preference",filtername)}))})(root=$(root),timelineViewRoot),root.find(SELECTORS_NO_COURSES_EMPTY_MESSAGE).length||(function(root,timelineViewRoot){var timelineDaySelectorContainer=root.find(SELECTORS_TIMELINE_DAY_FILTER);CustomEvents.define(timelineDaySelectorContainer,[CustomEvents.events.activate]),timelineDaySelectorContainer.on(CustomEvents.events.activate,SELECTORS_TIMELINE_DAY_FILTER_OPTION,(function(e,data){var filtername=$(e.currentTarget).data("filtername");updateUserPreferences("block_timeline_user_filter_preference",filtername);var option=$(e.target).closest(SELECTORS_TIMELINE_DAY_FILTER_OPTION);if("true"!=option.attr("aria-current")){var daysOffset=option.attr("data-from"),daysLimit=option.attr("data-to"),elementsWithDaysOffset=root.find(SELECTORS_DATA_DAYS_OFFSET);elementsWithDaysOffset.attr("data-days-offset",daysOffset),null!=daysLimit?elementsWithDaysOffset.attr("data-days-limit",daysLimit):elementsWithDaysOffset.removeAttr("data-days-limit"),"overdue"===option.attr("data-filtername")?elementsWithDaysOffset.attr("data-filter-overdue",!0):elementsWithDaysOffset.removeAttr("data-filter-overdue"),View.reset(timelineViewRoot),data.originalEvent.preventDefault()}}))}(root,timelineViewRoot),((root,timelineViewRoot)=>{const searchInput=root.find(SELECTORS_TIMELINE_SEARCH_INPUT),clearSearchIcon=root.find(SELECTORS_TIMELINE_SEARCH_CLEAR_ICON);searchInput.on("input",Utils.debounce((()=>{""!==searchInput.val()?activeSearchState(clearSearchIcon,timelineViewRoot):clearSearchState(clearSearchIcon,timelineViewRoot)}),1e3)),clearSearchIcon.on("click",(()=>{searchInput.val(""),clearSearchState(clearSearchIcon,timelineViewRoot),searchInput.focus()}))})(root,timelineViewRoot))}}})); +define("block_timeline/view_nav",["jquery","core/custom_interaction_events","block_timeline/view","core/notification","core/utils","core_user/repository"],(function($,CustomEvents,View,Notification,Utils,UserRepository){var SELECTORS_TIMELINE_DAY_FILTER='[data-region="day-filter"]',SELECTORS_TIMELINE_DAY_FILTER_OPTION="[data-from]",SELECTORS_TIMELINE_VIEW_SELECTOR='[data-region="view-selector"]',SELECTORS_DATA_DAYS_OFFSET="[data-days-offset]",SELECTORS_TIMELINE_SEARCH_INPUT='[data-action="search"]',SELECTORS_TIMELINE_SEARCH_CLEAR_ICON='[data-action="clearsearch"]',SELECTORS_NO_COURSES_EMPTY_MESSAGE='[data-region="no-courses-empty-message"]';const activeSearchState=(clearSearchIcon,timelineViewRoot)=>{clearSearchIcon.removeClass("d-none"),View.reset(timelineViewRoot)},clearSearchState=(clearSearchIcon,timelineViewRoot)=>{clearSearchIcon.addClass("d-none"),View.reset(timelineViewRoot)};return{init:function(root,timelineViewRoot){(function(root,timelineViewRoot){var viewSelector=root.find(SELECTORS_TIMELINE_VIEW_SELECTOR);viewSelector.on("shown shown.bs.tab",(function(e){View.shown(timelineViewRoot),$(e.target).removeClass("active")})),CustomEvents.define(viewSelector,[CustomEvents.events.activate]),viewSelector.on(CustomEvents.events.activate,"[data-toggle='tab']",(function(e){var filtername=$(e.currentTarget).data("filtername");UserRepository.setUserPreference("block_timeline_user_sort_preference",filtername).catch(Notification.exception)}))})(root=$(root),timelineViewRoot),root.find(SELECTORS_NO_COURSES_EMPTY_MESSAGE).length||(function(root,timelineViewRoot){var timelineDaySelectorContainer=root.find(SELECTORS_TIMELINE_DAY_FILTER);CustomEvents.define(timelineDaySelectorContainer,[CustomEvents.events.activate]),timelineDaySelectorContainer.on(CustomEvents.events.activate,SELECTORS_TIMELINE_DAY_FILTER_OPTION,(function(e,data){var filtername=$(e.currentTarget).data("filtername");UserRepository.setUserPreference("block_timeline_user_filter_preference",filtername).catch(Notification.exception);var option=$(e.target).closest(SELECTORS_TIMELINE_DAY_FILTER_OPTION);if("true"!=option.attr("aria-current")){var daysOffset=option.attr("data-from"),daysLimit=option.attr("data-to"),elementsWithDaysOffset=root.find(SELECTORS_DATA_DAYS_OFFSET);elementsWithDaysOffset.attr("data-days-offset",daysOffset),null!=daysLimit?elementsWithDaysOffset.attr("data-days-limit",daysLimit):elementsWithDaysOffset.removeAttr("data-days-limit"),"overdue"===option.attr("data-filtername")?elementsWithDaysOffset.attr("data-filter-overdue",!0):elementsWithDaysOffset.removeAttr("data-filter-overdue"),View.reset(timelineViewRoot),data.originalEvent.preventDefault()}}))}(root,timelineViewRoot),((root,timelineViewRoot)=>{const searchInput=root.find(SELECTORS_TIMELINE_SEARCH_INPUT),clearSearchIcon=root.find(SELECTORS_TIMELINE_SEARCH_CLEAR_ICON);searchInput.on("input",Utils.debounce((()=>{""!==searchInput.val()?activeSearchState(clearSearchIcon,timelineViewRoot):clearSearchState(clearSearchIcon,timelineViewRoot)}),1e3)),clearSearchIcon.on("click",(()=>{searchInput.val(""),clearSearchState(clearSearchIcon,timelineViewRoot),searchInput.focus()}))})(root,timelineViewRoot))}}})); //# sourceMappingURL=view_nav.min.js.map \ No newline at end of file diff --git a/blocks/timeline/amd/build/view_nav.min.js.map b/blocks/timeline/amd/build/view_nav.min.js.map index c84536dfd6f..855bce1c620 100644 --- a/blocks/timeline/amd/build/view_nav.min.js.map +++ b/blocks/timeline/amd/build/view_nav.min.js.map @@ -1 +1 @@ -{"version":3,"file":"view_nav.min.js","sources":["../src/view_nav.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 timeline view navigation for the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n[\n 'jquery',\n 'core/custom_interaction_events',\n 'block_timeline/view',\n 'core/ajax',\n 'core/notification',\n 'core/utils'\n],\nfunction(\n $,\n CustomEvents,\n View,\n Ajax,\n Notification,\n Utils\n) {\n\n var SELECTORS = {\n TIMELINE_DAY_FILTER: '[data-region=\"day-filter\"]',\n TIMELINE_DAY_FILTER_OPTION: '[data-from]',\n TIMELINE_VIEW_SELECTOR: '[data-region=\"view-selector\"]',\n DATA_DAYS_OFFSET: '[data-days-offset]',\n DATA_DAYS_LIMIT: '[data-days-limit]',\n TIMELINE_SEARCH_INPUT: '[data-action=\"search\"]',\n TIMELINE_SEARCH_CLEAR_ICON: '[data-action=\"clearsearch\"]',\n NO_COURSES_EMPTY_MESSAGE: '[data-region=\"no-courses-empty-message\"]',\n };\n\n /**\n * Generic handler to persist user preferences\n *\n * @param {string} type The name of the attribute you're updating\n * @param {string} value The value of the attribute you're updating\n */\n var updateUserPreferences = function(type, value) {\n var request = {\n methodname: 'core_user_update_user_preferences',\n args: {\n preferences: [\n {\n type: type,\n value: value\n }\n ]\n }\n };\n\n Ajax.call([request])[0]\n .fail(Notification.exception);\n };\n\n /**\n * Event listener for the day selector (\"Next 7 days\", \"Next 30 days\", etc).\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n var registerTimelineDaySelector = function(root, timelineViewRoot) {\n var timelineDaySelectorContainer = root.find(SELECTORS.TIMELINE_DAY_FILTER);\n\n CustomEvents.define(timelineDaySelectorContainer, [CustomEvents.events.activate]);\n timelineDaySelectorContainer.on(\n CustomEvents.events.activate,\n SELECTORS.TIMELINE_DAY_FILTER_OPTION,\n function(e, data) {\n // Update the user preference\n var filtername = $(e.currentTarget).data('filtername');\n var type = 'block_timeline_user_filter_preference';\n updateUserPreferences(type, filtername);\n\n var option = $(e.target).closest(SELECTORS.TIMELINE_DAY_FILTER_OPTION);\n\n if (option.attr('aria-current') == 'true') {\n // If it's already active then we don't need to do anything.\n return;\n }\n\n var daysOffset = option.attr('data-from');\n var daysLimit = option.attr('data-to');\n var elementsWithDaysOffset = root.find(SELECTORS.DATA_DAYS_OFFSET);\n\n elementsWithDaysOffset.attr('data-days-offset', daysOffset);\n\n if (daysLimit != undefined) {\n elementsWithDaysOffset.attr('data-days-limit', daysLimit);\n } else {\n elementsWithDaysOffset.removeAttr('data-days-limit');\n }\n\n if (option.attr('data-filtername') === 'overdue') {\n elementsWithDaysOffset.attr('data-filter-overdue', true);\n } else {\n elementsWithDaysOffset.removeAttr('data-filter-overdue');\n }\n\n // Reset the views to reinitialise the event lists now that we've\n // updated the day limits.\n View.reset(timelineViewRoot);\n\n data.originalEvent.preventDefault();\n }\n );\n };\n\n /**\n * Event listener for the \"sort\" button in the timeline navigation that allows for\n * changing between the timeline dates and courses views.\n *\n * On a view change we tell the timeline view module that the view has been shown\n * so that it can handle how to display the appropriate view.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n var registerViewSelector = function(root, timelineViewRoot) {\n var viewSelector = root.find(SELECTORS.TIMELINE_VIEW_SELECTOR);\n\n // Listen for when the user changes tab so that we can show the first set of courses\n // and load their events when they request the sort by courses view for the first time.\n viewSelector.on('shown shown.bs.tab', function(e) {\n View.shown(timelineViewRoot);\n $(e.target).removeClass('active');\n });\n\n\n // Event selector for user_sort\n CustomEvents.define(viewSelector, [CustomEvents.events.activate]);\n viewSelector.on(CustomEvents.events.activate, \"[data-toggle='tab']\", function(e) {\n var filtername = $(e.currentTarget).data('filtername');\n var type = 'block_timeline_user_sort_preference';\n updateUserPreferences(type, filtername);\n });\n };\n\n /**\n * Event listener for the \"search\" input field in the timeline navigation that allows for\n * searching the activity name, course name and activity type.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n const registerSearch = (root, timelineViewRoot) => {\n const searchInput = root.find(SELECTORS.TIMELINE_SEARCH_INPUT);\n const clearSearchIcon = root.find(SELECTORS.TIMELINE_SEARCH_CLEAR_ICON);\n searchInput.on('input', Utils.debounce(() => {\n if (searchInput.val() !== '') {\n activeSearchState(clearSearchIcon, timelineViewRoot);\n } else {\n clearSearchState(clearSearchIcon, timelineViewRoot);\n }\n }, 1000));\n clearSearchIcon.on('click', () => {\n searchInput.val('');\n clearSearchState(clearSearchIcon, timelineViewRoot);\n searchInput.focus();\n });\n };\n\n /**\n * Show the clear search icon.\n *\n * @param {object} clearSearchIcon Clear search icon element.\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n const activeSearchState = (clearSearchIcon, timelineViewRoot) => {\n clearSearchIcon.removeClass('d-none');\n View.reset(timelineViewRoot);\n };\n\n /**\n * Hide the clear search icon.\n *\n * @param {object} clearSearchIcon Clear search icon element.\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n const clearSearchState = (clearSearchIcon, timelineViewRoot) => {\n clearSearchIcon.addClass('d-none');\n View.reset(timelineViewRoot);\n };\n\n /**\n * Initialise the timeline view navigation by adding event listeners to\n * the navigation elements.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n var init = function(root, timelineViewRoot) {\n root = $(root);\n\n registerViewSelector(root, timelineViewRoot);\n\n // Only need to handle filtering if the user is actively enrolled in a course.\n if (!root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length) {\n registerTimelineDaySelector(root, timelineViewRoot);\n registerSearch(root, timelineViewRoot);\n }\n };\n\n return {\n init: init\n };\n});\n"],"names":["define","$","CustomEvents","View","Ajax","Notification","Utils","SELECTORS","updateUserPreferences","type","value","request","methodname","args","preferences","call","fail","exception","activeSearchState","clearSearchIcon","timelineViewRoot","removeClass","reset","clearSearchState","addClass","init","root","viewSelector","find","on","e","shown","target","events","activate","filtername","currentTarget","data","registerViewSelector","length","timelineDaySelectorContainer","option","closest","attr","daysOffset","daysLimit","elementsWithDaysOffset","undefined","removeAttr","originalEvent","preventDefault","registerTimelineDaySelector","searchInput","debounce","val","focus","registerSearch"],"mappings":";;;;;;AAsBAA,iCACA,CACI,SACA,iCACA,sBACA,YACA,oBACA,eAEJ,SACIC,EACAC,aACAC,KACAC,KACAC,aACAC,WAGIC,8BACqB,6BADrBA,qCAE4B,cAF5BA,iCAGwB,gCAHxBA,2BAIkB,qBAJlBA,gCAMuB,yBANvBA,qCAO4B,8BAP5BA,mCAQ0B,2CAS1BC,sBAAwB,SAASC,KAAMC,WACnCC,QAAU,CACVC,WAAY,oCACZC,KAAM,CACFC,YAAa,CACT,CACIL,KAAMA,KACNC,MAAOA,UAMvBN,KAAKW,KAAK,CAACJ,UAAU,GAChBK,KAAKX,aAAaY,kBAoHrBC,kBAAoB,CAACC,gBAAiBC,oBACxCD,gBAAgBE,YAAY,UAC5BlB,KAAKmB,MAAMF,mBASTG,iBAAmB,CAACJ,gBAAiBC,oBACvCD,gBAAgBK,SAAS,UACzBrB,KAAKmB,MAAMF,yBAsBR,CACHK,KAbO,SAASC,KAAMN,mBAzEC,SAASM,KAAMN,sBAClCO,aAAeD,KAAKE,KAAKrB,kCAI7BoB,aAAaE,GAAG,sBAAsB,SAASC,GAC3C3B,KAAK4B,MAAMX,kBACXnB,EAAE6B,EAAEE,QAAQX,YAAY,aAK5BnB,aAAaF,OAAO2B,aAAc,CAACzB,aAAa+B,OAAOC,WACvDP,aAAaE,GAAG3B,aAAa+B,OAAOC,SAAU,uBAAuB,SAASJ,OACtEK,WAAalC,EAAE6B,EAAEM,eAAeC,KAAK,cAEzC7B,sBADW,sCACiB2B,gBA4DhCG,CAFAZ,KAAOzB,EAAEyB,MAEkBN,kBAGtBM,KAAKE,KAAKrB,oCAAoCgC,SAxIrB,SAASb,KAAMN,sBACzCoB,6BAA+Bd,KAAKE,KAAKrB,+BAE7CL,aAAaF,OAAOwC,6BAA8B,CAACtC,aAAa+B,OAAOC,WACvEM,6BAA6BX,GACzB3B,aAAa+B,OAAOC,SACpB3B,sCACA,SAASuB,EAAGO,UAEJF,WAAalC,EAAE6B,EAAEM,eAAeC,KAAK,cAEzC7B,sBADW,wCACiB2B,gBAExBM,OAASxC,EAAE6B,EAAEE,QAAQU,QAAQnC,yCAEE,QAA/BkC,OAAOE,KAAK,qBAKZC,WAAaH,OAAOE,KAAK,aACzBE,UAAYJ,OAAOE,KAAK,WACxBG,uBAAyBpB,KAAKE,KAAKrB,4BAEvCuC,uBAAuBH,KAAK,mBAAoBC,YAE/BG,MAAbF,UACAC,uBAAuBH,KAAK,kBAAmBE,WAE/CC,uBAAuBE,WAAW,mBAGC,YAAnCP,OAAOE,KAAK,mBACZG,uBAAuBH,KAAK,uBAAuB,GAEnDG,uBAAuBE,WAAW,uBAKtC7C,KAAKmB,MAAMF,kBAEXiB,KAAKY,cAAcC,qBA+FvBC,CAA4BzB,KAAMN,kBArDnB,EAACM,KAAMN,0BACpBgC,YAAc1B,KAAKE,KAAKrB,iCACxBY,gBAAkBO,KAAKE,KAAKrB,sCAClC6C,YAAYvB,GAAG,QAASvB,MAAM+C,UAAS,KACT,KAAtBD,YAAYE,MACZpC,kBAAkBC,gBAAiBC,kBAEnCG,iBAAiBJ,gBAAiBC,oBAEvC,MACHD,gBAAgBU,GAAG,SAAS,KACxBuB,YAAYE,IAAI,IAChB/B,iBAAiBJ,gBAAiBC,kBAClCgC,YAAYG,YAyCZC,CAAe9B,KAAMN"} \ No newline at end of file +{"version":3,"file":"view_nav.min.js","sources":["../src/view_nav.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 timeline view navigation for the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n[\n 'jquery',\n 'core/custom_interaction_events',\n 'block_timeline/view',\n 'core/notification',\n 'core/utils',\n 'core_user/repository'\n],\nfunction(\n $,\n CustomEvents,\n View,\n Notification,\n Utils,\n UserRepository,\n) {\n\n var SELECTORS = {\n TIMELINE_DAY_FILTER: '[data-region=\"day-filter\"]',\n TIMELINE_DAY_FILTER_OPTION: '[data-from]',\n TIMELINE_VIEW_SELECTOR: '[data-region=\"view-selector\"]',\n DATA_DAYS_OFFSET: '[data-days-offset]',\n DATA_DAYS_LIMIT: '[data-days-limit]',\n TIMELINE_SEARCH_INPUT: '[data-action=\"search\"]',\n TIMELINE_SEARCH_CLEAR_ICON: '[data-action=\"clearsearch\"]',\n NO_COURSES_EMPTY_MESSAGE: '[data-region=\"no-courses-empty-message\"]',\n };\n\n /**\n * Event listener for the day selector (\"Next 7 days\", \"Next 30 days\", etc).\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n var registerTimelineDaySelector = function(root, timelineViewRoot) {\n var timelineDaySelectorContainer = root.find(SELECTORS.TIMELINE_DAY_FILTER);\n\n CustomEvents.define(timelineDaySelectorContainer, [CustomEvents.events.activate]);\n timelineDaySelectorContainer.on(\n CustomEvents.events.activate,\n SELECTORS.TIMELINE_DAY_FILTER_OPTION,\n function(e, data) {\n // Update the user preference\n var filtername = $(e.currentTarget).data('filtername');\n var type = 'block_timeline_user_filter_preference';\n UserRepository.setUserPreference(type, filtername)\n .catch(Notification.exception);\n\n var option = $(e.target).closest(SELECTORS.TIMELINE_DAY_FILTER_OPTION);\n\n if (option.attr('aria-current') == 'true') {\n // If it's already active then we don't need to do anything.\n return;\n }\n\n var daysOffset = option.attr('data-from');\n var daysLimit = option.attr('data-to');\n var elementsWithDaysOffset = root.find(SELECTORS.DATA_DAYS_OFFSET);\n\n elementsWithDaysOffset.attr('data-days-offset', daysOffset);\n\n if (daysLimit != undefined) {\n elementsWithDaysOffset.attr('data-days-limit', daysLimit);\n } else {\n elementsWithDaysOffset.removeAttr('data-days-limit');\n }\n\n if (option.attr('data-filtername') === 'overdue') {\n elementsWithDaysOffset.attr('data-filter-overdue', true);\n } else {\n elementsWithDaysOffset.removeAttr('data-filter-overdue');\n }\n\n // Reset the views to reinitialise the event lists now that we've\n // updated the day limits.\n View.reset(timelineViewRoot);\n\n data.originalEvent.preventDefault();\n }\n );\n };\n\n /**\n * Event listener for the \"sort\" button in the timeline navigation that allows for\n * changing between the timeline dates and courses views.\n *\n * On a view change we tell the timeline view module that the view has been shown\n * so that it can handle how to display the appropriate view.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n var registerViewSelector = function(root, timelineViewRoot) {\n var viewSelector = root.find(SELECTORS.TIMELINE_VIEW_SELECTOR);\n\n // Listen for when the user changes tab so that we can show the first set of courses\n // and load their events when they request the sort by courses view for the first time.\n viewSelector.on('shown shown.bs.tab', function(e) {\n View.shown(timelineViewRoot);\n $(e.target).removeClass('active');\n });\n\n\n // Event selector for user_sort\n CustomEvents.define(viewSelector, [CustomEvents.events.activate]);\n viewSelector.on(CustomEvents.events.activate, \"[data-toggle='tab']\", function(e) {\n var filtername = $(e.currentTarget).data('filtername');\n var type = 'block_timeline_user_sort_preference';\n UserRepository.setUserPreference(type, filtername)\n .catch(Notification.exception);\n });\n };\n\n /**\n * Event listener for the \"search\" input field in the timeline navigation that allows for\n * searching the activity name, course name and activity type.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n const registerSearch = (root, timelineViewRoot) => {\n const searchInput = root.find(SELECTORS.TIMELINE_SEARCH_INPUT);\n const clearSearchIcon = root.find(SELECTORS.TIMELINE_SEARCH_CLEAR_ICON);\n searchInput.on('input', Utils.debounce(() => {\n if (searchInput.val() !== '') {\n activeSearchState(clearSearchIcon, timelineViewRoot);\n } else {\n clearSearchState(clearSearchIcon, timelineViewRoot);\n }\n }, 1000));\n clearSearchIcon.on('click', () => {\n searchInput.val('');\n clearSearchState(clearSearchIcon, timelineViewRoot);\n searchInput.focus();\n });\n };\n\n /**\n * Show the clear search icon.\n *\n * @param {object} clearSearchIcon Clear search icon element.\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n const activeSearchState = (clearSearchIcon, timelineViewRoot) => {\n clearSearchIcon.removeClass('d-none');\n View.reset(timelineViewRoot);\n };\n\n /**\n * Hide the clear search icon.\n *\n * @param {object} clearSearchIcon Clear search icon element.\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n const clearSearchState = (clearSearchIcon, timelineViewRoot) => {\n clearSearchIcon.addClass('d-none');\n View.reset(timelineViewRoot);\n };\n\n /**\n * Initialise the timeline view navigation by adding event listeners to\n * the navigation elements.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n var init = function(root, timelineViewRoot) {\n root = $(root);\n\n registerViewSelector(root, timelineViewRoot);\n\n // Only need to handle filtering if the user is actively enrolled in a course.\n if (!root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length) {\n registerTimelineDaySelector(root, timelineViewRoot);\n registerSearch(root, timelineViewRoot);\n }\n };\n\n return {\n init: init\n };\n});\n"],"names":["define","$","CustomEvents","View","Notification","Utils","UserRepository","SELECTORS","activeSearchState","clearSearchIcon","timelineViewRoot","removeClass","reset","clearSearchState","addClass","init","root","viewSelector","find","on","e","shown","target","events","activate","filtername","currentTarget","data","setUserPreference","catch","exception","registerViewSelector","length","timelineDaySelectorContainer","option","closest","attr","daysOffset","daysLimit","elementsWithDaysOffset","undefined","removeAttr","originalEvent","preventDefault","registerTimelineDaySelector","searchInput","debounce","val","focus","registerSearch"],"mappings":";;;;;;AAsBAA,iCACA,CACI,SACA,iCACA,sBACA,oBACA,aACA,yBAEJ,SACIC,EACAC,aACAC,KACAC,aACAC,MACAC,oBAGIC,8BACqB,6BADrBA,qCAE4B,cAF5BA,iCAGwB,gCAHxBA,2BAIkB,qBAJlBA,gCAMuB,yBANvBA,qCAO4B,8BAP5BA,mCAQ0B,iDAsHxBC,kBAAoB,CAACC,gBAAiBC,oBACxCD,gBAAgBE,YAAY,UAC5BR,KAAKS,MAAMF,mBASTG,iBAAmB,CAACJ,gBAAiBC,oBACvCD,gBAAgBK,SAAS,UACzBX,KAAKS,MAAMF,yBAsBR,CACHK,KAbO,SAASC,KAAMN,mBA1EC,SAASM,KAAMN,sBAClCO,aAAeD,KAAKE,KAAKX,kCAI7BU,aAAaE,GAAG,sBAAsB,SAASC,GAC3CjB,KAAKkB,MAAMX,kBACXT,EAAEmB,EAAEE,QAAQX,YAAY,aAK5BT,aAAaF,OAAOiB,aAAc,CAACf,aAAaqB,OAAOC,WACvDP,aAAaE,GAAGjB,aAAaqB,OAAOC,SAAU,uBAAuB,SAASJ,OACtEK,WAAaxB,EAAEmB,EAAEM,eAAeC,KAAK,cAEzCrB,eAAesB,kBADJ,sCAC4BH,YAClCI,MAAMzB,aAAa0B,eA4D5BC,CAFAf,KAAOf,EAAEe,MAEkBN,kBAGtBM,KAAKE,KAAKX,oCAAoCyB,SA1IrB,SAAShB,KAAMN,sBACzCuB,6BAA+BjB,KAAKE,KAAKX,+BAE7CL,aAAaF,OAAOiC,6BAA8B,CAAC/B,aAAaqB,OAAOC,WACvES,6BAA6Bd,GACzBjB,aAAaqB,OAAOC,SACpBjB,sCACA,SAASa,EAAGO,UAEJF,WAAaxB,EAAEmB,EAAEM,eAAeC,KAAK,cAEzCrB,eAAesB,kBADJ,wCAC4BH,YAClCI,MAAMzB,aAAa0B,eAEpBI,OAASjC,EAAEmB,EAAEE,QAAQa,QAAQ5B,yCAEE,QAA/B2B,OAAOE,KAAK,qBAKZC,WAAaH,OAAOE,KAAK,aACzBE,UAAYJ,OAAOE,KAAK,WACxBG,uBAAyBvB,KAAKE,KAAKX,4BAEvCgC,uBAAuBH,KAAK,mBAAoBC,YAE/BG,MAAbF,UACAC,uBAAuBH,KAAK,kBAAmBE,WAE/CC,uBAAuBE,WAAW,mBAGC,YAAnCP,OAAOE,KAAK,mBACZG,uBAAuBH,KAAK,uBAAuB,GAEnDG,uBAAuBE,WAAW,uBAKtCtC,KAAKS,MAAMF,kBAEXiB,KAAKe,cAAcC,qBAgGvBC,CAA4B5B,KAAMN,kBArDnB,EAACM,KAAMN,0BACpBmC,YAAc7B,KAAKE,KAAKX,iCACxBE,gBAAkBO,KAAKE,KAAKX,sCAClCsC,YAAY1B,GAAG,QAASd,MAAMyC,UAAS,KACT,KAAtBD,YAAYE,MACZvC,kBAAkBC,gBAAiBC,kBAEnCG,iBAAiBJ,gBAAiBC,oBAEvC,MACHD,gBAAgBU,GAAG,SAAS,KACxB0B,YAAYE,IAAI,IAChBlC,iBAAiBJ,gBAAiBC,kBAClCmC,YAAYG,YAyCZC,CAAejC,KAAMN"} \ No newline at end of file diff --git a/blocks/timeline/amd/src/view_nav.js b/blocks/timeline/amd/src/view_nav.js index 3bd745203ac..a57e35f54a1 100644 --- a/blocks/timeline/amd/src/view_nav.js +++ b/blocks/timeline/amd/src/view_nav.js @@ -25,17 +25,17 @@ define( 'jquery', 'core/custom_interaction_events', 'block_timeline/view', - 'core/ajax', 'core/notification', - 'core/utils' + 'core/utils', + 'core_user/repository' ], function( $, CustomEvents, View, - Ajax, Notification, - Utils + Utils, + UserRepository, ) { var SELECTORS = { @@ -49,29 +49,6 @@ function( NO_COURSES_EMPTY_MESSAGE: '[data-region="no-courses-empty-message"]', }; - /** - * Generic handler to persist user preferences - * - * @param {string} type The name of the attribute you're updating - * @param {string} value The value of the attribute you're updating - */ - var updateUserPreferences = function(type, value) { - var request = { - methodname: 'core_user_update_user_preferences', - args: { - preferences: [ - { - type: type, - value: value - } - ] - } - }; - - Ajax.call([request])[0] - .fail(Notification.exception); - }; - /** * Event listener for the day selector ("Next 7 days", "Next 30 days", etc). * @@ -89,7 +66,8 @@ function( // Update the user preference var filtername = $(e.currentTarget).data('filtername'); var type = 'block_timeline_user_filter_preference'; - updateUserPreferences(type, filtername); + UserRepository.setUserPreference(type, filtername) + .catch(Notification.exception); var option = $(e.target).closest(SELECTORS.TIMELINE_DAY_FILTER_OPTION); @@ -151,7 +129,8 @@ function( viewSelector.on(CustomEvents.events.activate, "[data-toggle='tab']", function(e) { var filtername = $(e.currentTarget).data('filtername'); var type = 'block_timeline_user_sort_preference'; - updateUserPreferences(type, filtername); + UserRepository.setUserPreference(type, filtername) + .catch(Notification.exception); }); }; diff --git a/contentbank/amd/build/sort.min.js b/contentbank/amd/build/sort.min.js index 1c17f64ebb7..f0490a9261b 100644 --- a/contentbank/amd/build/sort.min.js +++ b/contentbank/amd/build/sort.min.js @@ -1,10 +1,10 @@ -define("core_contentbank/sort",["exports","./selectors","core/str","core/prefetch","core/ajax","core/notification"],(function(_exports,_selectors,_str,_prefetch,_ajax,_notification){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +define("core_contentbank/sort",["exports","./selectors","core/str","core/prefetch","core/notification","core_user/repository"],(function(_exports,_selectors,_str,_prefetch,_notification,_repository){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Content bank UI actions. * * @module core_contentbank/sort * @copyright 2020 Bas Brands * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_selectors=_interopRequireDefault(_selectors),_prefetch=_interopRequireDefault(_prefetch),_ajax=_interopRequireDefault(_ajax),_notification=_interopRequireDefault(_notification);_exports.init=()=>{const contentBank=document.querySelector(_selectors.default.regions.contentbank);_prefetch.default.prefetchStrings("contentbank",["contentname","uses","lastmodified","size","type","author"]),_prefetch.default.prefetchStrings("moodle",["sortbyx","sortbyxreverse"]),registerListenerEvents(contentBank)};const registerListenerEvents=contentBank=>{contentBank.addEventListener("click",(e=>{const viewList=contentBank.querySelector(_selectors.default.actions.viewlist),viewGrid=contentBank.querySelector(_selectors.default.actions.viewgrid),fileArea=contentBank.querySelector(_selectors.default.regions.filearea),shownItems=fileArea.querySelectorAll(_selectors.default.elements.listitem);if(e.target.closest(_selectors.default.actions.viewgrid)){if(contentBank.classList.remove("view-list"),contentBank.classList.add("view-grid"),fileArea&&shownItems){fileArea.setAttribute("role","list"),shownItems.forEach((listItem=>{listItem.setAttribute("role","listitem"),listItem.querySelectorAll(_selectors.default.elements.cell).forEach((cell=>cell.removeAttribute("role")))}));const heading=fileArea.querySelector(_selectors.default.elements.heading);heading.removeAttribute("role"),heading.querySelectorAll(_selectors.default.elements.cell).forEach((cell=>cell.removeAttribute("role")))}return viewGrid.classList.add("active"),viewList.classList.remove("active"),void setViewListPreference(!1)}if(e.target.closest(_selectors.default.actions.viewlist)){if(contentBank.classList.remove("view-grid"),contentBank.classList.add("view-list"),fileArea&&shownItems){fileArea.setAttribute("role","table"),shownItems.forEach((listItem=>{listItem.setAttribute("role","row"),listItem.querySelectorAll(_selectors.default.elements.cell).forEach((cell=>cell.setAttribute("role","cell")))}));const heading=fileArea.querySelector(_selectors.default.elements.heading);heading.setAttribute("role","row"),heading.querySelectorAll(_selectors.default.elements.cell).forEach((cell=>cell.setAttribute("role","columnheader")))}return viewList.classList.add("active"),viewGrid.classList.remove("active"),void setViewListPreference(!0)}if(fileArea&&shownItems){const sortByName=e.target.closest(_selectors.default.actions.sortname);if(sortByName){const ascending=updateSortButtons(contentBank,sortByName);return void updateSortOrder(fileArea,shownItems,"data-file",ascending)}const sortByUses=e.target.closest(_selectors.default.actions.sortuses);if(sortByUses){const ascending=updateSortButtons(contentBank,sortByUses);return void updateSortOrder(fileArea,shownItems,"data-uses",ascending)}const sortByDate=e.target.closest(_selectors.default.actions.sortdate);if(sortByDate){const ascending=updateSortButtons(contentBank,sortByDate);return void updateSortOrder(fileArea,shownItems,"data-timemodified",ascending)}const sortBySize=e.target.closest(_selectors.default.actions.sortsize);if(sortBySize){const ascending=updateSortButtons(contentBank,sortBySize);return void updateSortOrder(fileArea,shownItems,"data-bytes",ascending)}const sortByType=e.target.closest(_selectors.default.actions.sorttype);if(sortByType){const ascending=updateSortButtons(contentBank,sortByType);return void updateSortOrder(fileArea,shownItems,"data-type",ascending)}const sortByAuthor=e.target.closest(_selectors.default.actions.sortauthor);if(sortByAuthor){const ascending=updateSortButtons(contentBank,sortByAuthor);updateSortOrder(fileArea,shownItems,"data-author",ascending)}}else;}))},setViewListPreference=function(viewList){!1===viewList&&(viewList=null);const request={methodname:"core_user_update_user_preferences",args:{preferences:[{type:"core_contentbank_view_list",value:viewList}]}};return _ajax.default.call([request])[0].catch(_notification.default.exception)},updateSortButtons=(contentBank,sortButton)=>{contentBank.querySelectorAll(_selectors.default.elements.sortbutton).forEach((button=>{button!==sortButton&&(button.classList.remove("dir-asc"),button.classList.remove("dir-desc"),button.classList.add("dir-none"),button.closest(_selectors.default.elements.cell).setAttribute("aria-sort","none"),updateButtonTitle(button,!1))}));let ascending=!0;return sortButton.classList.contains("dir-none")?(sortButton.classList.remove("dir-none"),sortButton.classList.add("dir-asc"),sortButton.closest(_selectors.default.elements.cell).setAttribute("aria-sort","ascending")):sortButton.classList.contains("dir-asc")?(sortButton.classList.remove("dir-asc"),sortButton.classList.add("dir-desc"),sortButton.closest(_selectors.default.elements.cell).setAttribute("aria-sort","descending"),ascending=!1):sortButton.classList.contains("dir-desc")&&(sortButton.classList.remove("dir-desc"),sortButton.classList.add("dir-asc"),sortButton.closest(_selectors.default.elements.cell).setAttribute("aria-sort","ascending")),updateButtonTitle(sortButton,ascending),ascending},updateButtonTitle=(button,ascending)=>{const sortString=ascending?"sortbyxreverse":"sortbyx";return(0,_str.get_string)(button.dataset.string,"contentbank").then((columnName=>(0,_str.get_string)(sortString,"core",columnName))).then((sortByString=>(button.setAttribute("title",sortByString),sortByString))).catch()},updateSortOrder=(fileArea,itemList,attribute,ascending)=>{[].slice.call(itemList).sort((function(a,b){let aa=a.getAttribute(attribute),bb=b.getAttribute(attribute);return isNaN(aa)||(aa=parseInt(aa),bb=parseInt(bb)),ascending?aa>bb?1:-1:aafileArea.appendChild(listItem)))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_selectors=_interopRequireDefault(_selectors),_prefetch=_interopRequireDefault(_prefetch),_notification=_interopRequireDefault(_notification);_exports.init=()=>{const contentBank=document.querySelector(_selectors.default.regions.contentbank);_prefetch.default.prefetchStrings("contentbank",["contentname","uses","lastmodified","size","type","author"]),_prefetch.default.prefetchStrings("moodle",["sortbyx","sortbyxreverse"]),registerListenerEvents(contentBank)};const registerListenerEvents=contentBank=>{contentBank.addEventListener("click",(e=>{const viewList=contentBank.querySelector(_selectors.default.actions.viewlist),viewGrid=contentBank.querySelector(_selectors.default.actions.viewgrid),fileArea=contentBank.querySelector(_selectors.default.regions.filearea),shownItems=fileArea.querySelectorAll(_selectors.default.elements.listitem);if(e.target.closest(_selectors.default.actions.viewgrid)){if(contentBank.classList.remove("view-list"),contentBank.classList.add("view-grid"),fileArea&&shownItems){fileArea.setAttribute("role","list"),shownItems.forEach((listItem=>{listItem.setAttribute("role","listitem"),listItem.querySelectorAll(_selectors.default.elements.cell).forEach((cell=>cell.removeAttribute("role")))}));const heading=fileArea.querySelector(_selectors.default.elements.heading);heading.removeAttribute("role"),heading.querySelectorAll(_selectors.default.elements.cell).forEach((cell=>cell.removeAttribute("role")))}return viewGrid.classList.add("active"),viewList.classList.remove("active"),void setViewListPreference(!1)}if(e.target.closest(_selectors.default.actions.viewlist)){if(contentBank.classList.remove("view-grid"),contentBank.classList.add("view-list"),fileArea&&shownItems){fileArea.setAttribute("role","table"),shownItems.forEach((listItem=>{listItem.setAttribute("role","row"),listItem.querySelectorAll(_selectors.default.elements.cell).forEach((cell=>cell.setAttribute("role","cell")))}));const heading=fileArea.querySelector(_selectors.default.elements.heading);heading.setAttribute("role","row"),heading.querySelectorAll(_selectors.default.elements.cell).forEach((cell=>cell.setAttribute("role","columnheader")))}return viewList.classList.add("active"),viewGrid.classList.remove("active"),void setViewListPreference(!0)}if(fileArea&&shownItems){const sortByName=e.target.closest(_selectors.default.actions.sortname);if(sortByName){const ascending=updateSortButtons(contentBank,sortByName);return void updateSortOrder(fileArea,shownItems,"data-file",ascending)}const sortByUses=e.target.closest(_selectors.default.actions.sortuses);if(sortByUses){const ascending=updateSortButtons(contentBank,sortByUses);return void updateSortOrder(fileArea,shownItems,"data-uses",ascending)}const sortByDate=e.target.closest(_selectors.default.actions.sortdate);if(sortByDate){const ascending=updateSortButtons(contentBank,sortByDate);return void updateSortOrder(fileArea,shownItems,"data-timemodified",ascending)}const sortBySize=e.target.closest(_selectors.default.actions.sortsize);if(sortBySize){const ascending=updateSortButtons(contentBank,sortBySize);return void updateSortOrder(fileArea,shownItems,"data-bytes",ascending)}const sortByType=e.target.closest(_selectors.default.actions.sorttype);if(sortByType){const ascending=updateSortButtons(contentBank,sortByType);return void updateSortOrder(fileArea,shownItems,"data-type",ascending)}const sortByAuthor=e.target.closest(_selectors.default.actions.sortauthor);if(sortByAuthor){const ascending=updateSortButtons(contentBank,sortByAuthor);updateSortOrder(fileArea,shownItems,"data-author",ascending)}}else;}))},setViewListPreference=function(viewList){return!1===viewList&&(viewList=null),(0,_repository.setUserPreference)("core_contentbank_view_list",viewList).catch(_notification.default.exception)},updateSortButtons=(contentBank,sortButton)=>{contentBank.querySelectorAll(_selectors.default.elements.sortbutton).forEach((button=>{button!==sortButton&&(button.classList.remove("dir-asc"),button.classList.remove("dir-desc"),button.classList.add("dir-none"),button.closest(_selectors.default.elements.cell).setAttribute("aria-sort","none"),updateButtonTitle(button,!1))}));let ascending=!0;return sortButton.classList.contains("dir-none")?(sortButton.classList.remove("dir-none"),sortButton.classList.add("dir-asc"),sortButton.closest(_selectors.default.elements.cell).setAttribute("aria-sort","ascending")):sortButton.classList.contains("dir-asc")?(sortButton.classList.remove("dir-asc"),sortButton.classList.add("dir-desc"),sortButton.closest(_selectors.default.elements.cell).setAttribute("aria-sort","descending"),ascending=!1):sortButton.classList.contains("dir-desc")&&(sortButton.classList.remove("dir-desc"),sortButton.classList.add("dir-asc"),sortButton.closest(_selectors.default.elements.cell).setAttribute("aria-sort","ascending")),updateButtonTitle(sortButton,ascending),ascending},updateButtonTitle=(button,ascending)=>{const sortString=ascending?"sortbyxreverse":"sortbyx";return(0,_str.get_string)(button.dataset.string,"contentbank").then((columnName=>(0,_str.get_string)(sortString,"core",columnName))).then((sortByString=>(button.setAttribute("title",sortByString),sortByString))).catch()},updateSortOrder=(fileArea,itemList,attribute,ascending)=>{[].slice.call(itemList).sort((function(a,b){let aa=a.getAttribute(attribute),bb=b.getAttribute(attribute);return isNaN(aa)||(aa=parseInt(aa),bb=parseInt(bb)),ascending?aa>bb?1:-1:aafileArea.appendChild(listItem)))}})); //# sourceMappingURL=sort.min.js.map \ No newline at end of file diff --git a/contentbank/amd/build/sort.min.js.map b/contentbank/amd/build/sort.min.js.map index 2bd1546a85b..15e3494db19 100644 --- a/contentbank/amd/build/sort.min.js.map +++ b/contentbank/amd/build/sort.min.js.map @@ -1 +1 @@ -{"version":3,"file":"sort.min.js","sources":["../src/sort.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 * Content bank UI actions.\n *\n * @module core_contentbank/sort\n * @copyright 2020 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport selectors from './selectors';\nimport {get_string as getString} from 'core/str';\nimport Prefetch from 'core/prefetch';\nimport Ajax from 'core/ajax';\nimport Notification from 'core/notification';\n\n/**\n * Set up the contentbank views.\n *\n * @method init\n */\nexport const init = () => {\n const contentBank = document.querySelector(selectors.regions.contentbank);\n Prefetch.prefetchStrings('contentbank', ['contentname', 'uses', 'lastmodified', 'size', 'type', 'author']);\n Prefetch.prefetchStrings('moodle', ['sortbyx', 'sortbyxreverse']);\n registerListenerEvents(contentBank);\n};\n\n/**\n * Register contentbank related event listeners.\n *\n * @method registerListenerEvents\n * @param {HTMLElement} contentBank The DOM node of the content bank\n */\nconst registerListenerEvents = (contentBank) => {\n\n contentBank.addEventListener('click', e => {\n const viewList = contentBank.querySelector(selectors.actions.viewlist);\n const viewGrid = contentBank.querySelector(selectors.actions.viewgrid);\n const fileArea = contentBank.querySelector(selectors.regions.filearea);\n const shownItems = fileArea.querySelectorAll(selectors.elements.listitem);\n\n // View as Grid button.\n if (e.target.closest(selectors.actions.viewgrid)) {\n contentBank.classList.remove('view-list');\n contentBank.classList.add('view-grid');\n if (fileArea && shownItems) {\n fileArea.setAttribute('role', 'list');\n shownItems.forEach(listItem => {\n listItem.setAttribute('role', 'listitem');\n listItem.querySelectorAll(selectors.elements.cell).forEach(cell => cell.removeAttribute('role'));\n });\n\n const heading = fileArea.querySelector(selectors.elements.heading);\n heading.removeAttribute('role');\n heading.querySelectorAll(selectors.elements.cell).forEach(cell => cell.removeAttribute('role'));\n }\n viewGrid.classList.add('active');\n viewList.classList.remove('active');\n setViewListPreference(false);\n\n return;\n }\n\n // View as List button.\n if (e.target.closest(selectors.actions.viewlist)) {\n contentBank.classList.remove('view-grid');\n contentBank.classList.add('view-list');\n if (fileArea && shownItems) {\n fileArea.setAttribute('role', 'table');\n shownItems.forEach(listItem => {\n listItem.setAttribute('role', 'row');\n listItem.querySelectorAll(selectors.elements.cell).forEach(cell => cell.setAttribute('role', 'cell'));\n });\n\n const heading = fileArea.querySelector(selectors.elements.heading);\n heading.setAttribute('role', 'row');\n heading.querySelectorAll(selectors.elements.cell).forEach(cell => cell.setAttribute('role', 'columnheader'));\n }\n viewList.classList.add('active');\n viewGrid.classList.remove('active');\n setViewListPreference(true);\n\n return;\n }\n\n if (fileArea && shownItems) {\n\n // Sort by file name alphabetical\n const sortByName = e.target.closest(selectors.actions.sortname);\n if (sortByName) {\n const ascending = updateSortButtons(contentBank, sortByName);\n updateSortOrder(fileArea, shownItems, 'data-file', ascending);\n return;\n }\n\n // Sort by uses.\n const sortByUses = e.target.closest(selectors.actions.sortuses);\n if (sortByUses) {\n const ascending = updateSortButtons(contentBank, sortByUses);\n updateSortOrder(fileArea, shownItems, 'data-uses', ascending);\n return;\n }\n\n // Sort by date.\n const sortByDate = e.target.closest(selectors.actions.sortdate);\n if (sortByDate) {\n const ascending = updateSortButtons(contentBank, sortByDate);\n updateSortOrder(fileArea, shownItems, 'data-timemodified', ascending);\n return;\n }\n\n // Sort by size.\n const sortBySize = e.target.closest(selectors.actions.sortsize);\n if (sortBySize) {\n const ascending = updateSortButtons(contentBank, sortBySize);\n updateSortOrder(fileArea, shownItems, 'data-bytes', ascending);\n return;\n }\n\n // Sort by type.\n const sortByType = e.target.closest(selectors.actions.sorttype);\n if (sortByType) {\n const ascending = updateSortButtons(contentBank, sortByType);\n updateSortOrder(fileArea, shownItems, 'data-type', ascending);\n return;\n }\n\n // Sort by author.\n const sortByAuthor = e.target.closest(selectors.actions.sortauthor);\n if (sortByAuthor) {\n const ascending = updateSortButtons(contentBank, sortByAuthor);\n updateSortOrder(fileArea, shownItems, 'data-author', ascending);\n }\n return;\n }\n });\n};\n\n\n/**\n * Set the contentbank user preference in list view\n *\n * @param {Bool} viewList view ContentBank as list.\n * @return {Promise} Repository promise.\n */\nconst setViewListPreference = function(viewList) {\n\n // If the given status is not hidden, the preference has to be deleted with a null value.\n if (viewList === false) {\n viewList = null;\n }\n\n const request = {\n methodname: 'core_user_update_user_preferences',\n args: {\n preferences: [\n {\n type: 'core_contentbank_view_list',\n value: viewList\n }\n ]\n }\n };\n\n return Ajax.call([request])[0].catch(Notification.exception);\n};\n\n/**\n * Update the sort button view.\n *\n * @method updateSortButtons\n * @param {HTMLElement} contentBank The DOM node of the contentbank button\n * @param {HTMLElement} sortButton The DOM node of the sort button\n * @return {Bool} sort ascending\n */\nconst updateSortButtons = (contentBank, sortButton) => {\n const sortButtons = contentBank.querySelectorAll(selectors.elements.sortbutton);\n\n sortButtons.forEach((button) => {\n if (button !== sortButton) {\n button.classList.remove('dir-asc');\n button.classList.remove('dir-desc');\n button.classList.add('dir-none');\n\n button.closest(selectors.elements.cell).setAttribute('aria-sort', 'none');\n\n updateButtonTitle(button, false);\n }\n });\n\n let ascending = true;\n\n if (sortButton.classList.contains('dir-none')) {\n sortButton.classList.remove('dir-none');\n sortButton.classList.add('dir-asc');\n sortButton.closest(selectors.elements.cell).setAttribute('aria-sort', 'ascending');\n } else if (sortButton.classList.contains('dir-asc')) {\n sortButton.classList.remove('dir-asc');\n sortButton.classList.add('dir-desc');\n sortButton.closest(selectors.elements.cell).setAttribute('aria-sort', 'descending');\n ascending = false;\n } else if (sortButton.classList.contains('dir-desc')) {\n sortButton.classList.remove('dir-desc');\n sortButton.classList.add('dir-asc');\n sortButton.closest(selectors.elements.cell).setAttribute('aria-sort', 'ascending');\n }\n\n updateButtonTitle(sortButton, ascending);\n\n return ascending;\n};\n\n/**\n * Update the button title.\n *\n * @method updateButtonTitle\n * @param {HTMLElement} button Button to update\n * @param {Bool} ascending Sort direction\n * @return {Promise} string promise\n */\nconst updateButtonTitle = (button, ascending) => {\n\n const sortString = (ascending ? 'sortbyxreverse' : 'sortbyx');\n\n return getString(button.dataset.string, 'contentbank')\n .then(columnName => {\n return getString(sortString, 'core', columnName);\n })\n .then(sortByString => {\n button.setAttribute('title', sortByString);\n return sortByString;\n })\n .catch();\n};\n\n/**\n * Update the sort order of the itemlist and update the DOM\n *\n * @method updateSortOrder\n * @param {HTMLElement} fileArea the Dom container for the itemlist\n * @param {Array} itemList Nodelist of Dom elements\n * @param {String} attribute the attribut to sort on\n * @param {Bool} ascending Sort Ascending\n */\nconst updateSortOrder = (fileArea, itemList, attribute, ascending) => {\n const sortList = [].slice.call(itemList).sort(function(a, b) {\n\n let aa = a.getAttribute(attribute);\n let bb = b.getAttribute(attribute);\n if (!isNaN(aa)) {\n aa = parseInt(aa);\n bb = parseInt(bb);\n }\n\n if (ascending) {\n return aa > bb ? 1 : -1;\n } else {\n return aa < bb ? 1 : -1;\n }\n });\n sortList.forEach(listItem => fileArea.appendChild(listItem));\n};\n"],"names":["contentBank","document","querySelector","selectors","regions","contentbank","prefetchStrings","registerListenerEvents","addEventListener","e","viewList","actions","viewlist","viewGrid","viewgrid","fileArea","filearea","shownItems","querySelectorAll","elements","listitem","target","closest","classList","remove","add","setAttribute","forEach","listItem","cell","removeAttribute","heading","setViewListPreference","sortByName","sortname","ascending","updateSortButtons","updateSortOrder","sortByUses","sortuses","sortByDate","sortdate","sortBySize","sortsize","sortByType","sorttype","sortByAuthor","sortauthor","request","methodname","args","preferences","type","value","Ajax","call","catch","Notification","exception","sortButton","sortbutton","button","updateButtonTitle","contains","sortString","dataset","string","then","columnName","sortByString","itemList","attribute","slice","sort","a","b","aa","getAttribute","bb","isNaN","parseInt","appendChild"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"sort.min.js","sources":["../src/sort.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 * Content bank UI actions.\n *\n * @module core_contentbank/sort\n * @copyright 2020 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport selectors from './selectors';\nimport {get_string as getString} from 'core/str';\nimport Prefetch from 'core/prefetch';\nimport Notification from 'core/notification';\nimport {setUserPreference} from 'core_user/repository';\n\n/**\n * Set up the contentbank views.\n *\n * @method init\n */\nexport const init = () => {\n const contentBank = document.querySelector(selectors.regions.contentbank);\n Prefetch.prefetchStrings('contentbank', ['contentname', 'uses', 'lastmodified', 'size', 'type', 'author']);\n Prefetch.prefetchStrings('moodle', ['sortbyx', 'sortbyxreverse']);\n registerListenerEvents(contentBank);\n};\n\n/**\n * Register contentbank related event listeners.\n *\n * @method registerListenerEvents\n * @param {HTMLElement} contentBank The DOM node of the content bank\n */\nconst registerListenerEvents = (contentBank) => {\n\n contentBank.addEventListener('click', e => {\n const viewList = contentBank.querySelector(selectors.actions.viewlist);\n const viewGrid = contentBank.querySelector(selectors.actions.viewgrid);\n const fileArea = contentBank.querySelector(selectors.regions.filearea);\n const shownItems = fileArea.querySelectorAll(selectors.elements.listitem);\n\n // View as Grid button.\n if (e.target.closest(selectors.actions.viewgrid)) {\n contentBank.classList.remove('view-list');\n contentBank.classList.add('view-grid');\n if (fileArea && shownItems) {\n fileArea.setAttribute('role', 'list');\n shownItems.forEach(listItem => {\n listItem.setAttribute('role', 'listitem');\n listItem.querySelectorAll(selectors.elements.cell).forEach(cell => cell.removeAttribute('role'));\n });\n\n const heading = fileArea.querySelector(selectors.elements.heading);\n heading.removeAttribute('role');\n heading.querySelectorAll(selectors.elements.cell).forEach(cell => cell.removeAttribute('role'));\n }\n viewGrid.classList.add('active');\n viewList.classList.remove('active');\n setViewListPreference(false);\n\n return;\n }\n\n // View as List button.\n if (e.target.closest(selectors.actions.viewlist)) {\n contentBank.classList.remove('view-grid');\n contentBank.classList.add('view-list');\n if (fileArea && shownItems) {\n fileArea.setAttribute('role', 'table');\n shownItems.forEach(listItem => {\n listItem.setAttribute('role', 'row');\n listItem.querySelectorAll(selectors.elements.cell).forEach(cell => cell.setAttribute('role', 'cell'));\n });\n\n const heading = fileArea.querySelector(selectors.elements.heading);\n heading.setAttribute('role', 'row');\n heading.querySelectorAll(selectors.elements.cell).forEach(cell => cell.setAttribute('role', 'columnheader'));\n }\n viewList.classList.add('active');\n viewGrid.classList.remove('active');\n setViewListPreference(true);\n\n return;\n }\n\n if (fileArea && shownItems) {\n\n // Sort by file name alphabetical\n const sortByName = e.target.closest(selectors.actions.sortname);\n if (sortByName) {\n const ascending = updateSortButtons(contentBank, sortByName);\n updateSortOrder(fileArea, shownItems, 'data-file', ascending);\n return;\n }\n\n // Sort by uses.\n const sortByUses = e.target.closest(selectors.actions.sortuses);\n if (sortByUses) {\n const ascending = updateSortButtons(contentBank, sortByUses);\n updateSortOrder(fileArea, shownItems, 'data-uses', ascending);\n return;\n }\n\n // Sort by date.\n const sortByDate = e.target.closest(selectors.actions.sortdate);\n if (sortByDate) {\n const ascending = updateSortButtons(contentBank, sortByDate);\n updateSortOrder(fileArea, shownItems, 'data-timemodified', ascending);\n return;\n }\n\n // Sort by size.\n const sortBySize = e.target.closest(selectors.actions.sortsize);\n if (sortBySize) {\n const ascending = updateSortButtons(contentBank, sortBySize);\n updateSortOrder(fileArea, shownItems, 'data-bytes', ascending);\n return;\n }\n\n // Sort by type.\n const sortByType = e.target.closest(selectors.actions.sorttype);\n if (sortByType) {\n const ascending = updateSortButtons(contentBank, sortByType);\n updateSortOrder(fileArea, shownItems, 'data-type', ascending);\n return;\n }\n\n // Sort by author.\n const sortByAuthor = e.target.closest(selectors.actions.sortauthor);\n if (sortByAuthor) {\n const ascending = updateSortButtons(contentBank, sortByAuthor);\n updateSortOrder(fileArea, shownItems, 'data-author', ascending);\n }\n return;\n }\n });\n};\n\n\n/**\n * Set the contentbank user preference in list view\n *\n * @param {Bool} viewList view ContentBank as list.\n * @return {Promise} Repository promise.\n */\nconst setViewListPreference = function(viewList) {\n\n // If the given status is not hidden, the preference has to be deleted with a null value.\n if (viewList === false) {\n viewList = null;\n }\n\n return setUserPreference('core_contentbank_view_list', viewList)\n .catch(Notification.exception);\n};\n\n/**\n * Update the sort button view.\n *\n * @method updateSortButtons\n * @param {HTMLElement} contentBank The DOM node of the contentbank button\n * @param {HTMLElement} sortButton The DOM node of the sort button\n * @return {Bool} sort ascending\n */\nconst updateSortButtons = (contentBank, sortButton) => {\n const sortButtons = contentBank.querySelectorAll(selectors.elements.sortbutton);\n\n sortButtons.forEach((button) => {\n if (button !== sortButton) {\n button.classList.remove('dir-asc');\n button.classList.remove('dir-desc');\n button.classList.add('dir-none');\n\n button.closest(selectors.elements.cell).setAttribute('aria-sort', 'none');\n\n updateButtonTitle(button, false);\n }\n });\n\n let ascending = true;\n\n if (sortButton.classList.contains('dir-none')) {\n sortButton.classList.remove('dir-none');\n sortButton.classList.add('dir-asc');\n sortButton.closest(selectors.elements.cell).setAttribute('aria-sort', 'ascending');\n } else if (sortButton.classList.contains('dir-asc')) {\n sortButton.classList.remove('dir-asc');\n sortButton.classList.add('dir-desc');\n sortButton.closest(selectors.elements.cell).setAttribute('aria-sort', 'descending');\n ascending = false;\n } else if (sortButton.classList.contains('dir-desc')) {\n sortButton.classList.remove('dir-desc');\n sortButton.classList.add('dir-asc');\n sortButton.closest(selectors.elements.cell).setAttribute('aria-sort', 'ascending');\n }\n\n updateButtonTitle(sortButton, ascending);\n\n return ascending;\n};\n\n/**\n * Update the button title.\n *\n * @method updateButtonTitle\n * @param {HTMLElement} button Button to update\n * @param {Bool} ascending Sort direction\n * @return {Promise} string promise\n */\nconst updateButtonTitle = (button, ascending) => {\n\n const sortString = (ascending ? 'sortbyxreverse' : 'sortbyx');\n\n return getString(button.dataset.string, 'contentbank')\n .then(columnName => {\n return getString(sortString, 'core', columnName);\n })\n .then(sortByString => {\n button.setAttribute('title', sortByString);\n return sortByString;\n })\n .catch();\n};\n\n/**\n * Update the sort order of the itemlist and update the DOM\n *\n * @method updateSortOrder\n * @param {HTMLElement} fileArea the Dom container for the itemlist\n * @param {Array} itemList Nodelist of Dom elements\n * @param {String} attribute the attribut to sort on\n * @param {Bool} ascending Sort Ascending\n */\nconst updateSortOrder = (fileArea, itemList, attribute, ascending) => {\n const sortList = [].slice.call(itemList).sort(function(a, b) {\n\n let aa = a.getAttribute(attribute);\n let bb = b.getAttribute(attribute);\n if (!isNaN(aa)) {\n aa = parseInt(aa);\n bb = parseInt(bb);\n }\n\n if (ascending) {\n return aa > bb ? 1 : -1;\n } else {\n return aa < bb ? 1 : -1;\n }\n });\n sortList.forEach(listItem => fileArea.appendChild(listItem));\n};\n"],"names":["contentBank","document","querySelector","selectors","regions","contentbank","prefetchStrings","registerListenerEvents","addEventListener","e","viewList","actions","viewlist","viewGrid","viewgrid","fileArea","filearea","shownItems","querySelectorAll","elements","listitem","target","closest","classList","remove","add","setAttribute","forEach","listItem","cell","removeAttribute","heading","setViewListPreference","sortByName","sortname","ascending","updateSortButtons","updateSortOrder","sortByUses","sortuses","sortByDate","sortdate","sortBySize","sortsize","sortByType","sorttype","sortByAuthor","sortauthor","catch","Notification","exception","sortButton","sortbutton","button","updateButtonTitle","contains","sortString","dataset","string","then","columnName","sortByString","itemList","attribute","slice","call","sort","a","b","aa","getAttribute","bb","isNaN","parseInt","appendChild"],"mappings":"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"} \ No newline at end of file diff --git a/contentbank/amd/src/sort.js b/contentbank/amd/src/sort.js index a2ee79fd738..fa919c7c366 100644 --- a/contentbank/amd/src/sort.js +++ b/contentbank/amd/src/sort.js @@ -24,8 +24,8 @@ import selectors from './selectors'; import {get_string as getString} from 'core/str'; import Prefetch from 'core/prefetch'; -import Ajax from 'core/ajax'; import Notification from 'core/notification'; +import {setUserPreference} from 'core_user/repository'; /** * Set up the contentbank views. @@ -164,19 +164,8 @@ const setViewListPreference = function(viewList) { viewList = null; } - const request = { - methodname: 'core_user_update_user_preferences', - args: { - preferences: [ - { - type: 'core_contentbank_view_list', - value: viewList - } - ] - } - }; - - return Ajax.call([request])[0].catch(Notification.exception); + return setUserPreference('core_contentbank_view_list', viewList) + .catch(Notification.exception); }; /** diff --git a/lib/amd/build/paged_content_factory.min.js b/lib/amd/build/paged_content_factory.min.js index c9c01e8a22e..b412da1062b 100644 --- a/lib/amd/build/paged_content_factory.min.js +++ b/lib/amd/build/paged_content_factory.min.js @@ -5,6 +5,6 @@ * @copyright 2018 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("core/paged_content_factory",["jquery","core/templates","core/notification","core/paged_content","core/paged_content_events","core/pubsub","core/ajax"],(function($,Templates,Notification,PagedContent,PagedContentEvents,PubSub,Ajax){var TEMPLATES_PAGED_CONTENT="core/paged_content",DEFAULT_ITEMS_PER_PAGE_SINGLE=25,DEFAULT_ITEMS_PER_PAGE_ARRAY=[25,50,100,0],DEFAULT_MAX_PAGES=3,buildItemsPerPagePagingBarContext=function(itemsPerPage){var context=[];$.isArray(itemsPerPage)?(context=itemsPerPage.map((function(num){return"number"==typeof num?{value:num,active:!1}:num}))).filter((function(item){return item.active})).length||(context[0].active=!0):context=[{value:itemsPerPage,active:!0}];return context},buildPagingBarTemplateContext=function(numberOfItems,itemsPerPage){return numberOfItems?function(numberOfItems,itemsPerPage){null===itemsPerPage&&(itemsPerPage=DEFAULT_ITEMS_PER_PAGE_SINGLE),$.isArray(itemsPerPage)&&(itemsPerPage=itemsPerPage[0]);var context={showitemsperpageselector:!1,itemsperpage:[{value:35,active:!0}],previous:!0,next:!0,activepagenumber:1,hidecontrolonsinglepage:!0,pages:[]};context.itemsperpage=buildItemsPerPagePagingBarContext(itemsPerPage);for(var numberOfPages=function(numberOfItems,itemsPerPage){var numberOfPages=1;if(numberOfItems>0){var partial=numberOfItems%itemsPerPage;numberOfPages=partial?(numberOfItems-=partial)/itemsPerPage+1:numberOfItems/itemsPerPage}return numberOfPages}(numberOfItems,itemsPerPage),i=1;i<=numberOfPages;i++){var page={number:i,page:""+i};1===i&&(page.active=!0),context.pages.push(page)}return context.barsize=10,context}(numberOfItems,itemsPerPage):function(itemsPerPage){null===itemsPerPage&&(itemsPerPage=DEFAULT_ITEMS_PER_PAGE_ARRAY);var context={showitemsperpageselector:!1,itemsperpage:[{value:35,active:!0}],previous:!0,next:!0,activepagenumber:1,hidecontrolonsinglepage:!0,pages:[]};return context.itemsperpage=buildItemsPerPagePagingBarContext(itemsPerPage),context.showitemsperpageselector=$.isArray(itemsPerPage)&&itemsPerPage.length>1,context}(itemsPerPage)},buildTemplateContext=function(numberOfItems,itemsPerPage,config){var context={pagingbar:!1,pagingdropdown:!1,skipjs:!0,ignorecontrolwhileloading:!0,controlplacementbottom:!1};return config.hasOwnProperty("ignoreControlWhileLoading")&&(context.ignorecontrolwhileloading=config.ignoreControlWhileLoading),config.hasOwnProperty("controlPlacementBottom")&&(context.controlplacementbottom=config.controlPlacementBottom),config.hasOwnProperty("hideControlOnSinglePage")&&(context.hidecontrolonsinglepage=config.hideControlOnSinglePage),config.hasOwnProperty("ariaLabels")&&(context.arialabels=config.ariaLabels),config.hasOwnProperty("dropdown")&&config.dropdown?context.pagingdropdown=function(itemsPerPage,config){if(null===itemsPerPage&&(itemsPerPage=DEFAULT_ITEMS_PER_PAGE_SINGLE),$.isArray(itemsPerPage))return{options:itemsPerPage};var context={options:[]},totalItems=0,lastIncrease=0,maxPages=DEFAULT_MAX_PAGES;config.hasOwnProperty("maxPages")&&(maxPages=config.maxPages);for(var i=1;i<=maxPages;i++){var itemCount=0;i<=2?(itemCount=itemsPerPage,lastIncrease=itemsPerPage):itemCount=lastIncrease*=2;var option={itemcount:itemCount,content:totalItems+=itemCount};1===i&&(option.active=!0),context.options.push(option)}return context}(itemsPerPage,config):(context.pagingbar=buildPagingBarTemplateContext(numberOfItems,itemsPerPage),config.hasOwnProperty("showFirstLast")&&config.showFirstLast&&(context.pagingbar.first=!0,context.pagingbar.last=!0)),context},createWithTotalAndLimit=function(numberOfItems,itemsPerPage,renderPagesContentCallback,config){config=config||{};var deferred=$.Deferred(),templateContext=buildTemplateContext(numberOfItems,itemsPerPage,config);return Templates.render(TEMPLATES_PAGED_CONTENT,templateContext).then((function(html,js){var id=(html=$(html)).attr("id");config.hasOwnProperty("eventNamespace")&&(id=config.eventNamespace);var container=html;PagedContent.init(container,renderPagesContentCallback,id),registerEvents(id,config),deferred.resolve(html,js)})).fail((function(exception){deferred.reject(exception)})).fail(Notification.exception),deferred.promise()},registerEvents=function(namespace,config){var persistentLimitKey;config.hasOwnProperty("persistentLimitKey")&&PubSub.subscribe(namespace+PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT,(persistentLimitKey=config.persistentLimitKey,function(limit){var request={methodname:"core_user_update_user_preferences",args:{preferences:[{type:persistentLimitKey,value:limit}]}};Ajax.call([request])}))};return{create:function(renderPagesContentCallback,config){return createWithTotalAndLimit(null,null,renderPagesContentCallback,config)},createWithLimit:function(itemsPerPage,renderPagesContentCallback,config){return createWithTotalAndLimit(null,itemsPerPage,renderPagesContentCallback,config)},createWithTotalAndLimit:createWithTotalAndLimit,createFromStaticList:function(contentItems,itemsPerPage,renderContentCallback,config){void 0===config&&(config={});var numberOfItems=contentItems.length;return createWithTotalAndLimit(numberOfItems,itemsPerPage,(function(pagesData){var contentToRender=[];return pagesData.forEach((function(pageData){var begin=pageData.offset,end=pageData.limit?begin+pageData.limit:numberOfItems,items=contentItems.slice(begin,end);contentToRender.push(items)})),renderContentCallback(contentToRender)}),config)},createFromAjax:createWithTotalAndLimit,resetLastPageNumber:function(id,lastPageNumber){PubSub.publish(id+PagedContentEvents.ALL_ITEMS_LOADED,lastPageNumber)}}})); +define("core/paged_content_factory",["jquery","core/templates","core/notification","core/paged_content","core/paged_content_events","core/pubsub","core_user/repository"],(function($,Templates,Notification,PagedContent,PagedContentEvents,PubSub,UserRepository){var TEMPLATES_PAGED_CONTENT="core/paged_content",DEFAULT_ITEMS_PER_PAGE_SINGLE=25,DEFAULT_ITEMS_PER_PAGE_ARRAY=[25,50,100,0],DEFAULT_MAX_PAGES=3,buildItemsPerPagePagingBarContext=function(itemsPerPage){var context=[];$.isArray(itemsPerPage)?(context=itemsPerPage.map((function(num){return"number"==typeof num?{value:num,active:!1}:num}))).filter((function(item){return item.active})).length||(context[0].active=!0):context=[{value:itemsPerPage,active:!0}];return context},buildPagingBarTemplateContext=function(numberOfItems,itemsPerPage){return numberOfItems?function(numberOfItems,itemsPerPage){null===itemsPerPage&&(itemsPerPage=DEFAULT_ITEMS_PER_PAGE_SINGLE),$.isArray(itemsPerPage)&&(itemsPerPage=itemsPerPage[0]);var context={showitemsperpageselector:!1,itemsperpage:[{value:35,active:!0}],previous:!0,next:!0,activepagenumber:1,hidecontrolonsinglepage:!0,pages:[]};context.itemsperpage=buildItemsPerPagePagingBarContext(itemsPerPage);for(var numberOfPages=function(numberOfItems,itemsPerPage){var numberOfPages=1;if(numberOfItems>0){var partial=numberOfItems%itemsPerPage;numberOfPages=partial?(numberOfItems-=partial)/itemsPerPage+1:numberOfItems/itemsPerPage}return numberOfPages}(numberOfItems,itemsPerPage),i=1;i<=numberOfPages;i++){var page={number:i,page:""+i};1===i&&(page.active=!0),context.pages.push(page)}return context.barsize=10,context}(numberOfItems,itemsPerPage):function(itemsPerPage){null===itemsPerPage&&(itemsPerPage=DEFAULT_ITEMS_PER_PAGE_ARRAY);var context={showitemsperpageselector:!1,itemsperpage:[{value:35,active:!0}],previous:!0,next:!0,activepagenumber:1,hidecontrolonsinglepage:!0,pages:[]};return context.itemsperpage=buildItemsPerPagePagingBarContext(itemsPerPage),context.showitemsperpageselector=$.isArray(itemsPerPage)&&itemsPerPage.length>1,context}(itemsPerPage)},buildTemplateContext=function(numberOfItems,itemsPerPage,config){var context={pagingbar:!1,pagingdropdown:!1,skipjs:!0,ignorecontrolwhileloading:!0,controlplacementbottom:!1};return config.hasOwnProperty("ignoreControlWhileLoading")&&(context.ignorecontrolwhileloading=config.ignoreControlWhileLoading),config.hasOwnProperty("controlPlacementBottom")&&(context.controlplacementbottom=config.controlPlacementBottom),config.hasOwnProperty("hideControlOnSinglePage")&&(context.hidecontrolonsinglepage=config.hideControlOnSinglePage),config.hasOwnProperty("ariaLabels")&&(context.arialabels=config.ariaLabels),config.hasOwnProperty("dropdown")&&config.dropdown?context.pagingdropdown=function(itemsPerPage,config){if(null===itemsPerPage&&(itemsPerPage=DEFAULT_ITEMS_PER_PAGE_SINGLE),$.isArray(itemsPerPage))return{options:itemsPerPage};var context={options:[]},totalItems=0,lastIncrease=0,maxPages=DEFAULT_MAX_PAGES;config.hasOwnProperty("maxPages")&&(maxPages=config.maxPages);for(var i=1;i<=maxPages;i++){var itemCount=0;i<=2?(itemCount=itemsPerPage,lastIncrease=itemsPerPage):itemCount=lastIncrease*=2;var option={itemcount:itemCount,content:totalItems+=itemCount};1===i&&(option.active=!0),context.options.push(option)}return context}(itemsPerPage,config):(context.pagingbar=buildPagingBarTemplateContext(numberOfItems,itemsPerPage),config.hasOwnProperty("showFirstLast")&&config.showFirstLast&&(context.pagingbar.first=!0,context.pagingbar.last=!0)),context},createWithTotalAndLimit=function(numberOfItems,itemsPerPage,renderPagesContentCallback,config){config=config||{};var deferred=$.Deferred(),templateContext=buildTemplateContext(numberOfItems,itemsPerPage,config);return Templates.render(TEMPLATES_PAGED_CONTENT,templateContext).then((function(html,js){var id=(html=$(html)).attr("id");config.hasOwnProperty("eventNamespace")&&(id=config.eventNamespace);var container=html;PagedContent.init(container,renderPagesContentCallback,id),registerEvents(id,config),deferred.resolve(html,js)})).fail((function(exception){deferred.reject(exception)})).fail(Notification.exception),deferred.promise()},registerEvents=function(namespace,config){var persistentLimitKey;config.hasOwnProperty("persistentLimitKey")&&PubSub.subscribe(namespace+PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT,(persistentLimitKey=config.persistentLimitKey,function(limit){UserRepository.setUserPreference(persistentLimitKey,limit)}))};return{create:function(renderPagesContentCallback,config){return createWithTotalAndLimit(null,null,renderPagesContentCallback,config)},createWithLimit:function(itemsPerPage,renderPagesContentCallback,config){return createWithTotalAndLimit(null,itemsPerPage,renderPagesContentCallback,config)},createWithTotalAndLimit:createWithTotalAndLimit,createFromStaticList:function(contentItems,itemsPerPage,renderContentCallback,config){void 0===config&&(config={});var numberOfItems=contentItems.length;return createWithTotalAndLimit(numberOfItems,itemsPerPage,(function(pagesData){var contentToRender=[];return pagesData.forEach((function(pageData){var begin=pageData.offset,end=pageData.limit?begin+pageData.limit:numberOfItems,items=contentItems.slice(begin,end);contentToRender.push(items)})),renderContentCallback(contentToRender)}),config)},createFromAjax:createWithTotalAndLimit,resetLastPageNumber:function(id,lastPageNumber){PubSub.publish(id+PagedContentEvents.ALL_ITEMS_LOADED,lastPageNumber)}}})); //# sourceMappingURL=paged_content_factory.min.js.map \ No newline at end of file diff --git a/lib/amd/build/paged_content_factory.min.js.map b/lib/amd/build/paged_content_factory.min.js.map index 8a272d19000..1b28b9e5d9b 100644 --- a/lib/amd/build/paged_content_factory.min.js.map +++ b/lib/amd/build/paged_content_factory.min.js.map @@ -1 +1 @@ -{"version":3,"file":"paged_content_factory.min.js","sources":["../src/paged_content_factory.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 * Factory to create a paged content widget.\n *\n * @module core/paged_content_factory\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/templates',\n 'core/notification',\n 'core/paged_content',\n 'core/paged_content_events',\n 'core/pubsub',\n 'core/ajax'\n],\nfunction(\n $,\n Templates,\n Notification,\n PagedContent,\n PagedContentEvents,\n PubSub,\n Ajax\n) {\n var TEMPLATES = {\n PAGED_CONTENT: 'core/paged_content'\n };\n\n var DEFAULT = {\n ITEMS_PER_PAGE_SINGLE: 25,\n ITEMS_PER_PAGE_ARRAY: [25, 50, 100, 0],\n MAX_PAGES: 3\n };\n\n /**\n * Get the default context to render the paged content mustache\n * template.\n *\n * @return {object}\n */\n var getDefaultTemplateContext = function() {\n return {\n pagingbar: false,\n pagingdropdown: false,\n skipjs: true,\n ignorecontrolwhileloading: true,\n controlplacementbottom: false\n };\n };\n\n /**\n * Get the default context to render the paging bar mustache template.\n *\n * @return {object}\n */\n var getDefaultPagingBarTemplateContext = function() {\n return {\n showitemsperpageselector: false,\n itemsperpage: [{value: 35, active: true}],\n previous: true,\n next: true,\n activepagenumber: 1,\n hidecontrolonsinglepage: true,\n pages: []\n };\n };\n\n /**\n * Calculate the number of pages required for the given number of items and\n * how many of each item should appear on a page.\n *\n * @param {Number} numberOfItems How many items in total.\n * @param {Number} itemsPerPage How many items will be shown per page.\n * @return {Number} The number of pages required.\n */\n var calculateNumberOfPages = function(numberOfItems, itemsPerPage) {\n var numberOfPages = 1;\n\n if (numberOfItems > 0) {\n var partial = numberOfItems % itemsPerPage;\n\n if (partial) {\n numberOfItems -= partial;\n numberOfPages = (numberOfItems / itemsPerPage) + 1;\n } else {\n numberOfPages = numberOfItems / itemsPerPage;\n }\n }\n\n return numberOfPages;\n };\n\n /**\n * Build the context for the paging bar template when we have a known number\n * of items.\n *\n * @param {Number} numberOfItems How many items in total.\n * @param {Number} itemsPerPage How many items will be shown per page.\n * @return {object} Mustache template\n */\n var buildPagingBarTemplateContextKnownLength = function(numberOfItems, itemsPerPage) {\n if (itemsPerPage === null) {\n itemsPerPage = DEFAULT.ITEMS_PER_PAGE_SINGLE;\n }\n\n if ($.isArray(itemsPerPage)) {\n // If we're given a total number of pages then we don't support a variable\n // set of items per page so just use the first one.\n itemsPerPage = itemsPerPage[0];\n }\n\n var context = getDefaultPagingBarTemplateContext();\n context.itemsperpage = buildItemsPerPagePagingBarContext(itemsPerPage);\n var numberOfPages = calculateNumberOfPages(numberOfItems, itemsPerPage);\n\n for (var i = 1; i <= numberOfPages; i++) {\n var page = {\n number: i,\n page: \"\" + i,\n };\n\n // Make the first page active by default.\n if (i === 1) {\n page.active = true;\n }\n\n context.pages.push(page);\n }\n\n context.barsize = 10;\n return context;\n };\n\n /**\n * Convert the itemsPerPage value into a format applicable for the mustache template.\n * The given value can be either a single integer or an array of integers / objects.\n *\n * E.g.\n * In: [5, 10]\n * out: [{value: 5, active: true}, {value: 10, active: false}]\n *\n * In: [5, {value: 10, active: true}]\n * Out: [{value: 5, active: false}, {value: 10, active: true}]\n *\n * In: [{value: 5, active: false}, {value: 10, active: true}]\n * Out: [{value: 5, active: false}, {value: 10, active: true}]\n *\n * @param {int|int[]} itemsPerPage Options for number of items per page.\n * @return {int|array}\n */\n var buildItemsPerPagePagingBarContext = function(itemsPerPage) {\n var context = [];\n\n if ($.isArray(itemsPerPage)) {\n // Convert the array into a format accepted by the template.\n context = itemsPerPage.map(function(num) {\n if (typeof num === 'number') {\n // If the item is just a plain number then convert it into\n // an object with value and active keys.\n return {\n value: num,\n active: false\n };\n } else {\n // Otherwise we assume the caller has specified things correctly.\n return num;\n }\n });\n\n var activeItems = context.filter(function(item) {\n return item.active;\n });\n\n // Default the first item to active if one hasn't been specified.\n if (!activeItems.length) {\n context[0].active = true;\n }\n } else {\n // Convert the integer into a format accepted by the template.\n context = [{value: itemsPerPage, active: true}];\n }\n\n return context;\n };\n\n /**\n * Build the context for the paging bar template when we have an unknown\n * number of items.\n *\n * @param {Number} itemsPerPage How many items will be shown per page.\n * @return {object} Mustache template\n */\n var buildPagingBarTemplateContextUnknownLength = function(itemsPerPage) {\n if (itemsPerPage === null) {\n itemsPerPage = DEFAULT.ITEMS_PER_PAGE_ARRAY;\n }\n\n var context = getDefaultPagingBarTemplateContext();\n context.itemsperpage = buildItemsPerPagePagingBarContext(itemsPerPage);\n // Only display the items per page selector if there is more than one to choose from.\n context.showitemsperpageselector = $.isArray(itemsPerPage) && itemsPerPage.length > 1;\n\n return context;\n };\n\n /**\n * Build the context to render the paging bar template with based on the number\n * of pages to show.\n *\n * @param {int|null} numberOfItems How many items are there total.\n * @param {int|null} itemsPerPage How many items will be shown per page.\n * @return {object} The template context.\n */\n var buildPagingBarTemplateContext = function(numberOfItems, itemsPerPage) {\n if (numberOfItems) {\n return buildPagingBarTemplateContextKnownLength(numberOfItems, itemsPerPage);\n } else {\n return buildPagingBarTemplateContextUnknownLength(itemsPerPage);\n }\n };\n\n /**\n * Build the context to render the paging dropdown template based on the number\n * of pages to show and items per page.\n *\n * This control is rendered with a gradual increase of the items per page to\n * limit the number of pages in the dropdown. Each page will show twice as much\n * as the previous page (except for the first two pages).\n *\n * By default there will only be 4 pages shown (including the \"All\" option) unless\n * a different number of pages is defined using the maxPages config value.\n *\n * For example:\n * Items per page = 25\n * Would render a dropdown will 4 options:\n * 25\n * 50\n * 100\n * All\n *\n * @param {Number} itemsPerPage How many items will be shown per page.\n * @param {object} config Configuration options provided by the client.\n * @return {object} The template context.\n */\n var buildPagingDropdownTemplateContext = function(itemsPerPage, config) {\n if (itemsPerPage === null) {\n itemsPerPage = DEFAULT.ITEMS_PER_PAGE_SINGLE;\n }\n\n if ($.isArray(itemsPerPage)) {\n // If we're given an array for the items per page, rather than a number,\n // then just use that as the options for the dropdown.\n return {\n options: itemsPerPage\n };\n }\n\n var context = {\n options: []\n };\n\n var totalItems = 0;\n var lastIncrease = 0;\n var maxPages = DEFAULT.MAX_PAGES;\n\n if (config.hasOwnProperty('maxPages')) {\n maxPages = config.maxPages;\n }\n\n for (var i = 1; i <= maxPages; i++) {\n var itemCount = 0;\n\n if (i <= 2) {\n itemCount = itemsPerPage;\n lastIncrease = itemsPerPage;\n } else {\n lastIncrease = lastIncrease * 2;\n itemCount = lastIncrease;\n }\n\n totalItems += itemCount;\n var option = {\n itemcount: itemCount,\n content: totalItems\n };\n\n // Make the first option active by default.\n if (i === 1) {\n option.active = true;\n }\n\n context.options.push(option);\n }\n\n return context;\n };\n\n /**\n * Build the context to render the paged content template with based on the number\n * of pages to show, items per page, and configuration option.\n *\n * By default the code will render a paging bar for the paging controls unless\n * otherwise specified in the provided config.\n *\n * @param {int|null} numberOfItems Total number of items.\n * @param {int|null|array} itemsPerPage How many items will be shown per page.\n * @param {object} config Configuration options provided by the client.\n * @return {object} The template context.\n */\n var buildTemplateContext = function(numberOfItems, itemsPerPage, config) {\n var context = getDefaultTemplateContext();\n\n if (config.hasOwnProperty('ignoreControlWhileLoading')) {\n context.ignorecontrolwhileloading = config.ignoreControlWhileLoading;\n }\n\n if (config.hasOwnProperty('controlPlacementBottom')) {\n context.controlplacementbottom = config.controlPlacementBottom;\n }\n\n if (config.hasOwnProperty('hideControlOnSinglePage')) {\n context.hidecontrolonsinglepage = config.hideControlOnSinglePage;\n }\n\n if (config.hasOwnProperty('ariaLabels')) {\n context.arialabels = config.ariaLabels;\n }\n\n if (config.hasOwnProperty('dropdown') && config.dropdown) {\n context.pagingdropdown = buildPagingDropdownTemplateContext(itemsPerPage, config);\n } else {\n context.pagingbar = buildPagingBarTemplateContext(numberOfItems, itemsPerPage);\n if (config.hasOwnProperty('showFirstLast') && config.showFirstLast) {\n context.pagingbar.first = true;\n context.pagingbar.last = true;\n }\n }\n\n return context;\n };\n\n /**\n * Create a paged content widget where the complete list of items is not loaded\n * up front but will instead be loaded by an ajax request (or similar).\n *\n * The client code must provide a callback function which loads and renders the\n * items for each page. See PagedContent.init for more details.\n *\n * The function will return a deferred that is resolved with a jQuery object\n * for the HTML content and a string for the JavaScript.\n *\n * The current list of configuration options available are:\n * dropdown {bool} True to render the page control as a dropdown (paging bar is default).\n * maxPages {Number} The maximum number of pages to show in the dropdown (only works with dropdown option)\n * ignoreControlWhileLoading {bool} Disable the pagination controls while loading a page (default to true)\n * controlPlacementBottom {bool} Render controls under paged content (default to false)\n *\n * @param {function} renderPagesContentCallback Callback for loading and rendering the items.\n * @param {object} config Configuration options provided by the client.\n * @return {promise} Resolved with jQuery HTML and string JS.\n */\n var create = function(renderPagesContentCallback, config) {\n return createWithTotalAndLimit(null, null, renderPagesContentCallback, config);\n };\n\n /**\n * Create a paged content widget where the complete list of items is not loaded\n * up front but will instead be loaded by an ajax request (or similar).\n *\n * The client code must provide a callback function which loads and renders the\n * items for each page. See PagedContent.init for more details.\n *\n * The function will return a deferred that is resolved with a jQuery object\n * for the HTML content and a string for the JavaScript.\n *\n * The current list of configuration options available are:\n * dropdown {bool} True to render the page control as a dropdown (paging bar is default).\n * maxPages {Number} The maximum number of pages to show in the dropdown (only works with dropdown option)\n * ignoreControlWhileLoading {bool} Disable the pagination controls while loading a page (default to true)\n * controlPlacementBottom {bool} Render controls under paged content (default to false)\n *\n * @param {int|array|null} itemsPerPage How many items will be shown per page.\n * @param {function} renderPagesContentCallback Callback for loading and rendering the items.\n * @param {object} config Configuration options provided by the client.\n * @return {promise} Resolved with jQuery HTML and string JS.\n */\n var createWithLimit = function(itemsPerPage, renderPagesContentCallback, config) {\n return createWithTotalAndLimit(null, itemsPerPage, renderPagesContentCallback, config);\n };\n\n /**\n * Create a paged content widget where the complete list of items is not loaded\n * up front but will instead be loaded by an ajax request (or similar).\n *\n * The client code must provide a callback function which loads and renders the\n * items for each page. See PagedContent.init for more details.\n *\n * The function will return a deferred that is resolved with a jQuery object\n * for the HTML content and a string for the JavaScript.\n *\n * The current list of configuration options available are:\n * dropdown {bool} True to render the page control as a dropdown (paging bar is default).\n * maxPages {Number} The maximum number of pages to show in the dropdown (only works with dropdown option)\n * ignoreControlWhileLoading {bool} Disable the pagination controls while loading a page (default to true)\n * controlPlacementBottom {bool} Render controls under paged content (default to false)\n *\n * @param {int|null} numberOfItems How many items are there in total.\n * @param {int|array|null} itemsPerPage How many items will be shown per page.\n * @param {function} renderPagesContentCallback Callback for loading and rendering the items.\n * @param {object} config Configuration options provided by the client.\n * @return {promise} Resolved with jQuery HTML and string JS.\n */\n var createWithTotalAndLimit = function(numberOfItems, itemsPerPage, renderPagesContentCallback, config) {\n config = config || {};\n\n var deferred = $.Deferred();\n var templateContext = buildTemplateContext(numberOfItems, itemsPerPage, config);\n\n Templates.render(TEMPLATES.PAGED_CONTENT, templateContext)\n .then(function(html, js) {\n html = $(html);\n var id = html.attr('id');\n\n // Set the id to the custom namespace provided\n if (config.hasOwnProperty('eventNamespace')) {\n id = config.eventNamespace;\n }\n\n var container = html;\n\n PagedContent.init(container, renderPagesContentCallback, id);\n\n registerEvents(id, config);\n\n deferred.resolve(html, js);\n return;\n })\n .fail(function(exception) {\n deferred.reject(exception);\n })\n .fail(Notification.exception);\n\n return deferred.promise();\n };\n\n /**\n * Create a paged content widget where the complete list of items is loaded\n * up front.\n *\n * The client code must provide a callback function which renders the\n * items for each page. The callback will be provided with an array where each\n * value in the array is a the list of items to render for the page.\n *\n * The function will return a deferred that is resolved with a jQuery object\n * for the HTML content and a string for the JavaScript.\n *\n * The current list of configuration options available are:\n * dropdown {bool} True to render the page control as a dropdown (paging bar is default).\n * maxPages {Number} The maximum number of pages to show in the dropdown (only works with dropdown option)\n * ignoreControlWhileLoading {bool} Disable the pagination controls while loading a page (default to true)\n * controlPlacementBottom {bool} Render controls under paged content (default to false)\n *\n * @param {array} contentItems The list of items to paginate.\n * @param {Number} itemsPerPage How many items will be shown per page.\n * @param {function} renderContentCallback Callback for rendering the items for the page.\n * @param {object} config Configuration options provided by the client.\n * @return {promise} Resolved with jQuery HTML and string JS.\n */\n var createFromStaticList = function(contentItems, itemsPerPage, renderContentCallback, config) {\n if (typeof config == 'undefined') {\n config = {};\n }\n\n var numberOfItems = contentItems.length;\n return createWithTotalAndLimit(numberOfItems, itemsPerPage, function(pagesData) {\n var contentToRender = [];\n pagesData.forEach(function(pageData) {\n var begin = pageData.offset;\n var end = pageData.limit ? begin + pageData.limit : numberOfItems;\n var items = contentItems.slice(begin, end);\n contentToRender.push(items);\n });\n\n return renderContentCallback(contentToRender);\n }, config);\n };\n\n /**\n * Reset the last page number for the generated paged-content\n * This is used when we need a way to update the last page number outside of the getters callback\n *\n * @param {String} id ID of the paged content container\n * @param {Int} lastPageNumber The last page number\n */\n var resetLastPageNumber = function(id, lastPageNumber) {\n PubSub.publish(id + PagedContentEvents.ALL_ITEMS_LOADED, lastPageNumber);\n };\n\n /**\n * Generate the callback handler for the page limit persistence functionality\n *\n * @param {String} persistentLimitKey\n * @return {callback}\n */\n var generateLimitHandler = function(persistentLimitKey) {\n var callback = function(limit) {\n var args = {\n preferences: [\n {\n type: persistentLimitKey,\n value: limit\n }\n ]\n };\n\n var request = {\n methodname: 'core_user_update_user_preferences',\n args: args\n };\n\n Ajax.call([request]);\n };\n\n return callback;\n };\n\n /**\n * Set up any events based on config key values\n *\n * @param {string} namespace The namespace for this component\n * @param {object} config Config options passed to the factory\n */\n var registerEvents = function(namespace, config) {\n if (config.hasOwnProperty('persistentLimitKey')) {\n PubSub.subscribe(namespace + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT,\n generateLimitHandler(config.persistentLimitKey));\n }\n };\n\n return {\n create: create,\n createWithLimit: createWithLimit,\n createWithTotalAndLimit: createWithTotalAndLimit,\n createFromStaticList: createFromStaticList,\n // Backwards compatibility just in case anyone was using this.\n createFromAjax: createWithTotalAndLimit,\n resetLastPageNumber: resetLastPageNumber\n };\n});\n"],"names":["define","$","Templates","Notification","PagedContent","PagedContentEvents","PubSub","Ajax","TEMPLATES","DEFAULT","buildItemsPerPagePagingBarContext","itemsPerPage","context","isArray","map","num","value","active","filter","item","length","buildPagingBarTemplateContext","numberOfItems","showitemsperpageselector","itemsperpage","previous","next","activepagenumber","hidecontrolonsinglepage","pages","numberOfPages","partial","calculateNumberOfPages","i","page","number","push","barsize","buildPagingBarTemplateContextKnownLength","buildPagingBarTemplateContextUnknownLength","buildTemplateContext","config","pagingbar","pagingdropdown","skipjs","ignorecontrolwhileloading","controlplacementbottom","hasOwnProperty","ignoreControlWhileLoading","controlPlacementBottom","hideControlOnSinglePage","arialabels","ariaLabels","dropdown","options","totalItems","lastIncrease","maxPages","itemCount","option","itemcount","content","buildPagingDropdownTemplateContext","showFirstLast","first","last","createWithTotalAndLimit","renderPagesContentCallback","deferred","Deferred","templateContext","render","then","html","js","id","attr","eventNamespace","container","init","registerEvents","resolve","fail","exception","reject","promise","namespace","persistentLimitKey","subscribe","SET_ITEMS_PER_PAGE_LIMIT","limit","request","methodname","args","preferences","type","call","create","createWithLimit","createFromStaticList","contentItems","renderContentCallback","pagesData","contentToRender","forEach","pageData","begin","offset","end","items","slice","createFromAjax","resetLastPageNumber","lastPageNumber","publish","ALL_ITEMS_LOADED"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"paged_content_factory.min.js","sources":["../src/paged_content_factory.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 * Factory to create a paged content widget.\n *\n * @module core/paged_content_factory\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/templates',\n 'core/notification',\n 'core/paged_content',\n 'core/paged_content_events',\n 'core/pubsub',\n 'core_user/repository'\n],\nfunction(\n $,\n Templates,\n Notification,\n PagedContent,\n PagedContentEvents,\n PubSub,\n UserRepository\n) {\n var TEMPLATES = {\n PAGED_CONTENT: 'core/paged_content'\n };\n\n var DEFAULT = {\n ITEMS_PER_PAGE_SINGLE: 25,\n ITEMS_PER_PAGE_ARRAY: [25, 50, 100, 0],\n MAX_PAGES: 3\n };\n\n /**\n * Get the default context to render the paged content mustache\n * template.\n *\n * @return {object}\n */\n var getDefaultTemplateContext = function() {\n return {\n pagingbar: false,\n pagingdropdown: false,\n skipjs: true,\n ignorecontrolwhileloading: true,\n controlplacementbottom: false\n };\n };\n\n /**\n * Get the default context to render the paging bar mustache template.\n *\n * @return {object}\n */\n var getDefaultPagingBarTemplateContext = function() {\n return {\n showitemsperpageselector: false,\n itemsperpage: [{value: 35, active: true}],\n previous: true,\n next: true,\n activepagenumber: 1,\n hidecontrolonsinglepage: true,\n pages: []\n };\n };\n\n /**\n * Calculate the number of pages required for the given number of items and\n * how many of each item should appear on a page.\n *\n * @param {Number} numberOfItems How many items in total.\n * @param {Number} itemsPerPage How many items will be shown per page.\n * @return {Number} The number of pages required.\n */\n var calculateNumberOfPages = function(numberOfItems, itemsPerPage) {\n var numberOfPages = 1;\n\n if (numberOfItems > 0) {\n var partial = numberOfItems % itemsPerPage;\n\n if (partial) {\n numberOfItems -= partial;\n numberOfPages = (numberOfItems / itemsPerPage) + 1;\n } else {\n numberOfPages = numberOfItems / itemsPerPage;\n }\n }\n\n return numberOfPages;\n };\n\n /**\n * Build the context for the paging bar template when we have a known number\n * of items.\n *\n * @param {Number} numberOfItems How many items in total.\n * @param {Number} itemsPerPage How many items will be shown per page.\n * @return {object} Mustache template\n */\n var buildPagingBarTemplateContextKnownLength = function(numberOfItems, itemsPerPage) {\n if (itemsPerPage === null) {\n itemsPerPage = DEFAULT.ITEMS_PER_PAGE_SINGLE;\n }\n\n if ($.isArray(itemsPerPage)) {\n // If we're given a total number of pages then we don't support a variable\n // set of items per page so just use the first one.\n itemsPerPage = itemsPerPage[0];\n }\n\n var context = getDefaultPagingBarTemplateContext();\n context.itemsperpage = buildItemsPerPagePagingBarContext(itemsPerPage);\n var numberOfPages = calculateNumberOfPages(numberOfItems, itemsPerPage);\n\n for (var i = 1; i <= numberOfPages; i++) {\n var page = {\n number: i,\n page: \"\" + i,\n };\n\n // Make the first page active by default.\n if (i === 1) {\n page.active = true;\n }\n\n context.pages.push(page);\n }\n\n context.barsize = 10;\n return context;\n };\n\n /**\n * Convert the itemsPerPage value into a format applicable for the mustache template.\n * The given value can be either a single integer or an array of integers / objects.\n *\n * E.g.\n * In: [5, 10]\n * out: [{value: 5, active: true}, {value: 10, active: false}]\n *\n * In: [5, {value: 10, active: true}]\n * Out: [{value: 5, active: false}, {value: 10, active: true}]\n *\n * In: [{value: 5, active: false}, {value: 10, active: true}]\n * Out: [{value: 5, active: false}, {value: 10, active: true}]\n *\n * @param {int|int[]} itemsPerPage Options for number of items per page.\n * @return {int|array}\n */\n var buildItemsPerPagePagingBarContext = function(itemsPerPage) {\n var context = [];\n\n if ($.isArray(itemsPerPage)) {\n // Convert the array into a format accepted by the template.\n context = itemsPerPage.map(function(num) {\n if (typeof num === 'number') {\n // If the item is just a plain number then convert it into\n // an object with value and active keys.\n return {\n value: num,\n active: false\n };\n } else {\n // Otherwise we assume the caller has specified things correctly.\n return num;\n }\n });\n\n var activeItems = context.filter(function(item) {\n return item.active;\n });\n\n // Default the first item to active if one hasn't been specified.\n if (!activeItems.length) {\n context[0].active = true;\n }\n } else {\n // Convert the integer into a format accepted by the template.\n context = [{value: itemsPerPage, active: true}];\n }\n\n return context;\n };\n\n /**\n * Build the context for the paging bar template when we have an unknown\n * number of items.\n *\n * @param {Number} itemsPerPage How many items will be shown per page.\n * @return {object} Mustache template\n */\n var buildPagingBarTemplateContextUnknownLength = function(itemsPerPage) {\n if (itemsPerPage === null) {\n itemsPerPage = DEFAULT.ITEMS_PER_PAGE_ARRAY;\n }\n\n var context = getDefaultPagingBarTemplateContext();\n context.itemsperpage = buildItemsPerPagePagingBarContext(itemsPerPage);\n // Only display the items per page selector if there is more than one to choose from.\n context.showitemsperpageselector = $.isArray(itemsPerPage) && itemsPerPage.length > 1;\n\n return context;\n };\n\n /**\n * Build the context to render the paging bar template with based on the number\n * of pages to show.\n *\n * @param {int|null} numberOfItems How many items are there total.\n * @param {int|null} itemsPerPage How many items will be shown per page.\n * @return {object} The template context.\n */\n var buildPagingBarTemplateContext = function(numberOfItems, itemsPerPage) {\n if (numberOfItems) {\n return buildPagingBarTemplateContextKnownLength(numberOfItems, itemsPerPage);\n } else {\n return buildPagingBarTemplateContextUnknownLength(itemsPerPage);\n }\n };\n\n /**\n * Build the context to render the paging dropdown template based on the number\n * of pages to show and items per page.\n *\n * This control is rendered with a gradual increase of the items per page to\n * limit the number of pages in the dropdown. Each page will show twice as much\n * as the previous page (except for the first two pages).\n *\n * By default there will only be 4 pages shown (including the \"All\" option) unless\n * a different number of pages is defined using the maxPages config value.\n *\n * For example:\n * Items per page = 25\n * Would render a dropdown will 4 options:\n * 25\n * 50\n * 100\n * All\n *\n * @param {Number} itemsPerPage How many items will be shown per page.\n * @param {object} config Configuration options provided by the client.\n * @return {object} The template context.\n */\n var buildPagingDropdownTemplateContext = function(itemsPerPage, config) {\n if (itemsPerPage === null) {\n itemsPerPage = DEFAULT.ITEMS_PER_PAGE_SINGLE;\n }\n\n if ($.isArray(itemsPerPage)) {\n // If we're given an array for the items per page, rather than a number,\n // then just use that as the options for the dropdown.\n return {\n options: itemsPerPage\n };\n }\n\n var context = {\n options: []\n };\n\n var totalItems = 0;\n var lastIncrease = 0;\n var maxPages = DEFAULT.MAX_PAGES;\n\n if (config.hasOwnProperty('maxPages')) {\n maxPages = config.maxPages;\n }\n\n for (var i = 1; i <= maxPages; i++) {\n var itemCount = 0;\n\n if (i <= 2) {\n itemCount = itemsPerPage;\n lastIncrease = itemsPerPage;\n } else {\n lastIncrease = lastIncrease * 2;\n itemCount = lastIncrease;\n }\n\n totalItems += itemCount;\n var option = {\n itemcount: itemCount,\n content: totalItems\n };\n\n // Make the first option active by default.\n if (i === 1) {\n option.active = true;\n }\n\n context.options.push(option);\n }\n\n return context;\n };\n\n /**\n * Build the context to render the paged content template with based on the number\n * of pages to show, items per page, and configuration option.\n *\n * By default the code will render a paging bar for the paging controls unless\n * otherwise specified in the provided config.\n *\n * @param {int|null} numberOfItems Total number of items.\n * @param {int|null|array} itemsPerPage How many items will be shown per page.\n * @param {object} config Configuration options provided by the client.\n * @return {object} The template context.\n */\n var buildTemplateContext = function(numberOfItems, itemsPerPage, config) {\n var context = getDefaultTemplateContext();\n\n if (config.hasOwnProperty('ignoreControlWhileLoading')) {\n context.ignorecontrolwhileloading = config.ignoreControlWhileLoading;\n }\n\n if (config.hasOwnProperty('controlPlacementBottom')) {\n context.controlplacementbottom = config.controlPlacementBottom;\n }\n\n if (config.hasOwnProperty('hideControlOnSinglePage')) {\n context.hidecontrolonsinglepage = config.hideControlOnSinglePage;\n }\n\n if (config.hasOwnProperty('ariaLabels')) {\n context.arialabels = config.ariaLabels;\n }\n\n if (config.hasOwnProperty('dropdown') && config.dropdown) {\n context.pagingdropdown = buildPagingDropdownTemplateContext(itemsPerPage, config);\n } else {\n context.pagingbar = buildPagingBarTemplateContext(numberOfItems, itemsPerPage);\n if (config.hasOwnProperty('showFirstLast') && config.showFirstLast) {\n context.pagingbar.first = true;\n context.pagingbar.last = true;\n }\n }\n\n return context;\n };\n\n /**\n * Create a paged content widget where the complete list of items is not loaded\n * up front but will instead be loaded by an ajax request (or similar).\n *\n * The client code must provide a callback function which loads and renders the\n * items for each page. See PagedContent.init for more details.\n *\n * The function will return a deferred that is resolved with a jQuery object\n * for the HTML content and a string for the JavaScript.\n *\n * The current list of configuration options available are:\n * dropdown {bool} True to render the page control as a dropdown (paging bar is default).\n * maxPages {Number} The maximum number of pages to show in the dropdown (only works with dropdown option)\n * ignoreControlWhileLoading {bool} Disable the pagination controls while loading a page (default to true)\n * controlPlacementBottom {bool} Render controls under paged content (default to false)\n *\n * @param {function} renderPagesContentCallback Callback for loading and rendering the items.\n * @param {object} config Configuration options provided by the client.\n * @return {promise} Resolved with jQuery HTML and string JS.\n */\n var create = function(renderPagesContentCallback, config) {\n return createWithTotalAndLimit(null, null, renderPagesContentCallback, config);\n };\n\n /**\n * Create a paged content widget where the complete list of items is not loaded\n * up front but will instead be loaded by an ajax request (or similar).\n *\n * The client code must provide a callback function which loads and renders the\n * items for each page. See PagedContent.init for more details.\n *\n * The function will return a deferred that is resolved with a jQuery object\n * for the HTML content and a string for the JavaScript.\n *\n * The current list of configuration options available are:\n * dropdown {bool} True to render the page control as a dropdown (paging bar is default).\n * maxPages {Number} The maximum number of pages to show in the dropdown (only works with dropdown option)\n * ignoreControlWhileLoading {bool} Disable the pagination controls while loading a page (default to true)\n * controlPlacementBottom {bool} Render controls under paged content (default to false)\n *\n * @param {int|array|null} itemsPerPage How many items will be shown per page.\n * @param {function} renderPagesContentCallback Callback for loading and rendering the items.\n * @param {object} config Configuration options provided by the client.\n * @return {promise} Resolved with jQuery HTML and string JS.\n */\n var createWithLimit = function(itemsPerPage, renderPagesContentCallback, config) {\n return createWithTotalAndLimit(null, itemsPerPage, renderPagesContentCallback, config);\n };\n\n /**\n * Create a paged content widget where the complete list of items is not loaded\n * up front but will instead be loaded by an ajax request (or similar).\n *\n * The client code must provide a callback function which loads and renders the\n * items for each page. See PagedContent.init for more details.\n *\n * The function will return a deferred that is resolved with a jQuery object\n * for the HTML content and a string for the JavaScript.\n *\n * The current list of configuration options available are:\n * dropdown {bool} True to render the page control as a dropdown (paging bar is default).\n * maxPages {Number} The maximum number of pages to show in the dropdown (only works with dropdown option)\n * ignoreControlWhileLoading {bool} Disable the pagination controls while loading a page (default to true)\n * controlPlacementBottom {bool} Render controls under paged content (default to false)\n *\n * @param {int|null} numberOfItems How many items are there in total.\n * @param {int|array|null} itemsPerPage How many items will be shown per page.\n * @param {function} renderPagesContentCallback Callback for loading and rendering the items.\n * @param {object} config Configuration options provided by the client.\n * @return {promise} Resolved with jQuery HTML and string JS.\n */\n var createWithTotalAndLimit = function(numberOfItems, itemsPerPage, renderPagesContentCallback, config) {\n config = config || {};\n\n var deferred = $.Deferred();\n var templateContext = buildTemplateContext(numberOfItems, itemsPerPage, config);\n\n Templates.render(TEMPLATES.PAGED_CONTENT, templateContext)\n .then(function(html, js) {\n html = $(html);\n var id = html.attr('id');\n\n // Set the id to the custom namespace provided\n if (config.hasOwnProperty('eventNamespace')) {\n id = config.eventNamespace;\n }\n\n var container = html;\n\n PagedContent.init(container, renderPagesContentCallback, id);\n\n registerEvents(id, config);\n\n deferred.resolve(html, js);\n return;\n })\n .fail(function(exception) {\n deferred.reject(exception);\n })\n .fail(Notification.exception);\n\n return deferred.promise();\n };\n\n /**\n * Create a paged content widget where the complete list of items is loaded\n * up front.\n *\n * The client code must provide a callback function which renders the\n * items for each page. The callback will be provided with an array where each\n * value in the array is a the list of items to render for the page.\n *\n * The function will return a deferred that is resolved with a jQuery object\n * for the HTML content and a string for the JavaScript.\n *\n * The current list of configuration options available are:\n * dropdown {bool} True to render the page control as a dropdown (paging bar is default).\n * maxPages {Number} The maximum number of pages to show in the dropdown (only works with dropdown option)\n * ignoreControlWhileLoading {bool} Disable the pagination controls while loading a page (default to true)\n * controlPlacementBottom {bool} Render controls under paged content (default to false)\n *\n * @param {array} contentItems The list of items to paginate.\n * @param {Number} itemsPerPage How many items will be shown per page.\n * @param {function} renderContentCallback Callback for rendering the items for the page.\n * @param {object} config Configuration options provided by the client.\n * @return {promise} Resolved with jQuery HTML and string JS.\n */\n var createFromStaticList = function(contentItems, itemsPerPage, renderContentCallback, config) {\n if (typeof config == 'undefined') {\n config = {};\n }\n\n var numberOfItems = contentItems.length;\n return createWithTotalAndLimit(numberOfItems, itemsPerPage, function(pagesData) {\n var contentToRender = [];\n pagesData.forEach(function(pageData) {\n var begin = pageData.offset;\n var end = pageData.limit ? begin + pageData.limit : numberOfItems;\n var items = contentItems.slice(begin, end);\n contentToRender.push(items);\n });\n\n return renderContentCallback(contentToRender);\n }, config);\n };\n\n /**\n * Reset the last page number for the generated paged-content\n * This is used when we need a way to update the last page number outside of the getters callback\n *\n * @param {String} id ID of the paged content container\n * @param {Int} lastPageNumber The last page number\n */\n var resetLastPageNumber = function(id, lastPageNumber) {\n PubSub.publish(id + PagedContentEvents.ALL_ITEMS_LOADED, lastPageNumber);\n };\n\n /**\n * Generate the callback handler for the page limit persistence functionality\n *\n * @param {String} persistentLimitKey\n * @return {callback}\n */\n var generateLimitHandler = function(persistentLimitKey) {\n return function(limit) {\n UserRepository.setUserPreference(persistentLimitKey, limit);\n };\n };\n\n /**\n * Set up any events based on config key values\n *\n * @param {string} namespace The namespace for this component\n * @param {object} config Config options passed to the factory\n */\n var registerEvents = function(namespace, config) {\n if (config.hasOwnProperty('persistentLimitKey')) {\n PubSub.subscribe(namespace + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT,\n generateLimitHandler(config.persistentLimitKey));\n }\n };\n\n return {\n create: create,\n createWithLimit: createWithLimit,\n createWithTotalAndLimit: createWithTotalAndLimit,\n createFromStaticList: createFromStaticList,\n // Backwards compatibility just in case anyone was using this.\n createFromAjax: createWithTotalAndLimit,\n resetLastPageNumber: resetLastPageNumber\n };\n});\n"],"names":["define","$","Templates","Notification","PagedContent","PagedContentEvents","PubSub","UserRepository","TEMPLATES","DEFAULT","buildItemsPerPagePagingBarContext","itemsPerPage","context","isArray","map","num","value","active","filter","item","length","buildPagingBarTemplateContext","numberOfItems","showitemsperpageselector","itemsperpage","previous","next","activepagenumber","hidecontrolonsinglepage","pages","numberOfPages","partial","calculateNumberOfPages","i","page","number","push","barsize","buildPagingBarTemplateContextKnownLength","buildPagingBarTemplateContextUnknownLength","buildTemplateContext","config","pagingbar","pagingdropdown","skipjs","ignorecontrolwhileloading","controlplacementbottom","hasOwnProperty","ignoreControlWhileLoading","controlPlacementBottom","hideControlOnSinglePage","arialabels","ariaLabels","dropdown","options","totalItems","lastIncrease","maxPages","itemCount","option","itemcount","content","buildPagingDropdownTemplateContext","showFirstLast","first","last","createWithTotalAndLimit","renderPagesContentCallback","deferred","Deferred","templateContext","render","then","html","js","id","attr","eventNamespace","container","init","registerEvents","resolve","fail","exception","reject","promise","namespace","persistentLimitKey","subscribe","SET_ITEMS_PER_PAGE_LIMIT","limit","setUserPreference","create","createWithLimit","createFromStaticList","contentItems","renderContentCallback","pagesData","contentToRender","forEach","pageData","begin","offset","end","items","slice","createFromAjax","resetLastPageNumber","lastPageNumber","publish","ALL_ITEMS_LOADED"],"mappings":"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"} \ No newline at end of file diff --git a/lib/amd/src/paged_content_factory.js b/lib/amd/src/paged_content_factory.js index 35f65ec0228..67efdf5c785 100644 --- a/lib/amd/src/paged_content_factory.js +++ b/lib/amd/src/paged_content_factory.js @@ -28,7 +28,7 @@ define( 'core/paged_content', 'core/paged_content_events', 'core/pubsub', - 'core/ajax' + 'core_user/repository' ], function( $, @@ -37,7 +37,7 @@ function( PagedContent, PagedContentEvents, PubSub, - Ajax + UserRepository ) { var TEMPLATES = { PAGED_CONTENT: 'core/paged_content' @@ -520,25 +520,9 @@ function( * @return {callback} */ var generateLimitHandler = function(persistentLimitKey) { - var callback = function(limit) { - var args = { - preferences: [ - { - type: persistentLimitKey, - value: limit - } - ] - }; - - var request = { - methodname: 'core_user_update_user_preferences', - args: args - }; - - Ajax.call([request]); + return function(limit) { + UserRepository.setUserPreference(persistentLimitKey, limit); }; - - return callback; }; /** diff --git a/mod/assign/amd/build/grading_navigation.min.js b/mod/assign/amd/build/grading_navigation.min.js index 71a11bb876c..912332de52f 100644 --- a/mod/assign/amd/build/grading_navigation.min.js +++ b/mod/assign/amd/build/grading_navigation.min.js @@ -6,6 +6,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.1 */ -define("mod_assign/grading_navigation",["jquery","core/notification","core/str","core/form-autocomplete","core/ajax","mod_assign/grading_form_change_checker"],(function($,notification,str,autocomplete,ajax,checker){var GradingNavigation=function(selector){this._regionSelector=selector,this._region=$(selector),this._filters=[],this._users=[],this._filteredUsers=[],this._lastXofYUpdate=0,this._firstLoadUsers=!0;let url=new URL(window.location);parseInt(url.searchParams.get("treset"))>0&&(url.searchParams.delete("treset"),window.history.replaceState({},"",url)),this._loadAllUsers(),this._region.find('[data-action="previous-user"]').on("click",this._handlePreviousUser.bind(this)),this._region.find('[data-action="next-user"]').on("click",this._handleNextUser.bind(this)),this._region.find('[data-action="change-user"]').on("change",this._handleChangeUser.bind(this)),this._region.find('[data-region="user-filters"]').on("click",this._toggleExpandFilters.bind(this)),this._region.find('[data-region="user-resettable"]').on("click",this._toggleResetTable.bind()),$(document).on("user-changed",this._refreshSelector.bind(this)),$(document).on("done-saving-show-next",this._handleNextUser.bind(this));var toggleLink=this._region.find('[data-region="user-filters"]');$(document.getElementById(toggleLink.attr("aria-controls"))).on("change","select",this._filterChanged.bind(this));var userid=$('[data-region="grading-navigation-panel"]').data("first-userid");userid&&this._selectUserById(userid),str.get_string("changeuser","mod_assign").done((function(s){autocomplete.enhance("[data-action=change-user]",!1,"mod_assign/participant_selector",s)})).fail(notification.exception),$(document).bind("start-loading-user",function(){this._isLoading=!0}.bind(this)),$(document).bind("finish-loading-user",function(){this._isLoading=!1}.bind(this))};return GradingNavigation.prototype._isLoading=!1,GradingNavigation.prototype._regionSelector=null,GradingNavigation.prototype._filters=null,GradingNavigation.prototype._users=null,GradingNavigation.prototype._region=null,GradingNavigation.prototype._lastFilters="",GradingNavigation.prototype._loadAllUsers=function(){var select=this._region.find("[data-action=change-user]"),assignmentid=select.attr("data-assignmentid"),groupid=select.attr("data-groupid"),filterPanel=this._region.find('[data-region="configure-filters"]'),filter=filterPanel.find('select[name="filter"]').val(),workflowFilter=filterPanel.find('select[name="workflowfilter"]');workflowFilter&&(filter+=","+workflowFilter.val());var markerFilter=filterPanel.find('select[name="markerfilter"]');return markerFilter&&(filter+=","+markerFilter.val()),this._lastFilters!=filter&&(this._lastFilters=filter,ajax.call([{methodname:"mod_assign_list_participants",args:{assignid:assignmentid,groupid:groupid,filter:"",onlyids:!0,tablesort:!0},done:this._usersLoaded.bind(this),fail:notification.exception}]),!0)},GradingNavigation.prototype._usersLoaded=function(users){if(this._firstLoadUsers=!1,this._filteredUsers=this._users=users,this._users.length){var toggleLink=this._region.find('[data-region="user-filters"]');$(document.getElementById(toggleLink.attr("aria-controls"))).find('select[name="filter"]').trigger("change")}else this._selectNoUser();this._triggerNextUserEvent()},GradingNavigation.prototype._checkClickOutsideConfigureFilters=function(event){var configPanel=this._region.find('[data-region="configure-filters"]');if(!configPanel.is(event.target)&&0===configPanel.has(event.target).length){var toggleLink=this._region.find('[data-region="user-filters"]');configPanel.hide(),configPanel.attr("aria-hidden","true"),toggleLink.attr("aria-expanded","false"),$(document).unbind("click.mod_assign_grading_navigation")}},GradingNavigation.prototype._updateFilterPreferences=function(userId,filterList,preferenceNames){var preferences=[],i=0;if(0==filterList.length||this._firstLoadUsers){var deferred=$.Deferred();return deferred.resolve(),deferred}for(i=0;i0&&$(document).trigger("user-changed",userid)))},GradingNavigation.prototype._toggleExpandFilters=function(event){event.preventDefault();var toggleLink=$(event.target).closest('[data-region="user-filters"]'),expanded="true"==toggleLink.attr("aria-expanded"),configPanel=$(document.getElementById(toggleLink.attr("aria-controls")));expanded?(configPanel.hide(),configPanel.attr("aria-hidden","true"),toggleLink.attr("aria-expanded","false"),$(document).unbind("click.mod_assign_grading_navigation")):(configPanel.css("display","inline-block"),configPanel.attr("aria-hidden","false"),toggleLink.attr("aria-expanded","true"),event.stopPropagation(),$(document).on("click.mod_assign_grading_navigation",this._checkClickOutsideConfigureFilters.bind(this)))},GradingNavigation.prototype._toggleResetTable=function(){let url=new URL(window.location);url.searchParams.set("treset","1"),window.location.href=url},GradingNavigation.prototype._handlePreviousUser=function(e){e.preventDefault();var currentUserId=this._region.find("[data-action=change-user]").attr("data-selected"),i=0,currentIndex=0;for(i=0;i0&&$(document).trigger("user-changed",userid)}else count&&this._selectUserById(this._filteredUsers[newIndex].id)},GradingNavigation.prototype._setCountString=function(x,y){var updateNumber;this._lastXofYUpdate++,updateNumber=this._lastXofYUpdate;var param={x:x,y:y};str.get_string("xofy","mod_assign",param).done(function(s){updateNumber==this._lastXofYUpdate&&this._region.find('[data-region="user-count-summary"]').text(s)}.bind(this)).fail(notification.exception)},GradingNavigation.prototype._refreshCount=function(){var userid=this._region.find("[data-action=change-user]").attr("data-selected"),i=0,currentIndex=0;if(isNaN(userid)||userid<=0)this._region.find('[data-region="user-count"]').hide();else{for(this._region.find('[data-region="user-count"]').show(),i=0;i0){var url=new URL(window.location);if(parseInt(url.searchParams.get("blindid"))>0){var newid=this._filteredUsers[currentIndex-1].recordid;url.searchParams.set("blindid",newid)}else url.searchParams.set("userid",userid);window.history.replaceState({},"",url)}}},GradingNavigation.prototype._refreshSelector=function(event,userid){var select=this._region.find("[data-action=change-user]");userid=parseInt(userid,10),!isNaN(userid)&&userid>0&&select.attr("data-selected",userid),this._refreshCount()},GradingNavigation.prototype._triggerNextUserEvent=function(){this._filteredUsers.length>1?$(document).trigger("next-user",{nextUserId:null,nextUser:!0}):$(document).trigger("next-user",{nextUser:!1})},GradingNavigation.prototype._handleChangeUser=function(){var select=this._region.find("[data-action=change-user]"),userid=parseInt(select.val(),10);this._isLoading||(checker.checkFormForChanges('[data-region="grade-panel"] .gradeform')?str.get_strings([{key:"unsavedchanges",component:"mod_assign"},{key:"unsavedchangesquestion",component:"mod_assign"},{key:"saveandcontinue",component:"mod_assign"},{key:"cancel",component:"core"}]).done((function(strs){notification.confirm(strs[0],strs[1],strs[2],strs[3],(function(){$(document).trigger("save-changes",userid)}))})):!isNaN(userid)&&userid>0&&(select.attr("data-selected",userid),$(document).trigger("user-changed",userid)))},GradingNavigation})); +define("mod_assign/grading_navigation",["jquery","core/notification","core/str","core/form-autocomplete","core/ajax","core_user/repository","mod_assign/grading_form_change_checker"],(function($,notification,str,autocomplete,ajax,UserRepository,checker){var GradingNavigation=function(selector){this._regionSelector=selector,this._region=$(selector),this._filters=[],this._users=[],this._filteredUsers=[],this._lastXofYUpdate=0,this._firstLoadUsers=!0;let url=new URL(window.location);parseInt(url.searchParams.get("treset"))>0&&(url.searchParams.delete("treset"),window.history.replaceState({},"",url)),this._loadAllUsers(),this._region.find('[data-action="previous-user"]').on("click",this._handlePreviousUser.bind(this)),this._region.find('[data-action="next-user"]').on("click",this._handleNextUser.bind(this)),this._region.find('[data-action="change-user"]').on("change",this._handleChangeUser.bind(this)),this._region.find('[data-region="user-filters"]').on("click",this._toggleExpandFilters.bind(this)),this._region.find('[data-region="user-resettable"]').on("click",this._toggleResetTable.bind()),$(document).on("user-changed",this._refreshSelector.bind(this)),$(document).on("done-saving-show-next",this._handleNextUser.bind(this));var toggleLink=this._region.find('[data-region="user-filters"]');$(document.getElementById(toggleLink.attr("aria-controls"))).on("change","select",this._filterChanged.bind(this));var userid=$('[data-region="grading-navigation-panel"]').data("first-userid");userid&&this._selectUserById(userid),str.get_string("changeuser","mod_assign").done((function(s){autocomplete.enhance("[data-action=change-user]",!1,"mod_assign/participant_selector",s)})).fail(notification.exception),$(document).bind("start-loading-user",function(){this._isLoading=!0}.bind(this)),$(document).bind("finish-loading-user",function(){this._isLoading=!1}.bind(this))};return GradingNavigation.prototype._isLoading=!1,GradingNavigation.prototype._regionSelector=null,GradingNavigation.prototype._filters=null,GradingNavigation.prototype._users=null,GradingNavigation.prototype._region=null,GradingNavigation.prototype._lastFilters="",GradingNavigation.prototype._loadAllUsers=function(){var select=this._region.find("[data-action=change-user]"),assignmentid=select.attr("data-assignmentid"),groupid=select.attr("data-groupid"),filterPanel=this._region.find('[data-region="configure-filters"]'),filter=filterPanel.find('select[name="filter"]').val(),workflowFilter=filterPanel.find('select[name="workflowfilter"]');workflowFilter&&(filter+=","+workflowFilter.val());var markerFilter=filterPanel.find('select[name="markerfilter"]');return markerFilter&&(filter+=","+markerFilter.val()),this._lastFilters!=filter&&(this._lastFilters=filter,ajax.call([{methodname:"mod_assign_list_participants",args:{assignid:assignmentid,groupid:groupid,filter:"",onlyids:!0,tablesort:!0},done:this._usersLoaded.bind(this),fail:notification.exception}]),!0)},GradingNavigation.prototype._usersLoaded=function(users){if(this._firstLoadUsers=!1,this._filteredUsers=this._users=users,this._users.length){var toggleLink=this._region.find('[data-region="user-filters"]');$(document.getElementById(toggleLink.attr("aria-controls"))).find('select[name="filter"]').trigger("change")}else this._selectNoUser();this._triggerNextUserEvent()},GradingNavigation.prototype._checkClickOutsideConfigureFilters=function(event){var configPanel=this._region.find('[data-region="configure-filters"]');if(!configPanel.is(event.target)&&0===configPanel.has(event.target).length){var toggleLink=this._region.find('[data-region="user-filters"]');configPanel.hide(),configPanel.attr("aria-hidden","true"),toggleLink.attr("aria-expanded","false"),$(document).unbind("click.mod_assign_grading_navigation")}},GradingNavigation.prototype._updateFilterPreferences=function(userId,filterList,preferenceNames){var preferences=[],i=0;if(0==filterList.length||this._firstLoadUsers){var deferred=$.Deferred();return deferred.resolve(),deferred}for(i=0;i0&&$(document).trigger("user-changed",userid)))},GradingNavigation.prototype._toggleExpandFilters=function(event){event.preventDefault();var toggleLink=$(event.target).closest('[data-region="user-filters"]'),expanded="true"==toggleLink.attr("aria-expanded"),configPanel=$(document.getElementById(toggleLink.attr("aria-controls")));expanded?(configPanel.hide(),configPanel.attr("aria-hidden","true"),toggleLink.attr("aria-expanded","false"),$(document).unbind("click.mod_assign_grading_navigation")):(configPanel.css("display","inline-block"),configPanel.attr("aria-hidden","false"),toggleLink.attr("aria-expanded","true"),event.stopPropagation(),$(document).on("click.mod_assign_grading_navigation",this._checkClickOutsideConfigureFilters.bind(this)))},GradingNavigation.prototype._toggleResetTable=function(){let url=new URL(window.location);url.searchParams.set("treset","1"),window.location.href=url},GradingNavigation.prototype._handlePreviousUser=function(e){e.preventDefault();var currentUserId=this._region.find("[data-action=change-user]").attr("data-selected"),i=0,currentIndex=0;for(i=0;i0&&$(document).trigger("user-changed",userid)}else count&&this._selectUserById(this._filteredUsers[newIndex].id)},GradingNavigation.prototype._setCountString=function(x,y){var updateNumber;this._lastXofYUpdate++,updateNumber=this._lastXofYUpdate;var param={x:x,y:y};str.get_string("xofy","mod_assign",param).done(function(s){updateNumber==this._lastXofYUpdate&&this._region.find('[data-region="user-count-summary"]').text(s)}.bind(this)).fail(notification.exception)},GradingNavigation.prototype._refreshCount=function(){var userid=this._region.find("[data-action=change-user]").attr("data-selected"),i=0,currentIndex=0;if(isNaN(userid)||userid<=0)this._region.find('[data-region="user-count"]').hide();else{for(this._region.find('[data-region="user-count"]').show(),i=0;i0){var url=new URL(window.location);if(parseInt(url.searchParams.get("blindid"))>0){var newid=this._filteredUsers[currentIndex-1].recordid;url.searchParams.set("blindid",newid)}else url.searchParams.set("userid",userid);window.history.replaceState({},"",url)}}},GradingNavigation.prototype._refreshSelector=function(event,userid){var select=this._region.find("[data-action=change-user]");userid=parseInt(userid,10),!isNaN(userid)&&userid>0&&select.attr("data-selected",userid),this._refreshCount()},GradingNavigation.prototype._triggerNextUserEvent=function(){this._filteredUsers.length>1?$(document).trigger("next-user",{nextUserId:null,nextUser:!0}):$(document).trigger("next-user",{nextUser:!1})},GradingNavigation.prototype._handleChangeUser=function(){var select=this._region.find("[data-action=change-user]"),userid=parseInt(select.val(),10);this._isLoading||(checker.checkFormForChanges('[data-region="grade-panel"] .gradeform')?str.get_strings([{key:"unsavedchanges",component:"mod_assign"},{key:"unsavedchangesquestion",component:"mod_assign"},{key:"saveandcontinue",component:"mod_assign"},{key:"cancel",component:"core"}]).done((function(strs){notification.confirm(strs[0],strs[1],strs[2],strs[3],(function(){$(document).trigger("save-changes",userid)}))})):!isNaN(userid)&&userid>0&&(select.attr("data-selected",userid),$(document).trigger("user-changed",userid)))},GradingNavigation})); //# sourceMappingURL=grading_navigation.min.js.map \ No newline at end of file diff --git a/mod/assign/amd/build/grading_navigation.min.js.map b/mod/assign/amd/build/grading_navigation.min.js.map index 52dec787aae..406102f9328 100644 --- a/mod/assign/amd/build/grading_navigation.min.js.map +++ b/mod/assign/amd/build/grading_navigation.min.js.map @@ -1 +1 @@ -{"version":3,"file":"grading_navigation.min.js","sources":["../src/grading_navigation.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript to handle changing users via the user selector in the header.\n *\n * @module mod_assign/grading_navigation\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(['jquery', 'core/notification', 'core/str', 'core/form-autocomplete',\n 'core/ajax', 'mod_assign/grading_form_change_checker'],\n function($, notification, str, autocomplete, ajax, checker) {\n\n /**\n * GradingNavigation class.\n *\n * @class mod_assign/grading_navigation\n * @param {String} selector The selector for the page region containing the user navigation.\n */\n var GradingNavigation = function(selector) {\n this._regionSelector = selector;\n this._region = $(selector);\n this._filters = [];\n this._users = [];\n this._filteredUsers = [];\n this._lastXofYUpdate = 0;\n this._firstLoadUsers = true;\n\n let url = new URL(window.location);\n if (parseInt(url.searchParams.get('treset')) > 0) {\n // Remove 'treset' url parameter to make sure that\n // table preferences won't be reset on page refresh.\n url.searchParams.delete('treset');\n window.history.replaceState({}, \"\", url);\n }\n\n // Get the current user list from a webservice.\n this._loadAllUsers();\n\n // We do not allow navigation while ajax requests are pending.\n // Attach listeners to the select and arrow buttons.\n\n this._region.find('[data-action=\"previous-user\"]').on('click', this._handlePreviousUser.bind(this));\n this._region.find('[data-action=\"next-user\"]').on('click', this._handleNextUser.bind(this));\n this._region.find('[data-action=\"change-user\"]').on('change', this._handleChangeUser.bind(this));\n this._region.find('[data-region=\"user-filters\"]').on('click', this._toggleExpandFilters.bind(this));\n this._region.find('[data-region=\"user-resettable\"]').on('click', this._toggleResetTable.bind());\n\n $(document).on('user-changed', this._refreshSelector.bind(this));\n $(document).on('done-saving-show-next', this._handleNextUser.bind(this));\n\n // Position the configure filters panel under the link that expands it.\n var toggleLink = this._region.find('[data-region=\"user-filters\"]');\n var configPanel = $(document.getElementById(toggleLink.attr('aria-controls')));\n\n configPanel.on('change', 'select', this._filterChanged.bind(this));\n\n var userid = $('[data-region=\"grading-navigation-panel\"]').data('first-userid');\n if (userid) {\n this._selectUserById(userid);\n }\n\n str.get_string('changeuser', 'mod_assign').done(function(s) {\n autocomplete.enhance('[data-action=change-user]', false, 'mod_assign/participant_selector', s);\n }\n ).fail(notification.exception);\n\n $(document).bind(\"start-loading-user\", function() {\n this._isLoading = true;\n }.bind(this));\n $(document).bind(\"finish-loading-user\", function() {\n this._isLoading = false;\n }.bind(this));\n };\n\n /** @property {Boolean} Boolean tracking active ajax requests. */\n GradingNavigation.prototype._isLoading = false;\n\n /** @property {String} Selector for the page region containing the user navigation. */\n GradingNavigation.prototype._regionSelector = null;\n\n /** @property {Array} The list of active filter keys */\n GradingNavigation.prototype._filters = null;\n\n /** @property {Array} The list of users */\n GradingNavigation.prototype._users = null;\n\n /** @property {JQuery} JQuery node for the page region containing the user navigation. */\n GradingNavigation.prototype._region = null;\n\n /** @property {String} Last active filters */\n GradingNavigation.prototype._lastFilters = '';\n\n /**\n * Load the list of all users for this assignment.\n *\n * @private\n * @method _loadAllUsers\n * @return {Boolean} True if the user list was fetched.\n */\n GradingNavigation.prototype._loadAllUsers = function() {\n var select = this._region.find('[data-action=change-user]');\n var assignmentid = select.attr('data-assignmentid');\n var groupid = select.attr('data-groupid');\n\n var filterPanel = this._region.find('[data-region=\"configure-filters\"]');\n var filter = filterPanel.find('select[name=\"filter\"]').val();\n var workflowFilter = filterPanel.find('select[name=\"workflowfilter\"]');\n if (workflowFilter) {\n filter += ',' + workflowFilter.val();\n }\n var markerFilter = filterPanel.find('select[name=\"markerfilter\"]');\n if (markerFilter) {\n filter += ',' + markerFilter.val();\n }\n\n if (this._lastFilters == filter) {\n return false;\n }\n this._lastFilters = filter;\n\n ajax.call([{\n methodname: 'mod_assign_list_participants',\n args: {assignid: assignmentid, groupid: groupid, filter: '', onlyids: true, tablesort: true},\n done: this._usersLoaded.bind(this),\n fail: notification.exception\n }]);\n return true;\n };\n\n /**\n * Call back to rebuild the user selector and x of y info when the user list is updated.\n *\n * @private\n * @method _usersLoaded\n * @param {Array} users\n */\n GradingNavigation.prototype._usersLoaded = function(users) {\n this._firstLoadUsers = false;\n this._filteredUsers = this._users = users;\n if (this._users.length) {\n // Position the configure filters panel under the link that expands it.\n var toggleLink = this._region.find('[data-region=\"user-filters\"]');\n var configPanel = $(document.getElementById(toggleLink.attr('aria-controls')));\n\n configPanel.find('select[name=\"filter\"]').trigger('change');\n } else {\n this._selectNoUser();\n }\n this._triggerNextUserEvent();\n };\n\n /**\n * Close the configure filters panel if a click is detected outside of it.\n *\n * @private\n * @method _checkClickOutsideConfigureFilters\n * @param {Event} event\n */\n GradingNavigation.prototype._checkClickOutsideConfigureFilters = function(event) {\n var configPanel = this._region.find('[data-region=\"configure-filters\"]');\n\n if (!configPanel.is(event.target) && configPanel.has(event.target).length === 0) {\n var toggleLink = this._region.find('[data-region=\"user-filters\"]');\n\n configPanel.hide();\n configPanel.attr('aria-hidden', 'true');\n toggleLink.attr('aria-expanded', 'false');\n $(document).unbind('click.mod_assign_grading_navigation');\n }\n };\n\n /**\n * Close the configure filters panel if a click is detected outside of it.\n *\n * @private\n * @method _updateFilterPreference\n * @param {Number} userId The current user id.\n * @param {Array} filterList The list of current filter values.\n * @param {Array} preferenceNames The names of the preferences to update\n * @return {Promise} Resolved when all the preferences are updated.\n */\n GradingNavigation.prototype._updateFilterPreferences = function(userId, filterList, preferenceNames) {\n var preferences = [],\n i = 0;\n\n if (filterList.length == 0 || this._firstLoadUsers) {\n // Nothing to update.\n var deferred = $.Deferred();\n deferred.resolve();\n return deferred;\n }\n // General filter.\n // Set the user preferences to the current filters.\n for (i = 0; i < filterList.length; i++) {\n var newValue = filterList[i];\n if (newValue == 'none') {\n newValue = '';\n }\n\n preferences.push({\n userid: userId,\n name: preferenceNames[i],\n value: newValue\n });\n }\n\n return ajax.call([{\n methodname: 'core_user_set_user_preferences',\n args: {\n preferences: preferences\n }\n }])[0];\n };\n /**\n * Turn a filter on or off.\n *\n * @private\n * @method _filterChanged\n */\n GradingNavigation.prototype._filterChanged = function() {\n // There are 3 types of filter right now.\n var filterPanel = this._region.find('[data-region=\"configure-filters\"]');\n var filters = filterPanel.find('select');\n var preferenceNames = [];\n\n this._filters = [];\n filters.each(function(idx, ele) {\n var element = $(ele);\n this._filters.push(element.val());\n preferenceNames.push('assign_' + element.prop('name'));\n }.bind(this));\n\n // Update the active filter string.\n var filterlist = [];\n filterPanel.find('option:checked').each(function(idx, ele) {\n filterlist[filterlist.length] = $(ele).text();\n });\n if (filterlist.length) {\n this._region.find('[data-region=\"user-filters\"] span').text(filterlist.join(', '));\n } else {\n str.get_string('nofilters', 'mod_assign').done(function(s) {\n this._region.find('[data-region=\"user-filters\"] span').text(s);\n }.bind(this)).fail(notification.exception);\n }\n\n var select = this._region.find('[data-action=change-user]');\n var currentUserID = select.data('currentuserid');\n this._updateFilterPreferences(currentUserID, this._filters, preferenceNames).done(function() {\n // Reload the list of users to apply the new filters.\n if (!this._loadAllUsers()) {\n var userid = parseInt(select.attr('data-selected'));\n let foundIndex = null;\n // Search the returned users for the current selection.\n $.each(this._filteredUsers, function(index, user) {\n if (userid == user.id) {\n foundIndex = index;\n }\n });\n\n if (this._filteredUsers.length && foundIndex !== null) {\n this._selectUserById(this._filteredUsers[foundIndex].id);\n } else {\n this._selectNoUser();\n }\n\n }\n }.bind(this)).fail(notification.exception);\n this._refreshCount();\n };\n\n /**\n * Select no users, because no users match the filters.\n *\n * @private\n * @method _selectNoUser\n */\n GradingNavigation.prototype._selectNoUser = function() {\n // Detect unsaved changes, and offer to save them - otherwise change user right now.\n if (this._isLoading) {\n return;\n }\n if (checker.checkFormForChanges('[data-region=\"grade-panel\"] .gradeform')) {\n // Form has changes, so we need to confirm before switching users.\n str.get_strings([\n {key: 'unsavedchanges', component: 'mod_assign'},\n {key: 'unsavedchangesquestion', component: 'mod_assign'},\n {key: 'saveandcontinue', component: 'mod_assign'},\n {key: 'cancel', component: 'core'},\n ]).done(function(strs) {\n notification.confirm(strs[0], strs[1], strs[2], strs[3], function() {\n $(document).trigger('save-changes', -1);\n });\n });\n } else {\n $(document).trigger('user-changed', -1);\n }\n };\n\n /**\n * Select the specified user by id.\n *\n * @private\n * @method _selectUserById\n * @param {Number} userid\n */\n GradingNavigation.prototype._selectUserById = function(userid) {\n var select = this._region.find('[data-action=change-user]');\n var useridnumber = parseInt(userid, 10);\n\n // Detect unsaved changes, and offer to save them - otherwise change user right now.\n if (this._isLoading) {\n return;\n }\n if (checker.checkFormForChanges('[data-region=\"grade-panel\"] .gradeform')) {\n // Form has changes, so we need to confirm before switching users.\n str.get_strings([\n {key: 'unsavedchanges', component: 'mod_assign'},\n {key: 'unsavedchangesquestion', component: 'mod_assign'},\n {key: 'saveandcontinue', component: 'mod_assign'},\n {key: 'cancel', component: 'core'},\n ]).done(function(strs) {\n notification.confirm(strs[0], strs[1], strs[2], strs[3], function() {\n $(document).trigger('save-changes', useridnumber);\n });\n });\n } else {\n select.attr('data-selected', userid);\n\n if (!isNaN(useridnumber) && useridnumber > 0) {\n $(document).trigger('user-changed', userid);\n }\n }\n };\n\n /**\n * Expand or collapse the filter config panel.\n *\n * @private\n * @method _toggleExpandFilters\n * @param {Event} event\n */\n GradingNavigation.prototype._toggleExpandFilters = function(event) {\n event.preventDefault();\n var toggleLink = $(event.target).closest('[data-region=\"user-filters\"]');\n var expanded = toggleLink.attr('aria-expanded') == 'true';\n var configPanel = $(document.getElementById(toggleLink.attr('aria-controls')));\n\n if (expanded) {\n configPanel.hide();\n configPanel.attr('aria-hidden', 'true');\n toggleLink.attr('aria-expanded', 'false');\n $(document).unbind('click.mod_assign_grading_navigation');\n } else {\n configPanel.css('display', 'inline-block');\n configPanel.attr('aria-hidden', 'false');\n toggleLink.attr('aria-expanded', 'true');\n event.stopPropagation();\n $(document).on('click.mod_assign_grading_navigation', this._checkClickOutsideConfigureFilters.bind(this));\n }\n };\n\n /**\n * Reset table preferences.\n *\n * @private\n * @method _toggleResetTable\n */\n GradingNavigation.prototype._toggleResetTable = function() {\n let url = new URL(window.location);\n url.searchParams.set('treset', '1');\n window.location.href = url;\n };\n\n /**\n * Change to the previous user in the grading list.\n *\n * @private\n * @method _handlePreviousUser\n * @param {Event} e\n */\n GradingNavigation.prototype._handlePreviousUser = function(e) {\n e.preventDefault();\n var select = this._region.find('[data-action=change-user]');\n var currentUserId = select.attr('data-selected');\n var i = 0;\n var currentIndex = 0;\n\n for (i = 0; i < this._filteredUsers.length; i++) {\n if (this._filteredUsers[i].id == currentUserId) {\n currentIndex = i;\n break;\n }\n }\n\n var count = this._filteredUsers.length;\n var newIndex = (currentIndex - 1);\n if (newIndex < 0) {\n newIndex = count - 1;\n }\n\n if (count) {\n this._selectUserById(this._filteredUsers[newIndex].id);\n }\n };\n\n /**\n * Change to the next user in the grading list.\n *\n * @param {Event} e\n * @param {Boolean} saved Has the form already been saved? Skips checking for changes if true.\n */\n GradingNavigation.prototype._handleNextUser = function(e, saved) {\n e.preventDefault();\n var select = this._region.find('[data-action=change-user]');\n var currentUserId = select.attr('data-selected');\n var i = 0;\n var currentIndex = 0;\n\n for (i = 0; i < this._filteredUsers.length; i++) {\n if (this._filteredUsers[i].id == currentUserId) {\n currentIndex = i;\n break;\n }\n }\n\n var count = this._filteredUsers.length;\n var newIndex = (currentIndex + 1) % count;\n\n if (saved && count) {\n // If we've already saved the grade, skip checking if we've made any changes.\n var userid = this._filteredUsers[newIndex].id;\n var useridnumber = parseInt(userid, 10);\n select.attr('data-selected', userid);\n if (!isNaN(useridnumber) && useridnumber > 0) {\n $(document).trigger('user-changed', userid);\n }\n } else if (count) {\n this._selectUserById(this._filteredUsers[newIndex].id);\n }\n };\n\n /**\n * Set count string. This method only sets the value for the last time it was ever called to deal\n * with promises that return in a non-predictable order.\n *\n * @private\n * @method _setCountString\n * @param {Number} x\n * @param {Number} y\n */\n GradingNavigation.prototype._setCountString = function(x, y) {\n var updateNumber = 0;\n this._lastXofYUpdate++;\n updateNumber = this._lastXofYUpdate;\n\n var param = {x: x, y: y};\n str.get_string('xofy', 'mod_assign', param).done(function(s) {\n if (updateNumber == this._lastXofYUpdate) {\n this._region.find('[data-region=\"user-count-summary\"]').text(s);\n }\n }.bind(this)).fail(notification.exception);\n };\n\n /**\n * Rebuild the x of y string.\n *\n * @private\n * @method _refreshCount\n */\n GradingNavigation.prototype._refreshCount = function() {\n var select = this._region.find('[data-action=change-user]');\n var userid = select.attr('data-selected');\n var i = 0;\n var currentIndex = 0;\n\n if (isNaN(userid) || userid <= 0) {\n this._region.find('[data-region=\"user-count\"]').hide();\n } else {\n this._region.find('[data-region=\"user-count\"]').show();\n\n for (i = 0; i < this._filteredUsers.length; i++) {\n if (this._filteredUsers[i].id == userid) {\n currentIndex = i;\n break;\n }\n }\n var count = this._filteredUsers.length;\n if (count) {\n currentIndex += 1;\n }\n this._setCountString(currentIndex, count);\n // Update window URL\n if (currentIndex > 0) {\n var url = new URL(window.location);\n if (parseInt(url.searchParams.get('blindid')) > 0) {\n var newid = this._filteredUsers[currentIndex - 1].recordid;\n url.searchParams.set('blindid', newid);\n } else {\n url.searchParams.set('userid', userid);\n }\n // We do this so a browser refresh will return to the same user.\n window.history.replaceState({}, \"\", url);\n }\n }\n };\n\n /**\n * Respond to a user-changed event by updating the selector.\n *\n * @private\n * @method _refreshSelector\n * @param {Event} event\n * @param {String} userid\n */\n GradingNavigation.prototype._refreshSelector = function(event, userid) {\n var select = this._region.find('[data-action=change-user]');\n userid = parseInt(userid, 10);\n\n if (!isNaN(userid) && userid > 0) {\n select.attr('data-selected', userid);\n }\n this._refreshCount();\n };\n\n /**\n * Trigger the next user event depending on the number of filtered users\n *\n * @private\n * @method _triggerNextUserEvent\n */\n GradingNavigation.prototype._triggerNextUserEvent = function() {\n if (this._filteredUsers.length > 1) {\n $(document).trigger('next-user', {nextUserId: null, nextUser: true});\n } else {\n $(document).trigger('next-user', {nextUser: false});\n }\n };\n\n /**\n * Change to a different user in the grading list.\n *\n * @private\n * @method _handleChangeUser\n */\n GradingNavigation.prototype._handleChangeUser = function() {\n var select = this._region.find('[data-action=change-user]');\n var userid = parseInt(select.val(), 10);\n\n if (this._isLoading) {\n return;\n }\n if (checker.checkFormForChanges('[data-region=\"grade-panel\"] .gradeform')) {\n // Form has changes, so we need to confirm before switching users.\n str.get_strings([\n {key: 'unsavedchanges', component: 'mod_assign'},\n {key: 'unsavedchangesquestion', component: 'mod_assign'},\n {key: 'saveandcontinue', component: 'mod_assign'},\n {key: 'cancel', component: 'core'},\n ]).done(function(strs) {\n notification.confirm(strs[0], strs[1], strs[2], strs[3], function() {\n $(document).trigger('save-changes', userid);\n });\n });\n } else {\n if (!isNaN(userid) && userid > 0) {\n select.attr('data-selected', userid);\n\n $(document).trigger('user-changed', userid);\n }\n }\n };\n\n return GradingNavigation;\n});\n"],"names":["define","$","notification","str","autocomplete","ajax","checker","GradingNavigation","selector","_regionSelector","_region","_filters","_users","_filteredUsers","_lastXofYUpdate","_firstLoadUsers","url","URL","window","location","parseInt","searchParams","get","delete","history","replaceState","_loadAllUsers","find","on","this","_handlePreviousUser","bind","_handleNextUser","_handleChangeUser","_toggleExpandFilters","_toggleResetTable","document","_refreshSelector","toggleLink","getElementById","attr","_filterChanged","userid","data","_selectUserById","get_string","done","s","enhance","fail","exception","_isLoading","prototype","_lastFilters","select","assignmentid","groupid","filterPanel","filter","val","workflowFilter","markerFilter","call","methodname","args","assignid","onlyids","tablesort","_usersLoaded","users","length","trigger","_selectNoUser","_triggerNextUserEvent","_checkClickOutsideConfigureFilters","event","configPanel","is","target","has","hide","unbind","_updateFilterPreferences","userId","filterList","preferenceNames","preferences","i","deferred","Deferred","resolve","newValue","push","name","value","filters","each","idx","ele","element","prop","filterlist","text","join","currentUserID","foundIndex","index","user","id","_refreshCount","checkFormForChanges","get_strings","key","component","strs","confirm","useridnumber","isNaN","preventDefault","closest","expanded","css","stopPropagation","set","href","e","currentUserId","currentIndex","count","newIndex","saved","_setCountString","x","y","updateNumber","param","show","newid","recordid","nextUserId","nextUser"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"grading_navigation.min.js","sources":["../src/grading_navigation.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript to handle changing users via the user selector in the header.\n *\n * @module mod_assign/grading_navigation\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(['jquery', 'core/notification', 'core/str', 'core/form-autocomplete',\n 'core/ajax', 'core_user/repository', 'mod_assign/grading_form_change_checker'],\n function($, notification, str, autocomplete, ajax, UserRepository, checker) {\n\n /**\n * GradingNavigation class.\n *\n * @class mod_assign/grading_navigation\n * @param {String} selector The selector for the page region containing the user navigation.\n */\n var GradingNavigation = function(selector) {\n this._regionSelector = selector;\n this._region = $(selector);\n this._filters = [];\n this._users = [];\n this._filteredUsers = [];\n this._lastXofYUpdate = 0;\n this._firstLoadUsers = true;\n\n let url = new URL(window.location);\n if (parseInt(url.searchParams.get('treset')) > 0) {\n // Remove 'treset' url parameter to make sure that\n // table preferences won't be reset on page refresh.\n url.searchParams.delete('treset');\n window.history.replaceState({}, \"\", url);\n }\n\n // Get the current user list from a webservice.\n this._loadAllUsers();\n\n // We do not allow navigation while ajax requests are pending.\n // Attach listeners to the select and arrow buttons.\n\n this._region.find('[data-action=\"previous-user\"]').on('click', this._handlePreviousUser.bind(this));\n this._region.find('[data-action=\"next-user\"]').on('click', this._handleNextUser.bind(this));\n this._region.find('[data-action=\"change-user\"]').on('change', this._handleChangeUser.bind(this));\n this._region.find('[data-region=\"user-filters\"]').on('click', this._toggleExpandFilters.bind(this));\n this._region.find('[data-region=\"user-resettable\"]').on('click', this._toggleResetTable.bind());\n\n $(document).on('user-changed', this._refreshSelector.bind(this));\n $(document).on('done-saving-show-next', this._handleNextUser.bind(this));\n\n // Position the configure filters panel under the link that expands it.\n var toggleLink = this._region.find('[data-region=\"user-filters\"]');\n var configPanel = $(document.getElementById(toggleLink.attr('aria-controls')));\n\n configPanel.on('change', 'select', this._filterChanged.bind(this));\n\n var userid = $('[data-region=\"grading-navigation-panel\"]').data('first-userid');\n if (userid) {\n this._selectUserById(userid);\n }\n\n str.get_string('changeuser', 'mod_assign').done(function(s) {\n autocomplete.enhance('[data-action=change-user]', false, 'mod_assign/participant_selector', s);\n }\n ).fail(notification.exception);\n\n $(document).bind(\"start-loading-user\", function() {\n this._isLoading = true;\n }.bind(this));\n $(document).bind(\"finish-loading-user\", function() {\n this._isLoading = false;\n }.bind(this));\n };\n\n /** @property {Boolean} Boolean tracking active ajax requests. */\n GradingNavigation.prototype._isLoading = false;\n\n /** @property {String} Selector for the page region containing the user navigation. */\n GradingNavigation.prototype._regionSelector = null;\n\n /** @property {Array} The list of active filter keys */\n GradingNavigation.prototype._filters = null;\n\n /** @property {Array} The list of users */\n GradingNavigation.prototype._users = null;\n\n /** @property {JQuery} JQuery node for the page region containing the user navigation. */\n GradingNavigation.prototype._region = null;\n\n /** @property {String} Last active filters */\n GradingNavigation.prototype._lastFilters = '';\n\n /**\n * Load the list of all users for this assignment.\n *\n * @private\n * @method _loadAllUsers\n * @return {Boolean} True if the user list was fetched.\n */\n GradingNavigation.prototype._loadAllUsers = function() {\n var select = this._region.find('[data-action=change-user]');\n var assignmentid = select.attr('data-assignmentid');\n var groupid = select.attr('data-groupid');\n\n var filterPanel = this._region.find('[data-region=\"configure-filters\"]');\n var filter = filterPanel.find('select[name=\"filter\"]').val();\n var workflowFilter = filterPanel.find('select[name=\"workflowfilter\"]');\n if (workflowFilter) {\n filter += ',' + workflowFilter.val();\n }\n var markerFilter = filterPanel.find('select[name=\"markerfilter\"]');\n if (markerFilter) {\n filter += ',' + markerFilter.val();\n }\n\n if (this._lastFilters == filter) {\n return false;\n }\n this._lastFilters = filter;\n\n ajax.call([{\n methodname: 'mod_assign_list_participants',\n args: {assignid: assignmentid, groupid: groupid, filter: '', onlyids: true, tablesort: true},\n done: this._usersLoaded.bind(this),\n fail: notification.exception\n }]);\n return true;\n };\n\n /**\n * Call back to rebuild the user selector and x of y info when the user list is updated.\n *\n * @private\n * @method _usersLoaded\n * @param {Array} users\n */\n GradingNavigation.prototype._usersLoaded = function(users) {\n this._firstLoadUsers = false;\n this._filteredUsers = this._users = users;\n if (this._users.length) {\n // Position the configure filters panel under the link that expands it.\n var toggleLink = this._region.find('[data-region=\"user-filters\"]');\n var configPanel = $(document.getElementById(toggleLink.attr('aria-controls')));\n\n configPanel.find('select[name=\"filter\"]').trigger('change');\n } else {\n this._selectNoUser();\n }\n this._triggerNextUserEvent();\n };\n\n /**\n * Close the configure filters panel if a click is detected outside of it.\n *\n * @private\n * @method _checkClickOutsideConfigureFilters\n * @param {Event} event\n */\n GradingNavigation.prototype._checkClickOutsideConfigureFilters = function(event) {\n var configPanel = this._region.find('[data-region=\"configure-filters\"]');\n\n if (!configPanel.is(event.target) && configPanel.has(event.target).length === 0) {\n var toggleLink = this._region.find('[data-region=\"user-filters\"]');\n\n configPanel.hide();\n configPanel.attr('aria-hidden', 'true');\n toggleLink.attr('aria-expanded', 'false');\n $(document).unbind('click.mod_assign_grading_navigation');\n }\n };\n\n /**\n * Close the configure filters panel if a click is detected outside of it.\n *\n * @private\n * @method _updateFilterPreference\n * @param {Number} userId The current user id.\n * @param {Array} filterList The list of current filter values.\n * @param {Array} preferenceNames The names of the preferences to update\n * @return {Promise} Resolved when all the preferences are updated.\n */\n GradingNavigation.prototype._updateFilterPreferences = function(userId, filterList, preferenceNames) {\n var preferences = [],\n i = 0;\n\n if (filterList.length == 0 || this._firstLoadUsers) {\n // Nothing to update.\n var deferred = $.Deferred();\n deferred.resolve();\n return deferred;\n }\n // General filter.\n // Set the user preferences to the current filters.\n for (i = 0; i < filterList.length; i++) {\n var newValue = filterList[i];\n if (newValue == 'none') {\n newValue = '';\n }\n\n preferences.push({\n userid: userId,\n name: preferenceNames[i],\n value: newValue\n });\n }\n\n return UserRepository.setUserPreferences(preferences);\n };\n /**\n * Turn a filter on or off.\n *\n * @private\n * @method _filterChanged\n */\n GradingNavigation.prototype._filterChanged = function() {\n // There are 3 types of filter right now.\n var filterPanel = this._region.find('[data-region=\"configure-filters\"]');\n var filters = filterPanel.find('select');\n var preferenceNames = [];\n\n this._filters = [];\n filters.each(function(idx, ele) {\n var element = $(ele);\n this._filters.push(element.val());\n preferenceNames.push('assign_' + element.prop('name'));\n }.bind(this));\n\n // Update the active filter string.\n var filterlist = [];\n filterPanel.find('option:checked').each(function(idx, ele) {\n filterlist[filterlist.length] = $(ele).text();\n });\n if (filterlist.length) {\n this._region.find('[data-region=\"user-filters\"] span').text(filterlist.join(', '));\n } else {\n str.get_string('nofilters', 'mod_assign').done(function(s) {\n this._region.find('[data-region=\"user-filters\"] span').text(s);\n }.bind(this)).fail(notification.exception);\n }\n\n var select = this._region.find('[data-action=change-user]');\n var currentUserID = select.data('currentuserid');\n this._updateFilterPreferences(currentUserID, this._filters, preferenceNames).done(function() {\n // Reload the list of users to apply the new filters.\n if (!this._loadAllUsers()) {\n var userid = parseInt(select.attr('data-selected'));\n let foundIndex = null;\n // Search the returned users for the current selection.\n $.each(this._filteredUsers, function(index, user) {\n if (userid == user.id) {\n foundIndex = index;\n }\n });\n\n if (this._filteredUsers.length && foundIndex !== null) {\n this._selectUserById(this._filteredUsers[foundIndex].id);\n } else {\n this._selectNoUser();\n }\n\n }\n }.bind(this)).fail(notification.exception);\n this._refreshCount();\n };\n\n /**\n * Select no users, because no users match the filters.\n *\n * @private\n * @method _selectNoUser\n */\n GradingNavigation.prototype._selectNoUser = function() {\n // Detect unsaved changes, and offer to save them - otherwise change user right now.\n if (this._isLoading) {\n return;\n }\n if (checker.checkFormForChanges('[data-region=\"grade-panel\"] .gradeform')) {\n // Form has changes, so we need to confirm before switching users.\n str.get_strings([\n {key: 'unsavedchanges', component: 'mod_assign'},\n {key: 'unsavedchangesquestion', component: 'mod_assign'},\n {key: 'saveandcontinue', component: 'mod_assign'},\n {key: 'cancel', component: 'core'},\n ]).done(function(strs) {\n notification.confirm(strs[0], strs[1], strs[2], strs[3], function() {\n $(document).trigger('save-changes', -1);\n });\n });\n } else {\n $(document).trigger('user-changed', -1);\n }\n };\n\n /**\n * Select the specified user by id.\n *\n * @private\n * @method _selectUserById\n * @param {Number} userid\n */\n GradingNavigation.prototype._selectUserById = function(userid) {\n var select = this._region.find('[data-action=change-user]');\n var useridnumber = parseInt(userid, 10);\n\n // Detect unsaved changes, and offer to save them - otherwise change user right now.\n if (this._isLoading) {\n return;\n }\n if (checker.checkFormForChanges('[data-region=\"grade-panel\"] .gradeform')) {\n // Form has changes, so we need to confirm before switching users.\n str.get_strings([\n {key: 'unsavedchanges', component: 'mod_assign'},\n {key: 'unsavedchangesquestion', component: 'mod_assign'},\n {key: 'saveandcontinue', component: 'mod_assign'},\n {key: 'cancel', component: 'core'},\n ]).done(function(strs) {\n notification.confirm(strs[0], strs[1], strs[2], strs[3], function() {\n $(document).trigger('save-changes', useridnumber);\n });\n });\n } else {\n select.attr('data-selected', userid);\n\n if (!isNaN(useridnumber) && useridnumber > 0) {\n $(document).trigger('user-changed', userid);\n }\n }\n };\n\n /**\n * Expand or collapse the filter config panel.\n *\n * @private\n * @method _toggleExpandFilters\n * @param {Event} event\n */\n GradingNavigation.prototype._toggleExpandFilters = function(event) {\n event.preventDefault();\n var toggleLink = $(event.target).closest('[data-region=\"user-filters\"]');\n var expanded = toggleLink.attr('aria-expanded') == 'true';\n var configPanel = $(document.getElementById(toggleLink.attr('aria-controls')));\n\n if (expanded) {\n configPanel.hide();\n configPanel.attr('aria-hidden', 'true');\n toggleLink.attr('aria-expanded', 'false');\n $(document).unbind('click.mod_assign_grading_navigation');\n } else {\n configPanel.css('display', 'inline-block');\n configPanel.attr('aria-hidden', 'false');\n toggleLink.attr('aria-expanded', 'true');\n event.stopPropagation();\n $(document).on('click.mod_assign_grading_navigation', this._checkClickOutsideConfigureFilters.bind(this));\n }\n };\n\n /**\n * Reset table preferences.\n *\n * @private\n * @method _toggleResetTable\n */\n GradingNavigation.prototype._toggleResetTable = function() {\n let url = new URL(window.location);\n url.searchParams.set('treset', '1');\n window.location.href = url;\n };\n\n /**\n * Change to the previous user in the grading list.\n *\n * @private\n * @method _handlePreviousUser\n * @param {Event} e\n */\n GradingNavigation.prototype._handlePreviousUser = function(e) {\n e.preventDefault();\n var select = this._region.find('[data-action=change-user]');\n var currentUserId = select.attr('data-selected');\n var i = 0;\n var currentIndex = 0;\n\n for (i = 0; i < this._filteredUsers.length; i++) {\n if (this._filteredUsers[i].id == currentUserId) {\n currentIndex = i;\n break;\n }\n }\n\n var count = this._filteredUsers.length;\n var newIndex = (currentIndex - 1);\n if (newIndex < 0) {\n newIndex = count - 1;\n }\n\n if (count) {\n this._selectUserById(this._filteredUsers[newIndex].id);\n }\n };\n\n /**\n * Change to the next user in the grading list.\n *\n * @param {Event} e\n * @param {Boolean} saved Has the form already been saved? Skips checking for changes if true.\n */\n GradingNavigation.prototype._handleNextUser = function(e, saved) {\n e.preventDefault();\n var select = this._region.find('[data-action=change-user]');\n var currentUserId = select.attr('data-selected');\n var i = 0;\n var currentIndex = 0;\n\n for (i = 0; i < this._filteredUsers.length; i++) {\n if (this._filteredUsers[i].id == currentUserId) {\n currentIndex = i;\n break;\n }\n }\n\n var count = this._filteredUsers.length;\n var newIndex = (currentIndex + 1) % count;\n\n if (saved && count) {\n // If we've already saved the grade, skip checking if we've made any changes.\n var userid = this._filteredUsers[newIndex].id;\n var useridnumber = parseInt(userid, 10);\n select.attr('data-selected', userid);\n if (!isNaN(useridnumber) && useridnumber > 0) {\n $(document).trigger('user-changed', userid);\n }\n } else if (count) {\n this._selectUserById(this._filteredUsers[newIndex].id);\n }\n };\n\n /**\n * Set count string. This method only sets the value for the last time it was ever called to deal\n * with promises that return in a non-predictable order.\n *\n * @private\n * @method _setCountString\n * @param {Number} x\n * @param {Number} y\n */\n GradingNavigation.prototype._setCountString = function(x, y) {\n var updateNumber = 0;\n this._lastXofYUpdate++;\n updateNumber = this._lastXofYUpdate;\n\n var param = {x: x, y: y};\n str.get_string('xofy', 'mod_assign', param).done(function(s) {\n if (updateNumber == this._lastXofYUpdate) {\n this._region.find('[data-region=\"user-count-summary\"]').text(s);\n }\n }.bind(this)).fail(notification.exception);\n };\n\n /**\n * Rebuild the x of y string.\n *\n * @private\n * @method _refreshCount\n */\n GradingNavigation.prototype._refreshCount = function() {\n var select = this._region.find('[data-action=change-user]');\n var userid = select.attr('data-selected');\n var i = 0;\n var currentIndex = 0;\n\n if (isNaN(userid) || userid <= 0) {\n this._region.find('[data-region=\"user-count\"]').hide();\n } else {\n this._region.find('[data-region=\"user-count\"]').show();\n\n for (i = 0; i < this._filteredUsers.length; i++) {\n if (this._filteredUsers[i].id == userid) {\n currentIndex = i;\n break;\n }\n }\n var count = this._filteredUsers.length;\n if (count) {\n currentIndex += 1;\n }\n this._setCountString(currentIndex, count);\n // Update window URL\n if (currentIndex > 0) {\n var url = new URL(window.location);\n if (parseInt(url.searchParams.get('blindid')) > 0) {\n var newid = this._filteredUsers[currentIndex - 1].recordid;\n url.searchParams.set('blindid', newid);\n } else {\n url.searchParams.set('userid', userid);\n }\n // We do this so a browser refresh will return to the same user.\n window.history.replaceState({}, \"\", url);\n }\n }\n };\n\n /**\n * Respond to a user-changed event by updating the selector.\n *\n * @private\n * @method _refreshSelector\n * @param {Event} event\n * @param {String} userid\n */\n GradingNavigation.prototype._refreshSelector = function(event, userid) {\n var select = this._region.find('[data-action=change-user]');\n userid = parseInt(userid, 10);\n\n if (!isNaN(userid) && userid > 0) {\n select.attr('data-selected', userid);\n }\n this._refreshCount();\n };\n\n /**\n * Trigger the next user event depending on the number of filtered users\n *\n * @private\n * @method _triggerNextUserEvent\n */\n GradingNavigation.prototype._triggerNextUserEvent = function() {\n if (this._filteredUsers.length > 1) {\n $(document).trigger('next-user', {nextUserId: null, nextUser: true});\n } else {\n $(document).trigger('next-user', {nextUser: false});\n }\n };\n\n /**\n * Change to a different user in the grading list.\n *\n * @private\n * @method _handleChangeUser\n */\n GradingNavigation.prototype._handleChangeUser = function() {\n var select = this._region.find('[data-action=change-user]');\n var userid = parseInt(select.val(), 10);\n\n if (this._isLoading) {\n return;\n }\n if (checker.checkFormForChanges('[data-region=\"grade-panel\"] .gradeform')) {\n // Form has changes, so we need to confirm before switching users.\n str.get_strings([\n {key: 'unsavedchanges', component: 'mod_assign'},\n {key: 'unsavedchangesquestion', component: 'mod_assign'},\n {key: 'saveandcontinue', component: 'mod_assign'},\n {key: 'cancel', component: 'core'},\n ]).done(function(strs) {\n notification.confirm(strs[0], strs[1], strs[2], strs[3], function() {\n $(document).trigger('save-changes', userid);\n });\n });\n } else {\n if (!isNaN(userid) && userid > 0) {\n select.attr('data-selected', userid);\n\n $(document).trigger('user-changed', userid);\n }\n }\n };\n\n return GradingNavigation;\n});\n"],"names":["define","$","notification","str","autocomplete","ajax","UserRepository","checker","GradingNavigation","selector","_regionSelector","_region","_filters","_users","_filteredUsers","_lastXofYUpdate","_firstLoadUsers","url","URL","window","location","parseInt","searchParams","get","delete","history","replaceState","_loadAllUsers","find","on","this","_handlePreviousUser","bind","_handleNextUser","_handleChangeUser","_toggleExpandFilters","_toggleResetTable","document","_refreshSelector","toggleLink","getElementById","attr","_filterChanged","userid","data","_selectUserById","get_string","done","s","enhance","fail","exception","_isLoading","prototype","_lastFilters","select","assignmentid","groupid","filterPanel","filter","val","workflowFilter","markerFilter","call","methodname","args","assignid","onlyids","tablesort","_usersLoaded","users","length","trigger","_selectNoUser","_triggerNextUserEvent","_checkClickOutsideConfigureFilters","event","configPanel","is","target","has","hide","unbind","_updateFilterPreferences","userId","filterList","preferenceNames","preferences","i","deferred","Deferred","resolve","newValue","push","name","value","setUserPreferences","filters","each","idx","ele","element","prop","filterlist","text","join","currentUserID","foundIndex","index","user","id","_refreshCount","checkFormForChanges","get_strings","key","component","strs","confirm","useridnumber","isNaN","preventDefault","closest","expanded","css","stopPropagation","set","href","e","currentUserId","currentIndex","count","newIndex","saved","_setCountString","x","y","updateNumber","param","show","newid","recordid","nextUserId","nextUser"],"mappings":"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"} \ No newline at end of file diff --git a/mod/assign/amd/src/grading_navigation.js b/mod/assign/amd/src/grading_navigation.js index fd3a09ced8e..fd95461d784 100644 --- a/mod/assign/amd/src/grading_navigation.js +++ b/mod/assign/amd/src/grading_navigation.js @@ -22,8 +22,8 @@ * @since 3.1 */ define(['jquery', 'core/notification', 'core/str', 'core/form-autocomplete', - 'core/ajax', 'mod_assign/grading_form_change_checker'], - function($, notification, str, autocomplete, ajax, checker) { + 'core/ajax', 'core_user/repository', 'mod_assign/grading_form_change_checker'], + function($, notification, str, autocomplete, ajax, UserRepository, checker) { /** * GradingNavigation class. @@ -219,12 +219,7 @@ define(['jquery', 'core/notification', 'core/str', 'core/form-autocomplete', }); } - return ajax.call([{ - methodname: 'core_user_set_user_preferences', - args: { - preferences: preferences - } - }])[0]; + return UserRepository.setUserPreferences(preferences); }; /** * Turn a filter on or off. From 82d7635d52f92d8d0264d1ef1d1f54736572b145 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Mon, 22 May 2023 16:32:28 +0800 Subject: [PATCH 3/4] MDL-62859 block_timeline: Rewrite view navigation as ESM --- blocks/timeline/amd/build/view_nav.min.js | 12 +- blocks/timeline/amd/build/view_nav.min.js.map | 2 +- blocks/timeline/amd/src/view_nav.js | 320 +++++++++--------- 3 files changed, 159 insertions(+), 175 deletions(-) diff --git a/blocks/timeline/amd/build/view_nav.min.js b/blocks/timeline/amd/build/view_nav.min.js index d144b7022c2..f33686eaad7 100644 --- a/blocks/timeline/amd/build/view_nav.min.js +++ b/blocks/timeline/amd/build/view_nav.min.js @@ -1,9 +1,9 @@ +define("block_timeline/view_nav",["exports","jquery","core/custom_interaction_events","block_timeline/view","core/notification","core/utils","core_user/repository"],(function(_exports,_jquery,CustomEvents,View,Notification,Utils,UserRepository){var obj; /** - * Manage the timeline view navigation for the timeline block. - * - * @copyright 2018 Ryan Wyllie - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -define("block_timeline/view_nav",["jquery","core/custom_interaction_events","block_timeline/view","core/notification","core/utils","core_user/repository"],(function($,CustomEvents,View,Notification,Utils,UserRepository){var SELECTORS_TIMELINE_DAY_FILTER='[data-region="day-filter"]',SELECTORS_TIMELINE_DAY_FILTER_OPTION="[data-from]",SELECTORS_TIMELINE_VIEW_SELECTOR='[data-region="view-selector"]',SELECTORS_DATA_DAYS_OFFSET="[data-days-offset]",SELECTORS_TIMELINE_SEARCH_INPUT='[data-action="search"]',SELECTORS_TIMELINE_SEARCH_CLEAR_ICON='[data-action="clearsearch"]',SELECTORS_NO_COURSES_EMPTY_MESSAGE='[data-region="no-courses-empty-message"]';const activeSearchState=(clearSearchIcon,timelineViewRoot)=>{clearSearchIcon.removeClass("d-none"),View.reset(timelineViewRoot)},clearSearchState=(clearSearchIcon,timelineViewRoot)=>{clearSearchIcon.addClass("d-none"),View.reset(timelineViewRoot)};return{init:function(root,timelineViewRoot){(function(root,timelineViewRoot){var viewSelector=root.find(SELECTORS_TIMELINE_VIEW_SELECTOR);viewSelector.on("shown shown.bs.tab",(function(e){View.shown(timelineViewRoot),$(e.target).removeClass("active")})),CustomEvents.define(viewSelector,[CustomEvents.events.activate]),viewSelector.on(CustomEvents.events.activate,"[data-toggle='tab']",(function(e){var filtername=$(e.currentTarget).data("filtername");UserRepository.setUserPreference("block_timeline_user_sort_preference",filtername).catch(Notification.exception)}))})(root=$(root),timelineViewRoot),root.find(SELECTORS_NO_COURSES_EMPTY_MESSAGE).length||(function(root,timelineViewRoot){var timelineDaySelectorContainer=root.find(SELECTORS_TIMELINE_DAY_FILTER);CustomEvents.define(timelineDaySelectorContainer,[CustomEvents.events.activate]),timelineDaySelectorContainer.on(CustomEvents.events.activate,SELECTORS_TIMELINE_DAY_FILTER_OPTION,(function(e,data){var filtername=$(e.currentTarget).data("filtername");UserRepository.setUserPreference("block_timeline_user_filter_preference",filtername).catch(Notification.exception);var option=$(e.target).closest(SELECTORS_TIMELINE_DAY_FILTER_OPTION);if("true"!=option.attr("aria-current")){var daysOffset=option.attr("data-from"),daysLimit=option.attr("data-to"),elementsWithDaysOffset=root.find(SELECTORS_DATA_DAYS_OFFSET);elementsWithDaysOffset.attr("data-days-offset",daysOffset),null!=daysLimit?elementsWithDaysOffset.attr("data-days-limit",daysLimit):elementsWithDaysOffset.removeAttr("data-days-limit"),"overdue"===option.attr("data-filtername")?elementsWithDaysOffset.attr("data-filter-overdue",!0):elementsWithDaysOffset.removeAttr("data-filter-overdue"),View.reset(timelineViewRoot),data.originalEvent.preventDefault()}}))}(root,timelineViewRoot),((root,timelineViewRoot)=>{const searchInput=root.find(SELECTORS_TIMELINE_SEARCH_INPUT),clearSearchIcon=root.find(SELECTORS_TIMELINE_SEARCH_CLEAR_ICON);searchInput.on("input",Utils.debounce((()=>{""!==searchInput.val()?activeSearchState(clearSearchIcon,timelineViewRoot):clearSearchState(clearSearchIcon,timelineViewRoot)}),1e3)),clearSearchIcon.on("click",(()=>{searchInput.val(""),clearSearchState(clearSearchIcon,timelineViewRoot),searchInput.focus()}))})(root,timelineViewRoot))}}})); + * Manage the timeline view navigation for the timeline block. + * + * @copyright 2018 Ryan Wyllie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj},CustomEvents=_interopRequireWildcard(CustomEvents),View=_interopRequireWildcard(View),Notification=_interopRequireWildcard(Notification),Utils=_interopRequireWildcard(Utils),UserRepository=_interopRequireWildcard(UserRepository);const SELECTORS_TIMELINE_DAY_FILTER='[data-region="day-filter"]',SELECTORS_TIMELINE_DAY_FILTER_OPTION="[data-from]",SELECTORS_TIMELINE_VIEW_SELECTOR='[data-region="view-selector"]',SELECTORS_DATA_DAYS_OFFSET="[data-days-offset]",SELECTORS_TIMELINE_SEARCH_INPUT='[data-action="search"]',SELECTORS_TIMELINE_SEARCH_CLEAR_ICON='[data-action="clearsearch"]',SELECTORS_NO_COURSES_EMPTY_MESSAGE='[data-region="no-courses-empty-message"]',activeSearchState=(clearSearchIcon,timelineViewRoot)=>{clearSearchIcon.removeClass("d-none"),View.reset(timelineViewRoot)},clearSearchState=(clearSearchIcon,timelineViewRoot)=>{clearSearchIcon.addClass("d-none"),View.reset(timelineViewRoot)};_exports.init=function(root,timelineViewRoot){(function(root,timelineViewRoot){const viewSelector=root.find(SELECTORS_TIMELINE_VIEW_SELECTOR);viewSelector.on("shown shown.bs.tab",(function(e){View.shown(timelineViewRoot),(0,_jquery.default)(e.target).removeClass("active")})),CustomEvents.define(viewSelector,[CustomEvents.events.activate]),viewSelector.on(CustomEvents.events.activate,"[data-toggle='tab']",(function(e){var filtername=(0,_jquery.default)(e.currentTarget).data("filtername");UserRepository.setUserPreference("block_timeline_user_sort_preference",filtername).catch(Notification.exception)}))})(root=(0,_jquery.default)(root),timelineViewRoot),root.find(SELECTORS_NO_COURSES_EMPTY_MESSAGE).length||(function(root,timelineViewRoot){const timelineDaySelectorContainer=root.find(SELECTORS_TIMELINE_DAY_FILTER);CustomEvents.define(timelineDaySelectorContainer,[CustomEvents.events.activate]),timelineDaySelectorContainer.on(CustomEvents.events.activate,SELECTORS_TIMELINE_DAY_FILTER_OPTION,(function(e,data){var filtername=(0,_jquery.default)(e.currentTarget).data("filtername");UserRepository.setUserPreference("block_timeline_user_filter_preference",filtername).catch(Notification.exception);var option=(0,_jquery.default)(e.target).closest(SELECTORS_TIMELINE_DAY_FILTER_OPTION);if("true"!=option.attr("aria-current")){var daysOffset=option.attr("data-from"),daysLimit=option.attr("data-to"),elementsWithDaysOffset=root.find(SELECTORS_DATA_DAYS_OFFSET);elementsWithDaysOffset.attr("data-days-offset",daysOffset),null!=daysLimit?elementsWithDaysOffset.attr("data-days-limit",daysLimit):elementsWithDaysOffset.removeAttr("data-days-limit"),"overdue"===option.attr("data-filtername")?elementsWithDaysOffset.attr("data-filter-overdue",!0):elementsWithDaysOffset.removeAttr("data-filter-overdue"),View.reset(timelineViewRoot),data.originalEvent.preventDefault()}}))}(root,timelineViewRoot),((root,timelineViewRoot)=>{const searchInput=root.find(SELECTORS_TIMELINE_SEARCH_INPUT),clearSearchIcon=root.find(SELECTORS_TIMELINE_SEARCH_CLEAR_ICON);searchInput.on("input",Utils.debounce((()=>{""!==searchInput.val()?activeSearchState(clearSearchIcon,timelineViewRoot):clearSearchState(clearSearchIcon,timelineViewRoot)}),1e3)),clearSearchIcon.on("click",(()=>{searchInput.val(""),clearSearchState(clearSearchIcon,timelineViewRoot),searchInput.focus()}))})(root,timelineViewRoot))}})); //# sourceMappingURL=view_nav.min.js.map \ No newline at end of file diff --git a/blocks/timeline/amd/build/view_nav.min.js.map b/blocks/timeline/amd/build/view_nav.min.js.map index 855bce1c620..f58acf683ec 100644 --- a/blocks/timeline/amd/build/view_nav.min.js.map +++ b/blocks/timeline/amd/build/view_nav.min.js.map @@ -1 +1 @@ -{"version":3,"file":"view_nav.min.js","sources":["../src/view_nav.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 timeline view navigation for the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n[\n 'jquery',\n 'core/custom_interaction_events',\n 'block_timeline/view',\n 'core/notification',\n 'core/utils',\n 'core_user/repository'\n],\nfunction(\n $,\n CustomEvents,\n View,\n Notification,\n Utils,\n UserRepository,\n) {\n\n var SELECTORS = {\n TIMELINE_DAY_FILTER: '[data-region=\"day-filter\"]',\n TIMELINE_DAY_FILTER_OPTION: '[data-from]',\n TIMELINE_VIEW_SELECTOR: '[data-region=\"view-selector\"]',\n DATA_DAYS_OFFSET: '[data-days-offset]',\n DATA_DAYS_LIMIT: '[data-days-limit]',\n TIMELINE_SEARCH_INPUT: '[data-action=\"search\"]',\n TIMELINE_SEARCH_CLEAR_ICON: '[data-action=\"clearsearch\"]',\n NO_COURSES_EMPTY_MESSAGE: '[data-region=\"no-courses-empty-message\"]',\n };\n\n /**\n * Event listener for the day selector (\"Next 7 days\", \"Next 30 days\", etc).\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n var registerTimelineDaySelector = function(root, timelineViewRoot) {\n var timelineDaySelectorContainer = root.find(SELECTORS.TIMELINE_DAY_FILTER);\n\n CustomEvents.define(timelineDaySelectorContainer, [CustomEvents.events.activate]);\n timelineDaySelectorContainer.on(\n CustomEvents.events.activate,\n SELECTORS.TIMELINE_DAY_FILTER_OPTION,\n function(e, data) {\n // Update the user preference\n var filtername = $(e.currentTarget).data('filtername');\n var type = 'block_timeline_user_filter_preference';\n UserRepository.setUserPreference(type, filtername)\n .catch(Notification.exception);\n\n var option = $(e.target).closest(SELECTORS.TIMELINE_DAY_FILTER_OPTION);\n\n if (option.attr('aria-current') == 'true') {\n // If it's already active then we don't need to do anything.\n return;\n }\n\n var daysOffset = option.attr('data-from');\n var daysLimit = option.attr('data-to');\n var elementsWithDaysOffset = root.find(SELECTORS.DATA_DAYS_OFFSET);\n\n elementsWithDaysOffset.attr('data-days-offset', daysOffset);\n\n if (daysLimit != undefined) {\n elementsWithDaysOffset.attr('data-days-limit', daysLimit);\n } else {\n elementsWithDaysOffset.removeAttr('data-days-limit');\n }\n\n if (option.attr('data-filtername') === 'overdue') {\n elementsWithDaysOffset.attr('data-filter-overdue', true);\n } else {\n elementsWithDaysOffset.removeAttr('data-filter-overdue');\n }\n\n // Reset the views to reinitialise the event lists now that we've\n // updated the day limits.\n View.reset(timelineViewRoot);\n\n data.originalEvent.preventDefault();\n }\n );\n };\n\n /**\n * Event listener for the \"sort\" button in the timeline navigation that allows for\n * changing between the timeline dates and courses views.\n *\n * On a view change we tell the timeline view module that the view has been shown\n * so that it can handle how to display the appropriate view.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n var registerViewSelector = function(root, timelineViewRoot) {\n var viewSelector = root.find(SELECTORS.TIMELINE_VIEW_SELECTOR);\n\n // Listen for when the user changes tab so that we can show the first set of courses\n // and load their events when they request the sort by courses view for the first time.\n viewSelector.on('shown shown.bs.tab', function(e) {\n View.shown(timelineViewRoot);\n $(e.target).removeClass('active');\n });\n\n\n // Event selector for user_sort\n CustomEvents.define(viewSelector, [CustomEvents.events.activate]);\n viewSelector.on(CustomEvents.events.activate, \"[data-toggle='tab']\", function(e) {\n var filtername = $(e.currentTarget).data('filtername');\n var type = 'block_timeline_user_sort_preference';\n UserRepository.setUserPreference(type, filtername)\n .catch(Notification.exception);\n });\n };\n\n /**\n * Event listener for the \"search\" input field in the timeline navigation that allows for\n * searching the activity name, course name and activity type.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n const registerSearch = (root, timelineViewRoot) => {\n const searchInput = root.find(SELECTORS.TIMELINE_SEARCH_INPUT);\n const clearSearchIcon = root.find(SELECTORS.TIMELINE_SEARCH_CLEAR_ICON);\n searchInput.on('input', Utils.debounce(() => {\n if (searchInput.val() !== '') {\n activeSearchState(clearSearchIcon, timelineViewRoot);\n } else {\n clearSearchState(clearSearchIcon, timelineViewRoot);\n }\n }, 1000));\n clearSearchIcon.on('click', () => {\n searchInput.val('');\n clearSearchState(clearSearchIcon, timelineViewRoot);\n searchInput.focus();\n });\n };\n\n /**\n * Show the clear search icon.\n *\n * @param {object} clearSearchIcon Clear search icon element.\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n const activeSearchState = (clearSearchIcon, timelineViewRoot) => {\n clearSearchIcon.removeClass('d-none');\n View.reset(timelineViewRoot);\n };\n\n /**\n * Hide the clear search icon.\n *\n * @param {object} clearSearchIcon Clear search icon element.\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n const clearSearchState = (clearSearchIcon, timelineViewRoot) => {\n clearSearchIcon.addClass('d-none');\n View.reset(timelineViewRoot);\n };\n\n /**\n * Initialise the timeline view navigation by adding event listeners to\n * the navigation elements.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n var init = function(root, timelineViewRoot) {\n root = $(root);\n\n registerViewSelector(root, timelineViewRoot);\n\n // Only need to handle filtering if the user is actively enrolled in a course.\n if (!root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length) {\n registerTimelineDaySelector(root, timelineViewRoot);\n registerSearch(root, timelineViewRoot);\n }\n };\n\n return {\n init: init\n };\n});\n"],"names":["define","$","CustomEvents","View","Notification","Utils","UserRepository","SELECTORS","activeSearchState","clearSearchIcon","timelineViewRoot","removeClass","reset","clearSearchState","addClass","init","root","viewSelector","find","on","e","shown","target","events","activate","filtername","currentTarget","data","setUserPreference","catch","exception","registerViewSelector","length","timelineDaySelectorContainer","option","closest","attr","daysOffset","daysLimit","elementsWithDaysOffset","undefined","removeAttr","originalEvent","preventDefault","registerTimelineDaySelector","searchInput","debounce","val","focus","registerSearch"],"mappings":";;;;;;AAsBAA,iCACA,CACI,SACA,iCACA,sBACA,oBACA,aACA,yBAEJ,SACIC,EACAC,aACAC,KACAC,aACAC,MACAC,oBAGIC,8BACqB,6BADrBA,qCAE4B,cAF5BA,iCAGwB,gCAHxBA,2BAIkB,qBAJlBA,gCAMuB,yBANvBA,qCAO4B,8BAP5BA,mCAQ0B,iDAsHxBC,kBAAoB,CAACC,gBAAiBC,oBACxCD,gBAAgBE,YAAY,UAC5BR,KAAKS,MAAMF,mBASTG,iBAAmB,CAACJ,gBAAiBC,oBACvCD,gBAAgBK,SAAS,UACzBX,KAAKS,MAAMF,yBAsBR,CACHK,KAbO,SAASC,KAAMN,mBA1EC,SAASM,KAAMN,sBAClCO,aAAeD,KAAKE,KAAKX,kCAI7BU,aAAaE,GAAG,sBAAsB,SAASC,GAC3CjB,KAAKkB,MAAMX,kBACXT,EAAEmB,EAAEE,QAAQX,YAAY,aAK5BT,aAAaF,OAAOiB,aAAc,CAACf,aAAaqB,OAAOC,WACvDP,aAAaE,GAAGjB,aAAaqB,OAAOC,SAAU,uBAAuB,SAASJ,OACtEK,WAAaxB,EAAEmB,EAAEM,eAAeC,KAAK,cAEzCrB,eAAesB,kBADJ,sCAC4BH,YAClCI,MAAMzB,aAAa0B,eA4D5BC,CAFAf,KAAOf,EAAEe,MAEkBN,kBAGtBM,KAAKE,KAAKX,oCAAoCyB,SA1IrB,SAAShB,KAAMN,sBACzCuB,6BAA+BjB,KAAKE,KAAKX,+BAE7CL,aAAaF,OAAOiC,6BAA8B,CAAC/B,aAAaqB,OAAOC,WACvES,6BAA6Bd,GACzBjB,aAAaqB,OAAOC,SACpBjB,sCACA,SAASa,EAAGO,UAEJF,WAAaxB,EAAEmB,EAAEM,eAAeC,KAAK,cAEzCrB,eAAesB,kBADJ,wCAC4BH,YAClCI,MAAMzB,aAAa0B,eAEpBI,OAASjC,EAAEmB,EAAEE,QAAQa,QAAQ5B,yCAEE,QAA/B2B,OAAOE,KAAK,qBAKZC,WAAaH,OAAOE,KAAK,aACzBE,UAAYJ,OAAOE,KAAK,WACxBG,uBAAyBvB,KAAKE,KAAKX,4BAEvCgC,uBAAuBH,KAAK,mBAAoBC,YAE/BG,MAAbF,UACAC,uBAAuBH,KAAK,kBAAmBE,WAE/CC,uBAAuBE,WAAW,mBAGC,YAAnCP,OAAOE,KAAK,mBACZG,uBAAuBH,KAAK,uBAAuB,GAEnDG,uBAAuBE,WAAW,uBAKtCtC,KAAKS,MAAMF,kBAEXiB,KAAKe,cAAcC,qBAgGvBC,CAA4B5B,KAAMN,kBArDnB,EAACM,KAAMN,0BACpBmC,YAAc7B,KAAKE,KAAKX,iCACxBE,gBAAkBO,KAAKE,KAAKX,sCAClCsC,YAAY1B,GAAG,QAASd,MAAMyC,UAAS,KACT,KAAtBD,YAAYE,MACZvC,kBAAkBC,gBAAiBC,kBAEnCG,iBAAiBJ,gBAAiBC,oBAEvC,MACHD,gBAAgBU,GAAG,SAAS,KACxB0B,YAAYE,IAAI,IAChBlC,iBAAiBJ,gBAAiBC,kBAClCmC,YAAYG,YAyCZC,CAAejC,KAAMN"} \ No newline at end of file +{"version":3,"file":"view_nav.min.js","sources":["../src/view_nav.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 timeline view navigation for the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as View from 'block_timeline/view';\nimport * as Notification from 'core/notification';\nimport * as Utils from 'core/utils';\nimport * as UserRepository from 'core_user/repository';\n\nconst SELECTORS = {\n TIMELINE_DAY_FILTER: '[data-region=\"day-filter\"]',\n TIMELINE_DAY_FILTER_OPTION: '[data-from]',\n TIMELINE_VIEW_SELECTOR: '[data-region=\"view-selector\"]',\n DATA_DAYS_OFFSET: '[data-days-offset]',\n DATA_DAYS_LIMIT: '[data-days-limit]',\n TIMELINE_SEARCH_INPUT: '[data-action=\"search\"]',\n TIMELINE_SEARCH_CLEAR_ICON: '[data-action=\"clearsearch\"]',\n NO_COURSES_EMPTY_MESSAGE: '[data-region=\"no-courses-empty-message\"]',\n};\n\n/**\n * Event listener for the day selector (\"Next 7 days\", \"Next 30 days\", etc).\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\nconst registerTimelineDaySelector = function(root, timelineViewRoot) {\n const timelineDaySelectorContainer = root.find(SELECTORS.TIMELINE_DAY_FILTER);\n\n CustomEvents.define(timelineDaySelectorContainer, [CustomEvents.events.activate]);\n timelineDaySelectorContainer.on(\n CustomEvents.events.activate,\n SELECTORS.TIMELINE_DAY_FILTER_OPTION,\n function(e, data) {\n // Update the user preference\n var filtername = $(e.currentTarget).data('filtername');\n var type = 'block_timeline_user_filter_preference';\n UserRepository.setUserPreference(type, filtername)\n .catch(Notification.exception);\n\n var option = $(e.target).closest(SELECTORS.TIMELINE_DAY_FILTER_OPTION);\n\n if (option.attr('aria-current') == 'true') {\n // If it's already active then we don't need to do anything.\n return;\n }\n\n var daysOffset = option.attr('data-from');\n var daysLimit = option.attr('data-to');\n var elementsWithDaysOffset = root.find(SELECTORS.DATA_DAYS_OFFSET);\n\n elementsWithDaysOffset.attr('data-days-offset', daysOffset);\n\n if (daysLimit != undefined) {\n elementsWithDaysOffset.attr('data-days-limit', daysLimit);\n } else {\n elementsWithDaysOffset.removeAttr('data-days-limit');\n }\n\n if (option.attr('data-filtername') === 'overdue') {\n elementsWithDaysOffset.attr('data-filter-overdue', true);\n } else {\n elementsWithDaysOffset.removeAttr('data-filter-overdue');\n }\n\n // Reset the views to reinitialise the event lists now that we've\n // updated the day limits.\n View.reset(timelineViewRoot);\n\n data.originalEvent.preventDefault();\n }\n );\n};\n\n/**\n * Event listener for the \"sort\" button in the timeline navigation that allows for\n * changing between the timeline dates and courses views.\n *\n * On a view change we tell the timeline view module that the view has been shown\n * so that it can handle how to display the appropriate view.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\nconst registerViewSelector = function(root, timelineViewRoot) {\n const viewSelector = root.find(SELECTORS.TIMELINE_VIEW_SELECTOR);\n\n // Listen for when the user changes tab so that we can show the first set of courses\n // and load their events when they request the sort by courses view for the first time.\n viewSelector.on('shown shown.bs.tab', function(e) {\n View.shown(timelineViewRoot);\n $(e.target).removeClass('active');\n });\n\n\n // Event selector for user_sort\n CustomEvents.define(viewSelector, [CustomEvents.events.activate]);\n viewSelector.on(CustomEvents.events.activate, \"[data-toggle='tab']\", function(e) {\n var filtername = $(e.currentTarget).data('filtername');\n var type = 'block_timeline_user_sort_preference';\n UserRepository.setUserPreference(type, filtername)\n .catch(Notification.exception);\n });\n};\n\n/**\n * Event listener for the \"search\" input field in the timeline navigation that allows for\n * searching the activity name, course name and activity type.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\nconst registerSearch = (root, timelineViewRoot) => {\n const searchInput = root.find(SELECTORS.TIMELINE_SEARCH_INPUT);\n const clearSearchIcon = root.find(SELECTORS.TIMELINE_SEARCH_CLEAR_ICON);\n searchInput.on('input', Utils.debounce(() => {\n if (searchInput.val() !== '') {\n activeSearchState(clearSearchIcon, timelineViewRoot);\n } else {\n clearSearchState(clearSearchIcon, timelineViewRoot);\n }\n }, 1000));\n clearSearchIcon.on('click', () => {\n searchInput.val('');\n clearSearchState(clearSearchIcon, timelineViewRoot);\n searchInput.focus();\n });\n};\n\n/**\n * Show the clear search icon.\n *\n * @param {object} clearSearchIcon Clear search icon element.\n * @param {object} timelineViewRoot The root element for the timeline view\n */\nconst activeSearchState = (clearSearchIcon, timelineViewRoot) => {\n clearSearchIcon.removeClass('d-none');\n View.reset(timelineViewRoot);\n};\n\n/**\n * Hide the clear search icon.\n *\n * @param {object} clearSearchIcon Clear search icon element.\n * @param {object} timelineViewRoot The root element for the timeline view\n */\nconst clearSearchState = (clearSearchIcon, timelineViewRoot) => {\n clearSearchIcon.addClass('d-none');\n View.reset(timelineViewRoot);\n};\n\n/**\n * Initialise the timeline view navigation by adding event listeners to\n * the navigation elements.\n *\n * @param {jQuery|HTMLElement} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\nexport const init = function(root, timelineViewRoot) {\n root = $(root);\n\n registerViewSelector(root, timelineViewRoot);\n\n // Only need to handle filtering if the user is actively enrolled in a course.\n if (!root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length) {\n registerTimelineDaySelector(root, timelineViewRoot);\n registerSearch(root, timelineViewRoot);\n }\n};\n"],"names":["SELECTORS","activeSearchState","clearSearchIcon","timelineViewRoot","removeClass","View","reset","clearSearchState","addClass","root","viewSelector","find","on","e","shown","target","CustomEvents","define","events","activate","filtername","currentTarget","data","UserRepository","setUserPreference","catch","Notification","exception","registerViewSelector","length","timelineDaySelectorContainer","option","closest","attr","daysOffset","daysLimit","elementsWithDaysOffset","undefined","removeAttr","originalEvent","preventDefault","registerTimelineDaySelector","searchInput","Utils","debounce","val","focus","registerSearch"],"mappings":";;;;;;4yCA6BMA,8BACmB,6BADnBA,qCAE0B,cAF1BA,iCAGsB,gCAHtBA,2BAIgB,qBAJhBA,gCAMqB,yBANrBA,qCAO0B,8BAP1BA,mCAQwB,2CAsHxBC,kBAAoB,CAACC,gBAAiBC,oBACxCD,gBAAgBE,YAAY,UAC5BC,KAAKC,MAAMH,mBASTI,iBAAmB,CAACL,gBAAiBC,oBACvCD,gBAAgBM,SAAS,UACzBH,KAAKC,MAAMH,iCAUK,SAASM,KAAMN,mBA1EN,SAASM,KAAMN,wBAClCO,aAAeD,KAAKE,KAAKX,kCAI/BU,aAAaE,GAAG,sBAAsB,SAASC,GAC3CR,KAAKS,MAAMX,sCACTU,EAAEE,QAAQX,YAAY,aAK5BY,aAAaC,OAAOP,aAAc,CAACM,aAAaE,OAAOC,WACvDT,aAAaE,GAAGI,aAAaE,OAAOC,SAAU,uBAAuB,SAASN,OACtEO,YAAa,mBAAEP,EAAEQ,eAAeC,KAAK,cAEzCC,eAAeC,kBADJ,sCAC4BJ,YAClCK,MAAMC,aAAaC,eA4D5BC,CAFAnB,MAAO,mBAAEA,MAEkBN,kBAGtBM,KAAKE,KAAKX,oCAAoC6B,SA1InB,SAASpB,KAAMN,wBACzC2B,6BAA+BrB,KAAKE,KAAKX,+BAE/CgB,aAAaC,OAAOa,6BAA8B,CAACd,aAAaE,OAAOC,WACvEW,6BAA6BlB,GACzBI,aAAaE,OAAOC,SACpBnB,sCACA,SAASa,EAAGS,UAEJF,YAAa,mBAAEP,EAAEQ,eAAeC,KAAK,cAEzCC,eAAeC,kBADJ,wCAC4BJ,YAClCK,MAAMC,aAAaC,eAEpBI,QAAS,mBAAElB,EAAEE,QAAQiB,QAAQhC,yCAEE,QAA/B+B,OAAOE,KAAK,qBAKZC,WAAaH,OAAOE,KAAK,aACzBE,UAAYJ,OAAOE,KAAK,WACxBG,uBAAyB3B,KAAKE,KAAKX,4BAEvCoC,uBAAuBH,KAAK,mBAAoBC,YAE/BG,MAAbF,UACAC,uBAAuBH,KAAK,kBAAmBE,WAE/CC,uBAAuBE,WAAW,mBAGC,YAAnCP,OAAOE,KAAK,mBACZG,uBAAuBH,KAAK,uBAAuB,GAEnDG,uBAAuBE,WAAW,uBAKtCjC,KAAKC,MAAMH,kBAEXmB,KAAKiB,cAAcC,qBAgGvBC,CAA4BhC,KAAMN,kBArDnB,EAACM,KAAMN,0BACpBuC,YAAcjC,KAAKE,KAAKX,iCACxBE,gBAAkBO,KAAKE,KAAKX,sCAClC0C,YAAY9B,GAAG,QAAS+B,MAAMC,UAAS,KACT,KAAtBF,YAAYG,MACZ5C,kBAAkBC,gBAAiBC,kBAEnCI,iBAAiBL,gBAAiBC,oBAEvC,MACHD,gBAAgBU,GAAG,SAAS,KACxB8B,YAAYG,IAAI,IAChBtC,iBAAiBL,gBAAiBC,kBAClCuC,YAAYI,YAyCZC,CAAetC,KAAMN"} \ No newline at end of file diff --git a/blocks/timeline/amd/src/view_nav.js b/blocks/timeline/amd/src/view_nav.js index a57e35f54a1..8e45995a4bf 100644 --- a/blocks/timeline/amd/src/view_nav.js +++ b/blocks/timeline/amd/src/view_nav.js @@ -20,186 +20,170 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define( -[ - 'jquery', - 'core/custom_interaction_events', - 'block_timeline/view', - 'core/notification', - 'core/utils', - 'core_user/repository' -], -function( - $, - CustomEvents, - View, - Notification, - Utils, - UserRepository, -) { +import $ from 'jquery'; +import * as CustomEvents from 'core/custom_interaction_events'; +import * as View from 'block_timeline/view'; +import * as Notification from 'core/notification'; +import * as Utils from 'core/utils'; +import * as UserRepository from 'core_user/repository'; - var SELECTORS = { - TIMELINE_DAY_FILTER: '[data-region="day-filter"]', - TIMELINE_DAY_FILTER_OPTION: '[data-from]', - TIMELINE_VIEW_SELECTOR: '[data-region="view-selector"]', - DATA_DAYS_OFFSET: '[data-days-offset]', - DATA_DAYS_LIMIT: '[data-days-limit]', - TIMELINE_SEARCH_INPUT: '[data-action="search"]', - TIMELINE_SEARCH_CLEAR_ICON: '[data-action="clearsearch"]', - NO_COURSES_EMPTY_MESSAGE: '[data-region="no-courses-empty-message"]', - }; +const SELECTORS = { + TIMELINE_DAY_FILTER: '[data-region="day-filter"]', + TIMELINE_DAY_FILTER_OPTION: '[data-from]', + TIMELINE_VIEW_SELECTOR: '[data-region="view-selector"]', + DATA_DAYS_OFFSET: '[data-days-offset]', + DATA_DAYS_LIMIT: '[data-days-limit]', + TIMELINE_SEARCH_INPUT: '[data-action="search"]', + TIMELINE_SEARCH_CLEAR_ICON: '[data-action="clearsearch"]', + NO_COURSES_EMPTY_MESSAGE: '[data-region="no-courses-empty-message"]', +}; - /** - * Event listener for the day selector ("Next 7 days", "Next 30 days", etc). - * - * @param {object} root The root element for the timeline block - * @param {object} timelineViewRoot The root element for the timeline view - */ - var registerTimelineDaySelector = function(root, timelineViewRoot) { - var timelineDaySelectorContainer = root.find(SELECTORS.TIMELINE_DAY_FILTER); +/** + * Event listener for the day selector ("Next 7 days", "Next 30 days", etc). + * + * @param {object} root The root element for the timeline block + * @param {object} timelineViewRoot The root element for the timeline view + */ +const registerTimelineDaySelector = function(root, timelineViewRoot) { + const timelineDaySelectorContainer = root.find(SELECTORS.TIMELINE_DAY_FILTER); - CustomEvents.define(timelineDaySelectorContainer, [CustomEvents.events.activate]); - timelineDaySelectorContainer.on( - CustomEvents.events.activate, - SELECTORS.TIMELINE_DAY_FILTER_OPTION, - function(e, data) { - // Update the user preference - var filtername = $(e.currentTarget).data('filtername'); - var type = 'block_timeline_user_filter_preference'; - UserRepository.setUserPreference(type, filtername) - .catch(Notification.exception); - - var option = $(e.target).closest(SELECTORS.TIMELINE_DAY_FILTER_OPTION); - - if (option.attr('aria-current') == 'true') { - // If it's already active then we don't need to do anything. - return; - } - - var daysOffset = option.attr('data-from'); - var daysLimit = option.attr('data-to'); - var elementsWithDaysOffset = root.find(SELECTORS.DATA_DAYS_OFFSET); - - elementsWithDaysOffset.attr('data-days-offset', daysOffset); - - if (daysLimit != undefined) { - elementsWithDaysOffset.attr('data-days-limit', daysLimit); - } else { - elementsWithDaysOffset.removeAttr('data-days-limit'); - } - - if (option.attr('data-filtername') === 'overdue') { - elementsWithDaysOffset.attr('data-filter-overdue', true); - } else { - elementsWithDaysOffset.removeAttr('data-filter-overdue'); - } - - // Reset the views to reinitialise the event lists now that we've - // updated the day limits. - View.reset(timelineViewRoot); - - data.originalEvent.preventDefault(); - } - ); - }; - - /** - * Event listener for the "sort" button in the timeline navigation that allows for - * changing between the timeline dates and courses views. - * - * On a view change we tell the timeline view module that the view has been shown - * so that it can handle how to display the appropriate view. - * - * @param {object} root The root element for the timeline block - * @param {object} timelineViewRoot The root element for the timeline view - */ - var registerViewSelector = function(root, timelineViewRoot) { - var viewSelector = root.find(SELECTORS.TIMELINE_VIEW_SELECTOR); - - // Listen for when the user changes tab so that we can show the first set of courses - // and load their events when they request the sort by courses view for the first time. - viewSelector.on('shown shown.bs.tab', function(e) { - View.shown(timelineViewRoot); - $(e.target).removeClass('active'); - }); - - - // Event selector for user_sort - CustomEvents.define(viewSelector, [CustomEvents.events.activate]); - viewSelector.on(CustomEvents.events.activate, "[data-toggle='tab']", function(e) { + CustomEvents.define(timelineDaySelectorContainer, [CustomEvents.events.activate]); + timelineDaySelectorContainer.on( + CustomEvents.events.activate, + SELECTORS.TIMELINE_DAY_FILTER_OPTION, + function(e, data) { + // Update the user preference var filtername = $(e.currentTarget).data('filtername'); - var type = 'block_timeline_user_sort_preference'; + var type = 'block_timeline_user_filter_preference'; UserRepository.setUserPreference(type, filtername) .catch(Notification.exception); - }); - }; - /** - * Event listener for the "search" input field in the timeline navigation that allows for - * searching the activity name, course name and activity type. - * - * @param {object} root The root element for the timeline block - * @param {object} timelineViewRoot The root element for the timeline view - */ - const registerSearch = (root, timelineViewRoot) => { - const searchInput = root.find(SELECTORS.TIMELINE_SEARCH_INPUT); - const clearSearchIcon = root.find(SELECTORS.TIMELINE_SEARCH_CLEAR_ICON); - searchInput.on('input', Utils.debounce(() => { - if (searchInput.val() !== '') { - activeSearchState(clearSearchIcon, timelineViewRoot); - } else { - clearSearchState(clearSearchIcon, timelineViewRoot); + var option = $(e.target).closest(SELECTORS.TIMELINE_DAY_FILTER_OPTION); + + if (option.attr('aria-current') == 'true') { + // If it's already active then we don't need to do anything. + return; } - }, 1000)); - clearSearchIcon.on('click', () => { - searchInput.val(''); - clearSearchState(clearSearchIcon, timelineViewRoot); - searchInput.focus(); - }); - }; - /** - * Show the clear search icon. - * - * @param {object} clearSearchIcon Clear search icon element. - * @param {object} timelineViewRoot The root element for the timeline view - */ - const activeSearchState = (clearSearchIcon, timelineViewRoot) => { - clearSearchIcon.removeClass('d-none'); - View.reset(timelineViewRoot); - }; + var daysOffset = option.attr('data-from'); + var daysLimit = option.attr('data-to'); + var elementsWithDaysOffset = root.find(SELECTORS.DATA_DAYS_OFFSET); - /** - * Hide the clear search icon. - * - * @param {object} clearSearchIcon Clear search icon element. - * @param {object} timelineViewRoot The root element for the timeline view - */ - const clearSearchState = (clearSearchIcon, timelineViewRoot) => { - clearSearchIcon.addClass('d-none'); - View.reset(timelineViewRoot); - }; + elementsWithDaysOffset.attr('data-days-offset', daysOffset); - /** - * Initialise the timeline view navigation by adding event listeners to - * the navigation elements. - * - * @param {object} root The root element for the timeline block - * @param {object} timelineViewRoot The root element for the timeline view - */ - var init = function(root, timelineViewRoot) { - root = $(root); + if (daysLimit != undefined) { + elementsWithDaysOffset.attr('data-days-limit', daysLimit); + } else { + elementsWithDaysOffset.removeAttr('data-days-limit'); + } - registerViewSelector(root, timelineViewRoot); + if (option.attr('data-filtername') === 'overdue') { + elementsWithDaysOffset.attr('data-filter-overdue', true); + } else { + elementsWithDaysOffset.removeAttr('data-filter-overdue'); + } - // Only need to handle filtering if the user is actively enrolled in a course. - if (!root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length) { - registerTimelineDaySelector(root, timelineViewRoot); - registerSearch(root, timelineViewRoot); + // Reset the views to reinitialise the event lists now that we've + // updated the day limits. + View.reset(timelineViewRoot); + + data.originalEvent.preventDefault(); } - }; + ); +}; - return { - init: init - }; -}); +/** + * Event listener for the "sort" button in the timeline navigation that allows for + * changing between the timeline dates and courses views. + * + * On a view change we tell the timeline view module that the view has been shown + * so that it can handle how to display the appropriate view. + * + * @param {object} root The root element for the timeline block + * @param {object} timelineViewRoot The root element for the timeline view + */ +const registerViewSelector = function(root, timelineViewRoot) { + const viewSelector = root.find(SELECTORS.TIMELINE_VIEW_SELECTOR); + + // Listen for when the user changes tab so that we can show the first set of courses + // and load their events when they request the sort by courses view for the first time. + viewSelector.on('shown shown.bs.tab', function(e) { + View.shown(timelineViewRoot); + $(e.target).removeClass('active'); + }); + + + // Event selector for user_sort + CustomEvents.define(viewSelector, [CustomEvents.events.activate]); + viewSelector.on(CustomEvents.events.activate, "[data-toggle='tab']", function(e) { + var filtername = $(e.currentTarget).data('filtername'); + var type = 'block_timeline_user_sort_preference'; + UserRepository.setUserPreference(type, filtername) + .catch(Notification.exception); + }); +}; + +/** + * Event listener for the "search" input field in the timeline navigation that allows for + * searching the activity name, course name and activity type. + * + * @param {object} root The root element for the timeline block + * @param {object} timelineViewRoot The root element for the timeline view + */ +const registerSearch = (root, timelineViewRoot) => { + const searchInput = root.find(SELECTORS.TIMELINE_SEARCH_INPUT); + const clearSearchIcon = root.find(SELECTORS.TIMELINE_SEARCH_CLEAR_ICON); + searchInput.on('input', Utils.debounce(() => { + if (searchInput.val() !== '') { + activeSearchState(clearSearchIcon, timelineViewRoot); + } else { + clearSearchState(clearSearchIcon, timelineViewRoot); + } + }, 1000)); + clearSearchIcon.on('click', () => { + searchInput.val(''); + clearSearchState(clearSearchIcon, timelineViewRoot); + searchInput.focus(); + }); +}; + +/** + * Show the clear search icon. + * + * @param {object} clearSearchIcon Clear search icon element. + * @param {object} timelineViewRoot The root element for the timeline view + */ +const activeSearchState = (clearSearchIcon, timelineViewRoot) => { + clearSearchIcon.removeClass('d-none'); + View.reset(timelineViewRoot); +}; + +/** + * Hide the clear search icon. + * + * @param {object} clearSearchIcon Clear search icon element. + * @param {object} timelineViewRoot The root element for the timeline view + */ +const clearSearchState = (clearSearchIcon, timelineViewRoot) => { + clearSearchIcon.addClass('d-none'); + View.reset(timelineViewRoot); +}; + +/** + * Initialise the timeline view navigation by adding event listeners to + * the navigation elements. + * + * @param {jQuery|HTMLElement} root The root element for the timeline block + * @param {object} timelineViewRoot The root element for the timeline view + */ +export const init = function(root, timelineViewRoot) { + root = $(root); + + registerViewSelector(root, timelineViewRoot); + + // Only need to handle filtering if the user is actively enrolled in a course. + if (!root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length) { + registerTimelineDaySelector(root, timelineViewRoot); + registerSearch(root, timelineViewRoot); + } +}; From dea64fa2661fe79d656c2bae96e44afb473f2413 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Mon, 22 May 2023 16:02:37 +0800 Subject: [PATCH 4/4] MDL-62859 block_online_users: Rewrite visibility changer as ESM --- .../amd/build/change_user_visibility.min.js | 16 +- .../build/change_user_visibility.min.js.map | 2 +- .../amd/src/change_user_visibility.js | 250 +++++++++--------- 3 files changed, 131 insertions(+), 137 deletions(-) diff --git a/blocks/online_users/amd/build/change_user_visibility.min.js b/blocks/online_users/amd/build/change_user_visibility.min.js index 88459892fb9..ebb6923be12 100644 --- a/blocks/online_users/amd/build/change_user_visibility.min.js +++ b/blocks/online_users/amd/build/change_user_visibility.min.js @@ -1,11 +1,11 @@ +define("block_online_users/change_user_visibility",["exports","core/str","core/notification","core_user/repository"],(function(_exports,_str,_notification,_repository){var obj; /** - * A javascript module that handles the change of the user's visibility in the - * online users block. - * - * @module block_online_users/change_user_visibility - * @copyright 2018 Mihail Geshoski - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -define("block_online_users/change_user_visibility",["jquery","core/str","core/notification","core_user/repository"],(function($,Str,Notification,UserRepository){var SELECTORS_CHANGE_VISIBILITY_LINK="#change-user-visibility",SELECTORS_CHANGE_VISIBILITY_ICON="#change-user-visibility .icon",oppositeAction=function(action){return"show"==action?"hide":"show"},changeVisibilityLinkAttr=function(action){getTitle(action).then((function(title){$(SELECTORS_CHANGE_VISIBILITY_LINK).attr({"data-action":action,title:title})})).catch(Notification.exception)},changeVisibilityIconAttr=function(action){var icon=$(SELECTORS_CHANGE_VISIBILITY_ICON);getTitle(action).then((function(title){$(icon).attr({title:title,"aria-label":title}),icon.is("img")?$(icon).attr({src:M.util.image_url("t/"+action),alt:title}):($(icon).addClass(getIconClass(action)),$(icon).removeClass(getIconClass(oppositeAction(action))))})).catch(Notification.exception)},getIconClass=function(action){return"show"==action?"fa-eye-slash":"fa-eye"},getTitle=function(action){return Str.get_string("online_status:"+action,"block_online_users")};return{init:function(){$(SELECTORS_CHANGE_VISIBILITY_LINK).on("click",(function(e){e.preventDefault(),function(action,userid){var value="show"==action?1:0;UserRepository.setUserPreference("block_online_users_uservisibility",value,userid).then((data=>{if(data.saved){var newAction=oppositeAction(action);changeVisibilityLinkAttr(newAction),changeVisibilityIconAttr(newAction)}})).catch(Notification.exception)}($(this).attr("data-action"),$(this).attr("data-userid"))}))}}})); + * A javascript module that handles the change of the user's visibility in the + * online users block. + * + * @module block_online_users/change_user_visibility + * @copyright 2018 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};const SELECTORS_CHANGE_VISIBILITY_LINK="#change-user-visibility",SELECTORS_CHANGE_VISIBILITY_ICON="#change-user-visibility .icon",oppositeAction=action=>"show"==action?"hide":"show",changeVisibilityLinkAttr=action=>getTitle(action).then((title=>{const link=document.querySelector(SELECTORS_CHANGE_VISIBILITY_LINK);return link.dataset.action=action,link.title=title,link})),changeVisibilityIconAttr=action=>getTitle(action).then((title=>{const icon=document.querySelector(SELECTORS_CHANGE_VISIBILITY_ICON);return icon.setAttribute("title",title),icon.setAttribute("aria-label",title),icon.closest("img")?(icon.setAttribute("src",M.util.image_url("t/".concat(action))),icon.setAttribute("alt",title)):(icon.classList.add(getIconClass(action)),icon.classList.remove(getIconClass(oppositeAction(action)))),title})),getIconClass=action=>"show"==action?"fa-eye-slash":"fa-eye",getTitle=action=>(0,_str.get_string)("online_status:".concat(action),"block_online_users");_exports.init=()=>{document.addEventListener("click",(e=>{const link=e.target.closest(SELECTORS_CHANGE_VISIBILITY_LINK);var action,userid;link&&(e.preventDefault(),action=link.dataset.action,userid=link.dataset.userid,(0,_repository.setUserPreference)("block_online_users_uservisibility","show"==action?1:0,userid).then((data=>{if(data.saved){const newAction=oppositeAction(action);changeVisibilityLinkAttr(newAction),changeVisibilityIconAttr(newAction)}return data})).catch(_notification.default.exception))}))}})); //# sourceMappingURL=change_user_visibility.min.js.map \ No newline at end of file diff --git a/blocks/online_users/amd/build/change_user_visibility.min.js.map b/blocks/online_users/amd/build/change_user_visibility.min.js.map index 892cbeca5b8..2f58f211bef 100644 --- a/blocks/online_users/amd/build/change_user_visibility.min.js.map +++ b/blocks/online_users/amd/build/change_user_visibility.min.js.map @@ -1 +1 @@ -{"version":3,"file":"change_user_visibility.min.js","sources":["../src/change_user_visibility.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 that handles the change of the user's visibility in the\n * online users block.\n *\n * @module block_online_users/change_user_visibility\n * @copyright 2018 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/str', 'core/notification', 'core_user/repository'],\n function($, Str, Notification, UserRepository) {\n\n /**\n * Selectors.\n *\n * @access private\n * @type {Object}\n */\n var SELECTORS = {\n CHANGE_VISIBILITY_LINK: '#change-user-visibility',\n CHANGE_VISIBILITY_ICON: '#change-user-visibility .icon'\n };\n\n /**\n * Change user visibility in the online users block.\n *\n * @method changeVisibility\n * @param {String} action\n * @param {String} userid\n * @private\n */\n var changeVisibility = function(action, userid) {\n\n var value = action == \"show\" ? 1 : 0;\n\n UserRepository.setUserPreference('block_online_users_uservisibility', value, userid)\n .then(data => {\n if (data.saved) {\n var newAction = oppositeAction(action);\n changeVisibilityLinkAttr(newAction);\n changeVisibilityIconAttr(newAction);\n }\n return;\n }).catch(Notification.exception);\n };\n\n /**\n * Get the opposite action.\n *\n * @method oppositeAction\n * @param {String} action\n * @return {String}\n * @private\n */\n var oppositeAction = function(action) {\n return action == 'show' ? 'hide' : 'show';\n };\n\n /**\n * Change the attribute values of the user visibility link in the online users block.\n *\n * @method changeVisibilityLinkAttr\n * @param {String} action\n * @private\n */\n var changeVisibilityLinkAttr = function(action) {\n getTitle(action).then(function(title) {\n $(SELECTORS.CHANGE_VISIBILITY_LINK).attr({\n 'data-action': action,\n 'title': title\n });\n return;\n }).catch(Notification.exception);\n };\n\n /**\n * Change the attribute values of the user visibility icon in the online users block.\n *\n * @method changeVisibilityIconAttr\n * @param {String} action\n * @private\n */\n var changeVisibilityIconAttr = function(action) {\n var icon = $(SELECTORS.CHANGE_VISIBILITY_ICON);\n getTitle(action).then(function(title) {\n // Add the proper title to the icon.\n $(icon).attr({\n 'title': title,\n 'aria-label': title\n });\n // If the icon is an image.\n if (icon.is(\"img\")) {\n $(icon).attr({\n 'src': M.util.image_url('t/' + action),\n 'alt': title\n });\n } else {\n // Add the new icon class and remove the old one.\n $(icon).addClass(getIconClass(action));\n $(icon).removeClass(getIconClass(oppositeAction(action)));\n }\n return;\n }).catch(Notification.exception);\n };\n\n /**\n * Get the proper class for the user visibility icon in the online users block.\n *\n * @method getIconClass\n * @param {String} action\n * @return {String}\n * @private\n */\n var getIconClass = function(action) {\n return action == 'show' ? 'fa-eye-slash' : 'fa-eye';\n };\n\n /**\n * Get the title description of the user visibility link in the online users block.\n *\n * @method getTitle\n * @param {String} action\n * @return {object} jQuery promise\n * @private\n */\n var getTitle = function(action) {\n return Str.get_string('online_status:' + action, 'block_online_users');\n };\n\n return {\n // Public variables and functions.\n /**\n * Initialise change user visibility function.\n *\n * @method init\n */\n init: function() {\n $(SELECTORS.CHANGE_VISIBILITY_LINK).on('click', function(e) {\n e.preventDefault();\n var action = ($(this).attr('data-action'));\n var userid = ($(this).attr('data-userid'));\n changeVisibility(action, userid);\n });\n }\n };\n});\n"],"names":["define","$","Str","Notification","UserRepository","SELECTORS","oppositeAction","action","changeVisibilityLinkAttr","getTitle","then","title","attr","catch","exception","changeVisibilityIconAttr","icon","is","M","util","image_url","addClass","getIconClass","removeClass","get_string","init","on","e","preventDefault","userid","value","setUserPreference","data","saved","newAction","changeVisibility","this"],"mappings":";;;;;;;;AAuBAA,mDAAO,CAAC,SAAU,WAAY,oBAAqB,yBAC3C,SAASC,EAAGC,IAAKC,aAAcC,oBAQ/BC,iCACwB,0BADxBA,iCAEwB,gCAkCxBC,eAAiB,SAASC,cACT,QAAVA,OAAmB,OAAS,QAUnCC,yBAA2B,SAASD,QACpCE,SAASF,QAAQG,MAAK,SAASC,OAC3BV,EAAEI,kCAAkCO,KAAK,eACtBL,aACNI,WAGdE,MAAMV,aAAaW,YAUtBC,yBAA2B,SAASR,YAChCS,KAAOf,EAAEI,kCACbI,SAASF,QAAQG,MAAK,SAASC,OAE3BV,EAAEe,MAAMJ,KAAK,OACAD,mBACKA,QAGdK,KAAKC,GAAG,OACRhB,EAAEe,MAAMJ,KAAK,KACFM,EAAEC,KAAKC,UAAU,KAAOb,YACxBI,SAIXV,EAAEe,MAAMK,SAASC,aAAaf,SAC9BN,EAAEe,MAAMO,YAAYD,aAAahB,eAAeC,cAGrDM,MAAMV,aAAaW,YAWtBQ,aAAe,SAASf,cACP,QAAVA,OAAmB,eAAiB,UAW3CE,SAAW,SAASF,eACbL,IAAIsB,WAAW,iBAAmBjB,OAAQ,6BAG9C,CAOHkB,KAAM,WACFxB,EAAEI,kCAAkCqB,GAAG,SAAS,SAASC,GACrDA,EAAEC,iBA3GS,SAASrB,OAAQsB,YAEhCC,MAAkB,QAAVvB,OAAmB,EAAI,EAEnCH,eAAe2B,kBAAkB,oCAAqCD,MAAOD,QAC5EnB,MAAKsB,UACEA,KAAKC,MAAO,KACRC,UAAY5B,eAAeC,QAC/BC,yBAAyB0B,WACzBnB,yBAAyBmB,eAG9BrB,MAAMV,aAAaW,WAkGdqB,CAFclC,EAAEmC,MAAMxB,KAAK,eACbX,EAAEmC,MAAMxB,KAAK"} \ No newline at end of file +{"version":3,"file":"change_user_visibility.min.js","sources":["../src/change_user_visibility.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 that handles the change of the user's visibility in the\n * online users block.\n *\n * @module block_online_users/change_user_visibility\n * @copyright 2018 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {get_string as getString} from 'core/str';\nimport Notification from 'core/notification';\nimport {setUserPreference} from 'core_user/repository';\n\n/**\n * Selectors.\n *\n * @access private\n * @type {Object}\n */\nconst SELECTORS = {\n CHANGE_VISIBILITY_LINK: '#change-user-visibility',\n CHANGE_VISIBILITY_ICON: '#change-user-visibility .icon',\n};\n\n/**\n * Change user visibility in the online users block.\n *\n * @method changeVisibility\n * @param {String} action\n * @param {String} userid\n * @returns {Promise}\n * @private\n */\nconst changeVisibility = (action, userid) => setUserPreference(\n 'block_online_users_uservisibility',\n action == \"show\" ? 1 : 0,\n userid,\n)\n.then((data) => {\n if (data.saved) {\n const newAction = oppositeAction(action);\n changeVisibilityLinkAttr(newAction);\n changeVisibilityIconAttr(newAction);\n }\n return data;\n}).catch(Notification.exception);\n\n/**\n * Get the opposite action.\n *\n * @method oppositeAction\n * @param {String} action\n * @return {String}\n * @private\n */\nconst oppositeAction = (action) => action == 'show' ? 'hide' : 'show';\n\n/**\n * Change the attribute values of the user visibility link in the online users block.\n *\n * @method changeVisibilityLinkAttr\n * @param {String} action\n * @returns {Promise}\n * @private\n */\nconst changeVisibilityLinkAttr = (action) => getTitle(action)\n .then((title) => {\n const link = document.querySelector(SELECTORS.CHANGE_VISIBILITY_LINK);\n link.dataset.action = action;\n link.title = title;\n return link;\n });\n\n/**\n * Change the attribute values of the user visibility icon in the online users block.\n *\n * @method changeVisibilityIconAttr\n * @param {String} action\n * @returns {Promise}\n * @private\n */\nconst changeVisibilityIconAttr = (action) => getTitle(action)\n .then((title) => {\n const icon = document.querySelector(SELECTORS.CHANGE_VISIBILITY_ICON);\n\n // Add the proper title to the icon.\n icon.setAttribute('title', title);\n icon.setAttribute('aria-label', title);\n\n if (icon.closest(\"img\")) {\n // If the icon is an image.\n icon.setAttribute('src', M.util.image_url(`t/${action}`));\n icon.setAttribute('alt', title);\n } else {\n // Add the new icon class and remove the old one.\n icon.classList.add(getIconClass(action));\n icon.classList.remove(getIconClass(oppositeAction(action)));\n }\n return title;\n });\n\n/**\n * Get the proper class for the user visibility icon in the online users block.\n *\n * @method getIconClass\n * @param {String} action\n * @return {String}\n * @private\n */\nconst getIconClass = (action) => (action == 'show') ? 'fa-eye-slash' : 'fa-eye';\n\n/**\n * Get the title description of the user visibility link in the online users block.\n *\n * @method getTitle\n * @param {String} action\n * @return {object} jQuery promise\n * @private\n */\nconst getTitle = (action) => getString(`online_status:${action}`, 'block_online_users');\n\n/**\n * Initialise change user visibility function.\n *\n * @method init\n */\nexport const init = () => {\n document.addEventListener('click', (e) => {\n const link = e.target.closest(SELECTORS.CHANGE_VISIBILITY_LINK);\n if (!link) {\n return;\n }\n e.preventDefault();\n changeVisibility(\n link.dataset.action,\n link.dataset.userid,\n );\n });\n};\n"],"names":["SELECTORS","oppositeAction","action","changeVisibilityLinkAttr","getTitle","then","title","link","document","querySelector","dataset","changeVisibilityIconAttr","icon","setAttribute","closest","M","util","image_url","classList","add","getIconClass","remove","addEventListener","e","target","userid","preventDefault","data","saved","newAction","catch","Notification","exception"],"mappings":";;;;;;;;4JAkCMA,iCACsB,0BADtBA,iCAEsB,gCAkCtBC,eAAkBC,QAAqB,QAAVA,OAAmB,OAAS,OAUzDC,yBAA4BD,QAAWE,SAASF,QACjDG,MAAMC,cACGC,KAAOC,SAASC,cAAcT,yCACpCO,KAAKG,QAAQR,OAASA,OACtBK,KAAKD,MAAQA,MACNC,QAWTI,yBAA4BT,QAAWE,SAASF,QACjDG,MAAMC,cACGM,KAAOJ,SAASC,cAAcT,yCAGpCY,KAAKC,aAAa,QAASP,OAC3BM,KAAKC,aAAa,aAAcP,OAE5BM,KAAKE,QAAQ,QAEbF,KAAKC,aAAa,MAAOE,EAAEC,KAAKC,sBAAef,UAC/CU,KAAKC,aAAa,MAAOP,SAGzBM,KAAKM,UAAUC,IAAIC,aAAalB,SAChCU,KAAKM,UAAUG,OAAOD,aAAanB,eAAeC,WAE/CI,SAWTc,aAAgBlB,QAAsB,QAAVA,OAAoB,eAAiB,SAUjEE,SAAYF,SAAW,2CAA2BA,QAAU,oCAO9C,KAChBM,SAASc,iBAAiB,SAAUC,UAC1BhB,KAAOgB,EAAEC,OAAOV,QAAQd,kCA/Fb,IAACE,OAAQuB,OAgGrBlB,OAGLgB,EAAEG,iBAnGgBxB,OAqGdK,KAAKG,QAAQR,OArGSuB,OAsGtBlB,KAAKG,QAAQe,QAtGoB,iCACzC,oCACU,QAAVvB,OAAmB,EAAI,EACvBuB,QAEHpB,MAAMsB,UACCA,KAAKC,MAAO,OACNC,UAAY5B,eAAeC,QACjCC,yBAAyB0B,WACzBlB,yBAAyBkB,kBAEtBF,QACRG,MAAMC,sBAAaC"} \ No newline at end of file diff --git a/blocks/online_users/amd/src/change_user_visibility.js b/blocks/online_users/amd/src/change_user_visibility.js index 452c28c1df0..23de8f2ee78 100644 --- a/blocks/online_users/amd/src/change_user_visibility.js +++ b/blocks/online_users/amd/src/change_user_visibility.js @@ -21,140 +21,134 @@ * @copyright 2018 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define(['jquery', 'core/str', 'core/notification', 'core_user/repository'], - function($, Str, Notification, UserRepository) { - /** - * Selectors. - * - * @access private - * @type {Object} - */ - var SELECTORS = { - CHANGE_VISIBILITY_LINK: '#change-user-visibility', - CHANGE_VISIBILITY_ICON: '#change-user-visibility .icon' - }; +import {get_string as getString} from 'core/str'; +import Notification from 'core/notification'; +import {setUserPreference} from 'core_user/repository'; - /** - * Change user visibility in the online users block. - * - * @method changeVisibility - * @param {String} action - * @param {String} userid - * @private - */ - var changeVisibility = function(action, userid) { +/** + * Selectors. + * + * @access private + * @type {Object} + */ +const SELECTORS = { + CHANGE_VISIBILITY_LINK: '#change-user-visibility', + CHANGE_VISIBILITY_ICON: '#change-user-visibility .icon', +}; - var value = action == "show" ? 1 : 0; +/** + * Change user visibility in the online users block. + * + * @method changeVisibility + * @param {String} action + * @param {String} userid + * @returns {Promise} + * @private + */ +const changeVisibility = (action, userid) => setUserPreference( + 'block_online_users_uservisibility', + action == "show" ? 1 : 0, + userid, +) +.then((data) => { + if (data.saved) { + const newAction = oppositeAction(action); + changeVisibilityLinkAttr(newAction); + changeVisibilityIconAttr(newAction); + } + return data; +}).catch(Notification.exception); - UserRepository.setUserPreference('block_online_users_uservisibility', value, userid) - .then(data => { - if (data.saved) { - var newAction = oppositeAction(action); - changeVisibilityLinkAttr(newAction); - changeVisibilityIconAttr(newAction); - } - return; - }).catch(Notification.exception); - }; +/** + * Get the opposite action. + * + * @method oppositeAction + * @param {String} action + * @return {String} + * @private + */ +const oppositeAction = (action) => action == 'show' ? 'hide' : 'show'; - /** - * Get the opposite action. - * - * @method oppositeAction - * @param {String} action - * @return {String} - * @private - */ - var oppositeAction = function(action) { - return action == 'show' ? 'hide' : 'show'; - }; +/** + * Change the attribute values of the user visibility link in the online users block. + * + * @method changeVisibilityLinkAttr + * @param {String} action + * @returns {Promise} + * @private + */ +const changeVisibilityLinkAttr = (action) => getTitle(action) + .then((title) => { + const link = document.querySelector(SELECTORS.CHANGE_VISIBILITY_LINK); + link.dataset.action = action; + link.title = title; + return link; + }); - /** - * Change the attribute values of the user visibility link in the online users block. - * - * @method changeVisibilityLinkAttr - * @param {String} action - * @private - */ - var changeVisibilityLinkAttr = function(action) { - getTitle(action).then(function(title) { - $(SELECTORS.CHANGE_VISIBILITY_LINK).attr({ - 'data-action': action, - 'title': title - }); - return; - }).catch(Notification.exception); - }; +/** + * Change the attribute values of the user visibility icon in the online users block. + * + * @method changeVisibilityIconAttr + * @param {String} action + * @returns {Promise} + * @private + */ +const changeVisibilityIconAttr = (action) => getTitle(action) + .then((title) => { + const icon = document.querySelector(SELECTORS.CHANGE_VISIBILITY_ICON); - /** - * Change the attribute values of the user visibility icon in the online users block. - * - * @method changeVisibilityIconAttr - * @param {String} action - * @private - */ - var changeVisibilityIconAttr = function(action) { - var icon = $(SELECTORS.CHANGE_VISIBILITY_ICON); - getTitle(action).then(function(title) { - // Add the proper title to the icon. - $(icon).attr({ - 'title': title, - 'aria-label': title - }); + // Add the proper title to the icon. + icon.setAttribute('title', title); + icon.setAttribute('aria-label', title); + + if (icon.closest("img")) { // If the icon is an image. - if (icon.is("img")) { - $(icon).attr({ - 'src': M.util.image_url('t/' + action), - 'alt': title - }); - } else { - // Add the new icon class and remove the old one. - $(icon).addClass(getIconClass(action)); - $(icon).removeClass(getIconClass(oppositeAction(action))); - } - return; - }).catch(Notification.exception); - }; - - /** - * Get the proper class for the user visibility icon in the online users block. - * - * @method getIconClass - * @param {String} action - * @return {String} - * @private - */ - var getIconClass = function(action) { - return action == 'show' ? 'fa-eye-slash' : 'fa-eye'; - }; - - /** - * Get the title description of the user visibility link in the online users block. - * - * @method getTitle - * @param {String} action - * @return {object} jQuery promise - * @private - */ - var getTitle = function(action) { - return Str.get_string('online_status:' + action, 'block_online_users'); - }; - - return { - // Public variables and functions. - /** - * Initialise change user visibility function. - * - * @method init - */ - init: function() { - $(SELECTORS.CHANGE_VISIBILITY_LINK).on('click', function(e) { - e.preventDefault(); - var action = ($(this).attr('data-action')); - var userid = ($(this).attr('data-userid')); - changeVisibility(action, userid); - }); + icon.setAttribute('src', M.util.image_url(`t/${action}`)); + icon.setAttribute('alt', title); + } else { + // Add the new icon class and remove the old one. + icon.classList.add(getIconClass(action)); + icon.classList.remove(getIconClass(oppositeAction(action))); } - }; -}); + return title; + }); + +/** + * Get the proper class for the user visibility icon in the online users block. + * + * @method getIconClass + * @param {String} action + * @return {String} + * @private + */ +const getIconClass = (action) => (action == 'show') ? 'fa-eye-slash' : 'fa-eye'; + +/** + * Get the title description of the user visibility link in the online users block. + * + * @method getTitle + * @param {String} action + * @return {object} jQuery promise + * @private + */ +const getTitle = (action) => getString(`online_status:${action}`, 'block_online_users'); + +/** + * Initialise change user visibility function. + * + * @method init + */ +export const init = () => { + document.addEventListener('click', (e) => { + const link = e.target.closest(SELECTORS.CHANGE_VISIBILITY_LINK); + if (!link) { + return; + } + e.preventDefault(); + changeVisibility( + link.dataset.action, + link.dataset.userid, + ); + }); +};