[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/mod/quiz/yui/src/toolboxes/js/ -> resource.js (source)

   1  /* global TOOLBOX, BODY, SELECTOR */
   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 quiz in editing mode.
   8   *
   9   * @module mod_quiz-resource-toolbox
  10   * @namespace M.mod_quiz.resource_toolbox
  11   */
  12  
  13  /**
  14   * Resource and activity toolbox class.
  15   *
  16   * This is a class extending TOOLBOX containing code specific to resources
  17   *
  18   * This class is responsible for managing AJAX interactions with activities and resources
  19   * when viewing a quiz in editing mode.
  20   *
  21   * @class resources
  22   * @constructor
  23   * @extends M.course.toolboxes.toolbox
  24   */
  25  var RESOURCETOOLBOX = function() {
  26      RESOURCETOOLBOX.superclass.constructor.apply(this, arguments);
  27  };
  28  
  29  Y.extend(RESOURCETOOLBOX, TOOLBOX, {
  30      /**
  31       * An Array of events added when editing a max mark field.
  32       * These should all be detached when editing is complete.
  33       *
  34       * @property editmaxmarkevents
  35       * @protected
  36       * @type Array
  37       * @protected
  38       */
  39      editmaxmarkevents: [],
  40  
  41      /**
  42       *
  43       */
  44      NODE_PAGE: 1,
  45      NODE_SLOT: 2,
  46      NODE_JOIN: 3,
  47  
  48      /**
  49       * Initialize the resource toolbox
  50       *
  51       * For each activity the commands are updated and a reference to the activity is attached.
  52       * This way it doesn't matter where the commands are going to called from they have a reference to the
  53       * activity that they relate to.
  54       * This is essential as some of the actions are displayed in an actionmenu which removes them from the
  55       * page flow.
  56       *
  57       * This function also creates a single event delegate to manage all AJAX actions for all activities on
  58       * the page.
  59       *
  60       * @method initializer
  61       * @protected
  62       */
  63      initializer: function() {
  64          M.mod_quiz.quizbase.register_module(this);
  65          Y.delegate('click', this.handle_data_action, BODY, SELECTOR.ACTIVITYACTION, this);
  66          Y.delegate('click', this.handle_data_action, BODY, SELECTOR.DEPENDENCY_LINK, this);
  67      },
  68  
  69      /**
  70       * Handles the delegation event. When this is fired someone has triggered an action.
  71       *
  72       * Note not all actions will result in an AJAX enhancement.
  73       *
  74       * @protected
  75       * @method handle_data_action
  76       * @param {EventFacade} ev The event that was triggered.
  77       * @returns {boolean}
  78       */
  79      handle_data_action: function(ev) {
  80          // We need to get the anchor element that triggered this event.
  81          var node = ev.target;
  82          if (!node.test('a')) {
  83              node = node.ancestor(SELECTOR.ACTIVITYACTION);
  84          }
  85  
  86          // From the anchor we can get both the activity (added during initialisation) and the action being
  87          // performed (added by the UI as a data attribute).
  88          var action = node.getData('action'),
  89              activity = node.ancestor(SELECTOR.ACTIVITYLI);
  90  
  91          if (!node.test('a') || !action || !activity) {
  92              // It wasn't a valid action node.
  93              return;
  94          }
  95  
  96          // Switch based upon the action and do the desired thing.
  97          switch (action) {
  98              case 'editmaxmark':
  99                  // The user wishes to edit the maxmark of the resource.
 100                  this.edit_maxmark(ev, node, activity, action);
 101                  break;
 102              case 'delete':
 103                  // The user is deleting the activity.
 104                  this.delete_with_confirmation(ev, node, activity, action);
 105                  break;
 106              case 'addpagebreak':
 107              case 'removepagebreak':
 108                  // The user is adding or removing a page break.
 109                  this.update_page_break(ev, node, activity, action);
 110                  break;
 111              case 'adddependency':
 112              case 'removedependency':
 113                  // The user is adding or removing a dependency between questions.
 114                  this.update_dependency(ev, node, activity, action);
 115                  break;
 116              default:
 117                  // Nothing to do here!
 118                  break;
 119          }
 120      },
 121  
 122      /**
 123       * Add a loading icon to the specified activity.
 124       *
 125       * The icon is added within the action area.
 126       *
 127       * @method add_spinner
 128       * @param {Node} activity The activity to add a loading icon to
 129       * @return {Node|null} The newly created icon, or null if the action area was not found.
 130       */
 131      add_spinner: function(activity) {
 132          var actionarea = activity.one(SELECTOR.ACTIONAREA);
 133          if (actionarea) {
 134              return M.util.add_spinner(Y, actionarea);
 135          }
 136          return null;
 137      },
 138  
 139      /**
 140       * Deletes the given activity or resource after confirmation.
 141       *
 142       * @protected
 143       * @method delete_with_confirmation
 144       * @param {EventFacade} ev The event that was fired.
 145       * @param {Node} button The button that triggered this action.
 146       * @param {Node} activity The activity node that this action will be performed on.
 147       * @chainable
 148       */
 149      delete_with_confirmation: function(ev, button, activity) {
 150          // Prevent the default button action.
 151          ev.preventDefault();
 152  
 153          // Get the element we're working on.
 154          var element = activity,
 155              // Create confirm string (different if element has or does not have name)
 156              confirmstring = '',
 157              qtypename = M.util.get_string('pluginname',
 158                          'qtype_' + element.getAttribute('class').match(/qtype_([^\s]*)/)[1]);
 159          confirmstring = M.util.get_string('confirmremovequestion', 'quiz', qtypename);
 160  
 161          // Create the confirmation dialogue.
 162          var confirm = new M.core.confirm({
 163              question: confirmstring,
 164              modal: true
 165          });
 166  
 167          // If it is confirmed.
 168          confirm.on('complete-yes', function() {
 169  
 170              var spinner = this.add_spinner(element);
 171              var data = {
 172                  'class': 'resource',
 173                  'action': 'DELETE',
 174                  'id': Y.Moodle.mod_quiz.util.slot.getId(element)
 175              };
 176              this.send_request(data, spinner, function(response) {
 177                  if (response.deleted) {
 178                      // Actually remove the element.
 179                      Y.Moodle.mod_quiz.util.slot.remove(element);
 180                      this.reorganise_edit_page();
 181                      if (M.core.actionmenu && M.core.actionmenu.instance) {
 182                          M.core.actionmenu.instance.hideMenu(ev);
 183                      }
 184                  }
 185              });
 186  
 187          }, this);
 188  
 189          return this;
 190      },
 191  
 192  
 193      /**
 194       * Edit the maxmark for the resource
 195       *
 196       * @protected
 197       * @method edit_maxmark
 198       * @param {EventFacade} ev The event that was fired.
 199       * @param {Node} button The button that triggered this action.
 200       * @param {Node} activity The activity node that this action will be performed on.
 201       * @param {String} action The action that has been requested.
 202       * @return Boolean
 203       */
 204      edit_maxmark: function(ev, button, activity) {
 205          // Get the element we're working on
 206          var instancemaxmark = activity.one(SELECTOR.INSTANCEMAXMARK),
 207              instance = activity.one(SELECTOR.ACTIVITYINSTANCE),
 208              currentmaxmark = instancemaxmark.get('firstChild'),
 209              oldmaxmark = currentmaxmark.get('data'),
 210              maxmarktext = oldmaxmark,
 211              thisevent,
 212              anchor = instancemaxmark, // Grab the anchor so that we can swap it with the edit form.
 213              data = {
 214                  'class': 'resource',
 215                  'field': 'getmaxmark',
 216                  'id': Y.Moodle.mod_quiz.util.slot.getId(activity)
 217              };
 218  
 219          // Prevent the default actions.
 220          ev.preventDefault();
 221  
 222          this.send_request(data, null, function(response) {
 223              if (M.core.actionmenu && M.core.actionmenu.instance) {
 224                  M.core.actionmenu.instance.hideMenu(ev);
 225              }
 226  
 227              // Try to retrieve the existing string from the server.
 228              if (response.instancemaxmark) {
 229                  maxmarktext = response.instancemaxmark;
 230              }
 231  
 232              // Create the editor and submit button.
 233              var editform = Y.Node.create('<form action="#" />');
 234              var editinstructions = Y.Node.create('<span class="' + CSS.EDITINSTRUCTIONS + '" id="id_editinstructions" />')
 235                  .set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle'));
 236              var editor = Y.Node.create('<input name="maxmark" type="text" class="' + CSS.TITLEEDITOR + '" />').setAttrs({
 237                  'value': maxmarktext,
 238                  'autocomplete': 'off',
 239                  'aria-describedby': 'id_editinstructions',
 240                  'maxLength': '12',
 241                  'size': parseInt(this.get('config').questiondecimalpoints, 10) + 2
 242              });
 243  
 244              // Clear the existing content and put the editor in.
 245              editform.appendChild(editor);
 246              editform.setData('anchor', anchor);
 247              instance.insert(editinstructions, 'before');
 248              anchor.replace(editform);
 249  
 250              // We hide various components whilst editing:
 251              activity.addClass(CSS.EDITINGMAXMARK);
 252  
 253              // Focus and select the editor text.
 254              editor.focus().select();
 255  
 256              // Cancel the edit if we lose focus or the escape key is pressed.
 257              thisevent = editor.on('blur', this.edit_maxmark_cancel, this, activity, false);
 258              this.editmaxmarkevents.push(thisevent);
 259              thisevent = editor.on('key', this.edit_maxmark_cancel, 'esc', this, activity, true);
 260              this.editmaxmarkevents.push(thisevent);
 261  
 262              // Handle form submission.
 263              thisevent = editform.on('submit', this.edit_maxmark_submit, this, activity, oldmaxmark);
 264              this.editmaxmarkevents.push(thisevent);
 265          });
 266      },
 267  
 268      /**
 269       * Handles the submit event when editing the activity or resources maxmark.
 270       *
 271       * @protected
 272       * @method edit_maxmark_submit
 273       * @param {EventFacade} ev The event that triggered this.
 274       * @param {Node} activity The activity whose maxmark we are altering.
 275       * @param {String} originalmaxmark The original maxmark the activity or resource had.
 276       */
 277      edit_maxmark_submit: function(ev, activity, originalmaxmark) {
 278          // We don't actually want to submit anything.
 279          ev.preventDefault();
 280          var newmaxmark = Y.Lang.trim(activity.one(SELECTOR.ACTIVITYFORM + ' ' + SELECTOR.ACTIVITYMAXMARK).get('value'));
 281          var spinner = this.add_spinner(activity);
 282          this.edit_maxmark_clear(activity);
 283          activity.one(SELECTOR.INSTANCEMAXMARK).setContent(newmaxmark);
 284          if (newmaxmark !== null && newmaxmark !== "" && newmaxmark !== originalmaxmark) {
 285              var data = {
 286                  'class': 'resource',
 287                  'field': 'updatemaxmark',
 288                  'maxmark': newmaxmark,
 289                  'id': Y.Moodle.mod_quiz.util.slot.getId(activity)
 290              };
 291              this.send_request(data, spinner, function(response) {
 292                  if (response.instancemaxmark) {
 293                      activity.one(SELECTOR.INSTANCEMAXMARK).setContent(response.instancemaxmark);
 294                  }
 295              });
 296          }
 297      },
 298  
 299      /**
 300       * Handles the cancel event when editing the activity or resources maxmark.
 301       *
 302       * @protected
 303       * @method edit_maxmark_cancel
 304       * @param {EventFacade} ev The event that triggered this.
 305       * @param {Node} activity The activity whose maxmark we are altering.
 306       * @param {Boolean} preventdefault If true we should prevent the default action from occuring.
 307       */
 308      edit_maxmark_cancel: function(ev, activity, preventdefault) {
 309          if (preventdefault) {
 310              ev.preventDefault();
 311          }
 312          this.edit_maxmark_clear(activity);
 313      },
 314  
 315      /**
 316       * Handles clearing the editing UI and returning things to the original state they were in.
 317       *
 318       * @protected
 319       * @method edit_maxmark_clear
 320       * @param {Node} activity  The activity whose maxmark we were altering.
 321       */
 322      edit_maxmark_clear: function(activity) {
 323          // Detach all listen events to prevent duplicate triggers
 324          new Y.EventHandle(this.editmaxmarkevents).detach();
 325  
 326          var editform = activity.one(SELECTOR.ACTIVITYFORM),
 327              instructions = activity.one('#id_editinstructions');
 328          if (editform) {
 329              editform.replace(editform.getData('anchor'));
 330          }
 331          if (instructions) {
 332              instructions.remove();
 333          }
 334  
 335          // Remove the editing class again to revert the display.
 336          activity.removeClass(CSS.EDITINGMAXMARK);
 337  
 338          // Refocus the link which was clicked originally so the user can continue using keyboard nav.
 339          Y.later(100, this, function() {
 340              activity.one(SELECTOR.EDITMAXMARK).focus();
 341          });
 342  
 343          // TODO MDL-50768 This hack is to keep Behat happy until they release a version of
 344          // MinkSelenium2Driver that fixes
 345          // https://github.com/Behat/MinkSelenium2Driver/issues/80.
 346          if (!Y.one('input[name=maxmark')) {
 347              Y.one('body').append('<input type="text" name="maxmark" style="display: none">');
 348          }
 349      },
 350  
 351      /**
 352       * Joins or separates the given slot with the page of the previous slot. Reorders the pages of
 353       * the other slots
 354       *
 355       * @protected
 356       * @method update_page_break
 357       * @param {EventFacade} ev The event that was fired.
 358       * @param {Node} button The button that triggered this action.
 359       * @param {Node} activity The activity node that this action will be performed on.
 360       * @param {String} action The action, addpagebreak or removepagebreak.
 361       * @chainable
 362       */
 363      update_page_break: function(ev, button, activity, action) {
 364          // Prevent the default button action
 365          ev.preventDefault();
 366  
 367          var nextactivity = activity.next('li.activity.slot');
 368          var spinner = this.add_spinner(nextactivity);
 369          var value = action === 'removepagebreak' ? 1 : 2;
 370  
 371          var data = {
 372              'class': 'resource',
 373              'field': 'updatepagebreak',
 374              'id':    Y.Moodle.mod_quiz.util.slot.getId(nextactivity),
 375              'value': value
 376          };
 377  
 378          this.send_request(data, spinner, function(response) {
 379              if (response.slots) {
 380                  if (action === 'addpagebreak') {
 381                      Y.Moodle.mod_quiz.util.page.add(activity);
 382                  } else {
 383                      var page = activity.next(Y.Moodle.mod_quiz.util.page.SELECTORS.PAGE);
 384                      Y.Moodle.mod_quiz.util.page.remove(page, true);
 385                  }
 386                  this.reorganise_edit_page();
 387              }
 388          });
 389  
 390          return this;
 391      },
 392  
 393      /**
 394       * Updates a slot to either require the question in the previous slot to
 395       * have been answered, or not,
 396       *
 397       * @protected
 398       * @method update_page_break
 399       * @param {EventFacade} ev The event that was fired.
 400       * @param {Node} button The button that triggered this action.
 401       * @param {Node} activity The activity node that this action will be performed on.
 402       * @param {String} action The action, adddependency or removedependency.
 403       * @chainable
 404       */
 405      update_dependency: function(ev, button, activity, action) {
 406          // Prevent the default button action.
 407          ev.preventDefault();
 408          var spinner = this.add_spinner(activity);
 409  
 410          var data = {
 411              'class': 'resource',
 412              'field': 'updatedependency',
 413              'id':    Y.Moodle.mod_quiz.util.slot.getId(activity),
 414              'value': action === 'adddependency' ? 1 : 0
 415          };
 416  
 417          this.send_request(data, spinner, function(response) {
 418              if (response.hasOwnProperty('requireprevious')) {
 419                  Y.Moodle.mod_quiz.util.slot.updateDependencyIcon(activity, response.requireprevious);
 420              }
 421          });
 422  
 423          return this;
 424      },
 425  
 426      /**
 427       * Reorganise the UI after every edit action.
 428       *
 429       * @protected
 430       * @method reorganise_edit_page
 431       */
 432      reorganise_edit_page: function() {
 433          Y.Moodle.mod_quiz.util.slot.reorderSlots();
 434          Y.Moodle.mod_quiz.util.slot.reorderPageBreaks();
 435          Y.Moodle.mod_quiz.util.page.reorderPages();
 436          Y.Moodle.mod_quiz.util.slot.updateOneSlotSections();
 437          Y.Moodle.mod_quiz.util.slot.updateAllDependencyIcons();
 438      },
 439  
 440      NAME: 'mod_quiz-resource-toolbox',
 441      ATTRS: {
 442          courseid: {
 443              'value': 0
 444          },
 445          quizid: {
 446              'value': 0
 447          }
 448      }
 449  });
 450  
 451  M.mod_quiz.resource_toolbox = null;
 452  M.mod_quiz.init_resource_toolbox = function(config) {
 453      M.mod_quiz.resource_toolbox = new RESOURCETOOLBOX(config);
 454      return M.mod_quiz.resource_toolbox;
 455  };


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