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