[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/grade/report/history/yui/src/userselector/js/ -> userselector.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   * The User Selector for the grade history report.
  18   *
  19   * @module     moodle-gradereport_history-userselector
  20   * @package    gradereport_history
  21   * @copyright  2013 NetSpot Pty Ltd (https://www.netspot.com.au)
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   * @main       moodle-gradereport_history-userselector
  24   */
  25  
  26  /**
  27   * @module moodle-gradereport_history-userselector
  28   */
  29  
  30  var COMPONENT = 'gradereport_history';
  31  var USP = {
  32      AJAXURL: 'ajaxurl',
  33      BASE: 'base',
  34      CHECKBOX_NAME_PREFIX: 'usp-u',
  35      COURSEID: 'courseid',
  36      DIALOGUE_PREFIX: 'moodle-dialogue',
  37      NAME: 'gradereport_history_usp',
  38      PAGE: 'page',
  39      PARAMS: 'params',
  40      PERPAGE: 'perPage',
  41      SEARCH: 'search',
  42      SEARCHBTN: 'searchbtn',
  43      SELECTEDUSERS: 'selectedUsers',
  44      URL: 'url',
  45      USERCOUNT: 'userCount'
  46  };
  47  var CSS = {
  48      ACCESSHIDE: 'accesshide',
  49      AJAXCONTENT: 'usp-ajax-content',
  50      CHECKBOX: 'usp-checkbox',
  51      CLOSE: 'close',
  52      CLOSEBTN: 'usp-finish',
  53      CONTENT: 'usp-content',
  54      DETAILS: 'details',
  55      EXTRAFIELDS: 'extrafields',
  56      FIRSTADDED: 'usp-first-added',
  57      FULLNAME: 'fullname',
  58      HEADER: 'usp-header',
  59      HIDDEN: 'hidden',
  60      LIGHTBOX: 'usp-loading-lightbox',
  61      LOADINGICON: 'loading-icon',
  62      MORERESULTS: 'usp-more-results',
  63      OPTIONS: 'options',
  64      PICTURE: 'usp-picture',
  65      RESULTSCOUNT: 'usp-results-count',
  66      SEARCH: 'usp-search',
  67      SEARCHBTN: 'usp-search-btn',
  68      SEARCHFIELD: 'usp-search-field',
  69      SEARCHRESULTS: 'usp-search-results',
  70      SELECTED: 'selected',
  71      USER: 'usp-user',
  72      USERS: 'usp-users',
  73      WRAP: 'usp-wrap'
  74  };
  75  var SELECTORS = {
  76      AJAXCONTENT: '.' + CSS.AJAXCONTENT,
  77      FINISHBTN: '.' + CSS.CLOSEBTN + ' input',
  78      FIRSTADDED: '.' + CSS.FIRSTADDED,
  79      FULLNAME: '.' + CSS.FULLNAME + ' label',
  80      LIGHTBOX: '.' + CSS.LIGHTBOX,
  81      MORERESULTS: '.' + CSS.MORERESULTS,
  82      OPTIONS: '.' + CSS.OPTIONS,
  83      PICTURE: '.' + CSS.USER + ' .userpicture',
  84      RESULTSCOUNT: '.' + CSS.RESULTSCOUNT,
  85      RESULTSUSERS: '.' + CSS.SEARCHRESULTS + ' .' + CSS.USERS,
  86      SEARCHBTN: '.' + CSS.SEARCHBTN,
  87      SEARCHFIELD: '.' + CSS.SEARCHFIELD,
  88      SELECTEDNAMES: '.felement .selectednames',
  89      TRIGGER: '.gradereport_history_plugin input.selectortrigger',
  90      USER: '.' + CSS.USER,
  91      USERFULLNAMES: 'input[name="userfullnames"]',
  92      USERIDS: 'input[name="userids"]',
  93      USERSELECT: '.' + CSS.CHECKBOX + ' input[type=checkbox]'
  94  };
  95  
  96  /**
  97   * User Selector.
  98   *
  99   * @namespace M.gradereport_history
 100   * @class UserSelector
 101   * @constructor
 102   */
 103  
 104  var USERSELECTOR = function() {
 105      USERSELECTOR.superclass.constructor.apply(this, arguments);
 106  };
 107  Y.namespace('M.gradereport_history').UserSelector = Y.extend(USERSELECTOR, M.core.dialogue, {
 108  
 109      /**
 110       * Whether or not this is the first time the user displays the dialogue within that request.
 111       *
 112       * @property _firstDisplay
 113       * @type Boolean
 114       * @private
 115       */
 116      _firstDisplay: true,
 117  
 118      /**
 119       * The list of all the users selected while the dialogue is open.
 120       *
 121       * @type Object
 122       * @property _usersBufferList
 123       * @private
 124       */
 125      _usersBufferList: null,
 126  
 127      /**
 128       * The Node on which the focus is set.
 129       *
 130       * @property _userTabFocus
 131       * @type Node
 132       * @private
 133       */
 134      _userTabFocus: null,
 135  
 136      /**
 137       * Compiled template function for a user node.
 138       *
 139       * @property _userTemplate
 140       * @type Function
 141       * @private
 142       */
 143      _userTemplate: null,
 144  
 145      initializer: function() {
 146          var bb = this.get('boundingBox'),
 147              content,
 148              params,
 149              tpl;
 150  
 151          tpl = Y.Handlebars.compile(
 152              '<div class="{{CSS.WRAP}}">' +
 153                  '<div class="{{CSS.HEADER}}">' +
 154                      '<div class="{{CSS.SEARCH}}" role="search">' +
 155                          '<form>' +
 156                              '<input type="text" class="{{CSS.SEARCHFIELD}}" ' +
 157                                  'aria-label="{{get_string "search" "moodle"}}" value="" />' +
 158                              '<input type="submit" class="{{CSS.SEARCHBTN}}"' +
 159                                  'value="{{get_string "search" "moodle"}}">' +
 160                          '</form>' +
 161                          '<div aria-live="polite" class="{{CSS.RESULTSCOUNT}}">{{get_string "loading" "admin"}}</div>' +
 162                      '</div>' +
 163                  '</div>' +
 164                  '<div class="{{CSS.CONTENT}}">' +
 165                      '<form>' +
 166                          '<div class="{{CSS.AJAXCONTENT}}" aria-live="polite"></div>' +
 167                          '<div class="{{CSS.LIGHTBOX}} {{CSS.HIDDEN}}">' +
 168                              '<img class="{{CSS.LOADINGICON}}" alt="{{get_string "loading" "admin"}}"' +
 169                                  'src="{{{loadingIcon}}}">' +
 170                          '</div>' +
 171                          '<div class="{{CSS.CLOSEBTN}}">' +
 172                              '<input type="submit" value="{{get_string "finishselectingusers" COMPONENT}}">' +
 173                          '</div>' +
 174                      '</form>' +
 175                  '</div>' +
 176              '</div>');
 177  
 178          content = Y.Node.create(
 179              tpl({
 180                  COMPONENT: COMPONENT,
 181                  CSS: CSS,
 182                  loadingIcon: M.util.image_url('i/loading', 'moodle')
 183              })
 184          );
 185  
 186          // Set the title and content.
 187          this.getStdModNode(Y.WidgetStdMod.HEADER).prepend(Y.Node.create('<h1>' + this.get('title') + '</h1>'));
 188          this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE);
 189  
 190          // Use standard dialogue class name. This removes the default styling of the footer.
 191          this.get('boundingBox').one('.moodle-dialogue-wrap').addClass('moodle-dialogue-content');
 192  
 193          // Add the event on the button that opens the dialogue.
 194          Y.one(SELECTORS.TRIGGER).on('click', this.show, this);
 195  
 196          // The button to finalize the selection.
 197          bb.one(SELECTORS.FINISHBTN).on('click', this.finishSelectingUsers, this);
 198  
 199          // Delegate the keyboard navigation in the users list.
 200          bb.delegate('key', this.userKeyboardNavigation, 'down:38,40', SELECTORS.AJAXCONTENT, this);
 201  
 202          // Delegate the action to select a user.
 203          Y.delegate('click', this.selectUser, SELECTORS.AJAXCONTENT, SELECTORS.USERSELECT, this);
 204          Y.delegate('click', this.selectUser, SELECTORS.AJAXCONTENT, SELECTORS.PICTURE, this);
 205  
 206          params = this.get(USP.PARAMS);
 207          params.id = this.get(USP.COURSEID);
 208          this.set(USP.PARAMS, params);
 209  
 210          bb.one(SELECTORS.SEARCHBTN).on('click', this.search, this, false);
 211      },
 212  
 213      /**
 214       * Display the dialogue.
 215       *
 216       * @method show
 217       */
 218      show: function(e) {
 219          var bb;
 220          this._usersBufferList = Y.clone(this.get(USP.SELECTEDUSERS));
 221          if (this._firstDisplay) {
 222              // Load the default list of users when the dialogue is loaded for the first time.
 223              this._firstDisplay = false;
 224              this.search(e, false);
 225          } else {
 226              // Leave the content as is, but reset the selection.
 227              bb = this.get('boundingBox');
 228  
 229              // Remove all the selected users.
 230              bb.all(SELECTORS.USER).each(function(node) {
 231                  this.markUserNode(node, false);
 232              }, this);
 233  
 234              // Select the users.
 235              Y.Object.each(this._usersBufferList, function(v, k) {
 236                  var user = bb.one(SELECTORS.USER + '[data-userid="' + k + '"]');
 237                  if (user) {
 238                      this.markUserNode(user, true);
 239                  }
 240              }, this);
 241  
 242              // Reset the tab focus.
 243              this.setUserTabFocus(bb.one(SELECTORS.USER));
 244          }
 245          return Y.namespace('M.gradereport_history.UserSelector').superclass.show.call(this);
 246      },
 247  
 248      /**
 249       * Search for users.
 250       *
 251       * @method search
 252       * @param {EventFacade} e The event.
 253       * @param {Boolean} append Whether we want to append the results to the current results or not.
 254       */
 255      search: function(e, append) {
 256          if (e) {
 257              e.preventDefault();
 258          }
 259          var params;
 260          if (append) {
 261              this.set(USP.PAGE, this.get(USP.PAGE) + 1);
 262          } else {
 263              this.set(USP.USERCOUNT, 0);
 264              this.set(USP.PAGE, 0);
 265          }
 266          params = this.get(USP.PARAMS);
 267          params.sesskey = M.cfg.sesskey;
 268          params.action = 'searchusers';
 269          params.search = this.get('boundingBox').one(SELECTORS.SEARCHFIELD).get('value');
 270          params.page = this.get(USP.PAGE);
 271          params.perpage = this.get(USP.PERPAGE);
 272  
 273          Y.io(M.cfg.wwwroot + this.get(USP.AJAXURL), {
 274              method: 'POST',
 275              data: window.build_querystring(params),
 276              on: {
 277                  start: this.preSearch,
 278                  complete: this.processSearchResults,
 279                  end: this.postSearch
 280              },
 281              context: this,
 282              "arguments": {      // Quoted because this is a reserved keyword.
 283                  append: append
 284              }
 285          });
 286      },
 287  
 288      /**
 289       * Pre search callback.
 290       *
 291       * @method preSearch
 292       * @param {String} transactionId The transaction ID.
 293       * @param {Object} args The arguments passed from YUI.io()
 294       */
 295      preSearch: function(unused, args) {
 296          var bb = this.get('boundingBox');
 297  
 298          // Display the lightbox.
 299          bb.one(SELECTORS.LIGHTBOX).removeClass(CSS.HIDDEN);
 300  
 301          // Set the number of results to 'loading...'.
 302          if (!args.append) {
 303              bb.one(SELECTORS.RESULTSCOUNT).setHTML(M.util.get_string('loading', 'admin'));
 304          }
 305      },
 306  
 307      /**
 308       * Post search callback.
 309       *
 310       * @method postSearch
 311       * @param {String} transactionId The transaction ID.
 312       * @param {Object} args The arguments passed from YUI.io()
 313       */
 314      postSearch: function(transactionId, args) {
 315          var bb = this.get('boundingBox'),
 316              firstAdded = bb.one(SELECTORS.FIRSTADDED),
 317              firstUser;
 318  
 319          // Hide the lightbox.
 320          bb.one(SELECTORS.LIGHTBOX).addClass(CSS.HIDDEN);
 321  
 322          if (args.append && firstAdded) {
 323              // Sets the focus on the newly added user if we are appending results.
 324              this.setUserTabFocus(firstAdded);
 325              firstAdded.one(SELECTORS.USERSELECT).focus();
 326          } else {
 327              // New search result, set the tab focus on the first user returned.
 328              firstUser = bb.one(SELECTORS.USER);
 329              if (firstUser) {
 330                  this.setUserTabFocus(firstUser);
 331              }
 332          }
 333      },
 334  
 335      /**
 336       * Process and display the search results.
 337       *
 338       * @method processSearchResults
 339       * @param {String} tid The transaction ID.
 340       * @param {Object} outcome The response object.
 341       * @param {Object} args The arguments passed from YUI.io().
 342       */
 343      processSearchResults: function(tid, outcome, args) {
 344          var result = false,
 345              error = false,
 346              bb = this.get('boundingBox'),
 347              users,
 348              userTemplate,
 349              count,
 350              selected,
 351              i,
 352              firstAdded = true,
 353              node,
 354              content,
 355              fetchmore,
 356              totalUsers;
 357  
 358          // Decodes the result.
 359          try {
 360              result = Y.JSON.parse(outcome.responseText);
 361              if (!result.success || result.error) {
 362                  error = true;
 363              }
 364          } catch (e) {
 365              error = true;
 366          }
 367  
 368          // There was an error.
 369          if (error) {
 370              this.setContent('');
 371              bb.one(SELECTORS.RESULTSCOUNT).setHTML(M.util.get_string('errajaxsearch', COMPONENT));
 372              return;
 373          }
 374  
 375          // Create the div containing the users when it is a fresh search.
 376          if (!args.append) {
 377              users = Y.Node.create('<div role="listbox" aria-activedescendant="" aria-multiselectable="true" class="' +
 378                                     CSS.USERS +
 379                                     '"></div>');
 380          } else {
 381              users = bb.one(SELECTORS.RESULTSUSERS);
 382          }
 383  
 384          // Compile the template for each user node.
 385          if (!this._userTemplate) {
 386              this._userTemplate = Y.Handlebars.compile(
 387                  '<div role="option" aria-selected="false" class="{{CSS.USER}} clearfix" ' +
 388                          'data-userid="{{userId}}">' +
 389                      '<div class="{{CSS.CHECKBOX}}">' +
 390                          '<input name="{{USP.CHECKBOX_NAME_PREFIX}}{{userId}}" type="checkbox" tabindex="-1"' +
 391                              'id="{{checkboxId}}" aria-describedby="{{checkboxId}} {{extraFieldsId}}"/>' +
 392                      '</div>' +
 393                      '<div class="{{CSS.PICTURE}}">{{{picture}}}</div>' +
 394                      '<div class="{{CSS.DETAILS}}">' +
 395                          '<div class="{{CSS.FULLNAME}}">' +
 396                              '<label for="{{checkboxId}}">{{fullname}}</label>' +
 397                          '</div>' +
 398                          '<div id="{{extraFieldsId}}" class="{{CSS.EXTRAFIELDS}}">{{extrafields}}</div>' +
 399                      '</div>' +
 400                  '</div>'
 401              );
 402          }
 403          userTemplate = this._userTemplate;
 404  
 405          // Append the users one by one.
 406          count = this.get(USP.USERCOUNT);
 407          selected = '';
 408          var user;
 409          for (i in result.response.users) {
 410              count++;
 411              user = result.response.users[i];
 412  
 413              // If already selected.
 414              if (Y.Object.hasKey(this._usersBufferList, user.userid)) {
 415                  selected = true;
 416              } else {
 417                  selected = false;
 418              }
 419  
 420              node = Y.Node.create(userTemplate({
 421                  checkboxId: Y.guid(),
 422                  COMPONENT: COMPONENT,
 423                  count: count,
 424                  CSS: CSS,
 425                  extrafields: user.extrafields,
 426                  extraFieldsId: Y.guid(),
 427                  fullname: user.fullname,
 428                  picture: user.picture,
 429                  userId: user.userid,
 430                  USP: USP
 431              }));
 432  
 433              this.markUserNode(node, selected);
 434  
 435              // Noting the first user that was when adding more results.
 436              if (args.append && firstAdded) {
 437                  users.all(SELECTORS.FIRSTADDED).removeClass(CSS.FIRSTADDED);
 438                  node.addClass(CSS.FIRSTADDED);
 439                  firstAdded = false;
 440              }
 441              users.append(node);
 442          }
 443          this.set(USP.USERCOUNT, count);
 444  
 445          // Update the count of users, and add a button to load more if need be.
 446          totalUsers = parseInt(result.response.totalusers, 10);
 447          if (!args.append) {
 448              if (totalUsers === 0) {
 449                  bb.one(SELECTORS.RESULTSCOUNT).setHTML(M.util.get_string('noresults', 'moodle'));
 450                  content = '';
 451              } else {
 452                  if (totalUsers === 1) {
 453                      bb.one(SELECTORS.RESULTSCOUNT).setHTML(M.util.get_string('foundoneuser', COMPONENT));
 454                  } else {
 455                      bb.one(SELECTORS.RESULTSCOUNT).setHTML(M.util.get_string('foundnusers', COMPONENT, totalUsers));
 456                  }
 457  
 458                  content = Y.Node.create('<div class="' + CSS.SEARCHRESULTS + '"></div>')
 459                      .append(users);
 460                  if (result.response.totalusers > (this.get(USP.PAGE) + 1) * this.get(USP.PERPAGE)) {
 461                      fetchmore = Y.Node.create('<div class="' + CSS.MORERESULTS + '">' +
 462                          '<a href="#" role="button">' + M.util.get_string('loadmoreusers', COMPONENT) + '</a></div>');
 463                      fetchmore.one('a').on('click', this.search, this, true);
 464                      fetchmore.one('a').on('key', this.search, 'space', this, true);
 465                      content.append(fetchmore);
 466                  }
 467              }
 468              this.setContent(content);
 469          } else {
 470              if (totalUsers <= (this.get(USP.PAGE) + 1) * this.get(USP.PERPAGE)) {
 471                  bb.one(SELECTORS.MORERESULTS).remove();
 472              }
 473          }
 474      },
 475  
 476      /**
 477       * When the user has finished selecting users.
 478       *
 479       * @method finishSelectingUsers
 480       * @param {EventFacade} e The event.
 481       */
 482      finishSelectingUsers: function(e) {
 483          e.preventDefault();
 484          this.applySelection();
 485          this.hide();
 486      },
 487  
 488      /**
 489       * Apply the selection made.
 490       *
 491       * @method applySelection
 492       * @param {EventFacade} e The event.
 493       */
 494      applySelection: function() {
 495          var userIds = Y.Object.keys(this._usersBufferList);
 496          this.set(USP.SELECTEDUSERS, Y.clone(this._usersBufferList))
 497              .setNameDisplay();
 498          Y.one(SELECTORS.USERIDS).set('value', userIds.join());
 499      },
 500  
 501      /**
 502       * Select a user.
 503       *
 504       * @method SelectUser
 505       * @param {EventFacade} e The event.
 506       */
 507      selectUser: function(e) {
 508          var user = e.currentTarget.ancestor(SELECTORS.USER),
 509              checkbox = user.one(SELECTORS.USERSELECT),
 510              fullname = user.one(SELECTORS.FULLNAME).get('innerHTML'),
 511              checked = checkbox.get('checked'),
 512              userId = user.getData('userid');
 513  
 514          if (e.currentTarget !== checkbox) {
 515              // We triggered the selection from another node, so we need to change the checkbox value.
 516              checked = !checked;
 517          }
 518  
 519          if (checked) {
 520              // Selecting the user.
 521              this._usersBufferList[userId] = fullname;
 522          } else {
 523              // De-selecting the user.
 524              delete this._usersBufferList[userId];
 525              delete this._usersBufferList[parseInt(userId, 10)]; // Also remove numbered keys.
 526          }
 527  
 528          this.markUserNode(user, checked);
 529      },
 530  
 531      /**
 532       * Mark a user node as selected or not.
 533       *
 534       * This only takes care of the DOM side of things, not the internal mechanism
 535       * storing what users have been selected or not.
 536       *
 537       * @param {Node} node The user node.
 538       * @param {Boolean} selected True to mark as selected.
 539       * @chainable
 540       */
 541      markUserNode: function(node, selected) {
 542          if (selected) {
 543              node.addClass(CSS.SELECTED)
 544                  .set('aria-selected', true)
 545                  .one(SELECTORS.USERSELECT)
 546                      .set('checked', true);
 547          } else {
 548              node.removeClass(CSS.SELECTED)
 549                  .set('aria-selected', false)
 550                  .one(SELECTORS.USERSELECT)
 551                      .set('checked', false);
 552          }
 553          return this;
 554      },
 555  
 556      /**
 557       * Set the content of the dialogue.
 558       *
 559       * @method setContent
 560       * @param {String} content The content.
 561       * @chainable
 562       */
 563      setContent: function(content) {
 564          this.get('boundingBox').one(SELECTORS.AJAXCONTENT).setHTML(content);
 565          return this;
 566      },
 567  
 568      /**
 569       * Display the names of the selected users in the form.
 570       *
 571       * @method setNameDisplay
 572       */
 573      setNameDisplay: function() {
 574          var namelist = Y.Object.values(this.get(USP.SELECTEDUSERS));
 575          Y.one(SELECTORS.SELECTEDNAMES).set('innerHTML', namelist.join(', '));
 576          Y.one(SELECTORS.USERFULLNAMES).set('value', namelist.join());
 577      },
 578  
 579      /**
 580       * User keyboard navigation.
 581       *
 582       * @method userKeyboardNavigation
 583       */
 584      userKeyboardNavigation: function(e) {
 585          var bb = this.get('boundingBox'),
 586              users = bb.all(SELECTORS.USER),
 587              direction = 1,
 588              user,
 589              current = e.target.ancestor(SELECTORS.USER, true);
 590  
 591          if (e.keyCode === 38) {
 592              direction = -1;
 593          }
 594  
 595          user = this.findFocusableUser(users, current, direction);
 596          if (user) {
 597              e.preventDefault();
 598              user.one(SELECTORS.USERSELECT).focus();
 599              this.setUserTabFocus(user);
 600          }
 601      },
 602  
 603      /**
 604       * Find the next or previous focusable node.
 605       *
 606       * @param {NodeList} users The list of users.
 607       * @param {Node} user The user to start with.
 608       * @param {Number} direction The direction in which to go.
 609       * @return {Node|null} A user node, or null if not found.
 610       * @method findFocusableUser
 611       */
 612      findFocusableUser: function(users, user, direction) {
 613          var index = users.indexOf(user);
 614  
 615          if (users.size() < 1) {
 616              Y.log('The users list is empty', 'debug', COMPONENT);
 617              return null;
 618          }
 619  
 620          if (index < 0) {
 621              Y.log('Unable to find the user in the list of users', 'debug', COMPONENT);
 622              return users.item(0);
 623          }
 624  
 625          index += direction;
 626  
 627          // Wrap the navigation when reaching the top of the bottom.
 628          if (index < 0) {
 629              index = users.size() - 1;
 630          } else if (index >= users.size()) {
 631              index = 0;
 632          }
 633  
 634          return users.item(index);
 635      },
 636  
 637      /**
 638       * Set the user tab focus.
 639       *
 640       * @param {Node} user The user node.
 641       * @method setUserTabFocus
 642       */
 643      setUserTabFocus: function(user) {
 644          if (this._userTabFocus) {
 645              this._userTabFocus.setAttribute('tabindex', '-1');
 646          }
 647          if (!user) {
 648              // We were not passed a user, there is apparently none in the dialogue. Nothing to do here \\\o/.
 649              return;
 650          }
 651  
 652          this._userTabFocus = user.one(SELECTORS.USERSELECT);
 653          this._userTabFocus.setAttribute('tabindex', '0');
 654  
 655          this.get('boundingBox').one(SELECTORS.RESULTSUSERS).setAttribute('aria-activedescendant', this._userTabFocus.generateID());
 656      }
 657  
 658  }, {
 659      NAME: USP.NAME,
 660      CSS_PREFIX: USP.CSS_PREFIX,
 661      ATTRS: {
 662  
 663          /**
 664           * The header.
 665           *
 666           * @attribute title
 667           * @default selectusers language string.
 668           * @type String
 669           */
 670          title: {
 671              validator: Y.Lang.isString,
 672              valueFn: function() {
 673                  return M.util.get_string('selectusers', COMPONENT);
 674              }
 675          },
 676  
 677          /**
 678           * The current page URL.
 679           *
 680           * @attribute url
 681           * @default null
 682           * @type String
 683           */
 684          url: {
 685              validator: Y.Lang.isString,
 686              value: null
 687          },
 688  
 689          /**
 690           * The URL to the Ajax file.
 691           *
 692           * @attribute ajaxurl
 693           * @default null
 694           * @type String
 695           */
 696          ajaxurl: {
 697              validator: Y.Lang.isString,
 698              value: null
 699          },
 700  
 701          /**
 702           * The names of the selected users.
 703           *
 704           * The keys are the user IDs, the values are their fullname.
 705           *
 706           * @attribute selectedUsers
 707           * @default null
 708           * @type Object
 709           */
 710          selectedUsers: {
 711              validator: Y.Lang.isObject,
 712              value: null,
 713              getter: function(v) {
 714                  if (v === null) {
 715                      return {};
 716                  }
 717                  return v;
 718              }
 719          },
 720  
 721          /**
 722           * The course ID.
 723           *
 724           * @attribute courseid
 725           * @default null
 726           * @type Number
 727           */
 728          courseid: {
 729              value: null
 730          },
 731  
 732          /**
 733           * Array of parameters.
 734           *
 735           * @attribute params
 736           * @default []
 737           * @type Array
 738           */
 739          params: {
 740              validator: Y.Lang.isArray,
 741              value: []
 742          },
 743  
 744          /**
 745           * The page we are on.
 746           *
 747           * @attribute page
 748           * @default 0
 749           * @type Number
 750           */
 751          page: {
 752              validator: Y.Lang.isNumber,
 753              value: 0
 754          },
 755  
 756          /**
 757           * The number of users displayed.
 758           *
 759           * @attribute userCount
 760           * @default 0
 761           * @type Number
 762           */
 763          userCount: {
 764              value: 0,
 765              validator: Y.Lang.isNumber
 766          },
 767  
 768          /**
 769           * The number of results per page.
 770           *
 771           * @attribute perPage
 772           * @default 25
 773           * @type Number
 774           */
 775          perPage: {
 776              value: 25,
 777              Validator: Y.Lang.isNumber
 778          }
 779  
 780      }
 781  });
 782  
 783  Y.Base.modifyAttrs(Y.namespace('M.gradereport_history.UserSelector'), {
 784  
 785      /**
 786       * List of extra classes.
 787       *
 788       * @attribute extraClasses
 789       * @default ['gradereport_history_usp']
 790       * @type Array
 791       */
 792      extraClasses: {
 793          value: [
 794              'gradereport_history_usp'
 795          ]
 796      },
 797  
 798      /**
 799       * Whether to focus on the target that caused the Widget to be shown.
 800       *
 801       * @attribute focusOnPreviousTargetAfterHide
 802       * @default true
 803       * @type Node
 804       */
 805      focusOnPreviousTargetAfterHide: {
 806          value: true
 807      },
 808  
 809      /**
 810       *
 811       * Width.
 812       *
 813       * @attribute width
 814       * @default '500px'
 815       * @type String|Number
 816       */
 817      width: {
 818          value: '500px'
 819      },
 820  
 821      /**
 822       * Boolean indicating whether or not the Widget is visible.
 823       *
 824       * @attribute visible
 825       * @default false
 826       * @type Boolean
 827       */
 828      visible: {
 829          value: false
 830      },
 831  
 832     /**
 833      * Whether the widget should be modal or not.
 834      *
 835      * @attribute modal
 836      * @type Boolean
 837      * @default true
 838      */
 839      modal: {
 840          value: true
 841      },
 842  
 843     /**
 844      * Whether the widget should be draggable or not.
 845      *
 846      * @attribute draggable
 847      * @type Boolean
 848      * @default true
 849      */
 850      draggable: {
 851          value: true
 852      }
 853  
 854  });
 855  
 856  Y.namespace('M.gradereport_history.UserSelector').init = function(cfg) {
 857      return new USERSELECTOR(cfg);
 858  };


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