[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/course/yui/build/moodle-course-management/ -> moodle-course-management-debug.js (source)

   1  YUI.add('moodle-course-management', function (Y, NAME) {
   2  
   3  /* global DragDrop, Category, Course */
   4  
   5  /**
   6   * Provides drop down menus for list of action links.
   7   *
   8   * @module moodle-course-management
   9   */
  10  
  11  /**
  12   * Management JS console.
  13   *
  14   * Provides the organisation for course and category management JS.
  15   *
  16   * @namespace M.course.management
  17   * @class Console
  18   * @constructor
  19   * @extends Base
  20   */
  21  function Console() {
  22      Console.superclass.constructor.apply(this, arguments);
  23  }
  24  Console.NAME = 'moodle-course-management';
  25  Console.CSS_PREFIX = 'management';
  26  Console.ATTRS = {
  27      /**
  28       * The HTML element containing the management interface.
  29       * @attribute element
  30       * @type Node
  31       */
  32      element: {
  33          setter: function(node) {
  34              if (typeof node === 'string') {
  35                  node = Y.one('#' + node);
  36              }
  37              return node;
  38          }
  39      },
  40  
  41      /**
  42       * The category listing container node.
  43       * @attribute categorylisting
  44       * @type Node
  45       * @default null
  46       */
  47      categorylisting: {
  48          value: null
  49      },
  50  
  51      /**
  52       * The course listing container node.
  53       * @attribute courselisting
  54       * @type Node
  55       * @default null
  56       */
  57      courselisting: {
  58          value: null
  59      },
  60  
  61      /**
  62       * The course details container node.
  63       * @attribute coursedetails
  64       * @type Node|null
  65       * @default null
  66       */
  67      coursedetails: {
  68          value: null
  69      },
  70  
  71      /**
  72       * The id of the currently active category.
  73       * @attribute activecategoryid
  74       * @type Number
  75       * @default null
  76       */
  77      activecategoryid: {
  78          value: null
  79      },
  80  
  81      /**
  82       * The id of the currently active course.
  83       * @attribute activecourseid
  84       * @type Number
  85       * @default Null
  86       */
  87      activecourseid: {
  88          value: null
  89      },
  90  
  91      /**
  92       * The categories that are currently available through the management interface.
  93       * @attribute categories
  94       * @type Array
  95       * @default []
  96       */
  97      categories: {
  98          setter: function(item, name) {
  99              if (Y.Lang.isArray(item)) {
 100                  return item;
 101              }
 102              var items = this.get(name);
 103              items.push(item);
 104              return items;
 105          },
 106          value: []
 107      },
 108  
 109      /**
 110       * The courses that are currently available through the management interface.
 111       * @attribute courses
 112       * @type Course[]
 113       * @default Array
 114       */
 115      courses: {
 116          validator: function(val) {
 117              return Y.Lang.isArray(val);
 118          },
 119          value: []
 120      },
 121  
 122      /**
 123       * The currently displayed page of courses.
 124       * @attribute page
 125       * @type Number
 126       * @default null
 127       */
 128      page: {
 129          getter: function(value, name) {
 130              if (value === null) {
 131                  value = this.get('element').getData(name);
 132                  this.set(name, value);
 133              }
 134              return value;
 135          },
 136          value: null
 137      },
 138  
 139      /**
 140       * The total pages of courses that can be shown for this category.
 141       * @attribute totalpages
 142       * @type Number
 143       * @default null
 144       */
 145      totalpages: {
 146          getter: function(value, name) {
 147              if (value === null) {
 148                  value = this.get('element').getData(name);
 149                  this.set(name, value);
 150              }
 151              return value;
 152          },
 153          value: null
 154      },
 155  
 156      /**
 157       * The total number of courses belonging to this category.
 158       * @attribute totalcourses
 159       * @type Number
 160       * @default null
 161       */
 162      totalcourses: {
 163          getter: function(value, name) {
 164              if (value === null) {
 165                  value = this.get('element').getData(name);
 166                  this.set(name, value);
 167              }
 168              return value;
 169          },
 170          value: null
 171      },
 172  
 173      /**
 174       * The URL to use for AJAX actions/requests.
 175       * @attribute ajaxurl
 176       * @type String
 177       * @default /course/ajax/management.php
 178       */
 179      ajaxurl: {
 180          getter: function(value) {
 181              if (value === null) {
 182                  value = M.cfg.wwwroot + '/course/ajax/management.php';
 183              }
 184              return value;
 185          },
 186          value: null
 187      },
 188  
 189      /**
 190       * The drag drop handler
 191       * @attribute dragdrop
 192       * @type DragDrop
 193       * @default null
 194       */
 195      dragdrop: {
 196          value: null
 197      }
 198  };
 199  Console.prototype = {
 200  
 201      /**
 202       * Gets set to true once the first categories have been initialised.
 203       * @property categoriesinit
 204       * @private
 205       * @type {boolean}
 206       */
 207      categoriesinit: false,
 208  
 209      /**
 210       * Initialises a new instance of the Console.
 211       * @method initializer
 212       */
 213      initializer: function() {
 214          Y.log('Initialising course category management console', 'info', 'moodle-course-management');
 215          this.set('element', 'coursecat-management');
 216          var element = this.get('element'),
 217              categorylisting = element.one('#category-listing'),
 218              courselisting = element.one('#course-listing'),
 219              selectedcategory = null,
 220              selectedcourse = null;
 221  
 222          if (categorylisting) {
 223              selectedcategory = categorylisting.one('.listitem[data-selected="1"]');
 224          }
 225          if (courselisting) {
 226              selectedcourse = courselisting.one('.listitem[data-selected="1"]');
 227          }
 228          this.set('categorylisting', categorylisting);
 229          this.set('courselisting', courselisting);
 230          this.set('coursedetails', element.one('#course-detail'));
 231          if (selectedcategory) {
 232              this.set('activecategoryid', selectedcategory.getData('id'));
 233          }
 234          if (selectedcourse) {
 235              this.set('activecourseid', selectedcourse.getData('id'));
 236          }
 237          this.initialiseCategories(categorylisting);
 238          this.initialiseCourses();
 239  
 240          if (courselisting) {
 241              // No need for dragdrop if we don't have a course listing.
 242              this.set('dragdrop', new DragDrop({console: this}));
 243          }
 244      },
 245  
 246      /**
 247       * Initialises all the categories being shown.
 248       * @method initialiseCategories
 249       * @private
 250       * @return {boolean}
 251       */
 252      initialiseCategories: function(listing) {
 253          var count = 0;
 254          if (!listing) {
 255              return false;
 256          }
 257  
 258          // Disable category bulk actions as nothing will be selected on initialise.
 259          var menumovecatto = listing.one('#menumovecategoriesto');
 260          if (menumovecatto) {
 261              menumovecatto.setAttribute('disabled', true);
 262          }
 263          var menuresortcategoriesby = listing.one('#menuresortcategoriesby');
 264          if (menuresortcategoriesby) {
 265              menuresortcategoriesby.setAttribute('disabled', true);
 266          }
 267          var menuresortcoursesby = listing.one('#menuresortcoursesby');
 268          if (menuresortcoursesby) {
 269              menuresortcoursesby.setAttribute('disabled', true);
 270          }
 271  
 272          listing.all('.listitem[data-id]').each(function(node) {
 273              this.set('categories', new Category({
 274                  node: node,
 275                  console: this
 276              }));
 277              count++;
 278          }, this);
 279          if (!this.categoriesinit) {
 280              this.get('categorylisting').delegate('click', this.handleCategoryDelegation, 'a[data-action]', this);
 281              this.get('categorylisting').delegate('click', this.handleCategoryDelegation, 'input[name="bcat[]"]', this);
 282              this.get('categorylisting').delegate('click', this.handleBulkSortByaction, '#menuselectsortby', this);
 283              this.categoriesinit = true;
 284              Y.log(count + ' categories being managed', 'info', 'moodle-course-management');
 285          } else {
 286              Y.log(count + ' new categories being managed', 'info', 'moodle-course-management');
 287          }
 288      },
 289  
 290      /**
 291       * Initialises all the categories being shown.
 292       * @method initialiseCourses
 293       * @private
 294       * @return {boolean}
 295       */
 296      initialiseCourses: function() {
 297          var category = this.getCategoryById(this.get('activecategoryid')),
 298              listing = this.get('courselisting'),
 299              count = 0;
 300          if (!listing) {
 301              return false;
 302          }
 303  
 304          // Disable course move to bulk action as nothing will be selected on initialise.
 305          var menumovecoursesto = listing.one('#menumovecoursesto');
 306          if (menumovecoursesto) {
 307              menumovecoursesto.setAttribute('disabled', true);
 308          }
 309  
 310          listing.all('.listitem[data-id]').each(function(node) {
 311              this.registerCourse(new Course({
 312                  node: node,
 313                  console: this,
 314                  category: category
 315              }));
 316              count++;
 317          }, this);
 318          listing.delegate('click', this.handleCourseDelegation, 'a[data-action]', this);
 319          listing.delegate('click', this.handleCourseDelegation, 'input[name="bc[]"]', this);
 320          Y.log(count + ' courses being managed', 'info', 'moodle-course-management');
 321      },
 322  
 323      /**
 324       * Registers a course within the management display.
 325       * @method registerCourse
 326       * @param {Course} course
 327       */
 328      registerCourse: function(course) {
 329          var courses = this.get('courses');
 330          courses.push(course);
 331          this.set('courses', courses);
 332      },
 333  
 334      /**
 335       * Handles the event fired by a delegated course listener.
 336       *
 337       * @method handleCourseDelegation
 338       * @protected
 339       * @param {EventFacade} e
 340       */
 341      handleCourseDelegation: function(e) {
 342          var target = e.currentTarget,
 343              action = target.getData('action'),
 344              courseid = target.ancestor('.listitem').getData('id'),
 345              course = this.getCourseById(courseid);
 346          if (course) {
 347              course.handle(action, e);
 348          } else {
 349              Y.log('Course with ID ' + courseid + ' could not be found for delegation', 'error', 'moodle-course-management');
 350          }
 351      },
 352  
 353      /**
 354       * Handles the event fired by a delegated course listener.
 355       *
 356       * @method handleCategoryDelegation
 357       * @protected
 358       * @param {EventFacade} e
 359       */
 360      handleCategoryDelegation: function(e) {
 361          var target = e.currentTarget,
 362              action = target.getData('action'),
 363              categoryid = target.ancestor('.listitem').getData('id'),
 364              category = this.getCategoryById(categoryid);
 365          if (category) {
 366              category.handle(action, e);
 367          } else {
 368              Y.log('Could not find category to delegate to.', 'error', 'moodle-course-management');
 369          }
 370      },
 371  
 372      /**
 373       * Check if any course is selected.
 374       *
 375       * @method isCourseSelected
 376       * @param {Node} checkboxnode Checkbox node on which action happened.
 377       * @return bool
 378       */
 379      isCourseSelected: function(checkboxnode) {
 380          var selected = false;
 381  
 382          // If any course selected then show move to category select box.
 383          if (checkboxnode && checkboxnode.get('checked')) {
 384              selected = true;
 385          } else {
 386              var i,
 387                  course,
 388                  courses = this.get('courses'),
 389                  length = courses.length;
 390              for (i = 0; i < length; i++) {
 391                  if (courses.hasOwnProperty(i)) {
 392                      course = courses[i];
 393                      if (course.get('node').one('input[name="bc[]"]').get('checked')) {
 394                          selected = true;
 395                          break;
 396                      }
 397                  }
 398              }
 399          }
 400          return selected;
 401      },
 402  
 403      /**
 404       * Check if any category is selected.
 405       *
 406       * @method isCategorySelected
 407       * @param {Node} checkboxnode Checkbox node on which action happened.
 408       * @return bool
 409       */
 410      isCategorySelected: function(checkboxnode) {
 411          var selected = false;
 412  
 413          // If any category selected then show move to category select box.
 414          if (checkboxnode && checkboxnode.get('checked')) {
 415              selected = true;
 416          } else {
 417              var i,
 418                  category,
 419                  categories = this.get('categories'),
 420                  length = categories.length;
 421              for (i = 0; i < length; i++) {
 422                  if (categories.hasOwnProperty(i)) {
 423                      category = categories[i];
 424                      if (category.get('node').one('input[name="bcat[]"]').get('checked')) {
 425                          selected = true;
 426                          break;
 427                      }
 428                  }
 429              }
 430          }
 431          return selected;
 432      },
 433  
 434      /**
 435       * Handle bulk sort action.
 436       *
 437       * @method handleBulkSortByaction
 438       * @protected
 439       * @param {EventFacade} e
 440       */
 441      handleBulkSortByaction: function(e) {
 442          var sortcategoryby = this.get('categorylisting').one('#menuresortcategoriesby'),
 443              sortcourseby = this.get('categorylisting').one('#menuresortcoursesby'),
 444              sortbybutton = this.get('categorylisting').one('input[name="bulksort"]'),
 445              sortby = e;
 446  
 447          if (!sortby) {
 448              sortby = this.get('categorylisting').one('#menuselectsortby');
 449          } else {
 450              if (e && e.currentTarget) {
 451                  sortby = e.currentTarget;
 452              }
 453          }
 454  
 455          // If no sortby select found then return as we can't do anything.
 456          if (!sortby) {
 457              return;
 458          }
 459  
 460          if ((this.get('categories').length <= 1) || (!this.isCategorySelected() &&
 461                  (sortby.get("options").item(sortby.get('selectedIndex')).getAttribute('value') === 'selectedcategories'))) {
 462              if (sortcategoryby) {
 463                  sortcategoryby.setAttribute('disabled', true);
 464              }
 465              if (sortcourseby) {
 466                  sortcourseby.setAttribute('disabled', true);
 467              }
 468              if (sortbybutton) {
 469                  sortbybutton.setAttribute('disabled', true);
 470              }
 471          } else {
 472              if (sortcategoryby) {
 473                  sortcategoryby.removeAttribute('disabled');
 474              }
 475              if (sortcourseby) {
 476                  sortcourseby.removeAttribute('disabled');
 477              }
 478              if (sortbybutton) {
 479                  sortbybutton.removeAttribute('disabled');
 480              }
 481          }
 482      },
 483  
 484      /**
 485       * Returns the category with the given ID.
 486       * @method getCategoryById
 487       * @param {Number} id
 488       * @return {Category|Boolean} The category or false if it can't be found.
 489       */
 490      getCategoryById: function(id) {
 491          var i,
 492              category,
 493              categories = this.get('categories'),
 494              length = categories.length;
 495          for (i = 0; i < length; i++) {
 496              if (categories.hasOwnProperty(i)) {
 497                  category = categories[i];
 498                  if (category.get('categoryid') === id) {
 499                      return category;
 500                  }
 501              }
 502          }
 503          return false;
 504      },
 505  
 506      /**
 507       * Returns the course with the given id.
 508       * @method getCourseById
 509       * @param {Number} id
 510       * @return {Course|Boolean} The course or false if not found/
 511       */
 512      getCourseById: function(id) {
 513          var i,
 514              course,
 515              courses = this.get('courses'),
 516              length = courses.length;
 517          for (i = 0; i < length; i++) {
 518              if (courses.hasOwnProperty(i)) {
 519                  course = courses[i];
 520                  if (course.get('courseid') === id) {
 521                      return course;
 522                  }
 523              }
 524          }
 525          return false;
 526      },
 527  
 528      /**
 529       * Removes the course with the given ID.
 530       * @method removeCourseById
 531       * @param {Number} id
 532       */
 533      removeCourseById: function(id) {
 534          var courses = this.get('courses'),
 535              length = courses.length,
 536              course,
 537              i;
 538          for (i = 0; i < length; i++) {
 539              course = courses[i];
 540              if (course.get('courseid') === id) {
 541                  courses.splice(i, 1);
 542                  break;
 543              }
 544          }
 545      },
 546  
 547      /**
 548       * Performs an AJAX action.
 549       *
 550       * @method performAjaxAction
 551       * @param {String} action The action to perform.
 552       * @param {Object} args The arguments to pass through with teh request.
 553       * @param {Function} callback The function to call when all is done.
 554       * @param {Object} context The object to use as the context for the callback.
 555       */
 556      performAjaxAction: function(action, args, callback, context) {
 557          var io = new Y.IO();
 558          args.action = action;
 559          args.ajax = '1';
 560          args.sesskey = M.cfg.sesskey;
 561          if (callback === null) {
 562              callback = function() {
 563                  Y.log("'Action '" + action + "' completed", 'debug', 'moodle-course-management');
 564              };
 565          }
 566          io.send(this.get('ajaxurl'), {
 567              method: 'POST',
 568              on: {
 569                  complete: callback
 570              },
 571              context: context,
 572              data: args,
 573              'arguments': args
 574          });
 575      }
 576  };
 577  Y.extend(Console, Y.Base, Console.prototype);
 578  
 579  M.course = M.course || {};
 580  M.course.management = M.course.management || {};
 581  M.course.management.console = null;
 582  
 583  /**
 584   * Initalises the course management console.
 585   *
 586   * @method M.course.management.init
 587   * @static
 588   * @param {Object} config
 589   */
 590  M.course.management.init = function(config) {
 591      M.course.management.console = new Console(config);
 592  };
 593  /* global Console */
 594  
 595  /**
 596   * Drag and Drop handler
 597   *
 598   * @namespace M.course.management
 599   * @class DragDrop
 600   * @constructor
 601   * @extends Base
 602   */
 603  function DragDrop(config) {
 604      Console.superclass.constructor.apply(this, [config]);
 605  }
 606  DragDrop.NAME = 'moodle-course-management-dd';
 607  DragDrop.CSS_PREFIX = 'management-dd';
 608  DragDrop.ATTRS = {
 609      /**
 610       * The management console this drag and drop has been set up for.
 611       * @attribute console
 612       * @type Console
 613       * @writeOnce
 614       */
 615      console: {
 616          writeOnce: 'initOnly'
 617      }
 618  };
 619  DragDrop.prototype = {
 620      /**
 621       * True if the user is dragging a course upwards.
 622       * @property goingup
 623       * @protected
 624       * @default false
 625       */
 626      goingup: false,
 627  
 628      /**
 629       * The last Y position of the course being dragged
 630       * @property lasty
 631       * @protected
 632       * @default null
 633       */
 634      lasty: null,
 635  
 636      /**
 637       * The sibling above the course being dragged currently (tracking its original position).
 638       *
 639       * @property previoussibling
 640       * @protected
 641       * @default false
 642       */
 643      previoussibling: null,
 644  
 645      /**
 646       * Initialises the DragDrop instance.
 647       * @method initializer
 648       */
 649      initializer: function() {
 650          var managementconsole = this.get('console'),
 651              container = managementconsole.get('element'),
 652              categorylisting = container.one('#category-listing'),
 653              courselisting = container.one('#course-listing > .course-listing'),
 654              categoryul = (categorylisting) ? categorylisting.one('ul.ml') : null,
 655              courseul = (courselisting) ? courselisting.one('ul.ml') : null,
 656              canmoveoutof = (courselisting) ? courselisting.getData('canmoveoutof') : false,
 657              contstraint = (canmoveoutof) ? container : courseul;
 658  
 659          if (!courseul) {
 660              // No course listings found.
 661              return false;
 662          }
 663  
 664          courseul.all('> li').each(function(li) {
 665              this.initCourseListing(li, contstraint);
 666          }, this);
 667          courseul.setData('dd', new Y.DD.Drop({
 668              node: courseul
 669          }));
 670          if (canmoveoutof && categoryul) {
 671              // Category UL may not be there if viewmode is just courses.
 672              categoryul.all('li > div').each(function(div) {
 673                  this.initCategoryListitem(div);
 674              }, this);
 675          }
 676          Y.DD.DDM.on('drag:start', this.dragStart, this);
 677          Y.DD.DDM.on('drag:end', this.dragEnd, this);
 678          Y.DD.DDM.on('drag:drag', this.dragDrag, this);
 679          Y.DD.DDM.on('drop:over', this.dropOver, this);
 680          Y.DD.DDM.on('drop:enter', this.dropEnter, this);
 681          Y.DD.DDM.on('drop:exit', this.dropExit, this);
 682          Y.DD.DDM.on('drop:hit', this.dropHit, this);
 683  
 684      },
 685  
 686      /**
 687       * Initialises a course listing.
 688       * @method initCourseListing
 689       * @param Node
 690       */
 691      initCourseListing: function(node, contstraint) {
 692          node.setData('dd', new Y.DD.Drag({
 693              node: node,
 694              target: {
 695                  padding: '0 0 0 20'
 696              }
 697          }).addHandle(
 698              '.drag-handle'
 699          ).plug(Y.Plugin.DDProxy, {
 700              moveOnEnd: false,
 701              borderStyle: false
 702          }).plug(Y.Plugin.DDConstrained, {
 703              constrain2node: contstraint
 704          }));
 705      },
 706  
 707      /**
 708       * Initialises a category listing.
 709       * @method initCategoryListitem
 710       * @param Node
 711       */
 712      initCategoryListitem: function(node) {
 713          node.setData('dd', new Y.DD.Drop({
 714              node: node
 715          }));
 716      },
 717  
 718      /**
 719       * Dragging has started.
 720       * @method dragStart
 721       * @private
 722       * @param {EventFacade} e
 723       */
 724      dragStart: function(e) {
 725          var drag = e.target,
 726              node = drag.get('node'),
 727              dragnode = drag.get('dragNode');
 728          node.addClass('course-being-dragged');
 729          dragnode.addClass('course-being-dragged-proxy').set('innerHTML', node.one('a.coursename').get('innerHTML'));
 730          this.previoussibling = node.get('previousSibling');
 731      },
 732  
 733      /**
 734       * Dragging has ended.
 735       * @method dragEnd
 736       * @private
 737       * @param {EventFacade} e
 738       */
 739      dragEnd: function(e) {
 740          var drag = e.target,
 741              node = drag.get('node');
 742          node.removeClass('course-being-dragged');
 743          this.get('console').get('element').all('#category-listing li.highlight').removeClass('highlight');
 744      },
 745  
 746      /**
 747       * Dragging in progress.
 748       * @method dragDrag
 749       * @private
 750       * @param {EventFacade} e
 751       */
 752      dragDrag: function(e) {
 753          var y = e.target.lastXY[1];
 754          if (y < this.lasty) {
 755              this.goingup = true;
 756          } else {
 757              this.goingup = false;
 758          }
 759          this.lasty = y;
 760      },
 761  
 762      /**
 763       * The course has been dragged over a drop target.
 764       * @method dropOver
 765       * @private
 766       * @param {EventFacade} e
 767       */
 768      dropOver: function(e) {
 769          // Get a reference to our drag and drop nodes
 770          var drag = e.drag.get('node'),
 771              drop = e.drop.get('node'),
 772              tag = drop.get('tagName').toLowerCase();
 773          if (tag === 'li' && drop.hasClass('listitem-course')) {
 774              if (!this.goingup) {
 775                  drop = drop.get('nextSibling');
 776                  if (!drop) {
 777                      drop = e.drop.get('node');
 778                      drop.get('parentNode').append(drag);
 779                      return false;
 780                  }
 781              }
 782              drop.get('parentNode').insertBefore(drag, drop);
 783              e.drop.sizeShim();
 784          }
 785      },
 786  
 787      /**
 788       * The course has been dragged over a drop target.
 789       * @method dropEnter
 790       * @private
 791       * @param {EventFacade} e
 792       */
 793      dropEnter: function(e) {
 794          var drop = e.drop.get('node'),
 795              tag = drop.get('tagName').toLowerCase();
 796          if (tag === 'div') {
 797              drop.ancestor('li.listitem-category').addClass('highlight');
 798          }
 799      },
 800  
 801      /**
 802       * The course has been dragged off a drop target.
 803       * @method dropExit
 804       * @private
 805       * @param {EventFacade} e
 806       */
 807      dropExit: function(e) {
 808          var drop = e.drop.get('node'),
 809              tag = drop.get('tagName').toLowerCase();
 810          if (tag === 'div') {
 811              drop.ancestor('li.listitem-category').removeClass('highlight');
 812          }
 813      },
 814  
 815      /**
 816       * The course has been dropped on a target.
 817       * @method dropHit
 818       * @private
 819       * @param {EventFacade} e
 820       */
 821      dropHit: function(e) {
 822          var drag = e.drag.get('node'),
 823              drop = e.drop.get('node'),
 824              iscategory = (drop.ancestor('.listitem-category') !== null),
 825              iscourse = !iscategory && (drop.test('.listitem-course')),
 826              managementconsole = this.get('console'),
 827              categoryid,
 828              category,
 829              courseid,
 830              course,
 831              aftercourseid,
 832              previoussibling,
 833              previousid;
 834  
 835          if (!drag.test('.listitem-course')) {
 836              Y.log('It was not a course being dragged.', 'warn', 'moodle-course-management');
 837              return false;
 838          }
 839          courseid = drag.getData('id');
 840          if (iscategory) {
 841              categoryid = drop.ancestor('.listitem-category').getData('id');
 842              Y.log('Course ' + courseid + ' dragged into category ' + categoryid);
 843              category = managementconsole.getCategoryById(categoryid);
 844              if (category) {
 845                  course = managementconsole.getCourseById(courseid);
 846                  if (course) {
 847                      category.moveCourseTo(course);
 848                  }
 849              }
 850          } else if (iscourse || drop.ancestor('#course-listing')) {
 851              course = managementconsole.getCourseById(courseid);
 852              previoussibling = drag.get('previousSibling');
 853              aftercourseid = (previoussibling) ? previoussibling.getData('id') || 0 : 0;
 854              previousid = (this.previoussibling) ? this.previoussibling.getData('id') : 0;
 855              if (aftercourseid !== previousid) {
 856                  course.moveAfter(aftercourseid, previousid);
 857              }
 858          } else {
 859              Y.log('Course dropped over unhandled target.', 'info', 'moodle-course-management');
 860          }
 861      }
 862  };
 863  Y.extend(DragDrop, Y.Base, DragDrop.prototype);
 864  /**
 865   * A managed course.
 866   *
 867   * @namespace M.course.management
 868   * @class Item
 869   * @constructor
 870   * @extends Base
 871   */
 872  function Item() {
 873      Item.superclass.constructor.apply(this, arguments);
 874  }
 875  Item.NAME = 'moodle-course-management-item';
 876  Item.CSS_PREFIX = 'management-item';
 877  Item.ATTRS = {
 878      /**
 879       * The node for this item.
 880       * @attribute node
 881       * @type Node
 882       */
 883      node: {},
 884  
 885      /**
 886       * The management console.
 887       * @attribute console
 888       * @type Console
 889       */
 890      console: {},
 891  
 892      /**
 893       * Describes the type of this item. Should be set by the extending class.
 894       * @attribute itemname
 895       * @type {String}
 896       * @default item
 897       */
 898      itemname: {
 899          value: 'item'
 900      }
 901  };
 902  Item.prototype = {
 903      /**
 904       * The highlight timeout for this item if there is one.
 905       * @property highlighttimeout
 906       * @protected
 907       * @type Timeout
 908       * @default null
 909       */
 910      highlighttimeout: null,
 911  
 912      /**
 913       * Checks and parses an AJAX response for an item.
 914       *
 915       * @method checkAjaxResponse
 916       * @protected
 917       * @param {Number} transactionid The transaction ID of the AJAX request (unique)
 918       * @param {Object} response The response from the AJAX request.
 919       * @param {Object} args The arguments given to the request.
 920       * @return {Object|Boolean}
 921       */
 922      checkAjaxResponse: function(transactionid, response, args) {
 923          if (response.status !== 200) {
 924              Y.log('Error: AJAX response resulted in non 200 status.', 'error', 'Item.checkAjaxResponse');
 925              return false;
 926          }
 927          if (transactionid === null || args === null) {
 928              Y.log('Error: Invalid AJAX response details provided.', 'error', 'Item.checkAjaxResponse');
 929              return false;
 930          }
 931          var outcome = Y.JSON.parse(response.responseText);
 932          if (outcome.error !== false) {
 933              new M.core.exception(outcome);
 934          }
 935          if (outcome.outcome === false) {
 936              return false;
 937          }
 938          return outcome;
 939      },
 940  
 941      /**
 942       * Moves an item up by one.
 943       *
 944       * @method moveup
 945       * @param {Number} transactionid The transaction ID of the AJAX request (unique)
 946       * @param {Object} response The response from the AJAX request.
 947       * @param {Object} args The arguments given to the request.
 948       * @return {Boolean}
 949       */
 950      moveup: function(transactionid, response, args) {
 951          var node,
 952              nodeup,
 953              nodedown,
 954              previous,
 955              previousup,
 956              previousdown,
 957              tmpnode,
 958              outcome = this.checkAjaxResponse(transactionid, response, args);
 959          if (outcome === false) {
 960              Y.log('AJAX request to move ' + this.get('itemname') + ' up failed by outcome.', 'warn', 'moodle-course-management');
 961              return false;
 962          }
 963          node = this.get('node');
 964          previous = node.previous('.listitem');
 965          if (previous) {
 966              previous.insert(node, 'before');
 967              previousup = previous.one(' > div a.action-moveup');
 968              nodedown = node.one(' > div a.action-movedown');
 969              if (!previousup || !nodedown) {
 970                  // We can have two situations here:
 971                  //   1. previousup is not set and nodedown is not set. This happens when there are only two courses.
 972                  //   2. nodedown is not set. This happens when they are moving the bottom course up.
 973                  // node up and previous down should always be there. They would be required to trigger the action.
 974                  nodeup = node.one(' > div a.action-moveup');
 975                  previousdown = previous.one(' > div a.action-movedown');
 976                  if (!previousup && !nodedown) {
 977                      // Ok, must be two courses. We need to switch the up and down icons.
 978                      tmpnode = Y.Node.create('<a style="visibility:hidden;">&nbsp;</a>');
 979                      previousdown.replace(tmpnode);
 980                      nodeup.replace(previousdown);
 981                      tmpnode.replace(nodeup);
 982                      tmpnode.destroy();
 983                  } else if (!nodedown) {
 984                      // previous down needs to be given to node.
 985                      nodeup.insert(previousdown, 'after');
 986                  }
 987              }
 988              nodeup = node.one(' > div a.action-moveup');
 989              if (nodeup) {
 990                  // Try to re-focus on up.
 991                  nodeup.focus();
 992              } else {
 993                  // If we can't focus up we're at the bottom, try to focus on up.
 994                  nodedown = node.one(' > div a.action-movedown');
 995                  if (nodedown) {
 996                      nodedown.focus();
 997                  }
 998              }
 999              this.updated(true);
1000              Y.log('Success: ' + this.get('itemname') + ' moved up by AJAX.', 'info', 'moodle-course-management');
1001          } else {
1002              // Aha it succeeded but this is the top item in the list. Pagination is in play!
1003              // Refresh to update the state of things.
1004              Y.log(this.get('itemname') + ' cannot be moved up as its the top item on this page.',
1005                      'info', 'moodle-course-management');
1006              window.location.reload();
1007          }
1008      },
1009  
1010      /**
1011       * Moves an item down by one.
1012       *
1013       * @method movedown
1014       * @param {Number} transactionid The transaction ID of the AJAX request (unique)
1015       * @param {Object} response The response from the AJAX request.
1016       * @param {Object} args The arguments given to the request.
1017       * @return {Boolean}
1018       */
1019      movedown: function(transactionid, response, args) {
1020          var node,
1021              next,
1022              nodeup,
1023              nodedown,
1024              nextup,
1025              nextdown,
1026              tmpnode,
1027              outcome = this.checkAjaxResponse(transactionid, response, args);
1028          if (outcome === false) {
1029              Y.log('AJAX request to move ' + this.get('itemname') + ' down failed by outcome.', 'warn', 'moodle-course-management');
1030              return false;
1031          }
1032          node = this.get('node');
1033          next = node.next('.listitem');
1034          if (next) {
1035              node.insert(next, 'before');
1036              nextdown = next.one(' > div a.action-movedown');
1037              nodeup = node.one(' > div a.action-moveup');
1038              if (!nextdown || !nodeup) {
1039                  // next up and node down should always be there. They would be required to trigger the action.
1040                  nextup = next.one(' > div a.action-moveup');
1041                  nodedown = node.one(' > div a.action-movedown');
1042                  if (!nextdown && !nodeup) {
1043                      // We can have two situations here:
1044                      //   1. nextdown is not set and nodeup is not set. This happens when there are only two courses.
1045                      //   2. nodeup is not set. This happens when we are moving the first course down.
1046                      // Ok, must be two courses. We need to switch the up and down icons.
1047                      tmpnode = Y.Node.create('<a style="visibility:hidden;">&nbsp;</a>');
1048                      nextup.replace(tmpnode);
1049                      nodedown.replace(nextup);
1050                      tmpnode.replace(nodedown);
1051                      tmpnode.destroy();
1052                  } else if (!nodeup) {
1053                      // next up needs to be given to node.
1054                      nodedown.insert(nextup, 'before');
1055                  }
1056              }
1057              nodedown = node.one(' > div a.action-movedown');
1058              if (nodedown) {
1059                  // Try to ensure the up is focused again.
1060                  nodedown.focus();
1061              } else {
1062                  // If we can't focus up we're at the top, try to focus on down.
1063                  nodeup = node.one(' > div a.action-moveup');
1064                  if (nodeup) {
1065                      nodeup.focus();
1066                  }
1067              }
1068              this.updated(true);
1069              Y.log('Success: ' + this.get('itemname') + ' moved down by AJAX.', 'info', 'moodle-course-management');
1070          } else {
1071              // Aha it succeeded but this is the bottom item in the list. Pagination is in play!
1072              // Refresh to update the state of things.
1073              Y.log(this.get('itemname') + ' cannot be moved down as its the top item on this page.',
1074                      'info', 'moodle-course-management');
1075              window.location.reload();
1076          }
1077      },
1078  
1079      /**
1080       * Makes an item visible.
1081       *
1082       * @method show
1083       * @param {Number} transactionid The transaction ID of the AJAX request (unique)
1084       * @param {Object} response The response from the AJAX request.
1085       * @param {Object} args The arguments given to the request.
1086       * @return {Boolean}
1087       */
1088      show: function(transactionid, response, args) {
1089          var outcome = this.checkAjaxResponse(transactionid, response, args),
1090              hidebtn;
1091          if (outcome === false) {
1092              Y.log('AJAX request to show ' + this.get('itemname') + ' by outcome.', 'warn', 'moodle-course-management');
1093              return false;
1094          }
1095  
1096          this.markVisible();
1097          hidebtn = this.get('node').one('a[data-action=hide]');
1098          if (hidebtn) {
1099              hidebtn.focus();
1100          }
1101          this.updated();
1102          Y.log('Success: ' + this.get('itemname') + ' made visible by AJAX.', 'info', 'moodle-course-management');
1103      },
1104  
1105      /**
1106       * Marks the item as visible
1107       * @method markVisible
1108       */
1109      markVisible: function() {
1110          this.get('node').setAttribute('data-visible', '1');
1111          Y.log('Marked ' + this.get('itemname') + ' as visible', 'info', 'moodle-course-management');
1112          return true;
1113      },
1114  
1115      /**
1116       * Hides an item.
1117       *
1118       * @method hide
1119       * @param {Number} transactionid The transaction ID of the AJAX request (unique)
1120       * @param {Object} response The response from the AJAX request.
1121       * @param {Object} args The arguments given to the request.
1122       * @return {Boolean}
1123       */
1124      hide: function(transactionid, response, args) {
1125          var outcome = this.checkAjaxResponse(transactionid, response, args),
1126              showbtn;
1127          if (outcome === false) {
1128              Y.log('AJAX request to hide ' + this.get('itemname') + ' by outcome.', 'warn', 'moodle-course-management');
1129              return false;
1130          }
1131          this.markHidden();
1132          showbtn = this.get('node').one('a[data-action=show]');
1133          if (showbtn) {
1134              showbtn.focus();
1135          }
1136          this.updated();
1137          Y.log('Success: ' + this.get('itemname') + ' made hidden by AJAX.', 'info', 'moodle-course-management');
1138      },
1139  
1140      /**
1141       * Marks the item as hidden.
1142       * @method makeHidden
1143       */
1144      markHidden: function() {
1145          this.get('node').setAttribute('data-visible', '0');
1146          Y.log('Marked ' + this.get('itemname') + ' as hidden', 'info', 'moodle-course-management');
1147          return true;
1148      },
1149  
1150      /**
1151       * Called when ever a node is updated.
1152       *
1153       * @method updated
1154       * @param {Boolean} moved True if this item was moved.
1155       */
1156      updated: function(moved) {
1157          if (moved) {
1158              this.highlight();
1159          }
1160      },
1161  
1162      /**
1163       * Highlights this option for a breif time.
1164       *
1165       * @method highlight
1166       */
1167      highlight: function() {
1168          var node = this.get('node');
1169          node.siblings('.highlight').removeClass('highlight');
1170          node.addClass('highlight');
1171          if (this.highlighttimeout) {
1172              window.clearTimeout(this.highlighttimeout);
1173          }
1174          this.highlighttimeout = window.setTimeout(function() {
1175              node.removeClass('highlight');
1176          }, 2500);
1177      }
1178  };
1179  Y.extend(Item, Y.Base, Item.prototype);
1180  /* global Item */
1181  
1182  /**
1183   * A managed category.
1184   *
1185   * @namespace M.course.management
1186   * @class Category
1187   * @constructor
1188   * @extends Item
1189   */
1190  function Category() {
1191      Category.superclass.constructor.apply(this, arguments);
1192  }
1193  Category.NAME = 'moodle-course-management-category';
1194  Category.CSS_PREFIX = 'management-category';
1195  Category.ATTRS = {
1196      /**
1197       * The category ID relating to this category.
1198       * @attribute categoryid
1199       * @type Number
1200       * @writeOnce
1201       * @default null
1202       */
1203      categoryid: {
1204          getter: function(value, name) {
1205              if (value === null) {
1206                  value = this.get('node').getData('id');
1207                  this.set(name, value);
1208              }
1209              return value;
1210          },
1211          value: null,
1212          writeOnce: true
1213      },
1214  
1215      /**
1216       * True if this category is the currently selected category.
1217       * @attribute selected
1218       * @type Boolean
1219       * @default null
1220       */
1221      selected: {
1222          getter: function(value, name) {
1223              if (value === null) {
1224                  value = this.get('node').getData(name);
1225                  if (value === null) {
1226                      value = false;
1227                  }
1228                  this.set(name, value);
1229              }
1230              return value;
1231          },
1232          value: null
1233      },
1234  
1235      /**
1236       * An array of courses belonging to this category.
1237       * @attribute courses
1238       * @type Course[]
1239       * @default Array
1240       */
1241      courses: {
1242          validator: function(val) {
1243              return Y.Lang.isArray(val);
1244          },
1245          value: []
1246      }
1247  };
1248  Category.prototype = {
1249      /**
1250       * Initialises an instance of a Category.
1251       * @method initializer
1252       */
1253      initializer: function() {
1254          this.set('itemname', 'category');
1255      },
1256  
1257      /**
1258       * Returns the name of the category.
1259       * @method getName
1260       * @return {String}
1261       */
1262      getName: function() {
1263          return this.get('node').one('a.categoryname').get('innerHTML');
1264      },
1265  
1266      /**
1267       * Registers a course as belonging to this category.
1268       * @method registerCourse
1269       * @param {Course} course
1270       */
1271      registerCourse: function(course) {
1272          var courses = this.get('courses');
1273          courses.push(course);
1274          this.set('courses', courses);
1275      },
1276  
1277      /**
1278       * Handles a category related event.
1279       *
1280       * @method handle
1281       * @param {String} action
1282       * @param {EventFacade} e
1283       * @return {Boolean}
1284       */
1285      handle: function(action, e) {
1286          var catarg = {categoryid: this.get('categoryid')},
1287              selected = this.get('console').get('activecategoryid');
1288          if (selected && selected !== catarg.categoryid) {
1289              catarg.selectedcategory = selected;
1290          }
1291          switch (action) {
1292              case 'moveup':
1293                  e.preventDefault();
1294                  this.get('console').performAjaxAction('movecategoryup', catarg, this.moveup, this);
1295                  break;
1296              case 'movedown':
1297                  e.preventDefault();
1298                  this.get('console').performAjaxAction('movecategorydown', catarg, this.movedown, this);
1299                  break;
1300              case 'show':
1301                  e.preventDefault();
1302                  this.get('console').performAjaxAction('showcategory', catarg, this.show, this);
1303                  break;
1304              case 'hide':
1305                  e.preventDefault();
1306                  this.get('console').performAjaxAction('hidecategory', catarg, this.hide, this);
1307                  break;
1308              case 'expand':
1309                  e.preventDefault();
1310                  if (this.get('node').getData('expanded') === '0') {
1311                      this.get('node').setAttribute('data-expanded', '1').setData('expanded', 'true');
1312                      this.get('console').performAjaxAction('getsubcategorieshtml', catarg, this.loadSubcategories, this);
1313                  }
1314                  this.expand();
1315                  break;
1316              case 'collapse':
1317                  e.preventDefault();
1318                  this.collapse();
1319                  break;
1320              case 'select':
1321                  var c = this.get('console'),
1322                      movecategoryto = c.get('categorylisting').one('#menumovecategoriesto');
1323                  // If any category is selected and there are more then one categories.
1324                  if (movecategoryto) {
1325                      if (c.isCategorySelected(e.currentTarget) &&
1326                              c.get('categories').length > 1) {
1327                          movecategoryto.removeAttribute('disabled');
1328                      } else {
1329                          movecategoryto.setAttribute('disabled', true);
1330                      }
1331                      c.handleBulkSortByaction();
1332                  }
1333                  break;
1334              default:
1335                  Y.log('Invalid AJAX action requested of managed category.', 'warn', 'moodle-course-management');
1336                  return false;
1337          }
1338      },
1339  
1340      /**
1341       * Expands the category making its sub categories visible.
1342       * @method expand
1343       */
1344      expand: function() {
1345          var node = this.get('node'),
1346              action = node.one('a[data-action=expand]'),
1347              ul = node.one('ul[role=group]');
1348          node.removeClass('collapsed').setAttribute('aria-expanded', 'true');
1349          action.setAttribute('data-action', 'collapse').setAttrs({
1350              title: M.util.get_string('collapsecategory', 'moodle', this.getName())
1351          }).one('img').setAttrs({
1352              src: M.util.image_url('t/switch_minus', 'moodle'),
1353              alt: M.util.get_string('collapse', 'moodle')
1354          });
1355          if (ul) {
1356              ul.setAttribute('aria-hidden', 'false');
1357          }
1358          this.get('console').performAjaxAction('expandcategory', {categoryid: this.get('categoryid')}, null, this);
1359      },
1360  
1361      /**
1362       * Collapses the category making its sub categories hidden.
1363       * @method collapse
1364       */
1365      collapse: function() {
1366          var node = this.get('node'),
1367              action = node.one('a[data-action=collapse]'),
1368              ul = node.one('ul[role=group]');
1369          node.addClass('collapsed').setAttribute('aria-expanded', 'false');
1370          action.setAttribute('data-action', 'expand').setAttrs({
1371              title: M.util.get_string('expandcategory', 'moodle', this.getName())
1372          }).one('img').setAttrs({
1373              src: M.util.image_url('t/switch_plus', 'moodle'),
1374              alt: M.util.get_string('expand', 'moodle')
1375          });
1376          if (ul) {
1377              ul.setAttribute('aria-hidden', 'true');
1378          }
1379          this.get('console').performAjaxAction('collapsecategory', {categoryid: this.get('categoryid')}, null, this);
1380      },
1381  
1382      /**
1383       * Loads sub categories provided by an AJAX request..
1384       *
1385       * @method loadSubcategories
1386       * @protected
1387       * @param {Number} transactionid The transaction ID of the AJAX request (unique)
1388       * @param {Object} response The response from the AJAX request.
1389       * @param {Object} args The arguments given to the request.
1390       * @return {Boolean} Returns true on success - false otherwise.
1391       */
1392      loadSubcategories: function(transactionid, response, args) {
1393          var outcome = this.checkAjaxResponse(transactionid, response, args),
1394              node = this.get('node'),
1395              managementconsole = this.get('console'),
1396              ul,
1397              actionnode;
1398          if (outcome === false) {
1399              Y.log('AJAX failed to load sub categories for ' + this.get('itemname'), 'warn', 'moodle-course-management');
1400              return false;
1401          }
1402          Y.log('AJAX loaded subcategories for ' + this.get('itemname'), 'info', 'moodle-course-management');
1403          node.append(outcome.html);
1404          managementconsole.initialiseCategories(node);
1405          if (M.core && M.core.actionmenu && M.core.actionmenu.newDOMNode) {
1406              M.core.actionmenu.newDOMNode(node);
1407          }
1408          ul = node.one('ul[role=group]');
1409          actionnode = node.one('a[data-action=collapse]');
1410          if (ul && actionnode) {
1411              actionnode.setAttribute('aria-controls', ul.generateID());
1412          }
1413          return true;
1414      },
1415  
1416      /**
1417       * Moves the course to this category.
1418       *
1419       * @method moveCourseTo
1420       * @param {Course} course
1421       */
1422      moveCourseTo: function(course) {
1423          var self = this;
1424          Y.use('moodle-core-notification-confirm', function() {
1425              var confirm = new M.core.confirm({
1426                  title: M.util.get_string('confirm', 'moodle'),
1427                  question: M.util.get_string('confirmcoursemove', 'moodle', {
1428                      course: course.getName(),
1429                      category: self.getName()
1430                  }),
1431                  yesLabel: M.util.get_string('move', 'moodle'),
1432                  noLabel: M.util.get_string('cancel', 'moodle')
1433              });
1434              confirm.on('complete-yes', function() {
1435                  confirm.hide();
1436                  confirm.destroy();
1437                  this.get('console').performAjaxAction('movecourseintocategory', {
1438                      categoryid: this.get('categoryid'),
1439                      courseid: course.get('courseid')
1440                  }, this.completeMoveCourse, this);
1441              }, self);
1442              confirm.show();
1443          });
1444      },
1445  
1446      /**
1447       * Completes moving a course to this category.
1448       * @method completeMoveCourse
1449       * @protected
1450       * @param {Number} transactionid The transaction ID of the AJAX request (unique)
1451       * @param {Object} response The response from the AJAX request.
1452       * @param {Object} args The arguments given to the request.
1453       * @return {Boolean}
1454       */
1455      completeMoveCourse: function(transactionid, response, args) {
1456          var outcome = this.checkAjaxResponse(transactionid, response, args),
1457              managementconsole = this.get('console'),
1458              category,
1459              course,
1460              totals;
1461          if (outcome === false) {
1462              Y.log('AJAX failed to move courses into this category: ' + this.get('itemname'), 'warn', 'moodle-course-management');
1463              return false;
1464          }
1465          course = managementconsole.getCourseById(args.courseid);
1466          if (!course) {
1467              Y.log('Course was moved but the course listing could not be found to reflect this', 'warn', 'moodle-course-management');
1468              return false;
1469          }
1470          Y.log('Moved the course (' + course.getName() + ') into this category (' + this.getName() + ')',
1471              'debug', 'moodle-course-management');
1472          this.highlight();
1473          if (course) {
1474              if (outcome.paginationtotals) {
1475                  totals = managementconsole.get('courselisting').one('.listing-pagination-totals');
1476                  if (totals) {
1477                      totals.set('innerHTML', outcome.paginationtotals);
1478                  }
1479              }
1480              if (outcome.totalcatcourses !== 'undefined') {
1481                  totals = this.get('node').one('.course-count span');
1482                  if (totals) {
1483                      totals.set('innerHTML', totals.get('innerHTML').replace(/^\d+/, outcome.totalcatcourses));
1484                  }
1485              }
1486              if (typeof outcome.fromcatcoursecount !== 'undefined') {
1487                  category = managementconsole.get('activecategoryid');
1488                  category = managementconsole.getCategoryById(category);
1489                  if (category) {
1490                      totals = category.get('node').one('.course-count span');
1491                      if (totals) {
1492                          totals.set('innerHTML', totals.get('innerHTML').replace(/^\d+/, outcome.fromcatcoursecount));
1493                      }
1494                  }
1495              }
1496              course.remove();
1497          }
1498          return true;
1499      },
1500  
1501      /**
1502       * Makes an item visible.
1503       *
1504       * @method show
1505       * @param {Number} transactionid The transaction ID of the AJAX request (unique)
1506       * @param {Object} response The response from the AJAX request.
1507       * @param {Object} args The arguments given to the request.
1508       * @return {Boolean}
1509       */
1510      show: function(transactionid, response, args) {
1511          var outcome = this.checkAjaxResponse(transactionid, response, args),
1512              hidebtn;
1513          if (outcome === false) {
1514              Y.log('AJAX request to show ' + this.get('itemname') + ' by outcome.', 'warn', 'moodle-course-management');
1515              return false;
1516          }
1517  
1518          this.markVisible();
1519          hidebtn = this.get('node').one('a[data-action=hide]');
1520          if (hidebtn) {
1521              hidebtn.focus();
1522          }
1523          if (outcome.categoryvisibility) {
1524              this.updateChildVisibility(outcome.categoryvisibility);
1525          }
1526          if (outcome.coursevisibility) {
1527              this.updateCourseVisiblity(outcome.coursevisibility);
1528          }
1529          this.updated();
1530          Y.log('Success: category made visible by AJAX.', 'info', 'moodle-course-management');
1531      },
1532  
1533      /**
1534       * Hides an item.
1535       *
1536       * @method hide
1537       * @param {Number} transactionid The transaction ID of the AJAX request (unique)
1538       * @param {Object} response The response from the AJAX request.
1539       * @param {Object} args The arguments given to the request.
1540       * @return {Boolean}
1541       */
1542      hide: function(transactionid, response, args) {
1543          var outcome = this.checkAjaxResponse(transactionid, response, args),
1544              showbtn;
1545          if (outcome === false) {
1546              Y.log('AJAX request to hide ' + this.get('itemname') + ' by outcome.', 'warn', 'moodle-course-management');
1547              return false;
1548          }
1549          this.markHidden();
1550          showbtn = this.get('node').one('a[data-action=show]');
1551          if (showbtn) {
1552              showbtn.focus();
1553          }
1554          if (outcome.categoryvisibility) {
1555              this.updateChildVisibility(outcome.categoryvisibility);
1556          }
1557          if (outcome.coursevisibility) {
1558              this.updateCourseVisiblity(outcome.coursevisibility);
1559          }
1560          this.updated();
1561          Y.log('Success: ' + this.get('itemname') + ' made hidden by AJAX.', 'info', 'moodle-course-management');
1562      },
1563  
1564      /**
1565       * Updates the visibility of child courses if required.
1566       * @method updateCourseVisiblity
1567       * @chainable
1568       * @param courses
1569       */
1570      updateCourseVisiblity: function(courses) {
1571          var managementconsole = this.get('console'),
1572              key,
1573              course;
1574          Y.log('Changing categories course visibility', 'info', 'moodle-course-management');
1575          try {
1576              for (key in courses) {
1577                  if (typeof courses[key] === 'object') {
1578                      course = managementconsole.getCourseById(courses[key].id);
1579                      if (course) {
1580                          if (courses[key].visible === "1") {
1581                              course.markVisible();
1582                          } else {
1583                              course.markHidden();
1584                          }
1585                      }
1586                  }
1587              }
1588          } catch (err) {
1589              Y.log('Error trying to update course visibility: ' + err.message, 'warn', 'moodle-course-management');
1590          }
1591          return this;
1592      },
1593  
1594      /**
1595       * Updates the visibility of subcategories if required.
1596       * @method updateChildVisibility
1597       * @chainable
1598       * @param categories
1599       */
1600      updateChildVisibility: function(categories) {
1601          var managementconsole = this.get('console'),
1602              key,
1603              category;
1604          Y.log('Changing categories subcategory visibility', 'info', 'moodle-course-management');
1605          try {
1606              for (key in categories) {
1607                  if (typeof categories[key] === 'object') {
1608                      category = managementconsole.getCategoryById(categories[key].id);
1609                      if (category) {
1610                          if (categories[key].visible === "1") {
1611                              category.markVisible();
1612                          } else {
1613                              category.markHidden();
1614                          }
1615                      }
1616                  }
1617              }
1618          } catch (err) {
1619              Y.log('Error trying to update category visibility: ' + err.message, 'warn', 'moodle-course-management');
1620          }
1621          return this;
1622      }
1623  };
1624  Y.extend(Category, Item, Category.prototype);
1625  /* global Item */
1626  
1627  /**
1628   * A managed course.
1629   *
1630   * @namespace M.course.management
1631   * @class Course
1632   * @constructor
1633   * @extends Item
1634   */
1635  function Course() {
1636      Course.superclass.constructor.apply(this, arguments);
1637  }
1638  Course.NAME = 'moodle-course-management-course';
1639  Course.CSS_PREFIX = 'management-course';
1640  Course.ATTRS = {
1641  
1642      /**
1643       * The course ID of this course.
1644       * @attribute courseid
1645       * @type Number
1646       */
1647      courseid: {},
1648  
1649      /**
1650       * True if this is the selected course.
1651       * @attribute selected
1652       * @type Boolean
1653       * @default null
1654       */
1655      selected: {
1656          getter: function(value, name) {
1657              if (value === null) {
1658                  value = this.get('node').getData(name);
1659                  this.set(name, value);
1660              }
1661              return value;
1662          },
1663          value: null
1664      },
1665      node: {
1666  
1667      },
1668      /**
1669       * The management console tracking this course.
1670       * @attribute console
1671       * @type Console
1672       * @writeOnce
1673       */
1674      console: {
1675          writeOnce: 'initOnly'
1676      },
1677  
1678      /**
1679       * The category this course belongs to.
1680       * @attribute category
1681       * @type Category
1682       * @writeOnce
1683       */
1684      category: {
1685          writeOnce: 'initOnly'
1686      }
1687  };
1688  Course.prototype = {
1689      /**
1690       * Initialises the new course instance.
1691       * @method initializer
1692       */
1693      initializer: function() {
1694          var node = this.get('node'),
1695              category = this.get('category');
1696          this.set('courseid', node.getData('id'));
1697          if (category && category.registerCourse) {
1698              category.registerCourse(this);
1699          }
1700          this.set('itemname', 'course');
1701      },
1702  
1703      /**
1704       * Returns the name of the course.
1705       * @method getName
1706       * @return {String}
1707       */
1708      getName: function() {
1709          return this.get('node').one('a.coursename').get('innerHTML');
1710      },
1711  
1712      /**
1713       * Handles an event relating to this course.
1714       * @method handle
1715       * @param {String} action
1716       * @param {EventFacade} e
1717       * @return {Boolean}
1718       */
1719      handle: function(action, e) {
1720          var managementconsole = this.get('console'),
1721              args = {courseid: this.get('courseid')};
1722          switch (action) {
1723              case 'moveup':
1724                  e.halt();
1725                  managementconsole.performAjaxAction('movecourseup', args, this.moveup, this);
1726                  break;
1727              case 'movedown':
1728                  e.halt();
1729                  managementconsole.performAjaxAction('movecoursedown', args, this.movedown, this);
1730                  break;
1731              case 'show':
1732                  e.halt();
1733                  managementconsole.performAjaxAction('showcourse', args, this.show, this);
1734                  break;
1735              case 'hide':
1736                  e.halt();
1737                  managementconsole.performAjaxAction('hidecourse', args, this.hide, this);
1738                  break;
1739              case 'select':
1740                  var c = this.get('console'),
1741                      movetonode = c.get('courselisting').one('#menumovecoursesto');
1742                  if (movetonode) {
1743                      if (c.isCourseSelected(e.currentTarget)) {
1744                          movetonode.removeAttribute('disabled');
1745                      } else {
1746                          movetonode.setAttribute('disabled', true);
1747                      }
1748                  }
1749                  break;
1750              default:
1751                  Y.log('Invalid AJAX action requested of managed course.', 'warn', 'moodle-course-management');
1752                  return false;
1753          }
1754      },
1755  
1756      /**
1757       * Removes this course.
1758       * @method remove
1759       */
1760      remove: function() {
1761          this.get('console').removeCourseById(this.get('courseid'));
1762          this.get('node').remove();
1763      },
1764  
1765      /**
1766       * Moves this course after another course.
1767       *
1768       * @method moveAfter
1769       * @param {Number} moveaftercourse The course to move after or 0 to put it at the top.
1770       * @param {Number} previousid the course it was previously after in case we need to revert.
1771       */
1772      moveAfter: function(moveaftercourse, previousid) {
1773          var managementconsole = this.get('console'),
1774              args = {
1775                  courseid: this.get('courseid'),
1776                  moveafter: moveaftercourse,
1777                  previous: previousid
1778              };
1779          managementconsole.performAjaxAction('movecourseafter', args, this.moveAfterResponse, this);
1780      },
1781  
1782      /**
1783       * Performs the actual move.
1784       *
1785       * @method moveAfterResponse
1786       * @protected
1787       * @param {Number} transactionid The transaction ID for the request.
1788       * @param {Object} response The response to the request.
1789       * @param {Objects} args The arguments that were given with the request.
1790       * @return {Boolean}
1791       */
1792      moveAfterResponse: function(transactionid, response, args) {
1793          var outcome = this.checkAjaxResponse(transactionid, response, args),
1794              node = this.get('node'),
1795              previous;
1796          if (outcome === false) {
1797              previous = node.ancestor('ul').one('li[data-id=' + args.previous + ']');
1798              Y.log('AJAX failed to move this course after the requested course', 'warn', 'moodle-course-management');
1799              if (previous) {
1800                  // After the last previous.
1801                  previous.insertAfter(node, 'after');
1802              } else {
1803                  // Start of the list.
1804                  node.ancestor('ul').one('li').insert(node, 'before');
1805              }
1806              return false;
1807          }
1808          Y.log('AJAX successfully moved course (' + this.getName() + ')', 'info', 'moodle-course-management');
1809          this.highlight();
1810      }
1811  };
1812  Y.extend(Course, Item, Course.prototype);
1813  
1814  
1815  }, '@VERSION@', {
1816      "requires": [
1817          "base",
1818          "node",
1819          "io-base",
1820          "moodle-core-notification-exception",
1821          "json-parse",
1822          "dd-constrain",
1823          "dd-proxy",
1824          "dd-drop",
1825          "dd-delegate",
1826          "node-event-delegate"
1827      ]
1828  });


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