[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

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

   1  YUI.add('moodle-course-toolboxes', function (Y, NAME) {
   2  
   3  /**
   4   * Resource and activity toolbox class.
   5   *
   6   * This class is responsible for managing AJAX interactions with activities and resources
   7   * when viewing a course in editing mode.
   8   *
   9   * @module moodle-course-toolboxes
  10   * @namespace M.course.toolboxes
  11   */
  12  
  13  // The CSS classes we use.
  14  var CSS = {
  15          ACTIVITYINSTANCE : 'activityinstance',
  16          AVAILABILITYINFODIV : 'div.availabilityinfo',
  17          CONTENTWITHOUTLINK : 'contentwithoutlink',
  18          CONDITIONALHIDDEN : 'conditionalhidden',
  19          DIMCLASS : 'dimmed',
  20          DIMMEDTEXT : 'dimmed_text',
  21          EDITINSTRUCTIONS : 'editinstructions',
  22          EDITINGTITLE: 'editor_displayed',
  23          HIDE : 'hide',
  24          MODINDENTCOUNT : 'mod-indent-',
  25          MODINDENTHUGE : 'mod-indent-huge',
  26          MODULEIDPREFIX : 'module-',
  27          SECTIONHIDDENCLASS : 'hidden',
  28          SECTIONIDPREFIX : 'section-',
  29          SHOW : 'editing_show',
  30          TITLEEDITOR : 'titleeditor'
  31      },
  32      // The CSS selectors we use.
  33      SELECTOR = {
  34          ACTIONAREA: '.actions',
  35          ACTIONLINKTEXT : '.actionlinktext',
  36          ACTIVITYACTION : 'a.cm-edit-action[data-action], a.editing_title',
  37          ACTIVITYFORM : '.' + CSS.ACTIVITYINSTANCE + ' form',
  38          ACTIVITYICON : 'img.activityicon',
  39          ACTIVITYINSTANCE : '.' + CSS.ACTIVITYINSTANCE,
  40          ACTIVITYLINK: '.' + CSS.ACTIVITYINSTANCE + ' > a',
  41          ACTIVITYLI : 'li.activity',
  42          ACTIVITYTITLE : 'input[name=title]',
  43          COMMANDSPAN : '.commands',
  44          CONTENTAFTERLINK : 'div.contentafterlink',
  45          CONTENTWITHOUTLINK : 'div.contentwithoutlink',
  46          EDITTITLE: 'a.editing_title',
  47          HIDE : 'a.editing_hide',
  48          HIGHLIGHT : 'a.editing_highlight',
  49          INSTANCENAME : 'span.instancename',
  50          MODINDENTDIV : '.mod-indent',
  51          MODINDENTOUTER : '.mod-indent-outer',
  52          PAGECONTENT : 'body',
  53          SECTIONLI : 'li.section',
  54          SHOW : 'a.'+CSS.SHOW,
  55          SHOWHIDE : 'a.editing_showhide'
  56      },
  57      INDENTLIMITS = {
  58          MIN: 0,
  59          MAX: 16
  60      },
  61      BODY = Y.one(document.body);
  62  
  63  // Setup the basic namespace.
  64  M.course = M.course || {};
  65  
  66  /**
  67   * The toolbox class is a generic class which should never be directly
  68   * instantiated. Please extend it instead.
  69   *
  70   * @class toolbox
  71   * @constructor
  72   * @protected
  73   * @extends Base
  74   */
  75  var TOOLBOX = function() {
  76      TOOLBOX.superclass.constructor.apply(this, arguments);
  77  };
  78  
  79  Y.extend(TOOLBOX, Y.Base, {
  80      /**
  81       * Send a request using the REST API
  82       *
  83       * @method send_request
  84       * @param {Object} data The data to submit with the AJAX request
  85       * @param {Node} [statusspinner] A statusspinner which may contain a section loader
  86       * @param {Function} success_callback The callback to use on success
  87       * @param {Object} [optionalconfig] Any additional configuration to submit
  88       * @chainable
  89       */
  90      send_request: function(data, statusspinner, success_callback, optionalconfig) {
  91          // Default data structure
  92          if (!data) {
  93              data = {};
  94          }
  95          // Handle any variables which we must pass back through to
  96          var pageparams = this.get('config').pageparams,
  97              varname;
  98          for (varname in pageparams) {
  99              data[varname] = pageparams[varname];
 100          }
 101  
 102          data.sesskey = M.cfg.sesskey;
 103          data.courseId = this.get('courseid');
 104  
 105          var uri = M.cfg.wwwroot + this.get('ajaxurl');
 106  
 107          // Define the configuration to send with the request
 108          var responsetext = [];
 109          var config = {
 110              method: 'POST',
 111              data: data,
 112              on: {
 113                  success: function(tid, response) {
 114                      try {
 115                          responsetext = Y.JSON.parse(response.responseText);
 116                          if (responsetext.error) {
 117                              new M.core.ajaxException(responsetext);
 118                          }
 119                      } catch (e) {}
 120  
 121                      // Run the callback if we have one.
 122                      if (success_callback) {
 123                          Y.bind(success_callback, this, responsetext)();
 124                      }
 125  
 126                      if (statusspinner) {
 127                          window.setTimeout(function() {
 128                              statusspinner.hide();
 129                          }, 400);
 130                      }
 131                  },
 132                  failure: function(tid, response) {
 133                      if (statusspinner) {
 134                          statusspinner.hide();
 135                      }
 136                      new M.core.ajaxException(response);
 137                  }
 138              },
 139              context: this
 140          };
 141  
 142          // Apply optional config
 143          if (optionalconfig) {
 144              for (varname in optionalconfig) {
 145                  config[varname] = optionalconfig[varname];
 146              }
 147          }
 148  
 149          if (statusspinner) {
 150              statusspinner.show();
 151          }
 152  
 153          // Send the request
 154          Y.io(uri, config);
 155          return this;
 156      }
 157  },
 158  {
 159      NAME: 'course-toolbox',
 160      ATTRS: {
 161          /**
 162           * The ID of the Moodle Course being edited.
 163           *
 164           * @attribute courseid
 165           * @default 0
 166           * @type Number
 167           */
 168          courseid: {
 169              'value': 0
 170          },
 171  
 172          /**
 173           * The Moodle course format.
 174           *
 175           * @attribute format
 176           * @default 'topics'
 177           * @type String
 178           */
 179          format: {
 180              'value': 'topics'
 181          },
 182          /**
 183           * The URL to use when submitting requests.
 184           * @attribute ajaxurl
 185           * @default null
 186           * @type String
 187           */
 188          ajaxurl: {
 189              'value': null
 190          },
 191          /**
 192           * Any additional configuration passed when creating the instance.
 193           *
 194           * @attribute config
 195           * @default {}
 196           * @type Object
 197           */
 198          config: {
 199              'value': {}
 200          }
 201      }
 202  }
 203  );
 204  
 205  /**
 206   * Resource and activity toolbox class.
 207   *
 208   * This class is responsible for managing AJAX interactions with activities and resources
 209   * when viewing a course in editing mode.
 210   *
 211   * @module moodle-course-toolboxes
 212   * @namespace M.course.toolboxes
 213   */
 214  
 215  /**
 216   * Resource and activity toolbox class.
 217   *
 218   * This is a class extending TOOLBOX containing code specific to resources
 219   *
 220   * This class is responsible for managing AJAX interactions with activities and resources
 221   * when viewing a course in editing mode.
 222   *
 223   * @class resources
 224   * @constructor
 225   * @extends M.course.toolboxes.toolbox
 226   */
 227  var RESOURCETOOLBOX = function() {
 228      RESOURCETOOLBOX.superclass.constructor.apply(this, arguments);
 229  };
 230  
 231  Y.extend(RESOURCETOOLBOX, TOOLBOX, {
 232      /**
 233       * No groups are being used.
 234       *
 235       * @property GROUPS_NONE
 236       * @protected
 237       * @type Number
 238       */
 239      GROUPS_NONE: 0,
 240  
 241      /**
 242       * Separate groups are being used.
 243       *
 244       * @property GROUPS_SEPARATE
 245       * @protected
 246       * @type Number
 247       */
 248      GROUPS_SEPARATE: 1,
 249  
 250      /**
 251       * Visible groups are being used.
 252       *
 253       * @property GROUPS_VISIBLE
 254       * @protected
 255       * @type Number
 256       */
 257      GROUPS_VISIBLE: 2,
 258  
 259      /**
 260       * An Array of events added when editing a title.
 261       * These should all be detached when editing is complete.
 262       *
 263       * @property edittitleevents
 264       * @protected
 265       * @type Array
 266       * @protected
 267       */
 268      edittitleevents: [],
 269  
 270      /**
 271       * Initialize the resource toolbox
 272       *
 273       * For each activity the commands are updated and a reference to the activity is attached.
 274       * This way it doesn't matter where the commands are going to called from they have a reference to the
 275       * activity that they relate to.
 276       * This is essential as some of the actions are displayed in an actionmenu which removes them from the
 277       * page flow.
 278       *
 279       * This function also creates a single event delegate to manage all AJAX actions for all activities on
 280       * the page.
 281       *
 282       * @method initializer
 283       * @protected
 284       */
 285      initializer: function() {
 286          M.course.coursebase.register_module(this);
 287          BODY.delegate('key', this.handle_data_action, 'down:enter', SELECTOR.ACTIVITYACTION, this);
 288          Y.delegate('click', this.handle_data_action, BODY, SELECTOR.ACTIVITYACTION, this);
 289      },
 290  
 291      /**
 292       * Handles the delegation event. When this is fired someone has triggered an action.
 293       *
 294       * Note not all actions will result in an AJAX enhancement.
 295       *
 296       * @protected
 297       * @method handle_data_action
 298       * @param {EventFacade} ev The event that was triggered.
 299       * @return {boolean}
 300       */
 301      handle_data_action: function(ev) {
 302          // We need to get the anchor element that triggered this event.
 303          var node = ev.target;
 304          if (!node.test('a')) {
 305              node = node.ancestor(SELECTOR.ACTIVITYACTION);
 306          }
 307  
 308          // From the anchor we can get both the activity (added during initialisation) and the action being
 309          // performed (added by the UI as a data attribute).
 310          var action = node.getData('action'),
 311              activity = node.ancestor(SELECTOR.ACTIVITYLI);
 312  
 313          if (!node.test('a') || !action || !activity) {
 314              // It wasn't a valid action node.
 315              return;
 316          }
 317  
 318          // Switch based upon the action and do the desired thing.
 319          switch (action) {
 320              case 'edittitle':
 321                  // The user wishes to edit the title of the event.
 322                  this.edit_title(ev, node, activity, action);
 323                  break;
 324              case 'moveleft':
 325              case 'moveright':
 326                  // The user changing the indent of the activity.
 327                  this.change_indent(ev, node, activity, action);
 328                  break;
 329              case 'delete':
 330                  // The user is deleting the activity.
 331                  this.delete_with_confirmation(ev, node, activity, action);
 332                  break;
 333              case 'duplicate':
 334                  // The user is duplicating the activity.
 335                  this.duplicate(ev, node, activity, action);
 336                  break;
 337              case 'hide':
 338              case 'show':
 339                  // The user is changing the visibility of the activity.
 340                  this.change_visibility(ev, node, activity, action);
 341                  break;
 342              case 'groupsseparate':
 343              case 'groupsvisible':
 344              case 'groupsnone':
 345                  // The user is changing the group mode.
 346                  callback = 'change_groupmode';
 347                  this.change_groupmode(ev, node, activity, action);
 348                  break;
 349              case 'move':
 350              case 'update':
 351              case 'duplicate':
 352              case 'assignroles':
 353                  break;
 354              default:
 355                  // Nothing to do here!
 356                  break;
 357          }
 358      },
 359  
 360      /**
 361       * Add a loading icon to the specified activity.
 362       *
 363       * The icon is added within the action area.
 364       *
 365       * @method add_spinner
 366       * @param {Node} activity The activity to add a loading icon to
 367       * @return {Node|null} The newly created icon, or null if the action area was not found.
 368       */
 369      add_spinner: function(activity) {
 370          var actionarea = activity.one(SELECTOR.ACTIONAREA);
 371          if (actionarea) {
 372              return M.util.add_spinner(Y, actionarea);
 373          }
 374          return null;
 375      },
 376  
 377      /**
 378       * Change the indent of the activity or resource.
 379       *
 380       * @method change_indent
 381       * @protected
 382       * @param {EventFacade} ev The event that was fired.
 383       * @param {Node} button The button that triggered this action.
 384       * @param {Node} activity The activity node that this action will be performed on.
 385       * @param {String} action The action that has been requested. Will be 'moveleft' or 'moveright'.
 386       */
 387      change_indent: function(ev, button, activity, action) {
 388          // Prevent the default button action
 389          ev.preventDefault();
 390  
 391          var direction = (action === 'moveleft') ? -1: 1;
 392  
 393          // And we need to determine the current and new indent level
 394          var indentdiv = activity.one(SELECTOR.MODINDENTDIV),
 395              indent = indentdiv.getAttribute('class').match(/mod-indent-(\d{1,})/),
 396              oldindent = 0,
 397              newindent;
 398  
 399          if (indent) {
 400              oldindent = parseInt(indent[1], 10);
 401          }
 402          newindent = oldindent + parseInt(direction, 10);
 403  
 404          if (newindent < INDENTLIMITS.MIN || newindent > INDENTLIMITS.MAX) {
 405              return;
 406          }
 407  
 408          if (indent) {
 409              indentdiv.removeClass(indent[0]);
 410          }
 411  
 412          // Perform the move
 413          indentdiv.addClass(CSS.MODINDENTCOUNT + newindent);
 414          var data = {
 415              'class': 'resource',
 416              'field': 'indent',
 417              'value': newindent,
 418              'id': Y.Moodle.core_course.util.cm.getId(activity)
 419          };
 420          var spinner = this.add_spinner(activity);
 421          this.send_request(data, spinner);
 422  
 423          var remainingmove;
 424  
 425          // Handle removal/addition of the moveleft button.
 426          if (newindent === INDENTLIMITS.MIN) {
 427              button.addClass('hidden');
 428              remainingmove = activity.one('.editing_moveright');
 429          } else if (newindent > INDENTLIMITS.MIN && oldindent === INDENTLIMITS.MIN) {
 430              button.ancestor('.menu').one('[data-action=moveleft]').removeClass('hidden');
 431          }
 432  
 433          if (newindent === INDENTLIMITS.MAX) {
 434              button.addClass('hidden');
 435              remainingmove = activity.one('.editing_moveleft');
 436          } else if (newindent < INDENTLIMITS.MAX && oldindent === INDENTLIMITS.MAX) {
 437              button.ancestor('.menu').one('[data-action=moveright]').removeClass('hidden');
 438          }
 439  
 440          // Handle massive indentation to match non-ajax display
 441          var hashugeclass = indentdiv.hasClass(CSS.MODINDENTHUGE);
 442          if (newindent > 15 && !hashugeclass) {
 443              indentdiv.addClass(CSS.MODINDENTHUGE);
 444          } else if (newindent <= 15 && hashugeclass) {
 445              indentdiv.removeClass(CSS.MODINDENTHUGE);
 446          }
 447  
 448          if (ev.type && ev.type === "key" && remainingmove) {
 449              remainingmove.focus();
 450          }
 451      },
 452  
 453      /**
 454       * Deletes the given activity or resource after confirmation.
 455       *
 456       * @protected
 457       * @method delete_with_confirmation
 458       * @param {EventFacade} ev The event that was fired.
 459       * @param {Node} button The button that triggered this action.
 460       * @param {Node} activity The activity node that this action will be performed on.
 461       * @chainable
 462       */
 463      delete_with_confirmation: function(ev, button, activity) {
 464          // Prevent the default button action
 465          ev.preventDefault();
 466  
 467          // Get the element we're working on
 468          var element   = activity,
 469              // Create confirm string (different if element has or does not have name)
 470              confirmstring = '',
 471              plugindata = {
 472                  type: M.util.get_string('pluginname', element.getAttribute('class').match(/modtype_([^\s]*)/)[1])
 473              };
 474          if (Y.Moodle.core_course.util.cm.getName(element) !== null) {
 475              plugindata.name = Y.Moodle.core_course.util.cm.getName(element);
 476              confirmstring = M.util.get_string('deletechecktypename', 'moodle', plugindata);
 477          } else {
 478              confirmstring = M.util.get_string('deletechecktype', 'moodle', plugindata);
 479          }
 480  
 481          // Create the confirmation dialogue.
 482          var confirm = new M.core.confirm({
 483              question: confirmstring,
 484              modal: true
 485          });
 486  
 487          // If it is confirmed.
 488          confirm.on('complete-yes', function() {
 489  
 490              // Actually remove the element.
 491              element.remove();
 492              var data = {
 493                  'class': 'resource',
 494                  'action': 'DELETE',
 495                  'id': Y.Moodle.core_course.util.cm.getId(element)
 496              };
 497              this.send_request(data);
 498              if (M.core.actionmenu && M.core.actionmenu.instance) {
 499                  M.core.actionmenu.instance.hideMenu();
 500              }
 501  
 502          }, this);
 503  
 504          return this;
 505      },
 506  
 507      /**
 508       * Duplicates the activity.
 509       *
 510       * @method duplicate
 511       * @protected
 512       * @param {EventFacade} ev The event that was fired.
 513       * @param {Node} button The button that triggered this action.
 514       * @param {Node} activity The activity node that this action will be performed on.
 515       * @chainable
 516       */
 517      duplicate: function(ev, button, activity) {
 518          // Prevent the default button action
 519          ev.preventDefault();
 520  
 521          // Get the element we're working on
 522          var element = activity;
 523  
 524          // Add the lightbox.
 525          var section = activity.ancestor(M.course.format.get_section_selector(Y)),
 526              lightbox = M.util.add_lightbox(Y, section).show();
 527  
 528          // Build and send the request.
 529          var data = {
 530              'class': 'resource',
 531              'field': 'duplicate',
 532              'id': Y.Moodle.core_course.util.cm.getId(element),
 533              'sr': button.getData('sr')
 534          };
 535          this.send_request(data, lightbox, function(response) {
 536              var newcm = Y.Node.create(response.fullcontent);
 537  
 538              // Append to the section?
 539              activity.insert(newcm, 'after');
 540              Y.use('moodle-course-coursebase', function() {
 541                  M.course.coursebase.invoke_function('setup_for_resource', newcm);
 542              });
 543              if (M.core.actionmenu && M.core.actionmenu.newDOMNode) {
 544                  M.core.actionmenu.newDOMNode(newcm);
 545              }
 546          });
 547          return this;
 548      },
 549  
 550      /**
 551       * Changes the visibility of this activity or resource.
 552       *
 553       * @method change_visibility
 554       * @protected
 555       * @param {EventFacade} ev The event that was fired.
 556       * @param {Node} button The button that triggered this action.
 557       * @param {Node} activity The activity node that this action will be performed on.
 558       * @param {String} action The action that has been requested.
 559       * @chainable
 560       */
 561      change_visibility: function(ev, button, activity, action) {
 562          // Prevent the default button action
 563          ev.preventDefault();
 564  
 565          // Get the element we're working on
 566          var element = activity;
 567          var value = this.handle_resource_dim(button, activity, action);
 568  
 569          // Send the request
 570          var data = {
 571              'class': 'resource',
 572              'field': 'visible',
 573              'value': value,
 574              'id': Y.Moodle.core_course.util.cm.getId(element)
 575          };
 576          var spinner = this.add_spinner(element);
 577          this.send_request(data, spinner);
 578  
 579          return this;
 580      },
 581  
 582      /**
 583       * Handles the UI aspect of dimming the activity or resource.
 584       *
 585       * @method handle_resource_dim
 586       * @protected
 587       * @param {Node} button The button that triggered the action.
 588       * @param {Node} activity The activity node that this action will be performed on.
 589       * @param {String} action 'show' or 'hide'.
 590       * @return {Number} 1 if we changed to visible, 0 if we were hiding.
 591       */
 592      handle_resource_dim: function(button, activity, action) {
 593          var toggleclass = CSS.DIMCLASS,
 594              dimarea = activity.one([
 595                      SELECTOR.ACTIVITYLINK,
 596                      SELECTOR.CONTENTWITHOUTLINK
 597                  ].join(', ')),
 598              availabilityinfo = activity.one(CSS.AVAILABILITYINFODIV),
 599              nextaction = (action === 'hide') ? 'show': 'hide',
 600              buttontext = button.one('span'),
 601              newstring = M.util.get_string(nextaction, 'moodle'),
 602              buttonimg = button.one('img');
 603  
 604          // Update button info.
 605          buttonimg.setAttrs({
 606              'src': M.util.image_url('t/' + nextaction)
 607          });
 608  
 609          if (Y.Lang.trim(button.getAttribute('title'))) {
 610              button.setAttribute('title', newstring);
 611          }
 612  
 613          if (Y.Lang.trim(buttonimg.getAttribute('alt'))) {
 614              buttonimg.setAttribute('alt', newstring);
 615          }
 616  
 617          button.replaceClass('editing_'+action, 'editing_'+nextaction);
 618          button.setData('action', nextaction);
 619          if (buttontext) {
 620              buttontext.set('text', newstring);
 621          }
 622  
 623          if (activity.one(SELECTOR.CONTENTWITHOUTLINK)) {
 624              dimarea = activity.one(SELECTOR.CONTENTWITHOUTLINK);
 625              toggleclass = CSS.DIMMEDTEXT;
 626          }
 627  
 628          // If activity is conditionally hidden, then don't toggle.
 629          if (!dimarea.hasClass(CSS.CONDITIONALHIDDEN)) {
 630              // Change the UI.
 631              dimarea.toggleClass(toggleclass);
 632              // We need to toggle dimming on the description too.
 633              activity.all(SELECTOR.CONTENTAFTERLINK).toggleClass(CSS.DIMMEDTEXT);
 634          }
 635          // Toggle availablity info for conditional activities.
 636          if (availabilityinfo) {
 637              availabilityinfo.toggleClass(CSS.HIDE);
 638          }
 639          return (action === 'hide') ? 0: 1;
 640      },
 641  
 642      /**
 643       * Changes the groupmode of the activity to the next groupmode in the sequence.
 644       *
 645       * @method change_groupmode
 646       * @protected
 647       * @param {EventFacade} ev The event that was fired.
 648       * @param {Node} button The button that triggered this action.
 649       * @param {Node} activity The activity node that this action will be performed on.
 650       * @chainable
 651       */
 652      change_groupmode: function(ev, button, activity) {
 653          // Prevent the default button action.
 654          ev.preventDefault();
 655  
 656          // Current Mode
 657          var groupmode = parseInt(button.getData('nextgroupmode'), 10),
 658              newtitle = '',
 659              iconsrc = '',
 660              newtitlestr,
 661              data,
 662              spinner,
 663              nextgroupmode = groupmode + 1,
 664              buttonimg = button.one('img');
 665  
 666          if (nextgroupmode > 2) {
 667              nextgroupmode = 0;
 668          }
 669  
 670          if (groupmode === this.GROUPS_NONE) {
 671              newtitle = 'groupsnone';
 672              iconsrc = M.util.image_url('i/groupn', 'moodle');
 673          } else if (groupmode === this.GROUPS_SEPARATE) {
 674              newtitle = 'groupsseparate';
 675              iconsrc = M.util.image_url('i/groups', 'moodle');
 676          } else if (groupmode === this.GROUPS_VISIBLE) {
 677              newtitle = 'groupsvisible';
 678              iconsrc = M.util.image_url('i/groupv', 'moodle');
 679          }
 680          newtitlestr = M.util.get_string('clicktochangeinbrackets', 'moodle', M.util.get_string(newtitle, 'moodle'));
 681  
 682          // Change the UI
 683          buttonimg.setAttrs({
 684              'src': iconsrc
 685          });
 686          if (Y.Lang.trim(button.getAttribute('title'))) {
 687              button.setAttribute('title', newtitlestr).setData('action', newtitle).setData('nextgroupmode', nextgroupmode);
 688          }
 689  
 690          if (Y.Lang.trim(buttonimg.getAttribute('alt'))) {
 691              buttonimg.setAttribute('alt', newtitlestr);
 692          }
 693  
 694          // And send the request
 695          data = {
 696              'class': 'resource',
 697              'field': 'groupmode',
 698              'value': groupmode,
 699              'id': Y.Moodle.core_course.util.cm.getId(activity)
 700          };
 701  
 702          spinner = this.add_spinner(activity);
 703          this.send_request(data, spinner);
 704          return this;
 705      },
 706  
 707      /**
 708       * Edit the title for the resource
 709       *
 710       * @method edit_title
 711       * @protected
 712       * @param {EventFacade} ev The event that was fired.
 713       * @param {Node} button The button that triggered this action.
 714       * @param {Node} activity The activity node that this action will be performed on.
 715       * @param {String} action The action that has been requested.
 716       * @chainable
 717       */
 718      edit_title: function(ev, button, activity) {
 719          // Get the element we're working on
 720          var activityid = Y.Moodle.core_course.util.cm.getId(activity),
 721              instancename  = activity.one(SELECTOR.INSTANCENAME),
 722              instance = activity.one(SELECTOR.ACTIVITYINSTANCE),
 723              currenttitle = instancename.get('firstChild'),
 724              oldtitle = currenttitle.get('data'),
 725              titletext = oldtitle,
 726              thisevent,
 727              anchor = instancename.ancestor('a'),// Grab the anchor so that we can swap it with the edit form.
 728              data = {
 729                  'class': 'resource',
 730                  'field': 'gettitle',
 731                  'id': activityid
 732              };
 733  
 734          // Prevent the default actions.
 735          ev.preventDefault();
 736  
 737          this.send_request(data, null, function(response) {
 738              if (M.core.actionmenu && M.core.actionmenu.instance) {
 739                  M.core.actionmenu.instance.hideMenu();
 740              }
 741  
 742              // Try to retrieve the existing string from the server
 743              if (response.instancename) {
 744                  titletext = response.instancename;
 745              }
 746  
 747              // Create the editor and submit button
 748              var editform = Y.Node.create('<form action="#" />');
 749              var editinstructions = Y.Node.create('<span class="'+CSS.EDITINSTRUCTIONS+'" id="id_editinstructions" />')
 750                  .set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle'));
 751              var editor = Y.Node.create('<input name="title" type="text" class="'+CSS.TITLEEDITOR+'" />').setAttrs({
 752                  'value': titletext,
 753                  'autocomplete': 'off',
 754                  'aria-describedby': 'id_editinstructions',
 755                  'maxLength': '255'
 756              });
 757  
 758              // Clear the existing content and put the editor in
 759              editform.appendChild(activity.one(SELECTOR.ACTIVITYICON).cloneNode());
 760              editform.appendChild(editor);
 761              editform.setData('anchor', anchor);
 762              instance.insert(editinstructions, 'before');
 763              anchor.replace(editform);
 764  
 765              // Force the editing instruction to match the mod-indent position.
 766              var padside = 'left';
 767              if (right_to_left()) {
 768                  padside = 'right';
 769              }
 770  
 771              // We hide various components whilst editing:
 772              activity.addClass(CSS.EDITINGTITLE);
 773  
 774              // Focus and select the editor text
 775              editor.focus().select();
 776  
 777              // Cancel the edit if we lose focus or the escape key is pressed.
 778              thisevent = editor.on('blur', this.edit_title_cancel, this, activity, false);
 779              this.edittitleevents.push(thisevent);
 780              thisevent = editor.on('key', this.edit_title_cancel, 'esc', this, activity, true);
 781              this.edittitleevents.push(thisevent);
 782  
 783              // Handle form submission.
 784              thisevent = editform.on('submit', this.edit_title_submit, this, activity, oldtitle);
 785              this.edittitleevents.push(thisevent);
 786          });
 787          return this;
 788      },
 789  
 790      /**
 791       * Handles the submit event when editing the activity or resources title.
 792       *
 793       * @method edit_title_submit
 794       * @protected
 795       * @param {EventFacade} ev The event that triggered this.
 796       * @param {Node} activity The activity whose title we are altering.
 797       * @param {String} originaltitle The original title the activity or resource had.
 798       */
 799      edit_title_submit: function(ev, activity, originaltitle) {
 800          // We don't actually want to submit anything
 801          ev.preventDefault();
 802  
 803          var newtitle = Y.Lang.trim(activity.one(SELECTOR.ACTIVITYFORM + ' ' + SELECTOR.ACTIVITYTITLE).get('value'));
 804          this.edit_title_clear(activity);
 805          var spinner = this.add_spinner(activity);
 806          if (newtitle !== null && newtitle !== "" && newtitle !== originaltitle) {
 807              var data = {
 808                  'class': 'resource',
 809                  'field': 'updatetitle',
 810                  'title': newtitle,
 811                  'id': Y.Moodle.core_course.util.cm.getId(activity)
 812              };
 813              this.send_request(data, spinner, function(response) {
 814                  if (response.instancename) {
 815                      activity.one(SELECTOR.INSTANCENAME).setContent(response.instancename);
 816                  }
 817              });
 818          }
 819      },
 820  
 821      /**
 822       * Handles the cancel event when editing the activity or resources title.
 823       *
 824       * @method edit_title_cancel
 825       * @protected
 826       * @param {EventFacade} ev The event that triggered this.
 827       * @param {Node} activity The activity whose title we are altering.
 828       * @param {Boolean} preventdefault If true we should prevent the default action from occuring.
 829       */
 830      edit_title_cancel: function(ev, activity, preventdefault) {
 831          if (preventdefault) {
 832              ev.preventDefault();
 833          }
 834          this.edit_title_clear(activity);
 835      },
 836  
 837      /**
 838       * Handles clearing the editing UI and returning things to the original state they were in.
 839       *
 840       * @method edit_title_clear
 841       * @protected
 842       * @param {Node} activity  The activity whose title we were altering.
 843       */
 844      edit_title_clear: function(activity) {
 845          // Detach all listen events to prevent duplicate triggers
 846          new Y.EventHandle(this.edittitleevents).detach();
 847  
 848          var editform = activity.one(SELECTOR.ACTIVITYFORM),
 849              instructions = activity.one('#id_editinstructions');
 850          if (editform) {
 851              editform.replace(editform.getData('anchor'));
 852          }
 853          if (instructions) {
 854              instructions.remove();
 855          }
 856  
 857          // Remove the editing class again to revert the display.
 858          activity.removeClass(CSS.EDITINGTITLE);
 859  
 860          // Refocus the link which was clicked originally so the user can continue using keyboard nav.
 861          Y.later(100, this, function() {
 862              activity.one(SELECTOR.EDITTITLE).focus();
 863          });
 864      },
 865  
 866      /**
 867       * Set the visibility of the specified resource to match the visible parameter.
 868       *
 869       * Note: This is not a toggle function and only changes the visibility
 870       * in the browser (no ajax update is performed).
 871       *
 872       * @method set_visibility_resource_ui
 873       * @param {object} args An object containing the required information to trigger a change.
 874       * @param {Node} args.element The resource to toggle
 875       * @param {Boolean} args.visible The target visibility
 876       */
 877      set_visibility_resource_ui: function(args) {
 878          var element = args.element,
 879              buttonnode = element.one(SELECTOR.HIDE),
 880              // By default we assume that the item is visible and we're going to hide it.
 881              currentVisibility = true,
 882              targetVisibility = false;
 883  
 884          if (!buttonnode) {
 885              // If the buttonnode was not found, try to find the HIDE button
 886              // and change the target visibility setting to false.
 887              buttonnode = element.one(SELECTOR.SHOW);
 888              currentVisibility = false;
 889              targetVisibility = true;
 890          }
 891  
 892          if (typeof args.visible !== 'undefined') {
 893              // If we were provided with a visibility argument, use that instead.
 894              targetVisibility = args.visible;
 895          }
 896  
 897          // Only trigger a change if necessary.
 898          if (currentVisibility !== targetVisibility) {
 899              var action = 'hide';
 900              if (targetVisibility) {
 901                  action = 'show';
 902              }
 903  
 904              this.handle_resource_dim(buttonnode, element, action);
 905          }
 906      }
 907  }, {
 908      NAME: 'course-resource-toolbox',
 909      ATTRS: {
 910      }
 911  });
 912  
 913  M.course.resource_toolbox = null;
 914  M.course.init_resource_toolbox = function(config) {
 915      M.course.resource_toolbox = new RESOURCETOOLBOX(config);
 916      return M.course.resource_toolbox;
 917  };
 918  /**
 919   * Resource and activity toolbox class.
 920   *
 921   * This class is responsible for managing AJAX interactions with activities and resources
 922   * when viewing a course in editing mode.
 923   *
 924   * @module moodle-course-toolboxes
 925   * @namespace M.course.toolboxes
 926   */
 927  
 928  /**
 929   * Section toolbox class.
 930   *
 931   * This class is responsible for managing AJAX interactions with sections
 932   * when viewing a course in editing mode.
 933   *
 934   * @class section
 935   * @constructor
 936   * @extends M.course.toolboxes.toolbox
 937   */
 938  var SECTIONTOOLBOX = function() {
 939      SECTIONTOOLBOX.superclass.constructor.apply(this, arguments);
 940  };
 941  
 942  Y.extend(SECTIONTOOLBOX, TOOLBOX, {
 943      /**
 944       * Initialize the section toolboxes module.
 945       *
 946       * Updates all span.commands with relevant handlers and other required changes.
 947       *
 948       * @method initializer
 949       * @protected
 950       */
 951      initializer : function() {
 952          M.course.coursebase.register_module(this);
 953  
 954          // Section Highlighting.
 955          Y.delegate('click', this.toggle_highlight, SELECTOR.PAGECONTENT, SELECTOR.SECTIONLI + ' ' + SELECTOR.HIGHLIGHT, this);
 956  
 957          // Section Visibility.
 958          Y.delegate('click', this.toggle_hide_section, SELECTOR.PAGECONTENT, SELECTOR.SECTIONLI + ' ' + SELECTOR.SHOWHIDE, this);
 959      },
 960  
 961      toggle_hide_section : function(e) {
 962          // Prevent the default button action.
 963          e.preventDefault();
 964  
 965          // Get the section we're working on.
 966          var section = e.target.ancestor(M.course.format.get_section_selector(Y)),
 967              button = e.target.ancestor('a', true),
 968              hideicon = button.one('img'),
 969  
 970          // The value to submit
 971              value,
 972  
 973          // The text for strings and images. Also determines the icon to display.
 974              action,
 975              nextaction;
 976  
 977          if (!section.hasClass(CSS.SECTIONHIDDENCLASS)) {
 978              section.addClass(CSS.SECTIONHIDDENCLASS);
 979              value = 0;
 980              action = 'hide';
 981              nextaction = 'show';
 982          } else {
 983              section.removeClass(CSS.SECTIONHIDDENCLASS);
 984              value = 1;
 985              action = 'show';
 986              nextaction = 'hide';
 987          }
 988  
 989          var newstring = M.util.get_string(nextaction + 'fromothers', 'format_' + this.get('format'));
 990          hideicon.setAttrs({
 991              'alt' : newstring,
 992              'src'   : M.util.image_url('i/' + nextaction)
 993          });
 994          button.set('title', newstring);
 995  
 996          // Change the highlight status
 997          var data = {
 998              'class' : 'section',
 999              'field' : 'visible',
1000              'id'    : Y.Moodle.core_course.util.section.getId(section.ancestor(M.course.format.get_section_wrapper(Y), true)),
1001              'value' : value
1002          };
1003  
1004          var lightbox = M.util.add_lightbox(Y, section);
1005          lightbox.show();
1006  
1007          this.send_request(data, lightbox, function(response) {
1008              var activities = section.all(SELECTOR.ACTIVITYLI);
1009              activities.each(function(node) {
1010                  var button;
1011                  if (node.one(SELECTOR.SHOW)) {
1012                      button = node.one(SELECTOR.SHOW);
1013                  } else {
1014                      button = node.one(SELECTOR.HIDE);
1015                  }
1016                  var activityid = Y.Moodle.core_course.util.cm.getId(node);
1017  
1018                  // NOTE: resourcestotoggle is returned as a string instead
1019                  // of a Number so we must cast our activityid to a String.
1020                  if (Y.Array.indexOf(response.resourcestotoggle, "" + activityid) !== -1) {
1021                      M.course.resource_toolbox.handle_resource_dim(button, node, action);
1022                  }
1023              }, this);
1024          });
1025      },
1026  
1027      /**
1028       * Toggle highlighting the current section.
1029       *
1030       * @method toggle_highlight
1031       * @param {EventFacade} e
1032       */
1033      toggle_highlight : function(e) {
1034          // Prevent the default button action.
1035          e.preventDefault();
1036  
1037          // Get the section we're working on.
1038          var section = e.target.ancestor(M.course.format.get_section_selector(Y));
1039          var button = e.target.ancestor('a', true);
1040          var buttonicon = button.one('img');
1041  
1042          // Determine whether the marker is currently set.
1043          var togglestatus = section.hasClass('current');
1044          var value = 0;
1045  
1046          // Set the current highlighted item text.
1047          var old_string = M.util.get_string('markthistopic', 'moodle');
1048          Y.one(SELECTOR.PAGECONTENT)
1049              .all(M.course.format.get_section_selector(Y) + '.current ' + SELECTOR.HIGHLIGHT)
1050              .set('title', old_string);
1051          Y.one(SELECTOR.PAGECONTENT)
1052              .all(M.course.format.get_section_selector(Y) + '.current ' + SELECTOR.HIGHLIGHT + ' img')
1053              .set('alt', old_string)
1054              .set('src', M.util.image_url('i/marker'));
1055  
1056          // Remove the highlighting from all sections.
1057          Y.one(SELECTOR.PAGECONTENT).all(M.course.format.get_section_selector(Y))
1058              .removeClass('current');
1059  
1060          // Then add it if required to the selected section.
1061          if (!togglestatus) {
1062              section.addClass('current');
1063              value = Y.Moodle.core_course.util.section.getId(section.ancestor(M.course.format.get_section_wrapper(Y), true));
1064              var new_string = M.util.get_string('markedthistopic', 'moodle');
1065              button
1066                  .set('title', new_string);
1067              buttonicon
1068                  .set('alt', new_string)
1069                  .set('src', M.util.image_url('i/marked'));
1070          }
1071  
1072          // Change the highlight status.
1073          var data = {
1074              'class' : 'course',
1075              'field' : 'marker',
1076              'value' : value
1077          };
1078          var lightbox = M.util.add_lightbox(Y, section);
1079          lightbox.show();
1080          this.send_request(data, lightbox);
1081      }
1082  }, {
1083      NAME : 'course-section-toolbox',
1084      ATTRS : {
1085      }
1086  });
1087  
1088  M.course.init_section_toolbox = function(config) {
1089      return new SECTIONTOOLBOX(config);
1090  };
1091  
1092  
1093  }, '@VERSION@', {"requires": ["node", "base", "event-key", "node", "io", "moodle-course-coursebase", "moodle-course-util"]});


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