1 YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
4 NAME : 'Enrolment Manager',
8 SEARCHBTN : 'searchbtn',
12 MULTIPLE : 'multiple',
14 COURSEID : 'courseid',
16 USERCOUNT : 'userCount',
17 REQUIREREFRESH : 'requiresRefresh',
18 LASTSEARCH : 'lastPreSearchValue',
19 INSTANCES : 'instances',
20 OPTIONSTARTDATE : 'optionsStartDate',
21 DEFAULTROLE : 'defaultRole',
22 DEFAULTSTARTDATE : 'defaultStartDate',
23 DEFAULTDURATION : 'defaultDuration',
24 ASSIGNABLEROLES : 'assignableRoles',
25 DISABLEGRADEHISTORY : 'disableGradeHistory',
26 RECOVERGRADESDEFAULT : 'recoverGradesDefault',
27 ENROLCOUNT : 'enrolCount',
29 COHORTSAVAILABLE : 'cohortsAvailable',
30 COHORTCOUNT : 'cohortCount'
32 /** CSS classes for nodes in structure **/
34 PANEL : 'user-enroller-panel',
36 HEADER : 'uep-header',
37 CONTENT : 'uep-content',
38 AJAXCONTENT : 'uep-ajax-content',
39 SEARCHRESULTS : 'uep-search-results',
40 TOTALUSERS : 'totalusers',
43 MORERESULTS : 'uep-more-results',
44 LIGHTBOX : 'uep-loading-lightbox',
45 LOADINGICON : 'loading-icon',
46 FOOTER : 'uep-footer',
48 ENROLLED : 'enrolled',
52 FULLNAME : 'fullname',
53 EXTRAFIELDS : 'extrafields',
58 RECOVERGRADES : 'recovergrades',
59 RECOVERGRADESTITLE : 'recovergradestitle',
60 SEARCHOPTIONS : 'uep-searchoptions',
61 COLLAPSIBLEHEADING : 'collapsibleheading',
62 COLLAPSIBLEAREA : 'collapsiblearea',
63 ENROLMENTOPTION : 'uep-enrolment-option',
64 SEARCHCONTROLS : 'uep-controls',
66 STARTDATE : 'startdate',
67 DURATION : 'duration',
69 SEARCH : 'uep-search',
70 SEARCHBTN : 'uep-search-btn',
72 CLOSEBTN : 'close-button',
73 ENTITYSELECTOR : 'uep-entity-selector',
76 COHORTNAME : 'cohortname',
77 TOTALCOHORTS : 'totalcohorts'
79 var create = Y.Node.create;
81 var USERENROLLER = function(config) {
82 USERENROLLER.superclass.constructor.apply(this, arguments);
84 Y.extend(USERENROLLER, Y.Base, {
85 _searchTimeout : null,
87 _escCloseEvent : null,
88 initializer : function(config) {
89 var recovergrades = null;
90 if (this.get(UEP.DISABLEGRADEHISTORY) != true) {
91 recovergrades = create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.RECOVERGRADES+'"></div>')
92 .append(create('<label class="'+CSS.RECOVERGRADESTITLE+'" for="'+CSS.RECOVERGRADES+'">'+M.util.get_string('recovergrades', 'enrol')+'</label>'))
93 .append(create('<input type="checkbox" id="'+CSS.RECOVERGRADES+'" name="'+CSS.RECOVERGRADES+'"'+ this.get(UEP.RECOVERGRADESDEFAULT) +' />'))
96 this.set(UEP.BASE, create('<div class="'+CSS.PANEL+' '+CSS.HIDDEN+'"></div>')
97 .append(create('<div class="'+CSS.WRAP+'"></div>')
98 .append(create('<div class="'+CSS.HEADER+' header"></div>')
99 .append(create('<div class="'+CSS.CLOSE+'"></div>'))
100 .append(create('<h2>'+M.util.get_string('enrolusers', 'enrol')+'</h2>')))
101 .append(create('<div class="'+CSS.CONTENT+'"></div>')
102 .append(create('<div class="'+CSS.SEARCHCONTROLS+'"></div>')
103 .append(create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.ROLE+'"><label for="id_enrol_manual_assignable_roles">'+M.util.get_string('assignroles', 'role')+'</label></div>')
104 .append(create('<select id="id_enrol_manual_assignable_roles"><option value="">'+M.util.get_string('none', 'enrol')+'</option></select>'))
106 .append(create('<div class="'+CSS.ENTITYSELECTOR+'"></div>'))
107 .append(create('<div class="'+CSS.SEARCHOPTIONS+'"></div>')
108 .append(create('<div class="'+CSS.COLLAPSIBLEHEADING+'"><img alt="" />'+M.util.get_string('enrolmentoptions', 'enrol')+'</div>'))
109 .append(create('<div class="'+CSS.COLLAPSIBLEAREA+' '+CSS.HIDDEN+'"></div>')
110 .append(recovergrades)
111 .append(create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.STARTDATE+'">'+M.util.get_string('startingfrom', 'moodle')+'</div>')
112 .append(create('<select></select>')))
113 .append(create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.DURATION+'">'+M.util.get_string('enrolperiod', 'enrol')+'</div>')
114 .append(create('<select><option value="0" selected="selected">'+M.util.get_string('unlimitedduration', 'enrol')+'</option></select>')))
118 .append(create('<div class="'+CSS.AJAXCONTENT+'"></div>'))
119 .append(create('<div class="'+CSS.LIGHTBOX+' '+CSS.HIDDEN+'"></div>')
120 .append(create('<img alt="loading" class="'+CSS.LOADINGICON+'" />')
121 .setAttribute('src', M.util.image_url('i/loading', 'moodle')))
122 .setStyle('opacity', 0.5)))
123 .append(create('<div class="'+CSS.FOOTER+'"></div>')
124 .append(create('<div class="'+CSS.SEARCH+'"><label for="enrolusersearch" class="accesshide">'+M.util.get_string('usersearch', 'enrol')+'</label></div>')
125 .append(create('<input type="text" id="enrolusersearch" value="" />'))
126 .append(create('<input type="button" id="searchbtn" class="'+CSS.SEARCHBTN+'" value="'+M.util.get_string('usersearch', 'enrol')+'" />'))
128 .append(create('<div class="'+CSS.CLOSEBTN+'"></div>')
129 .append(create('<input type="button" value="'+M.util.get_string('finishenrollingusers', 'enrol')+'" />'))
135 this.set(UEP.SEARCH, this.get(UEP.BASE).one('#enrolusersearch'));
136 this.set(UEP.SEARCHBTN, this.get(UEP.BASE).one('#searchbtn'));
137 Y.all('.enrol_manual_plugin input').each(function(node){
138 if (node.getAttribute('type', 'submit')) {
139 node.on('click', this.show, this);
142 this.get(UEP.BASE).one('.'+CSS.HEADER+' .'+CSS.CLOSE).on('click', this.hide, this);
143 this.get(UEP.BASE).one('.'+CSS.FOOTER+' .'+CSS.CLOSEBTN+' input').on('click', this.hide, this);
144 this._loadingNode = this.get(UEP.BASE).one('.'+CSS.CONTENT+' .'+CSS.LIGHTBOX);
145 var params = this.get(UEP.PARAMS);
146 params['id'] = this.get(UEP.COURSEID);
147 this.set(UEP.PARAMS, params);
149 Y.on('key', this.preSearch, this.get(UEP.SEARCH), 'down:13', this);
150 this.get(UEP.SEARCHBTN).on('click', this.preSearch, this);
152 if (this.get(UEP.COHORTSAVAILABLE)) {
153 this.get(UEP.BASE).one('.'+CSS.ENTITYSELECTOR)
154 .append(create('<input type="radio" id="id_enrol_manual_entity_users" name="enrol_manual_entity" value="users" checked="checked"/>'))
155 .append(create('<label for="id_enrol_manual_entity_users">'+ M.util.get_string('browseusers', 'enrol_manual')+'</label>'))
156 .append(create('<input type="radio" id="id_enrol_manual_entity_cohorts" name="enrol_manual_entity" value="cohorts"/>'))
157 .append(create('<label for="id_enrol_manual_entity_cohorts">'+M.util.get_string('browsecohorts', 'enrol_manual')+'</label>'));
158 this.get(UEP.BASE).one('#id_enrol_manual_entity_cohorts').on('change', this.search, this);
159 this.get(UEP.BASE).one('#id_enrol_manual_entity_users').on('change', this.search, this);
161 this.get(UEP.BASE).one('.'+CSS.ENTITYSELECTOR)
162 .append(create('<input type="hidden" name="enrol_manual_entity" value="users"/>'));
165 Y.one(document.body).append(this.get(UEP.BASE));
167 var base = this.get(UEP.BASE);
168 base.plug(Y.Plugin.Drag);
169 base.dd.addHandle('.'+CSS.HEADER+' h2');
170 base.one('.'+CSS.HEADER+' h2').setStyle('cursor', 'move');
172 var collapsedimage = 't/collapsed'; // ltr mode
173 if ( Y.one(document.body).hasClass('dir-rtl') ) {
174 collapsedimage = 't/collapsed_rtl';
176 collapsedimage = 't/collapsed';
179 this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).one('img').setAttribute('src', M.util.image_url(collapsedimage, 'moodle'));
180 this.populateStartDates();
181 this.populateDuration();
182 this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).on('click', function(){
183 this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).toggleClass(CSS.ACTIVE);
184 this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEAREA).toggleClass(CSS.HIDDEN);
185 if (this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEAREA).hasClass(CSS.HIDDEN)) {
186 this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).one('img').setAttribute('src', M.util.image_url(collapsedimage, 'moodle'));
188 this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).one('img').setAttribute('src', M.util.image_url('t/expanded', 'moodle'));
191 this.populateAssignableRoles();
193 populateAssignableRoles : function() {
194 this.on('assignablerolesloaded', function(){
195 var roles = this.get(UEP.ASSIGNABLEROLES);
196 var s = this.get(UEP.BASE).one('.'+CSS.ENROLMENTOPTION+'.'+CSS.ROLE+' select');
197 var v = this.get(UEP.DEFAULTROLE);
198 var index = 0, count = 0;
199 for (var i in roles) {
201 var option = create('<option value="'+i+'">'+roles[i]+'</option>');
207 s.set('selectedIndex', index);
208 Y.one('#id_enrol_manual_assignable_roles').focus();
210 this.getAssignableRoles();
212 populateStartDates : function() {
213 var select = this.get(UEP.BASE).one('.'+CSS.ENROLMENTOPTION+'.'+CSS.STARTDATE+' select');
214 var defaultvalue = this.get(UEP.DEFAULTSTARTDATE);
215 var options = this.get(UEP.OPTIONSTARTDATE);
216 var index = 0, count = 0;
217 for (var i in options) {
219 var option = create('<option value="'+i+'">'+options[i]+'</option>');
220 if (i == defaultvalue) {
223 select.append(option);
225 select.set('selectedIndex', index);
227 populateDuration : function() {
228 var select = this.get(UEP.BASE).one('.'+CSS.ENROLMENTOPTION+'.'+CSS.DURATION+' select');
229 var defaultvalue = this.get(UEP.DEFAULTDURATION);
230 var index = 0, count = 0;
231 var durationdays = M.util.get_string('durationdays', 'enrol', '{a}');
232 for (var i = 1; i <= 365; i++) {
234 var option = create('<option value="'+i+'">'+durationdays.replace('{a}', i)+'</option>');
235 if (i == defaultvalue) {
238 select.append(option);
240 select.set('selectedIndex', index);
242 getAssignableRoles : function(){
243 Y.io(M.cfg.wwwroot+'/enrol/ajax.php', {
245 data:'id='+this.get(UEP.COURSEID)+'&action=getassignable&sesskey='+M.cfg.sesskey,
247 complete: function(tid, outcome, args) {
249 var roles = Y.JSON.parse(outcome.responseText);
250 this.set(UEP.ASSIGNABLEROLES, roles.response);
252 new M.core.exception(e);
254 this.getAssignableRoles = function() {
255 this.fire('assignablerolesloaded');
257 this.getAssignableRoles();
263 preSearch : function(e) {
264 this.search(e, false);
266 var value = this.get(UEP.SEARCH).get('value');
267 if (value.length < 3 || value == this.get(UEP.LASTSEARCH)) {
270 this.set(UEP.LASTSEARCH, value);
271 if (this._searchTimeout) {
272 clearTimeout(this._searchTimeout);
273 this._searchTimeout = null;
276 this._searchTimeout = setTimeout(function(){
277 self._searchTimeout = null;
278 self.search(null, false);
286 var base = this.get(UEP.BASE);
287 base.removeClass(CSS.HIDDEN);
288 var x = (base.get('winWidth') - 400)/2;
289 var y = (parseInt(base.get('winHeight'))-base.get('offsetHeight'))/2 + parseInt(base.get('docScrollY'));
290 if (y < parseInt(base.get('winHeight'))*0.1) {
291 y = parseInt(base.get('winHeight'))*0.1;
295 if (this.get(UEP.USERS)===null) {
296 this.search(e, false);
299 this._escCloseEvent = Y.on('key', this.hide, document.body, 'down:27', this);
300 var rolesselect = Y.one('#id_enrol_manual_assignable_roles');
306 if (this._escCloseEvent) {
307 this._escCloseEvent.detach();
308 this._escCloseEvent = null;
310 this.get(UEP.BASE).addClass(CSS.HIDDEN);
311 if (this.get(UEP.REQUIREREFRESH)) {
312 window.location = this.get(UEP.URL);
315 currentEntity : function() {
316 var entity = CSS.USER;
317 var cohortsinput = Y.one('#id_enrol_manual_entity_cohorts');
318 if (cohortsinput && cohortsinput.get('checked')) {
323 search : function(e, append) {
324 var entity = this.currentEntity();
332 this.set(UEP.PAGE, this.get(UEP.PAGE)+1);
334 this.set(UEP.USERCOUNT, 0);
335 this.set(UEP.COHORTCOUNT, 0);
336 this.set(UEP.PAGE, 0);
338 params = this.get(UEP.PARAMS);
339 params['sesskey'] = M.cfg.sesskey;
340 params['action'] = (entity === CSS.USER) ? 'searchusers' : 'searchcohorts';
341 params['search'] = this.get(UEP.SEARCH).get('value');
342 params['page'] = this.get(UEP.PAGE);
343 params['enrolcount'] = this.get(UEP.ENROLCOUNT);
344 params['perpage'] = this.get(UEP.PERPAGE);
346 if (this.get(UEP.MULTIPLE)) {
347 alert('oh no there are multiple');
349 var instance = this.get(UEP.INSTANCES)[0];
350 params['enrolid'] = instance.id;
352 Y.io(M.cfg.wwwroot+this.get(UEP.AJAXURL), {
354 data:build_querystring(params),
356 start : this.displayLoading,
357 complete: ((entity === CSS.USER) ? this.processSearchResults : this.processCohortsSearchResults),
358 end : this.removeLoading
363 enrolid:params['enrolid']
367 displayLoading : function() {
368 this._loadingNode.removeClass(CSS.HIDDEN);
370 removeLoading : function() {
371 this._loadingNode.addClass(CSS.HIDDEN);
373 processSearchResults : function(tid, outcome, args) {
375 var result = Y.JSON.parse(outcome.responseText);
377 return new M.core.ajaxException(result);
380 new M.core.exception(e);
382 if (!result.success) {
383 this.setContent = M.util.get_string('errajaxsearch', 'enrol');
387 users = create('<div class="'+CSS.USERS+'"></div>');
389 users = this.get(UEP.BASE).one('.'+CSS.SEARCHRESULTS+' .'+CSS.USERS);
391 var count = this.get(UEP.USERCOUNT);
392 for (var i in result.response.users) {
394 var user = result.response.users[i];
395 users.append(create('<div class="'+CSS.USER+' clearfix" rel="'+user.id+'"></div>')
396 .addClass((count%2)?CSS.ODD:CSS.EVEN)
397 .append(create('<div class="'+CSS.COUNT+'">'+count+'</div>'))
398 .append(create('<div class="'+CSS.PICTURE+'"></div>')
399 .append(create(user.picture)))
400 .append(create('<div class="'+CSS.DETAILS+'"></div>')
401 .append(create('<div class="'+CSS.FULLNAME+'">'+user.fullname+'</div>'))
402 .append(create('<div class="'+CSS.EXTRAFIELDS+'">'+user.extrafields+'</div>')))
403 .append(create('<div class="'+CSS.OPTIONS+'"></div>')
404 .append(create('<input type="button" class="'+CSS.ENROL+'" value="'+M.util.get_string('enrol', 'enrol')+'" />')))
407 this.set(UEP.USERCOUNT, count);
409 var usersstr = (result.response.totalusers == '1')?M.util.get_string('ajaxoneuserfound', 'enrol'):M.util.get_string('ajaxxusersfound','enrol', result.response.totalusers);
410 var content = create('<div class="'+CSS.SEARCHRESULTS+'"></div>')
411 .append(create('<div class="'+CSS.TOTALUSERS+'">'+usersstr+'</div>'))
413 if (result.response.totalusers > (this.get(UEP.PAGE)+1)*this.get(UEP.PERPAGE)) {
414 var fetchmore = create('<div class="'+CSS.MORERESULTS+'"><a href="#">'+M.util.get_string('ajaxnext25', 'enrol')+'</a></div>');
415 fetchmore.on('click', this.search, this, true);
416 content.append(fetchmore)
418 this.setContent(content);
419 Y.delegate("click", this.enrolUser, users, '.'+CSS.USER+' .'+CSS.ENROL, this, args);
421 if (result.response.totalusers <= (this.get(UEP.PAGE)+1)*this.get(UEP.PERPAGE)) {
422 this.get(UEP.BASE).one('.'+CSS.MORERESULTS).remove();
426 processCohortsSearchResults : function(tid, outcome, args) {
428 var result = Y.JSON.parse(outcome.responseText);
430 return new M.core.ajaxException(result);
433 new M.core.exception(e);
435 if (!result.success) {
436 this.setContent = M.util.get_string('errajaxsearch', 'enrol');
440 cohorts = create('<div class="'+CSS.COHORTS+'"></div>');
442 cohorts = this.get(UEP.BASE).one('.'+CSS.SEARCHRESULTS+' .'+CSS.COHORTS);
444 var count = this.get(UEP.COHORTCOUNT);
445 for (var i in result.response.cohorts) {
447 var cohort = result.response.cohorts[i];
448 cohorts.append(create('<div class="'+CSS.COHORT+' clearfix" rel="'+cohort.id+'"></div>')
449 .addClass((count%2)?CSS.ODD:CSS.EVEN)
450 .append(create('<div class="'+CSS.COUNT+'">'+count+'</div>'))
451 .append(create('<div class="'+CSS.DETAILS+'"></div>')
452 .append(create('<div class="'+CSS.COHORTNAME+'">'+cohort.name+'</div>')))
453 .append(create('<div class="'+CSS.OPTIONS+'"></div>')
454 .append(create('<input type="button" class="' + CSS.ENROL + '" value="' + M.util.get_string('enrolxusers', 'enrol', cohort.cnt) + '" />')))
457 this.set(UEP.COHORTCOUNT, count);
459 //var usersstr = (result.response.totalusers == '1')?M.util.get_string('ajaxoneuserfound', 'enrol'):M.util.get_string('ajaxxusersfound','enrol', result.response.totalusers);
460 var cohortsstr = M.util.get_string('foundxcohorts', 'enrol', result.response.totalcohorts);
461 var content = create('<div class="'+CSS.SEARCHRESULTS+'"></div>')
462 .append(create('<div class="'+CSS.TOTALCOHORTS+'">'+cohortsstr+'</div>'))
464 if (result.response.totalcohorts > (this.get(UEP.PAGE)+1)*this.get(UEP.PERPAGE)) {
465 var fetchmore = create('<div class="'+CSS.MORERESULTS+'"><a href="#">'+M.util.get_string('ajaxnext25', 'enrol')+'</a></div>');
466 fetchmore.on('click', this.search, this, true);
467 content.append(fetchmore)
469 this.setContent(content);
470 Y.delegate("click", this.enrolUser, cohorts, '.'+CSS.COHORT+' .'+CSS.ENROL, this, args);
472 if (result.response.totalcohorts <= (this.get(UEP.PAGE)+1)*this.get(UEP.PERPAGE)) {
473 this.get(UEP.BASE).one('.'+CSS.MORERESULTS).remove();
477 enrolUser : function(e, args) {
478 var entityname = this.currentEntity();
480 var entity = e.currentTarget.ancestor('.'+entityname);
482 params['id'] = this.get(UEP.COURSEID);
483 if (entityname === CSS.USER) {
484 params['userid'] = entity.getAttribute("rel");
486 params['cohortid'] = entity.getAttribute("rel");
488 params['enrolid'] = args.enrolid;
489 params['sesskey'] = M.cfg.sesskey;
490 params['action'] = 'enrol';
491 params['role'] = this.get(UEP.BASE).one('.'+CSS.ENROLMENTOPTION+'.'+CSS.ROLE+' select').get('value');
492 params['startdate'] = this.get(UEP.BASE).one('.'+CSS.ENROLMENTOPTION+'.'+CSS.STARTDATE+' select').get('value');
493 params['duration'] = this.get(UEP.BASE).one('.'+CSS.ENROLMENTOPTION+'.'+CSS.DURATION+' select').get('value');
494 if (this.get(UEP.DISABLEGRADEHISTORY) != true) {
495 params['recovergrades'] = this.get(UEP.BASE).one('#'+CSS.RECOVERGRADES).get('checked')?1:0;
497 params['recovergrades'] = 0;
500 Y.io(M.cfg.wwwroot+this.get(UEP.AJAXURL), {
502 data:build_querystring(params),
504 start : this.displayLoading,
505 complete : function(tid, outcome, args) {
507 var result = Y.JSON.parse(outcome.responseText);
509 return new M.core.ajaxException(result);
511 args.entityNode.addClass(CSS.ENROLLED);
512 args.entityNode.one('.'+CSS.ENROL).remove();
513 this.set(UEP.REQUIREREFRESH, true);
514 var countenrol = this.get(UEP.ENROLCOUNT)+1;
515 this.set(UEP.ENROLCOUNT, countenrol);
518 new M.core.exception(e);
521 end : this.removeLoading
531 setContent: function(content) {
532 this.get(UEP.BASE).one('.'+CSS.CONTENT+' .'+CSS.AJAXCONTENT).setContent(content);
538 validator : Y.Lang.isString
541 validator : Y.Lang.isString
544 setter : function(node) {
547 Y.fail(UEP.NAME+': invalid base node set');
553 validator : Y.Lang.isArray,
560 validator : Y.Lang.isArray,
564 validator : Y.Lang.isArray,
565 setter : function(instances) {
566 var i,ia = [], count=0;
567 for (i in instances) {
568 ia.push(instances[i]);
571 this.set(UEP.MULTIPLE, (count>1));
575 validator : Y.Lang.isBool,
579 validator : Y.Lang.isNumber,
584 validator : Y.Lang.isNumber
588 validator : Y.Lang.isBool
591 setter : function(node) {
594 Y.fail(UEP.NAME+': invalid search node set');
599 lastPreSearchValue : {
601 validator : Y.Lang.isString
605 validator : Y.Lang.isObject
612 validator : Y.Lang.isNumber
623 disableGradeHistory : {
626 recoverGradesDefault : {
631 validator : Y.Lang.isNumber
635 Validator: Y.Lang.isNumber
639 validator : Y.Lang.isNumber
646 Y.augment(USERENROLLER, Y.EventTarget);
648 M.enrol_manual = M.enrol_manual || {};
649 M.enrol_manual.quickenrolment = {
650 init : function(cfg) {
651 new USERENROLLER(cfg);
655 }, '@VERSION@', {requires:['base','node', 'overlay', 'io-base', 'test', 'json-parse', 'event-delegate', 'dd-plugin', 'event-key', 'moodle-core-notification']});