2881b90bac3164c9cbe575d53efd5875454a4e9f
[moodle.git] / lib / yui / notification / notification.js
1 YUI.add('moodle-core-notification', function(Y) {
3 var 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',
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',
22         FOOTER : 'moodle-dialogue-ft',
23         HIDDEN : 'hidden',
24         LIGHTBOX : 'moodle-dialogue-lightbox'
25     };
27 var DIALOGUE = function(config) {
28     COUNT++;
29     var id = 'moodle-dialogue-'+COUNT;
30     config.notificationBase =
31         C('<div class="'+CSS.BASE+'">')
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>'))
35                 .append(C('<div class="'+CSS.FOOTER+' yui3-widget-ft"></div>')));
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;
43     // lightbox param to keep the stable versions API.
44     if (config.lightbox !== false) {
45         config.modal = true;
46     }
47     delete config.lightbox;
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     }
63     DIALOGUE.superclass.constructor.apply(this, [config]);
64 };
65 Y.extend(DIALOGUE, Y.Panel, {
66     initializer : function(config) {
67         this.after('visibleChange', this.visibilityChanged, this);
68         this.render();
69         this.show();
70     },
71     visibilityChanged : function(e) {
72         switch (e.attrName) {
73             case 'visible':
74                 this.get('maskNode').addClass(CSS.LIGHTBOX);
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]});
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');
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 : {
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 });
125 var ALERT = function(config) {
126     config.closeButton = false;
127     ALERT.superclass.constructor.apply(this, [config]);
128 };
129 Y.extend(ALERT, DIALOGUE, {
130     _enterKeypress : null,
131     initializer : function(config) {
132         this.publish('complete');
133         var yes = C('<input type="button" value="'+this.get(CONFIRMYES)+'" />'),
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);
140         this.setStdModContent(Y.WidgetStdMod.HEADER, '<h1>' + this.get(TITLE) + '</h1>', Y.WidgetStdMod.REPLACE);
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 });
176 var CONFIRM = function(config) {
177     CONFIRM.superclass.constructor.apply(this, [config]);
178 };
179 Y.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');
186         var yes = C('<input type="button" value="'+this.get(CONFIRMYES)+'" />'),
187             no = C('<input type="button" value="'+this.get(CONFIRMNO)+'" />'),
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);
195         this.setStdModContent(Y.WidgetStdMod.HEADER, '<h1>' + this.get(TITLE) + '</h1>', Y.WidgetStdMod.REPLACE);
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 });
236 Y.augment(CONFIRM, Y.EventTarget);
238 var 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 };
243 Y.extend(EXCEPTION, DIALOGUE, {
244     _hideTimeout : null,
245     _keypress : null,
246     initializer : function(config) {
247         this.get(BASE).addClass('moodle-dialogue-exception');
248         this.setStdModContent(Y.WidgetStdMod.HEADER, '<h1>' + config.name + '</h1>', Y.WidgetStdMod.REPLACE);
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);
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 });
310 var AJAXEXCEPTION = function(config) {
311     config.name = config.name || 'Error';
312     config.closeButton = true;
313     AJAXEXCEPTION.superclass.constructor.apply(this, [config]);
314 };
315 Y.extend(AJAXEXCEPTION, DIALOGUE, {
316     _keypress : null,
317     initializer : function(config) {
318         this.get(BASE).addClass('moodle-dialogue-exception');
319         this.setStdModContent(Y.WidgetStdMod.HEADER, '<h1>' + config.name + '</h1>', Y.WidgetStdMod.REPLACE);
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);
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 });
376 M.core = M.core || {};
377 M.core.dialogue = DIALOGUE;
378 M.core.alert = ALERT;
379 M.core.confirm = CONFIRM;
380 M.core.exception = EXCEPTION;
381 M.core.ajaxException = AJAXEXCEPTION;
383 }, '@VERSION@', {requires:['base','node','panel','event-key', 'moodle-core-notification-skin', 'dd-plugin']});