[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 // This file is part of Moodle - http://moodle.org/ 2 // 3 // Moodle is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU General Public License as published by 5 // the Free Software Foundation, either version 3 of the License, or 6 // (at your option) any later version. 7 // 8 // Moodle is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU General Public License for more details. 12 // 13 // You should have received a copy of the GNU General Public License 14 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 15 16 /** 17 * Javascript to handle changing users via the user selector in the header. 18 * 19 * @module mod_assign/grading_navigation 20 * @package mod_assign 21 * @copyright 2016 Damyon Wiese <damyon@moodle.com> 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 * @since 3.1 24 */ 25 define(['jquery', 'core/notification', 'core/str', 'core/form-autocomplete', 26 'core/ajax', 'mod_assign/grading_form_change_checker'], 27 function($, notification, str, autocomplete, ajax, checker) { 28 29 /** 30 * GradingNavigation class. 31 * 32 * @class GradingNavigation 33 * @param {String} selector The selector for the page region containing the user navigation. 34 */ 35 var GradingNavigation = function(selector) { 36 this._regionSelector = selector; 37 this._region = $(selector); 38 this._filters = []; 39 this._users = []; 40 this._filteredUsers = []; 41 42 // Get the current user list from a webservice. 43 this._loadAllUsers(); 44 45 // Attach listeners to the select and arrow buttons. 46 47 this._region.find('[data-action="previous-user"]').on('click', this._handlePreviousUser.bind(this)); 48 this._region.find('[data-action="next-user"]').on('click', this._handleNextUser.bind(this)); 49 this._region.find('[data-action="change-user"]').on('change', this._handleChangeUser.bind(this)); 50 this._region.find('[data-region="user-filters"]').on('click', this._toggleExpandFilters.bind(this)); 51 52 $(document).on('user-changed', this._refreshSelector.bind(this)); 53 54 // Position the configure filters panel under the link that expands it. 55 var toggleLink = this._region.find('[data-region="user-filters"]'); 56 var configPanel = $(document.getElementById(toggleLink.attr('aria-controls'))); 57 58 configPanel.on('change', '[type="checkbox"]', this._filterChanged.bind(this)); 59 60 var userid = $('[data-region="grading-navigation-panel"]').data('first-userid'); 61 if (userid) { 62 this._selectUserById(userid); 63 } 64 65 str.get_string('changeuser', 'mod_assign').done(function(s) { 66 autocomplete.enhance('[data-action=change-user]', false, 'mod_assign/participant_selector', s); 67 } 68 ).fail(notification.exception); 69 70 // We do not allow navigation while ajax requests are pending. 71 72 $(document).bind("start-loading-user", function() { 73 this._isLoading = true; 74 }.bind(this)); 75 $(document).bind("finish-loading-user", function() { 76 this._isLoading = false; 77 }.bind(this)); 78 }; 79 80 /** @type {Boolean} Boolean tracking active ajax requests. */ 81 GradingNavigation.prototype._isLoading = false; 82 83 /** @type {String} Selector for the page region containing the user navigation. */ 84 GradingNavigation.prototype._regionSelector = null; 85 86 /** @type {Array} The list of active filter keys */ 87 GradingNavigation.prototype._filters = null; 88 89 /** @type {Array} The list of users */ 90 GradingNavigation.prototype._users = null; 91 92 /** @type {JQuery} JQuery node for the page region containing the user navigation. */ 93 GradingNavigation.prototype._region = null; 94 95 /** 96 * Load the list of all users for this assignment. 97 * 98 * @private 99 * @method _loadAllUsers 100 */ 101 GradingNavigation.prototype._loadAllUsers = function() { 102 var select = this._region.find('[data-action=change-user]'); 103 var assignmentid = select.attr('data-assignmentid'); 104 var groupid = select.attr('data-groupid'); 105 106 ajax.call([{ 107 methodname: 'mod_assign_list_participants', 108 args: {assignid: assignmentid, groupid: groupid, filter: '', onlyids: true}, 109 done: this._usersLoaded.bind(this), 110 fail: notification.exception 111 }]); 112 }; 113 114 /** 115 * Call back to rebuild the user selector and x of y info when the user list is updated. 116 * 117 * @private 118 * @method _usersLoaded 119 * @param {Array} users 120 */ 121 GradingNavigation.prototype._usersLoaded = function(users) { 122 this._filteredUsers = this._users = users; 123 if (this._users.length) { 124 // Position the configure filters panel under the link that expands it. 125 var toggleLink = this._region.find('[data-region="user-filters"]'); 126 var configPanel = $(document.getElementById(toggleLink.attr('aria-controls'))); 127 128 configPanel.find('[type="checkbox"]').trigger('change'); 129 } else { 130 this._selectNoUser(); 131 } 132 }; 133 134 /** 135 * Close the configure filters panel if a click is detected outside of it. 136 * 137 * @private 138 * @method _checkClickOutsideConfigureFilters 139 * @param {Event} event 140 */ 141 GradingNavigation.prototype._checkClickOutsideConfigureFilters = function(event) { 142 var configPanel = this._region.find('[data-region="configure-filters"]'); 143 144 if (!configPanel.is(event.target) && configPanel.has(event.target).length === 0) { 145 var toggleLink = this._region.find('[data-region="user-filters"]'); 146 147 configPanel.hide(); 148 configPanel.attr('aria-hidden', 'true'); 149 toggleLink.attr('aria-expanded', 'false'); 150 $(document).unbind('click.mod_assign_grading_navigation'); 151 } 152 }; 153 154 /** 155 * Turn a filter on or off. 156 * 157 * @private 158 * @method _filterChanged 159 * @param {Event} event 160 */ 161 GradingNavigation.prototype._filterChanged = function(event) { 162 var name = $(event.target).attr('name'); 163 var key = name.split('_').pop(); 164 var enabled = $(event.target).prop('checked'); 165 166 if (enabled) { 167 if (this._filters.indexOf(key) == -1) { 168 this._filters[this._filters.length] = key; 169 } 170 } else { 171 var index = this._filters.indexOf(key); 172 if (index != -1) { 173 this._filters.splice(index, 1); 174 } 175 } 176 177 // Update the active filter string. 178 var filterlist = []; 179 this._region.find('[data-region="configure-filters"]').find('[type="checkbox"]').each(function(idx, ele) { 180 if ($(ele).prop('checked')) { 181 filterlist[filterlist.length] = $(ele).closest('label').text(); 182 } 183 }); 184 if (filterlist.length) { 185 this._region.find('[data-region="user-filters"] span').text(filterlist.join(', ')); 186 } else { 187 str.get_string('nofilters', 'mod_assign').done(function(s) { 188 this._region.find('[data-region="user-filters"] span').text(s); 189 }.bind(this)).fail(notification.exception); 190 } 191 192 // Filter the options in the select box that do not match the current filters. 193 194 var select = this._region.find('[data-action=change-user]'); 195 var userid = select.attr('data-selected'); 196 var foundIndex = 0; 197 198 this._filteredUsers = []; 199 200 $.each(this._users, function(index, user) { 201 var show = true; 202 $.each(this._filters, function(filterindex, filter) { 203 if (filter == "submitted") { 204 if (user.submitted == "0") { 205 show = false; 206 } 207 } else if (filter == "notsubmitted") { 208 if (user.submitted == "1") { 209 show = false; 210 } 211 } else if (filter == "requiregrading") { 212 if (user.requiregrading == "0") { 213 show = false; 214 } 215 } 216 }); 217 218 if (show) { 219 this._filteredUsers[this._filteredUsers.length] = user; 220 if (userid == user.id) { 221 foundIndex = index; 222 } 223 } 224 }.bind(this)); 225 226 if (this._filteredUsers.length) { 227 this._selectUserById(this._filteredUsers[foundIndex].id); 228 } else { 229 this._selectNoUser(); 230 } 231 }; 232 233 /** 234 * Select no users, because no users match the filters. 235 * 236 * @private 237 * @method _selectNoUser 238 */ 239 GradingNavigation.prototype._selectNoUser = function() { 240 // Detect unsaved changes, and offer to save them - otherwise change user right now. 241 if (this._isLoading) { 242 return; 243 } 244 if (checker.checkFormForChanges('[data-region="grade-panel"] .gradeform')) { 245 // Form has changes, so we need to confirm before switching users. 246 str.get_strings([ 247 {key: 'unsavedchanges', component: 'mod_assign'}, 248 {key: 'unsavedchangesquestion', component: 'mod_assign'}, 249 {key: 'saveandcontinue', component: 'mod_assign'}, 250 {key: 'cancel', component: 'core'}, 251 ]).done(function(strs) { 252 notification.confirm(strs[0], strs[1], strs[2], strs[3], function() { 253 $(document).trigger('save-changes', -1); 254 }); 255 }); 256 } else { 257 $(document).trigger('user-changed', -1); 258 } 259 }; 260 261 /** 262 * Select the specified user by id. 263 * 264 * @private 265 * @method _selectUserById 266 * @param {Number} userid 267 */ 268 GradingNavigation.prototype._selectUserById = function(userid) { 269 var select = this._region.find('[data-action=change-user]'); 270 var useridnumber = parseInt(userid, 10); 271 272 // Detect unsaved changes, and offer to save them - otherwise change user right now. 273 if (this._isLoading) { 274 return; 275 } 276 if (checker.checkFormForChanges('[data-region="grade-panel"] .gradeform')) { 277 // Form has changes, so we need to confirm before switching users. 278 str.get_strings([ 279 {key: 'unsavedchanges', component: 'mod_assign'}, 280 {key: 'unsavedchangesquestion', component: 'mod_assign'}, 281 {key: 'saveandcontinue', component: 'mod_assign'}, 282 {key: 'cancel', component: 'core'}, 283 ]).done(function(strs) { 284 notification.confirm(strs[0], strs[1], strs[2], strs[3], function() { 285 $(document).trigger('save-changes', useridnumber); 286 }); 287 }); 288 } else { 289 select.attr('data-selected', userid); 290 291 if (!isNaN(useridnumber) && useridnumber > 0) { 292 $(document).trigger('user-changed', userid); 293 } 294 } 295 }; 296 297 /** 298 * Expand or collapse the filter config panel. 299 * 300 * @private 301 * @method _toggleExpandFilters 302 * @param {Event} event 303 */ 304 GradingNavigation.prototype._toggleExpandFilters = function(event) { 305 event.preventDefault(); 306 var toggleLink = $(event.target).closest('[data-region="user-filters"]'); 307 var expanded = toggleLink.attr('aria-expanded') == 'true'; 308 var configPanel = $(document.getElementById(toggleLink.attr('aria-controls'))); 309 310 if (expanded) { 311 configPanel.hide(); 312 configPanel.attr('aria-hidden', 'true'); 313 toggleLink.attr('aria-expanded', 'false'); 314 $(document).unbind('click.mod_assign_grading_navigation'); 315 } else { 316 configPanel.css('display', 'inline-block'); 317 configPanel.attr('aria-hidden', 'false'); 318 toggleLink.attr('aria-expanded', 'true'); 319 event.stopPropagation(); 320 $(document).on('click.mod_assign_grading_navigation', this._checkClickOutsideConfigureFilters.bind(this)); 321 } 322 }; 323 324 /** 325 * Change to the previous user in the grading list. 326 * 327 * @private 328 * @method _handlePreviousUser 329 * @param {Event} e 330 */ 331 GradingNavigation.prototype._handlePreviousUser = function(e) { 332 e.preventDefault(); 333 var select = this._region.find('[data-action=change-user]'); 334 var currentUserId = select.attr('data-selected'); 335 var i = 0; 336 var currentIndex = 0; 337 338 for (i = 0; i < this._filteredUsers.length; i++) { 339 if (this._filteredUsers[i].id == currentUserId) { 340 currentIndex = i; 341 break; 342 } 343 } 344 345 var count = this._filteredUsers.length; 346 var newIndex = (currentIndex - 1); 347 if (newIndex < 0) { 348 newIndex = count - 1; 349 } 350 351 if (count) { 352 this._selectUserById(this._filteredUsers[newIndex].id); 353 } 354 }; 355 356 /** 357 * Change to the next user in the grading list. 358 * 359 * @param {Event} e 360 */ 361 GradingNavigation.prototype._handleNextUser = function(e) { 362 e.preventDefault(); 363 var select = this._region.find('[data-action=change-user]'); 364 var currentUserId = select.attr('data-selected'); 365 var i = 0; 366 var currentIndex = 0; 367 368 for (i = 0; i < this._filteredUsers.length; i++) { 369 if (this._filteredUsers[i].id == currentUserId) { 370 currentIndex = i; 371 break; 372 } 373 } 374 375 var count = this._filteredUsers.length; 376 var newIndex = (currentIndex + 1) % count; 377 378 if (count) { 379 this._selectUserById(this._filteredUsers[newIndex].id); 380 } 381 }; 382 383 /** 384 * Rebuild the x of y string. 385 * 386 * @private 387 * @method _refreshCount 388 */ 389 GradingNavigation.prototype._refreshCount = function() { 390 var select = this._region.find('[data-action=change-user]'); 391 var userid = select.attr('data-selected'); 392 var i = 0; 393 var currentIndex = 0; 394 395 if (isNaN(userid) || userid <= 0) { 396 this._region.find('[data-region="user-count"]').hide(); 397 } else { 398 this._region.find('[data-region="user-count"]').show(); 399 400 for (i = 0; i < this._filteredUsers.length; i++) { 401 if (this._filteredUsers[i].id == userid) { 402 currentIndex = i; 403 break; 404 } 405 } 406 var count = this._filteredUsers.length; 407 if (count) { 408 currentIndex += 1; 409 } 410 var param = {x: currentIndex, y: count}; 411 412 str.get_string('xofy', 'mod_assign', param).done(function(s) { 413 this._region.find('[data-region="user-count-summary"]').text(s); 414 }.bind(this)).fail(notification.exception); 415 } 416 }; 417 418 /** 419 * Respond to a user-changed event by updating the selector. 420 * 421 * @private 422 * @method _refreshSelector 423 * @param {Event} event 424 * @param {String} userid 425 */ 426 GradingNavigation.prototype._refreshSelector = function(event, userid) { 427 var select = this._region.find('[data-action=change-user]'); 428 userid = parseInt(userid, 10); 429 430 if (!isNaN(userid) && userid > 0) { 431 select.attr('data-selected', userid); 432 } 433 this._refreshCount(); 434 }; 435 436 /** 437 * Change to a different user in the grading list. 438 * 439 * @private 440 * @method _handleChangeUser 441 * @param {Event} event 442 */ 443 GradingNavigation.prototype._handleChangeUser = function() { 444 var select = this._region.find('[data-action=change-user]'); 445 var userid = parseInt(select.val(), 10); 446 447 if (this._isLoading) { 448 return; 449 } 450 if (checker.checkFormForChanges('[data-region="grade-panel"] .gradeform')) { 451 // Form has changes, so we need to confirm before switching users. 452 str.get_strings([ 453 {key: 'unsavedchanges', component: 'mod_assign'}, 454 {key: 'unsavedchangesquestion', component: 'mod_assign'}, 455 {key: 'saveandcontinue', component: 'mod_assign'}, 456 {key: 'cancel', component: 'core'}, 457 ]).done(function(strs) { 458 notification.confirm(strs[0], strs[1], strs[2], strs[3], function() { 459 $(document).trigger('save-changes', userid); 460 }); 461 }); 462 } else { 463 if (!isNaN(userid) && userid > 0) { 464 select.attr('data-selected', userid); 465 466 $(document).trigger('user-changed', userid); 467 } 468 } 469 }; 470 471 return GradingNavigation; 472 });
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |