Merge branch 'MDL-51922-master' of git://github.com/andrewnicols/moodle
[moodle.git] / comment / comment.js
blob91f87a4c9109a869003b3c2f6a4904ea345061ac
1 // This file is part of Moodle - http://moodle.org/
2 //
3 // Moodle is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // Moodle is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16 /**
17  * Comment Helper
18  * @author Dongsheng Cai <dongsheng@moodle.com>
19  */
20 M.core_comment = {
21     /**
22      * Initialize commenting system
23      */
24     init: function(Y, options) {
25         var CommentHelper = function(args) {
26             CommentHelper.superclass.constructor.apply(this, arguments);
27         };
28         CommentHelper.NAME = "COMMENT";
29         CommentHelper.ATTRS = {
30             options: {},
31             lang: {}
32         };
33         Y.extend(CommentHelper, Y.Base, {
34             api: M.cfg.wwwroot+'/comment/comment_ajax.php',
35             initializer: function(args) {
36                 var scope = this;
37                 this.client_id = args.client_id;
38                 this.itemid = args.itemid;
39                 this.commentarea = args.commentarea;
40                 this.component = args.component;
41                 this.courseid = args.courseid;
42                 this.contextid = args.contextid;
43                 this.autostart = (args.autostart);
44                 // expand comments?
45                 if (this.autostart) {
46                     this.view(args.page);
47                 }
48                 // load comments
49                 var handle = Y.one('#comment-link-'+this.client_id);
50                 // hide toggle link
51                 if (handle) {
52                     if (args.notoggle) {
53                         handle.setStyle('display', 'none');
54                     }
55                     handle.on('click', function(e) {
56                         e.preventDefault();
57                         this.view(0);
58                         return false;
59                     }, this);
60                 }
61                 scope.toggle_textarea(false);
62             },
63             post: function() {
64                 var ta = Y.one('#dlg-content-'+this.client_id);
65                 var scope = this;
66                 var value = ta.get('value');
67                 if (value && value != M.util.get_string('addcomment', 'moodle')) {
68                     ta.set('disabled', true);
69                     ta.setStyles({
70                         'backgroundImage': 'url(' + M.util.image_url('i/loading_small', 'core') + ')',
71                         'backgroundRepeat': 'no-repeat',
72                         'backgroundPosition': 'center center'
73                     });
74                     var params = {'content': value};
75                     this.request({
76                         action: 'add',
77                         scope: scope,
78                         params: params,
79                         callback: function(id, obj, args) {
80                             var scope = args.scope;
81                             var cid = scope.client_id;
82                             var ta = Y.one('#dlg-content-'+cid);
83                             ta.set('value', '');
84                             ta.set('disabled', false);
85                             ta.setStyle('backgroundImage', 'none');
86                             scope.toggle_textarea(false);
87                             var container = Y.one('#comment-list-'+cid);
88                             var result = scope.render([obj], true);
89                             var newcomment = Y.Node.create(result.html);
90                             container.appendChild(newcomment);
91                             var ids = result.ids;
92                             var linkText = Y.one('#comment-link-text-' + cid);
93                             if (linkText) {
94                                 linkText.set('innerHTML', M.util.get_string('commentscount', 'moodle', obj.count));
95                             }
96                             for(var i in ids) {
97                                 var attributes = {
98                                     color: { to: '#06e' },
99                                     backgroundColor: { to: '#FFE390' }
100                                 };
101                                 var anim = new Y.YUI2.util.ColorAnim(ids[i], attributes);
102                                 anim.animate();
103                             }
104                             scope.register_pagination();
105                             scope.register_delete_buttons();
106                         }
107                     }, true);
108                 } else {
109                     var attributes = {
110                         backgroundColor: { from: '#FFE390', to:'#FFFFFF' }
111                     };
112                     var anim = new Y.YUI2.util.ColorAnim('dlg-content-'+cid, attributes);
113                     anim.animate();
114                 }
115             },
116             request: function(args, noloading) {
117                 var params = {};
118                 var scope = this;
119                 if (args['scope']) {
120                     scope = args['scope'];
121                 }
122                 //params['page'] = args.page?args.page:'';
123                 // the form element only accept certain file types
124                 params['sesskey']   = M.cfg.sesskey;
125                 params['action']    = args.action?args.action:'';
126                 params['client_id'] = this.client_id;
127                 params['itemid']    = this.itemid;
128                 params['area']      = this.commentarea;
129                 params['courseid']  = this.courseid;
130                 params['contextid'] = this.contextid;
131                 params['component'] = this.component;
132                 if (args['params']) {
133                     for (i in args['params']) {
134                         params[i] = args['params'][i];
135                     }
136                 }
137                 var cfg = {
138                     method: 'POST',
139                     on: {
140                         complete: function(id,o,p) {
141                             if (!o) {
142                                 alert('IO FATAL');
143                                 return false;
144                             }
145                             var data = Y.JSON.parse(o.responseText);
146                             if (data.error) {
147                                 if (data.error == 'require_login') {
148                                     args.callback(id,data,p);
149                                     return true;
150                                 }
151                                 alert(data.error);
152                                 return false;
153                             } else {
154                                 args.callback(id,data,p);
155                                 return true;
156                             }
157                         }
158                     },
159                     arguments: {
160                         scope: scope
161                     },
162                     headers: {
163                         'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
164                     },
165                     data: build_querystring(params)
166                 };
167                 if (args.form) {
168                     cfg.form = args.form;
169                 }
170                 Y.io(this.api, cfg);
171                 if (!noloading) {
172                     this.wait();
173                 }
174             },
175             render: function(list, newcmt) {
176                 var ret = {};
177                 ret.ids = [];
178                 var template = Y.one('#cmt-tmpl');
179                 var html = '';
180                 for(var i in list) {
181                     var htmlid = 'comment-'+list[i].id+'-'+this.client_id;
182                     var val = template.get('innerHTML');
183                     if (list[i].profileurl) {
184                         val = val.replace('___name___', '<a href="'+list[i].profileurl+'">'+list[i].fullname+'</a>');
185                     } else {
186                         val = val.replace('___name___', list[i].fullname);
187                     }
188                     if (list[i]['delete']||newcmt) {
189                         list[i].content = '<div class="comment-delete"><a href="#" id ="comment-delete-'+this.client_id+'-'+list[i].id+'" title="'+M.util.get_string('deletecomment', 'moodle')+'"><img alt="" src="'+M.util.image_url('t/delete', 'core')+'" /></a></div>' + list[i].content;
190                     }
191                     val = val.replace('___time___', list[i].time);
192                     val = val.replace('___picture___', list[i].avatar);
193                     val = val.replace('___content___', list[i].content);
194                     val = '<li id="'+htmlid+'">'+val+'</li>';
195                     ret.ids.push(htmlid);
196                     html = (val+html);
197                 }
198                 ret.html = html;
199                 return ret;
200             },
201             load: function(page) {
202                 var scope = this;
203                 var container = Y.one('#comment-ctrl-'+this.client_id);
204                 var params = {
205                     'action': 'get',
206                     'page': page
207                 };
208                 this.request({
209                     scope: scope,
210                     params: params,
211                     callback: function(id, ret, args) {
212                         var linkText = Y.one('#comment-link-text-' + scope.client_id);
213                         if (ret.count && linkText) {
214                             linkText.set('innerHTML', M.util.get_string('commentscount', 'moodle', ret.count));
215                         }
216                         var container = Y.one('#comment-list-'+scope.client_id);
217                         var pagination = Y.one('#comment-pagination-'+scope.client_id);
218                         if (ret.pagination) {
219                             pagination.set('innerHTML', ret.pagination);
220                         } else {
221                             //empty paging bar
222                             pagination.set('innerHTML', '');
223                         }
224                         if (ret.error == 'require_login') {
225                             var result = {};
226                             result.html = M.util.get_string('commentsrequirelogin', 'moodle');
227                         } else {
228                             var result = scope.render(ret.list);
229                         }
230                         container.set('innerHTML', result.html);
231                         var img = Y.one('#comment-img-'+scope.client_id);
232                         if (img) {
233                             img.set('src', M.util.image_url('t/expanded', 'core'));
234                         }
235                         args.scope.register_pagination();
236                         args.scope.register_delete_buttons();
237                     }
238                 });
239             },
241             dodelete: function(id) { // note: delete is a reserved word in javascript, chrome and safary do not like it at all here!
242                 var scope = this,
243                     cid = scope.client_id,
244                     params = {'commentid': id};
245                 function remove_dom(type, anim, cmt) {
246                     cmt.remove();
247                     var linkText = Y.one('#comment-link-text-' + cid),
248                         comments = Y.all('#comment-list-' + cid + ' li');
249                     if (linkText && comments) {
250                         linkText.set('innerHTML', M.util.get_string('commentscount', 'moodle', comments.size()));
251                     }
252                 }
253                 this.request({
254                     action: 'delete',
255                     scope: scope,
256                     params: params,
257                     callback: function(id, resp, args) {
258                         var htmlid= 'comment-'+resp.commentid+'-'+resp.client_id;
259                         var attributes = {
260                             width:{to:0},
261                             height:{to:0}
262                         };
263                         var cmt = Y.one('#'+htmlid);
264                         cmt.setStyle('overflow', 'hidden');
265                         var anim = new Y.YUI2.util.Anim(htmlid, attributes, 1, Y.YUI2.util.Easing.easeOut);
266                         anim.onComplete.subscribe(remove_dom, cmt, this);
267                         anim.animate();
268                     }
269                 }, true);
270             },
271             register_actions: function() {
272                 // add new comment
273                 var action_btn = Y.one('#comment-action-post-'+this.client_id);
274                 if (action_btn) {
275                     action_btn.on('click', function(e) {
276                         e.preventDefault();
277                         this.post();
278                         return false;
279                     }, this);
280                 }
281                 // cancel comment box
282                 var cancel = Y.one('#comment-action-cancel-'+this.client_id);
283                 if (cancel) {
284                     cancel.on('click', function(e) {
285                         e.preventDefault();
286                         this.view(0);
287                         return false;
288                     }, this);
289                 }
290             },
291             register_delete_buttons: function() {
292                 var scope = this;
293                 // page buttons
294                 Y.all('div.comment-delete a').each(
295                     function(node, id) {
296                         var theid = node.get('id');
297                         var parseid = new RegExp("comment-delete-"+scope.client_id+"-(\\d+)", "i");
298                         var commentid = theid.match(parseid);
299                         if (!commentid) {
300                             return;
301                         }
302                         if (commentid[1]) {
303                             Y.Event.purgeElement('#'+theid, false, 'click');
304                         }
305                         node.on('click', function(e) {
306                             e.preventDefault();
307                             if (commentid[1]) {
308                                 scope.dodelete(commentid[1]);
309                             }
310                         });
311                         // Also handle space/enter key.
312                         node.on('key', function(e) {
313                             e.preventDefault();
314                             if (commentid[1]) {
315                                 scope.dodelete(commentid[1]);
316                             }
317                         }, '13,32');
318                         // 13 and 32 are the keycodes for space and enter.
319                     }
320                 );
321             },
322             register_pagination: function() {
323                 var scope = this;
324                 // page buttons
325                 Y.all('#comment-pagination-'+this.client_id+' a').each(
326                     function(node, id) {
327                         node.on('click', function(e, node) {
328                             e.preventDefault();
329                             var id = node.get('id');
330                             var re = new RegExp("comment-page-"+this.client_id+"-(\\d+)", "i");
331                             var result = id.match(re);
332                             this.load(result[1]);
333                         }, scope, node);
334                     }
335                 );
336             },
337             view: function(page) {
338                 var container = Y.one('#comment-ctrl-'+this.client_id);
339                 var ta = Y.one('#dlg-content-'+this.client_id);
340                 var img = Y.one('#comment-img-'+this.client_id);
341                 var d = container.getStyle('display');
342                 if (d=='none'||d=='') {
343                     // show
344                     if (!this.autostart) {
345                         this.load(page);
346                     } else {
347                         this.register_delete_buttons();
348                         this.register_pagination();
349                     }
350                     container.setStyle('display', 'block');
351                     if (img) {
352                         img.set('src', M.util.image_url('t/expanded', 'core'));
353                     }
354                 } else {
355                     // hide
356                     container.setStyle('display', 'none');
357                     var collapsedimage = 't/collapsed'; // ltr mode
358                     if ( Y.one(document.body).hasClass('dir-rtl') ) {
359                         collapsedimage = 't/collapsed_rtl';
360                     } else {
361                         collapsedimage = 't/collapsed';
362                     }
363                     img.set('src', M.util.image_url(collapsedimage, 'core'));
364                     if (ta) {
365                         ta.set('value','');
366                     }
367                 }
368                 if (ta) {
369                     //toggle_textarea.apply(ta, [false]);
370                     //// reset textarea size
371                     ta.on('focus', function() {
372                         this.toggle_textarea(true);
373                     }, this);
374                     //ta.onkeypress = function() {
375                         //if (this.scrollHeight > this.clientHeight && !window.opera)
376                             //this.rows += 1;
377                     //}
378                     ta.on('blur', function() {
379                         this.toggle_textarea(false);
380                     }, this);
381                 }
382                 this.register_actions();
383                 return false;
384             },
385             toggle_textarea: function(focus) {
386                 var t = Y.one('#dlg-content-'+this.client_id);
387                 if (!t) {
388                     return false;
389                 }
390                 if (focus) {
391                     if (t.get('value') == M.util.get_string('addcomment', 'moodle')) {
392                         t.set('value', '');
393                         t.setStyle('color', 'black');
394                     }
395                 }else{
396                     if (t.get('value') == '') {
397                         t.set('value', M.util.get_string('addcomment', 'moodle'));
398                         t.setStyle('color','grey');
399                         t.set('rows', 2);
400                     }
401                 }
402             },
403             wait: function() {
404                 var container = Y.one('#comment-list-'+this.client_id);
405                 container.set('innerHTML', '<div class="mdl-align"><img src="'+M.util.image_url('i/loading_small', 'core')+'" /></div>');
406             }
407         });
409         new CommentHelper(options);
410     },
411     init_admin: function(Y) {
412         var select_all = Y.one('#comment_select_all');
413         select_all.on('click', function(e) {
414             var comments = document.getElementsByName('comments');
415             var checked = false;
416             for (var i in comments) {
417                 if (comments[i].checked) {
418                     checked=true;
419                 }
420             }
421             for (i in comments) {
422                 comments[i].checked = !checked;
423             }
424             this.set('checked', !checked);
425         });
427         var comments_delete = Y.one('#comments_delete');
428         if (comments_delete) {
429             comments_delete.on('click', function(e) {
430                 e.preventDefault();
431                 var list = '';
432                 var comments = document.getElementsByName('comments');
433                 for (var i in comments) {
434                     if (typeof comments[i] == 'object' && comments[i].checked) {
435                         list += (comments[i].value + '-');
436                     }
437                 }
438                 if (!list) {
439                     return;
440                 }
441                 var args = {};
442                 args.message = M.util.get_string('confirmdeletecomments', 'admin');
443                 args.callback = function() {
444                     var url = M.cfg.wwwroot + '/comment/index.php';
446                     var data = {
447                         'commentids': list,
448                         'sesskey': M.cfg.sesskey,
449                         'action': 'delete'
450                     };
451                     var cfg = {
452                         method: 'POST',
453                         on: {
454                             complete: function(id,o,p) {
455                                 if (!o) {
456                                     alert('IO FATAL');
457                                     return;
458                                 }
459                                 if (o.responseText == 'yes') {
460                                     location.reload();
461                                 }
462                             }
463                         },
464                         arguments: {
465                             scope: this
466                         },
467                         headers: {
468                             'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
469                         },
470                         data: build_querystring(data)
471                     };
472                     Y.io(url, cfg);
473                 };
474                 M.util.show_confirm_dialog(e, args);
475             });
476         }
477     }