[ 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 * 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 };
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 |