[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/mod/assign/amd/src/ -> grading_navigation.js (source)

   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  });


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1