[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/mod/quiz/yui/build/moodle-mod_quiz-toolboxes/ -> moodle-mod_quiz-toolboxes.js (source)

   1  YUI.add('moodle-mod_quiz-toolboxes', function (Y, NAME) {
   2  
   3  /* eslint-disable no-unused-vars */
   4  /**
   5   * Resource and activity toolbox class.
   6   *
   7   * This class is responsible for managing AJAX interactions with activities and resources
   8   * when viewing a course in editing mode.
   9   *
  10   * @module moodle-course-toolboxes
  11   * @namespace M.course.toolboxes
  12   */
  13  
  14  // The CSS classes we use.
  15  var CSS = {
  16          ACTIVITYINSTANCE: 'activityinstance',
  17          AVAILABILITYINFODIV: 'div.availabilityinfo',
  18          CONTENTWITHOUTLINK: 'contentwithoutlink',
  19          CONDITIONALHIDDEN: 'conditionalhidden',
  20          DIMCLASS: 'dimmed',
  21          DIMMEDTEXT: 'dimmed_text',
  22          EDITINSTRUCTIONS: 'editinstructions',
  23          EDITINGMAXMARK: 'editor_displayed',
  24          HIDE: 'hide',
  25          JOIN: 'page_join',
  26          MODINDENTCOUNT: 'mod-indent-',
  27          MODINDENTHUGE: 'mod-indent-huge',
  28          PAGE: 'page',
  29          SECTIONHIDDENCLASS: 'hidden',
  30          SECTIONIDPREFIX: 'section-',
  31          SLOT: 'slot',
  32          SHOW: 'editing_show',
  33          TITLEEDITOR: 'titleeditor'
  34      },
  35      // The CSS selectors we use.
  36      SELECTOR = {
  37          ACTIONAREA: '.actions',
  38          ACTIONLINKTEXT: '.actionlinktext',
  39          ACTIVITYACTION: 'a.cm-edit-action[data-action], a.editing_maxmark, a.editing_section, input.shuffle_questions',
  40          ACTIVITYFORM: 'span.instancemaxmarkcontainer form',
  41          ACTIVITYINSTANCE: '.' + CSS.ACTIVITYINSTANCE,
  42          SECTIONINSTANCE: '.sectioninstance',
  43          ACTIVITYLI: 'li.activity, li.section',
  44          ACTIVITYMAXMARK: 'input[name=maxmark]',
  45          COMMANDSPAN: '.commands',
  46          CONTENTAFTERLINK: 'div.contentafterlink',
  47          CONTENTWITHOUTLINK: 'div.contentwithoutlink',
  48          DELETESECTIONICON: 'a.editing_delete img',
  49          EDITMAXMARK: 'a.editing_maxmark',
  50          EDITSECTION: 'a.editing_section',
  51          EDITSECTIONICON: 'a.editing_section img',
  52          EDITSHUFFLEQUESTIONSACTION: 'input.cm-edit-action[data-action]',
  53          EDITSHUFFLEAREA: '.instanceshufflequestions .shuffle-progress',
  54          HIDE: 'a.editing_hide',
  55          HIGHLIGHT: 'a.editing_highlight',
  56          INSTANCENAME: 'span.instancename',
  57          INSTANCEMAXMARK: 'span.instancemaxmark',
  58          INSTANCESECTION: 'span.instancesection',
  59          INSTANCESECTIONAREA: 'div.section-heading',
  60          MODINDENTDIV: '.mod-indent',
  61          MODINDENTOUTER: '.mod-indent-outer',
  62          NUMQUESTIONS: '.numberofquestions',
  63          PAGECONTENT: 'div#page-content',
  64          PAGELI: 'li.page',
  65          SECTIONUL: 'ul.section',
  66          SECTIONFORM: '.instancesectioncontainer form',
  67          SECTIONINPUT: 'input[name=section]',
  68          SHOW: 'a.' + CSS.SHOW,
  69          SLOTLI: 'li.slot',
  70          SUMMARKS: '.mod_quiz_summarks'
  71      },
  72      BODY = Y.one(document.body);
  73  
  74  // Setup the basic namespace.
  75  M.mod_quiz = M.mod_quiz || {};
  76  
  77  /**
  78   * The toolbox class is a generic class which should never be directly
  79   * instantiated. Please extend it instead.
  80   *
  81   * @class toolbox
  82   * @constructor
  83   * @protected
  84   * @extends Base
  85   */
  86  var TOOLBOX = function() {
  87      TOOLBOX.superclass.constructor.apply(this, arguments);
  88  };
  89  
  90  Y.extend(TOOLBOX, Y.Base, {
  91      /**
  92       * Send a request using the REST API
  93       *
  94       * @method send_request
  95       * @param {Object} data The data to submit with the AJAX request
  96       * @param {Node} [statusspinner] A statusspinner which may contain a section loader
  97       * @param {Function} success_callback The callback to use on success
  98       * @param {Object} [optionalconfig] Any additional configuration to submit
  99       * @chainable
 100       */
 101      send_request: function(data, statusspinner, success_callback, optionalconfig) {
 102          // Default data structure
 103          if (!data) {
 104              data = {};
 105          }
 106          // Handle any variables which we must pass back through to
 107          var pageparams = this.get('config').pageparams,
 108              varname;
 109          for (varname in pageparams) {
 110              data[varname] = pageparams[varname];
 111          }
 112  
 113          data.sesskey = M.cfg.sesskey;
 114          data.courseid = this.get('courseid');
 115          data.quizid = this.get('quizid');
 116  
 117          var uri = M.cfg.wwwroot + this.get('ajaxurl');
 118  
 119          // Define the configuration to send with the request
 120          var responsetext = [];
 121          var config = {
 122              method: 'POST',
 123              data: data,
 124              on: {
 125                  success: function(tid, response) {
 126                      try {
 127                          responsetext = Y.JSON.parse(response.responseText);
 128                          if (responsetext.error) {
 129                              new M.core.ajaxException(responsetext);
 130                          }
 131                      } catch (e) {
 132                          // Ignore.
 133                      }
 134  
 135                      // Run the callback if we have one.
 136                      if (responsetext.hasOwnProperty('newsummarks')) {
 137                          Y.one(SELECTOR.SUMMARKS).setHTML(responsetext.newsummarks);
 138                      }
 139                      if (responsetext.hasOwnProperty('newnumquestions')) {
 140                          Y.one(SELECTOR.NUMQUESTIONS).setHTML(
 141                                  M.util.get_string('numquestionsx', 'quiz', responsetext.newnumquestions)
 142                              );
 143                      }
 144                      if (success_callback) {
 145                          Y.bind(success_callback, this, responsetext)();
 146                      }
 147  
 148                      if (statusspinner) {
 149                          window.setTimeout(function() {
 150                              statusspinner.hide();
 151                          }, 400);
 152                      }
 153                  },
 154                  failure: function(tid, response) {
 155                      if (statusspinner) {
 156                          statusspinner.hide();
 157                      }
 158                      new M.core.ajaxException(response);
 159                  }
 160              },
 161              context: this
 162          };
 163  
 164          // Apply optional config
 165          if (optionalconfig) {
 166              for (varname in optionalconfig) {
 167                  config[varname] = optionalconfig[varname];
 168              }
 169          }
 170  
 171          if (statusspinner) {
 172              statusspinner.show();
 173          }
 174  
 175          // Send the request
 176          Y.io(uri, config);
 177          return this;
 178      }
 179  },
 180  {
 181      NAME: 'mod_quiz-toolbox',
 182      ATTRS: {
 183          /**
 184           * The ID of the Moodle Course being edited.
 185           *
 186           * @attribute courseid
 187           * @default 0
 188           * @type Number
 189           */
 190          courseid: {
 191              'value': 0
 192          },
 193  
 194          /**
 195           * The Moodle course format.
 196           *
 197           * @attribute format
 198           * @default 'topics'
 199           * @type String
 200           */
 201          quizid: {
 202              'value': 0
 203          },
 204          /**
 205           * The URL to use when submitting requests.
 206           * @attribute ajaxurl
 207           * @default null
 208           * @type String
 209           */
 210          ajaxurl: {
 211              'value': null
 212          },
 213          /**
 214           * Any additional configuration passed when creating the instance.
 215           *
 216           * @attribute config
 217           * @default {}
 218           * @type Object
 219           */
 220          config: {
 221              'value': {}
 222          }
 223      }
 224  }
 225  );
 226  /* global TOOLBOX, BODY, SELECTOR */
 227  
 228  /**
 229   * Resource and activity toolbox class.
 230   *
 231   * This class is responsible for managing AJAX interactions with activities and resources
 232   * when viewing a quiz in editing mode.
 233   *
 234   * @module mod_quiz-resource-toolbox
 235   * @namespace M.mod_quiz.resource_toolbox
 236   */
 237  
 238  /**
 239   * Resource and activity toolbox class.
 240   *
 241   * This is a class extending TOOLBOX containing code specific to resources
 242   *
 243   * This class is responsible for managing AJAX interactions with activities and resources
 244   * when viewing a quiz in editing mode.
 245   *
 246   * @class resources
 247   * @constructor
 248   * @extends M.course.toolboxes.toolbox
 249   */
 250  var RESOURCETOOLBOX = function() {
 251      RESOURCETOOLBOX.superclass.constructor.apply(this, arguments);
 252  };
 253  
 254  Y.extend(RESOURCETOOLBOX, TOOLBOX, {
 255      /**
 256       * An Array of events added when editing a max mark field.
 257       * These should all be detached when editing is complete.
 258       *
 259       * @property editmaxmarkevents
 260       * @protected
 261       * @type Array
 262       * @protected
 263       */
 264      editmaxmarkevents: [],
 265  
 266      /**
 267       *
 268       */
 269      NODE_PAGE: 1,
 270      NODE_SLOT: 2,
 271      NODE_JOIN: 3,
 272  
 273      /**
 274       * Initialize the resource toolbox
 275       *
 276       * For each activity the commands are updated and a reference to the activity is attached.
 277       * This way it doesn't matter where the commands are going to called from they have a reference to the
 278       * activity that they relate to.
 279       * This is essential as some of the actions are displayed in an actionmenu which removes them from the
 280       * page flow.
 281       *
 282       * This function also creates a single event delegate to manage all AJAX actions for all activities on
 283       * the page.
 284       *
 285       * @method initializer
 286       * @protected
 287       */
 288      initializer: function() {
 289          M.mod_quiz.quizbase.register_module(this);
 290          Y.delegate('click', this.handle_data_action, BODY, SELECTOR.ACTIVITYACTION, this);
 291          Y.delegate('click', this.handle_data_action, BODY, SELECTOR.DEPENDENCY_LINK, this);
 292      },
 293  
 294      /**
 295       * Handles the delegation event. When this is fired someone has triggered an action.
 296       *
 297       * Note not all actions will result in an AJAX enhancement.
 298       *
 299       * @protected
 300       * @method handle_data_action
 301       * @param {EventFacade} ev The event that was triggered.
 302       * @returns {boolean}
 303       */
 304      handle_data_action: function(ev) {
 305          // We need to get the anchor element that triggered this event.
 306          var node = ev.target;
 307          if (!node.test('a')) {
 308              node = node.ancestor(SELECTOR.ACTIVITYACTION);
 309          }
 310  
 311          // From the anchor we can get both the activity (added during initialisation) and the action being
 312          // performed (added by the UI as a data attribute).
 313          var action = node.getData('action'),
 314              activity = node.ancestor(SELECTOR.ACTIVITYLI);
 315  
 316          if (!node.test('a') || !action || !activity) {
 317              // It wasn't a valid action node.
 318              return;
 319          }
 320  
 321          // Switch based upon the action and do the desired thing.
 322          switch (action) {
 323              case 'editmaxmark':
 324                  // The user wishes to edit the maxmark of the resource.
 325                  this.edit_maxmark(ev, node, activity, action);
 326                  break;
 327              case 'delete':
 328                  // The user is deleting the activity.
 329                  this.delete_with_confirmation(ev, node, activity, action);
 330                  break;
 331              case 'addpagebreak':
 332              case 'removepagebreak':
 333                  // The user is adding or removing a page break.
 334                  this.update_page_break(ev, node, activity, action);
 335                  break;
 336              case 'adddependency':
 337              case 'removedependency':
 338                  // The user is adding or removing a dependency between questions.
 339                  this.update_dependency(ev, node, activity, action);
 340                  break;
 341              default:
 342                  // Nothing to do here!
 343                  break;
 344          }
 345      },
 346  
 347      /**
 348       * Add a loading icon to the specified activity.
 349       *
 350       * The icon is added within the action area.
 351       *
 352       * @method add_spinner
 353       * @param {Node} activity The activity to add a loading icon to
 354       * @return {Node|null} The newly created icon, or null if the action area was not found.
 355       */
 356      add_spinner: function(activity) {
 357          var actionarea = activity.one(SELECTOR.ACTIONAREA);
 358          if (actionarea) {
 359              return M.util.add_spinner(Y, actionarea);
 360          }
 361          return null;
 362      },
 363  
 364      /**
 365       * Deletes the given activity or resource after confirmation.
 366       *
 367       * @protected
 368       * @method delete_with_confirmation
 369       * @param {EventFacade} ev The event that was fired.
 370       * @param {Node} button The button that triggered this action.
 371       * @param {Node} activity The activity node that this action will be performed on.
 372       * @chainable
 373       */
 374      delete_with_confirmation: function(ev, button, activity) {
 375          // Prevent the default button action.
 376          ev.preventDefault();
 377  
 378          // Get the element we're working on.
 379          var element = activity,
 380              // Create confirm string (different if element has or does not have name)
 381              confirmstring = '',
 382              qtypename = M.util.get_string('pluginname',
 383                          'qtype_' + element.getAttribute('class').match(/qtype_([^\s]*)/)[1]);
 384          confirmstring = M.util.get_string('confirmremovequestion', 'quiz', qtypename);
 385  
 386          // Create the confirmation dialogue.
 387          var confirm = new M.core.confirm({
 388              question: confirmstring,
 389              modal: true
 390          });
 391  
 392          // If it is confirmed.
 393          confirm.on('complete-yes', function() {
 394  
 395              var spinner = this.add_spinner(element);
 396              var data = {
 397                  'class': 'resource',
 398                  'action': 'DELETE',
 399                  'id': Y.Moodle.mod_quiz.util.slot.getId(element)
 400              };
 401              this.send_request(data, spinner, function(response) {
 402                  if (response.deleted) {
 403                      // Actually remove the element.
 404                      Y.Moodle.mod_quiz.util.slot.remove(element);
 405                      this.reorganise_edit_page();
 406                      if (M.core.actionmenu && M.core.actionmenu.instance) {
 407                          M.core.actionmenu.instance.hideMenu(ev);
 408                      }
 409                  }
 410              });
 411  
 412          }, this);
 413  
 414          return this;
 415      },
 416  
 417  
 418      /**
 419       * Edit the maxmark for the resource
 420       *
 421       * @protected
 422       * @method edit_maxmark
 423       * @param {EventFacade} ev The event that was fired.
 424       * @param {Node} button The button that triggered this action.
 425       * @param {Node} activity The activity node that this action will be performed on.
 426       * @param {String} action The action that has been requested.
 427       * @return Boolean
 428       */
 429      edit_maxmark: function(ev, button, activity) {
 430          // Get the element we're working on
 431          var instancemaxmark = activity.one(SELECTOR.INSTANCEMAXMARK),
 432              instance = activity.one(SELECTOR.ACTIVITYINSTANCE),
 433              currentmaxmark = instancemaxmark.get('firstChild'),
 434              oldmaxmark = currentmaxmark.get('data'),
 435              maxmarktext = oldmaxmark,
 436              thisevent,
 437              anchor = instancemaxmark, // Grab the anchor so that we can swap it with the edit form.
 438              data = {
 439                  'class': 'resource',
 440                  'field': 'getmaxmark',
 441                  'id': Y.Moodle.mod_quiz.util.slot.getId(activity)
 442              };
 443  
 444          // Prevent the default actions.
 445          ev.preventDefault();
 446  
 447          this.send_request(data, null, function(response) {
 448              if (M.core.actionmenu && M.core.actionmenu.instance) {
 449                  M.core.actionmenu.instance.hideMenu(ev);
 450              }
 451  
 452              // Try to retrieve the existing string from the server.
 453              if (response.instancemaxmark) {
 454                  maxmarktext = response.instancemaxmark;
 455              }
 456  
 457              // Create the editor and submit button.
 458              var editform = Y.Node.create('<form action="#" />');
 459              var editinstructions = Y.Node.create('<span class="' + CSS.EDITINSTRUCTIONS + '" id="id_editinstructions" />')
 460                  .set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle'));
 461              var editor = Y.Node.create('<input name="maxmark" type="text" class="' + CSS.TITLEEDITOR + '" />').setAttrs({
 462                  'value': maxmarktext,
 463                  'autocomplete': 'off',
 464                  'aria-describedby': 'id_editinstructions',
 465                  'maxLength': '12',
 466                  'size': parseInt(this.get('config').questiondecimalpoints, 10) + 2
 467              });
 468  
 469              // Clear the existing content and put the editor in.
 470              editform.appendChild(editor);
 471              editform.setData('anchor', anchor);
 472              instance.insert(editinstructions, 'before');
 473              anchor.replace(editform);
 474  
 475              // We hide various components whilst editing:
 476              activity.addClass(CSS.EDITINGMAXMARK);
 477  
 478              // Focus and select the editor text.
 479              editor.focus().select();
 480  
 481              // Cancel the edit if we lose focus or the escape key is pressed.
 482              thisevent = editor.on('blur', this.edit_maxmark_cancel, this, activity, false);
 483              this.editmaxmarkevents.push(thisevent);
 484              thisevent = editor.on('key', this.edit_maxmark_cancel, 'esc', this, activity, true);
 485              this.editmaxmarkevents.push(thisevent);
 486  
 487              // Handle form submission.
 488              thisevent = editform.on('submit', this.edit_maxmark_submit, this, activity, oldmaxmark);
 489              this.editmaxmarkevents.push(thisevent);
 490          });
 491      },
 492  
 493      /**
 494       * Handles the submit event when editing the activity or resources maxmark.
 495       *
 496       * @protected
 497       * @method edit_maxmark_submit
 498       * @param {EventFacade} ev The event that triggered this.
 499       * @param {Node} activity The activity whose maxmark we are altering.
 500       * @param {String} originalmaxmark The original maxmark the activity or resource had.
 501       */
 502      edit_maxmark_submit: function(ev, activity, originalmaxmark) {
 503          // We don't actually want to submit anything.
 504          ev.preventDefault();
 505          var newmaxmark = Y.Lang.trim(activity.one(SELECTOR.ACTIVITYFORM + ' ' + SELECTOR.ACTIVITYMAXMARK).get('value'));
 506          var spinner = this.add_spinner(activity);
 507          this.edit_maxmark_clear(activity);
 508          activity.one(SELECTOR.INSTANCEMAXMARK).setContent(newmaxmark);
 509          if (newmaxmark !== null && newmaxmark !== "" && newmaxmark !== originalmaxmark) {
 510              var data = {
 511                  'class': 'resource',
 512                  'field': 'updatemaxmark',
 513                  'maxmark': newmaxmark,
 514                  'id': Y.Moodle.mod_quiz.util.slot.getId(activity)
 515              };
 516              this.send_request(data, spinner, function(response) {
 517                  if (response.instancemaxmark) {
 518                      activity.one(SELECTOR.INSTANCEMAXMARK).setContent(response.instancemaxmark);
 519                  }
 520              });
 521          }
 522      },
 523  
 524      /**
 525       * Handles the cancel event when editing the activity or resources maxmark.
 526       *
 527       * @protected
 528       * @method edit_maxmark_cancel
 529       * @param {EventFacade} ev The event that triggered this.
 530       * @param {Node} activity The activity whose maxmark we are altering.
 531       * @param {Boolean} preventdefault If true we should prevent the default action from occuring.
 532       */
 533      edit_maxmark_cancel: function(ev, activity, preventdefault) {
 534          if (preventdefault) {
 535              ev.preventDefault();
 536          }
 537          this.edit_maxmark_clear(activity);
 538      },
 539  
 540      /**
 541       * Handles clearing the editing UI and returning things to the original state they were in.
 542       *
 543       * @protected
 544       * @method edit_maxmark_clear
 545       * @param {Node} activity  The activity whose maxmark we were altering.
 546       */
 547      edit_maxmark_clear: function(activity) {
 548          // Detach all listen events to prevent duplicate triggers
 549          new Y.EventHandle(this.editmaxmarkevents).detach();
 550  
 551          var editform = activity.one(SELECTOR.ACTIVITYFORM),
 552              instructions = activity.one('#id_editinstructions');
 553          if (editform) {
 554              editform.replace(editform.getData('anchor'));
 555          }
 556          if (instructions) {
 557              instructions.remove();
 558          }
 559  
 560          // Remove the editing class again to revert the display.
 561          activity.removeClass(CSS.EDITINGMAXMARK);
 562  
 563          // Refocus the link which was clicked originally so the user can continue using keyboard nav.
 564          Y.later(100, this, function() {
 565              activity.one(SELECTOR.EDITMAXMARK).focus();
 566          });
 567  
 568          // TODO MDL-50768 This hack is to keep Behat happy until they release a version of
 569          // MinkSelenium2Driver that fixes
 570          // https://github.com/Behat/MinkSelenium2Driver/issues/80.
 571          if (!Y.one('input[name=maxmark')) {
 572              Y.one('body').append('<input type="text" name="maxmark" style="display: none">');
 573          }
 574      },
 575  
 576      /**
 577       * Joins or separates the given slot with the page of the previous slot. Reorders the pages of
 578       * the other slots
 579       *
 580       * @protected
 581       * @method update_page_break
 582       * @param {EventFacade} ev The event that was fired.
 583       * @param {Node} button The button that triggered this action.
 584       * @param {Node} activity The activity node that this action will be performed on.
 585       * @param {String} action The action, addpagebreak or removepagebreak.
 586       * @chainable
 587       */
 588      update_page_break: function(ev, button, activity, action) {
 589          // Prevent the default button action
 590          ev.preventDefault();
 591  
 592          var nextactivity = activity.next('li.activity.slot');
 593          var spinner = this.add_spinner(nextactivity);
 594          var value = action === 'removepagebreak' ? 1 : 2;
 595  
 596          var data = {
 597              'class': 'resource',
 598              'field': 'updatepagebreak',
 599              'id':    Y.Moodle.mod_quiz.util.slot.getId(nextactivity),
 600              'value': value
 601          };
 602  
 603          this.send_request(data, spinner, function(response) {
 604              if (response.slots) {
 605                  if (action === 'addpagebreak') {
 606                      Y.Moodle.mod_quiz.util.page.add(activity);
 607                  } else {
 608                      var page = activity.next(Y.Moodle.mod_quiz.util.page.SELECTORS.PAGE);
 609                      Y.Moodle.mod_quiz.util.page.remove(page, true);
 610                  }
 611                  this.reorganise_edit_page();
 612              }
 613          });
 614  
 615          return this;
 616      },
 617  
 618      /**
 619       * Updates a slot to either require the question in the previous slot to
 620       * have been answered, or not,
 621       *
 622       * @protected
 623       * @method update_page_break
 624       * @param {EventFacade} ev The event that was fired.
 625       * @param {Node} button The button that triggered this action.
 626       * @param {Node} activity The activity node that this action will be performed on.
 627       * @param {String} action The action, adddependency or removedependency.
 628       * @chainable
 629       */
 630      update_dependency: function(ev, button, activity, action) {
 631          // Prevent the default button action.
 632          ev.preventDefault();
 633          var spinner = this.add_spinner(activity);
 634  
 635          var data = {
 636              'class': 'resource',
 637              'field': 'updatedependency',
 638              'id':    Y.Moodle.mod_quiz.util.slot.getId(activity),
 639              'value': action === 'adddependency' ? 1 : 0
 640          };
 641  
 642          this.send_request(data, spinner, function(response) {
 643              if (response.hasOwnProperty('requireprevious')) {
 644                  Y.Moodle.mod_quiz.util.slot.updateDependencyIcon(activity, response.requireprevious);
 645              }
 646          });
 647  
 648          return this;
 649      },
 650  
 651      /**
 652       * Reorganise the UI after every edit action.
 653       *
 654       * @protected
 655       * @method reorganise_edit_page
 656       */
 657      reorganise_edit_page: function() {
 658          Y.Moodle.mod_quiz.util.slot.reorderSlots();
 659          Y.Moodle.mod_quiz.util.slot.reorderPageBreaks();
 660          Y.Moodle.mod_quiz.util.page.reorderPages();
 661          Y.Moodle.mod_quiz.util.slot.updateOneSlotSections();
 662          Y.Moodle.mod_quiz.util.slot.updateAllDependencyIcons();
 663      },
 664  
 665      NAME: 'mod_quiz-resource-toolbox',
 666      ATTRS: {
 667          courseid: {
 668              'value': 0
 669          },
 670          quizid: {
 671              'value': 0
 672          }
 673      }
 674  });
 675  
 676  M.mod_quiz.resource_toolbox = null;
 677  M.mod_quiz.init_resource_toolbox = function(config) {
 678      M.mod_quiz.resource_toolbox = new RESOURCETOOLBOX(config);
 679      return M.mod_quiz.resource_toolbox;
 680  };
 681  /* global TOOLBOX, BODY, SELECTOR */
 682  
 683  /**
 684   * Section toolbox class.
 685   *
 686   * This class is responsible for managing AJAX interactions with sections
 687   * when adding, editing, removing section headings.
 688   *
 689   * @module moodle-mod_quiz-toolboxes
 690   * @namespace M.mod_quiz.toolboxes
 691   */
 692  
 693  /**
 694   * Section toolbox class.
 695   *
 696   * This class is responsible for managing AJAX interactions with sections
 697   * when adding, editing, removing section headings when editing a quiz.
 698   *
 699   * @class section
 700   * @constructor
 701   * @extends M.mod_quiz.toolboxes.toolbox
 702   */
 703  var SECTIONTOOLBOX = function() {
 704      SECTIONTOOLBOX.superclass.constructor.apply(this, arguments);
 705  };
 706  
 707  Y.extend(SECTIONTOOLBOX, TOOLBOX, {
 708      /**
 709       * An Array of events added when editing a max mark field.
 710       * These should all be detached when editing is complete.
 711       *
 712       * @property editsectionevents
 713       * @protected
 714       * @type Array
 715       * @protected
 716       */
 717      editsectionevents: [],
 718  
 719      /**
 720       * Initialize the section toolboxes module.
 721       *
 722       * Updates all span.commands with relevant handlers and other required changes.
 723       *
 724       * @method initializer
 725       * @protected
 726       */
 727      initializer: function() {
 728          M.mod_quiz.quizbase.register_module(this);
 729  
 730          BODY.delegate('key', this.handle_data_action, 'down:enter', SELECTOR.ACTIVITYACTION, this);
 731          Y.delegate('click', this.handle_data_action, BODY, SELECTOR.ACTIVITYACTION, this);
 732          Y.delegate('change', this.handle_data_action, BODY, SELECTOR.EDITSHUFFLEQUESTIONSACTION, this);
 733      },
 734  
 735      /**
 736       * Handles the delegation event. When this is fired someone has triggered an action.
 737       *
 738       * Note not all actions will result in an AJAX enhancement.
 739       *
 740       * @protected
 741       * @method handle_data_action
 742       * @param {EventFacade} ev The event that was triggered.
 743       * @returns {boolean}
 744       */
 745      handle_data_action: function(ev) {
 746          // We need to get the anchor element that triggered this event.
 747          var node = ev.target;
 748          if (!node.test('a') && !node.test('input[data-action]')) {
 749              node = node.ancestor(SELECTOR.ACTIVITYACTION);
 750          }
 751  
 752          // From the anchor we can get both the activity (added during initialisation) and the action being
 753          // performed (added by the UI as a data attribute).
 754          var action = node.getData('action'),
 755              activity = node.ancestor(SELECTOR.ACTIVITYLI);
 756  
 757          if ((!node.test('a') && !node.test('input[data-action]')) || !action || !activity) {
 758              // It wasn't a valid action node.
 759              return;
 760          }
 761  
 762          // Switch based upon the action and do the desired thing.
 763          switch (action) {
 764              case 'edit_section_title':
 765                  // The user wishes to edit the section headings.
 766                  this.edit_section_title(ev, node, activity, action);
 767                  break;
 768              case 'shuffle_questions':
 769                  // The user wishes to edit the shuffle questions of the section (resource).
 770                  this.edit_shuffle_questions(ev, node, activity, action);
 771                  break;
 772              case 'deletesection':
 773                  // The user is deleting the activity.
 774                  this.delete_section_with_confirmation(ev, node, activity, action);
 775                  break;
 776              default:
 777                  // Nothing to do here!
 778                  break;
 779          }
 780      },
 781  
 782      /**
 783       * Deletes the given section heading after confirmation.
 784       *
 785       * @protected
 786       * @method delete_section_with_confirmation
 787       * @param {EventFacade} ev The event that was fired.
 788       * @param {Node} button The button that triggered this action.
 789       * @param {Node} activity The activity node that this action will be performed on.
 790       * @chainable
 791       */
 792      delete_section_with_confirmation: function(ev, button, activity) {
 793          // Prevent the default button action.
 794          ev.preventDefault();
 795  
 796          // Create the confirmation dialogue.
 797          var confirm = new M.core.confirm({
 798              question: M.util.get_string('confirmremovesectionheading', 'quiz', activity.get('aria-label')),
 799              modal: true
 800          });
 801  
 802          // If it is confirmed.
 803          confirm.on('complete-yes', function() {
 804  
 805              var spinner = M.util.add_spinner(Y, activity.one(SELECTOR.ACTIONAREA));
 806              var data = {
 807                  'class':  'section',
 808                  'action': 'DELETE',
 809                  'id':     activity.get('id').replace('section-', '')
 810              };
 811              this.send_request(data, spinner, function(response) {
 812                  if (response.deleted) {
 813                      window.location.reload(true);
 814                  }
 815              });
 816  
 817          }, this);
 818      },
 819  
 820      /**
 821       * Edit the edit section title for the section
 822       *
 823       * @protected
 824       * @method edit_section_title
 825       * @param {EventFacade} ev The event that was fired.
 826       * @param {Node} button The button that triggered this action.
 827       * @param {Node} activity The activity node that this action will be performed on.
 828       * @param {String} action The action that has been requested.
 829       * @return Boolean
 830       */
 831      edit_section_title: function(ev, button, activity) {
 832          // Get the element we're working on
 833          var activityid = activity.get('id').replace('section-', ''),
 834              instancesection = activity.one(SELECTOR.INSTANCESECTION),
 835              thisevent,
 836              anchor = instancesection, // Grab the anchor so that we can swap it with the edit form.
 837              data = {
 838                  'class': 'section',
 839                  'field': 'getsectiontitle',
 840                  'id':    activityid
 841              };
 842  
 843          // Prevent the default actions.
 844          ev.preventDefault();
 845  
 846          this.send_request(data, null, function(response) {
 847              // Try to retrieve the existing string from the server.
 848              var oldtext = response.instancesection;
 849  
 850              // Create the editor and submit button.
 851              var editform = Y.Node.create('<form action="#" />');
 852              var editinstructions = Y.Node.create('<span class="' + CSS.EDITINSTRUCTIONS + '" id="id_editinstructions" />')
 853                  .set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle'));
 854              var editor = Y.Node.create('<input name="section" type="text" />').setAttrs({
 855                  'value': oldtext,
 856                  'autocomplete': 'off',
 857                  'aria-describedby': 'id_editinstructions',
 858                  'maxLength': '255' // This is the maxlength in DB.
 859              });
 860  
 861              // Clear the existing content and put the editor in.
 862              editform.appendChild(editor);
 863              editform.setData('anchor', anchor);
 864              instancesection.insert(editinstructions, 'before');
 865              anchor.replace(editform);
 866  
 867              // Focus and select the editor text.
 868              editor.focus().select();
 869              // Cancel the edit if we lose focus or the escape key is pressed.
 870              thisevent = editor.on('blur', this.edit_section_title_cancel, this, activity, false);
 871              this.editsectionevents.push(thisevent);
 872              thisevent = editor.on('key', this.edit_section_title_cancel, 'esc', this, activity, true);
 873              this.editsectionevents.push(thisevent);
 874              // Handle form submission.
 875              thisevent = editform.on('submit', this.edit_section_title_submit, this, activity, oldtext);
 876              this.editsectionevents.push(thisevent);
 877          });
 878      },
 879  
 880      /**
 881       * Handles the submit event when editing section heading.
 882       *
 883       * @protected
 884       * @method edit_section_title_submiy
 885       * @param {EventFacade} ev The event that triggered this.
 886       * @param {Node} activity The activity whose maxmark we are altering.
 887       * @param {String} oldtext The original maxmark the activity or resource had.
 888       */
 889      edit_section_title_submit: function(ev, activity, oldtext) {
 890           // We don't actually want to submit anything.
 891          ev.preventDefault();
 892          var newtext = Y.Lang.trim(activity.one(SELECTOR.SECTIONFORM + ' ' + SELECTOR.SECTIONINPUT).get('value'));
 893          var spinner = M.util.add_spinner(Y, activity.one(SELECTOR.INSTANCESECTIONAREA));
 894          this.edit_section_title_clear(activity);
 895          if (newtext !== null && newtext !== oldtext) {
 896              activity.one(SELECTOR.INSTANCESECTION).setContent(newtext);
 897              var data = {
 898                  'class':      'section',
 899                  'field':      'updatesectiontitle',
 900                  'newheading': newtext,
 901                  'id':         activity.get('id').replace('section-', '')
 902              };
 903              this.send_request(data, spinner, function(response) {
 904                  if (response) {
 905                      activity.one(SELECTOR.INSTANCESECTION).setContent(response.instancesection);
 906                      activity.one(SELECTOR.EDITSECTIONICON).set('title',
 907                              M.util.get_string('sectionheadingedit', 'quiz', response.instancesection));
 908                      activity.one(SELECTOR.EDITSECTIONICON).set('alt',
 909                              M.util.get_string('sectionheadingedit', 'quiz', response.instancesection));
 910                      var deleteicon = activity.one(SELECTOR.DELETESECTIONICON);
 911                      if (deleteicon) {
 912                          deleteicon.set('title', M.util.get_string('sectionheadingremove', 'quiz', response.instancesection));
 913                          deleteicon.set('alt', M.util.get_string('sectionheadingremove', 'quiz', response.instancesection));
 914                      }
 915                  }
 916              });
 917          }
 918      },
 919  
 920      /**
 921       * Handles the cancel event when editing the activity or resources maxmark.
 922       *
 923       * @protected
 924       * @method edit_maxmark_cancel
 925       * @param {EventFacade} ev The event that triggered this.
 926       * @param {Node} activity The activity whose maxmark we are altering.
 927       * @param {Boolean} preventdefault If true we should prevent the default action from occuring.
 928       */
 929      edit_section_title_cancel: function(ev, activity, preventdefault) {
 930          if (preventdefault) {
 931              ev.preventDefault();
 932          }
 933          this.edit_section_title_clear(activity);
 934      },
 935  
 936      /**
 937       * Handles clearing the editing UI and returning things to the original state they were in.
 938       *
 939       * @protected
 940       * @method edit_maxmark_clear
 941       * @param {Node} activity  The activity whose maxmark we were altering.
 942       */
 943      edit_section_title_clear: function(activity) {
 944          // Detach all listen events to prevent duplicate triggers
 945          new Y.EventHandle(this.editsectionevents).detach();
 946  
 947          var editform = activity.one(SELECTOR.SECTIONFORM),
 948              instructions = activity.one('#id_editinstructions');
 949          if (editform) {
 950              editform.replace(editform.getData('anchor'));
 951          }
 952          if (instructions) {
 953              instructions.remove();
 954          }
 955  
 956          // Refocus the link which was clicked originally so the user can continue using keyboard nav.
 957          Y.later(100, this, function() {
 958              activity.one(SELECTOR.EDITSECTION).focus();
 959          });
 960  
 961          // This hack is to keep Behat happy until they release a version of
 962          // MinkSelenium2Driver that fixes
 963          // https://github.com/Behat/MinkSelenium2Driver/issues/80.
 964          if (!Y.one('input[name=section]')) {
 965              Y.one('body').append('<input type="text" name="section" style="display: none">');
 966          }
 967      },
 968  
 969      /**
 970       * Edit the edit shuffle questions for the section
 971       *
 972       * @protected
 973       * @method edit_shuffle_questions
 974       * @param {EventFacade} ev The event that was fired.
 975       * @param {Node} button The button that triggered this action.
 976       * @param {Node} activity The activity node that this action will be performed on.
 977       * @param {String} action The action that has been requested.
 978       * @return Boolean
 979       */
 980      edit_shuffle_questions: function(ev, button, activity) {
 981          var newvalue;
 982          if (activity.one(SELECTOR.EDITSHUFFLEQUESTIONSACTION).get('checked')) {
 983              newvalue = 1;
 984          } else {
 985              newvalue = 0;
 986          }
 987  
 988          // Get the element we're working on
 989          var data = {
 990              'class': 'section',
 991              'field': 'updateshufflequestions',
 992              'id': activity.get('id').replace('section-', ''),
 993              'newshuffle': newvalue
 994          };
 995  
 996          // Prevent the default actions.
 997          ev.preventDefault();
 998  
 999          // Send request.
1000          var spinner = M.util.add_spinner(Y, activity.one(SELECTOR.EDITSHUFFLEAREA));
1001          this.send_request(data, spinner);
1002      }
1003  
1004  }, {
1005      NAME: 'mod_quiz-section-toolbox',
1006      ATTRS: {
1007          courseid: {
1008              'value': 0
1009          },
1010          quizid: {
1011              'value': 0
1012          }
1013      }
1014  });
1015  
1016  M.mod_quiz.init_section_toolbox = function(config) {
1017      return new SECTIONTOOLBOX(config);
1018  };
1019  
1020  
1021  }, '@VERSION@', {
1022      "requires": [
1023          "base",
1024          "node",
1025          "event",
1026          "event-key",
1027          "io",
1028          "moodle-mod_quiz-quizbase",
1029          "moodle-mod_quiz-util-slot",
1030          "moodle-core-notification-ajaxexception"
1031      ]
1032  });


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