[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

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


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