[ Index ]

PHP Cross Reference of moodle-2.8

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


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1