MDL-30899 moodle-core-notification Adding focus to non modal windows
[moodle.git] / lib / yui / notification / notification.js
CommitLineData
d2a27ab0
ARN
1YUI.add('moodle-core-notification', function(Y) {
2
3var DIALOGUE_NAME = 'Moodle dialogue',
4 DIALOGUE_PREFIX = 'moodle-dialogue',
5 CONFIRM_NAME = 'Moodle confirmation dialogue',
6 EXCEPTION_NAME = 'Moodle exception',
7 AJAXEXCEPTION_NAME = 'Moodle AJAX exception',
8 ALERT_NAME = 'Moodle alert',
9 C = Y.Node.create,
10 BASE = 'notificationBase',
d2a27ab0
ARN
11 COUNT = 0,
12 CONFIRMYES = 'yesLabel',
13 CONFIRMNO = 'noLabel',
14 TITLE = 'title',
15 QUESTION = 'question',
16 CSS = {
17 BASE : 'moodle-dialogue-base',
18 WRAP : 'moodle-dialogue-wrap',
19 HEADER : 'moodle-dialogue-hd',
20 BODY : 'moodle-dialogue-bd',
21 CONTENT : 'moodle-dialogue-content',
e4a8f00c 22 FOOTER : 'moodle-dialogue-ft',
d2a27ab0
ARN
23 HIDDEN : 'hidden',
24 LIGHTBOX : 'moodle-dialogue-lightbox'
25 };
26
27var DIALOGUE = function(config) {
28 COUNT++;
29 var id = 'moodle-dialogue-'+COUNT;
30 config.notificationBase =
31 C('<div class="'+CSS.BASE+'">')
d2a27ab0
ARN
32 .append(C('<div id="'+id+'" class="'+CSS.WRAP+'"></div>')
33 .append(C('<div class="'+CSS.HEADER+' yui3-widget-hd"></div>'))
34 .append(C('<div class="'+CSS.BODY+' yui3-widget-bd"></div>'))
e4a8f00c 35 .append(C('<div class="'+CSS.FOOTER+' yui3-widget-ft"></div>')));
d2a27ab0
ARN
36 Y.one(document.body).append(config.notificationBase);
37 config.srcNode = '#'+id;
38 config.width = config.width || '400px';
39 config.visible = config.visible || false;
40 config.center = config.centered || true;
41 config.centered = false;
7cd301d1
DM
42
43 // lightbox param to keep the stable versions API.
44 if (config.lightbox !== false) {
45 config.modal = true;
46 }
47 delete config.lightbox;
48
49 // closeButton param to keep the stable versions API.
50 if (config.closeButton === false) {
51 config.buttons = null;
52 } else {
53 config.buttons = [
54 {
55 section: Y.WidgetStdMod.HEADER,
56 classNames: 'closebutton',
57 action: function (e) {
58 this.hide();
59 }
60 }
61 ];
62 }
d2a27ab0
ARN
63 DIALOGUE.superclass.constructor.apply(this, [config]);
64};
7cd301d1 65Y.extend(DIALOGUE, Y.Panel, {
d2a27ab0 66 initializer : function(config) {
d2a27ab0 67 this.after('visibleChange', this.visibilityChanged, this);
d2a27ab0
ARN
68 this.render();
69 this.show();
70 },
71 visibilityChanged : function(e) {
72 switch (e.attrName) {
73 case 'visible':
7cd301d1 74 this.get('maskNode').addClass(CSS.LIGHTBOX);
d2a27ab0
ARN
75 if (this.get('center') && !e.prevVal && e.newVal) {
76 this.centerDialogue();
77 }
78 if (this.get('draggable')) {
79 var titlebar = '#' + this.get('id') + ' .' + CSS.HEADER;
80 this.plug(Y.Plugin.Drag, {handles : [titlebar]});
d2a27ab0
ARN
81 Y.one(titlebar).setStyle('cursor', 'move');
82 }
83 break;
84 }
85 },
86 centerDialogue : function() {
87 var bb = this.get('boundingBox'), hidden = bb.hasClass(DIALOGUE_PREFIX+'-hidden');
88 if (hidden) {
89 bb.setStyle('top', '-1000px').removeClass(DIALOGUE_PREFIX+'-hidden');
90 }
91 var x = Math.max(Math.round((bb.get('winWidth') - bb.get('offsetWidth'))/2), 15);
92 var y = Math.max(Math.round((bb.get('winHeight') - bb.get('offsetHeight'))/2), 15) + Y.one(window).get('scrollTop');
93
94 if (hidden) {
95 bb.addClass(DIALOGUE_PREFIX+'-hidden');
96 }
97 bb.setStyle('left', x).setStyle('top', y);
98 }
99}, {
100 NAME : DIALOGUE_NAME,
101 CSS_PREFIX : DIALOGUE_PREFIX,
102 ATTRS : {
103 notificationBase : {
104
d2a27ab0
ARN
105 },
106 lightbox : {
107 validator : Y.Lang.isBoolean,
108 value : true
109 },
110 closeButton : {
111 validator : Y.Lang.isBoolean,
112 value : true
113 },
114 center : {
115 validator : Y.Lang.isBoolean,
116 value : true
117 },
118 draggable : {
119 validator : Y.Lang.isBoolean,
120 value : false
121 }
122 }
123});
124
125var ALERT = function(config) {
126 config.closeButton = false;
127 ALERT.superclass.constructor.apply(this, [config]);
128};
129Y.extend(ALERT, DIALOGUE, {
130 _enterKeypress : null,
131 initializer : function(config) {
132 this.publish('complete');
f068cac1 133 var yes = C('<input type="button" id="id_yuialertconfirm-' + this.COUNT + '" value="'+this.get(CONFIRMYES)+'" />'),
d2a27ab0
ARN
134 content = C('<div class="confirmation-dialogue"></div>')
135 .append(C('<div class="confirmation-message">'+this.get('message')+'</div>'))
136 .append(C('<div class="confirmation-buttons"></div>')
137 .append(yes));
138 this.get(BASE).addClass('moodle-dialogue-confirm');
139 this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE);
729c1151 140 this.setStdModContent(Y.WidgetStdMod.HEADER, '<h1>' + this.get(TITLE) + '</h1>', Y.WidgetStdMod.REPLACE);
d2a27ab0
ARN
141 this.after('destroyedChange', function(){this.get(BASE).remove();}, this);
142 this._enterKeypress = Y.on('key', this.submit, window, 'down:13', this);
143 yes.on('click', this.submit, this);
144 },
145 submit : function(e, outcome) {
146 this._enterKeypress.detach();
147 this.fire('complete');
148 this.hide();
149 this.destroy();
150 }
151}, {
152 NAME : ALERT_NAME,
153 CSS_PREFIX : DIALOGUE_PREFIX,
154 ATTRS : {
155 title : {
156 validator : Y.Lang.isString,
157 value : 'Alert'
158 },
159 message : {
160 validator : Y.Lang.isString,
161 value : 'Confirm'
162 },
163 yesLabel : {
164 validator : Y.Lang.isString,
165 setter : function(txt) {
166 if (!txt) {
167 txt = 'Ok';
168 }
169 return txt;
170 },
171 value : 'Ok'
172 }
173 }
174});
175
176var CONFIRM = function(config) {
177 CONFIRM.superclass.constructor.apply(this, [config]);
178};
179Y.extend(CONFIRM, DIALOGUE, {
180 _enterKeypress : null,
181 _escKeypress : null,
182 initializer : function(config) {
183 this.publish('complete');
184 this.publish('complete-yes');
185 this.publish('complete-no');
f068cac1
DM
186 var yes = C('<input type="button" id="id_yuiconfirmyes-' + this.COUNT + '" value="'+this.get(CONFIRMYES)+'" />'),
187 no = C('<input type="button" id="id_yuiconfirmno-' + this.COUNT + '" value="'+this.get(CONFIRMNO)+'" />'),
d2a27ab0
ARN
188 content = C('<div class="confirmation-dialogue"></div>')
189 .append(C('<div class="confirmation-message">'+this.get(QUESTION)+'</div>'))
190 .append(C('<div class="confirmation-buttons"></div>')
191 .append(yes)
192 .append(no));
193 this.get(BASE).addClass('moodle-dialogue-confirm');
194 this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE);
729c1151 195 this.setStdModContent(Y.WidgetStdMod.HEADER, '<h1>' + this.get(TITLE) + '</h1>', Y.WidgetStdMod.REPLACE);
d2a27ab0
ARN
196 this.after('destroyedChange', function(){this.get(BASE).remove();}, this);
197 this._enterKeypress = Y.on('key', this.submit, window, 'down:13', this, true);
198 this._escKeypress = Y.on('key', this.submit, window, 'down:27', this, false);
199 yes.on('click', this.submit, this, true);
200 no.on('click', this.submit, this, false);
201 },
202 submit : function(e, outcome) {
203 this._enterKeypress.detach();
204 this._escKeypress.detach();
205 this.fire('complete', outcome);
206 if (outcome) {
207 this.fire('complete-yes');
208 } else {
209 this.fire('complete-no');
210 }
211 this.hide();
212 this.destroy();
213 }
214}, {
215 NAME : CONFIRM_NAME,
216 CSS_PREFIX : DIALOGUE_PREFIX,
217 ATTRS : {
218 yesLabel : {
219 validator : Y.Lang.isString,
220 value : 'Yes'
221 },
222 noLabel : {
223 validator : Y.Lang.isString,
224 value : 'No'
225 },
226 title : {
227 validator : Y.Lang.isString,
228 value : 'Confirm'
229 },
230 question : {
231 validator : Y.Lang.isString,
232 value : 'Are you sure?'
233 }
234 }
235});
236Y.augment(CONFIRM, Y.EventTarget);
237
238var EXCEPTION = function(config) {
239 config.width = config.width || (M.cfg.developerdebug)?Math.floor(Y.one(document.body).get('winWidth')/3)+'px':null;
240 config.closeButton = true;
241 EXCEPTION.superclass.constructor.apply(this, [config]);
242};
243Y.extend(EXCEPTION, DIALOGUE, {
244 _hideTimeout : null,
245 _keypress : null,
246 initializer : function(config) {
247 this.get(BASE).addClass('moodle-dialogue-exception');
729c1151 248 this.setStdModContent(Y.WidgetStdMod.HEADER, '<h1>' + config.name + '</h1>', Y.WidgetStdMod.REPLACE);
d2a27ab0
ARN
249 var content = C('<div class="moodle-exception"></div>')
250 .append(C('<div class="moodle-exception-message">'+this.get('message')+'</div>'))
251 .append(C('<div class="moodle-exception-param hidden param-filename"><label>File:</label> '+this.get('fileName')+'</div>'))
252 .append(C('<div class="moodle-exception-param hidden param-linenumber"><label>Line:</label> '+this.get('lineNumber')+'</div>'))
253 .append(C('<div class="moodle-exception-param hidden param-stacktrace"><label>Stack trace:</label> <pre>'+this.get('stack')+'</pre></div>'));
254 if (M.cfg.developerdebug) {
255 content.all('.moodle-exception-param').removeClass('hidden');
256 }
257 this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE);
258
259 var self = this;
260 var delay = this.get('hideTimeoutDelay');
261 if (delay) {
262 this._hideTimeout = setTimeout(function(){self.hide();}, delay);
263 }
264 this.after('visibleChange', this.visibilityChanged, this);
265 this.after('destroyedChange', function(){this.get(BASE).remove();}, this);
266 this._keypress = Y.on('key', this.hide, window, 'down:13,27', this);
267 this.centerDialogue();
268 },
269 visibilityChanged : function(e) {
270 if (e.attrName == 'visible' && e.prevVal && !e.newVal) {
271 if (this._keypress) this._keypress.detach();
272 var self = this;
273 setTimeout(function(){self.destroy();}, 1000);
274 }
275 }
276}, {
277 NAME : EXCEPTION_NAME,
278 CSS_PREFIX : DIALOGUE_PREFIX,
279 ATTRS : {
280 message : {
281 value : ''
282 },
283 name : {
284 value : ''
285 },
286 fileName : {
287 value : ''
288 },
289 lineNumber : {
290 value : ''
291 },
292 stack : {
293 setter : function(str) {
294 var lines = str.split("\n");
295 var pattern = new RegExp('^(.+)@('+M.cfg.wwwroot+')?(.{0,75}).*:(\\d+)$');
296 for (var i in lines) {
297 lines[i] = lines[i].replace(pattern, "<div class='stacktrace-line'>ln: $4</div><div class='stacktrace-file'>$3</div><div class='stacktrace-call'>$1</div>");
298 }
299 return lines.join('');
300 },
301 value : ''
302 },
303 hideTimeoutDelay : {
304 validator : Y.Lang.isNumber,
305 value : null
306 }
307 }
308});
309
310var AJAXEXCEPTION = function(config) {
311 config.name = config.name || 'Error';
312 config.closeButton = true;
313 AJAXEXCEPTION.superclass.constructor.apply(this, [config]);
314};
315Y.extend(AJAXEXCEPTION, DIALOGUE, {
316 _keypress : null,
317 initializer : function(config) {
318 this.get(BASE).addClass('moodle-dialogue-exception');
729c1151 319 this.setStdModContent(Y.WidgetStdMod.HEADER, '<h1>' + config.name + '</h1>', Y.WidgetStdMod.REPLACE);
d2a27ab0
ARN
320 var content = C('<div class="moodle-ajaxexception"></div>')
321 .append(C('<div class="moodle-exception-message">'+this.get('error')+'</div>'))
322 .append(C('<div class="moodle-exception-param hidden param-debuginfo"><label>URL:</label> '+this.get('reproductionlink')+'</div>'))
323 .append(C('<div class="moodle-exception-param hidden param-debuginfo"><label>Debug info:</label> '+this.get('debuginfo')+'</div>'))
324 .append(C('<div class="moodle-exception-param hidden param-stacktrace"><label>Stack trace:</label> <pre>'+this.get('stacktrace')+'</pre></div>'));
325 if (M.cfg.developerdebug) {
326 content.all('.moodle-exception-param').removeClass('hidden');
327 }
328 this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE);
329
330 var self = this;
331 var delay = this.get('hideTimeoutDelay');
332 if (delay) {
333 this._hideTimeout = setTimeout(function(){self.hide();}, delay);
334 }
335 this.after('visibleChange', this.visibilityChanged, this);
336 this._keypress = Y.on('key', this.hide, window, 'down:13, 27', this);
337 this.centerDialogue();
338 },
339 visibilityChanged : function(e) {
340 if (e.attrName == 'visible' && e.prevVal && !e.newVal) {
341 var self = this;
342 this._keypress.detach();
343 setTimeout(function(){self.destroy();}, 1000);
344 }
345 }
346}, {
347 NAME : AJAXEXCEPTION_NAME,
348 CSS_PREFIX : DIALOGUE_PREFIX,
349 ATTRS : {
350 error : {
351 validator : Y.Lang.isString,
352 value : 'Unknown error'
353 },
354 debuginfo : {
355 value : null
356 },
357 stacktrace : {
358 value : null
359 },
360 reproductionlink : {
361 setter : function(link) {
362 if (link !== null) {
363 link = '<a href="'+link+'">'+link.replace(M.cfg.wwwroot, '')+'</a>';
364 }
365 return link;
366 },
367 value : null
368 },
369 hideTimeoutDelay : {
370 validator : Y.Lang.isNumber,
371 value : null
372 }
373 }
374});
375
376M.core = M.core || {};
377M.core.dialogue = DIALOGUE;
378M.core.alert = ALERT;
379M.core.confirm = CONFIRM;
380M.core.exception = EXCEPTION;
381M.core.ajaxException = AJAXEXCEPTION;
382
7cd301d1 383}, '@VERSION@', {requires:['base','node','panel','event-key', 'moodle-core-notification-skin', 'dd-plugin']});