Merge branch '2.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / js / WCF.Comment.js
CommitLineData
285b1d92
MW
1/**
2 * Namespace for comments
3 */
b9f4bd69 4WCF.Comment = { };
285b1d92
MW
5
6/**
7 * Comment support for WCF
8 *
9 * @author Alexander Ebert
ca4ba303 10 * @copyright 2001-2014 WoltLab GmbH
285b1d92
MW
11 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
12 */
13WCF.Comment.Handler = Class.extend({
14 /**
15 * input element to add a comment
16 * @var jQuery
17 */
18 _commentAdd: null,
19
ebe32443
AE
20 /**
21 * list of comment buttons per comment
22 * @var object
23 */
24 _commentButtonList: { },
25
285b1d92
MW
26 /**
27 * list of comment objects
28 * @var object
29 */
30 _comments: { },
31
32 /**
33 * comment container object
34 * @var jQuery
35 */
36 _container: null,
37
38 /**
39 * container id
40 * @var string
41 */
42 _containerID: '',
43
44 /**
45 * number of currently displayed comments
46 * @var integer
47 */
48 _displayedComments: 0,
49
50 /**
51 * button to load next comments
52 * @var jQuery
53 */
54 _loadNextComments: null,
55
56 /**
57 * buttons to load next responses per comment
58 * @var object
59 */
60 _loadNextResponses: { },
61
62 /**
63 * action proxy
64 * @var WCF.Action.Proxy
65 */
66 _proxy: null,
67
68 /**
69 * list of response objects
70 * @var object
71 */
72 _responses: { },
73
74 /**
75 * user's avatar
76 * @var string
77 */
78 _userAvatar: '',
79
b9f4bd69
MS
80 /**
81 * data of the comment the active guest user is about to create
82 * @var object
83 */
84 _commentData: { },
85
86 /**
87 * guest dialog with username input field and recaptcha
88 * @var jQuery
89 */
90 _guestDialog: null,
91
92 /**
93 * true if the guest has to solve a recaptcha challenge to save the comment
94 * @var boolean
95 */
96 _useRecaptcha: true,
97
285b1d92
MW
98 /**
99 * Initializes the WCF.Comment.Handler class.
100 *
101 * @param string containerID
102 * @param string userAvatar
103 */
104 init: function(containerID, userAvatar) {
105 this._commentAdd = null;
ebe32443 106 this._commentButtonList = { };
285b1d92
MW
107 this._comments = { };
108 this._containerID = containerID;
109 this._displayedComments = 0;
110 this._loadNextComments = null;
111 this._loadNextResponses = { };
112 this._responses = { };
113 this._userAvatar = userAvatar;
114
115 this._container = $('#' + $.wcfEscapeID(this._containerID));
116 if (!this._container.length) {
117 console.debug("[WCF.Comment.Handler] Unable to find container identified by '" + this._containerID + "'");
118 }
119
120 this._proxy = new WCF.Action.Proxy({
b9f4bd69 121 failure: $.proxy(this._failure, this),
285b1d92
MW
122 success: $.proxy(this._success, this)
123 });
124
285b1d92
MW
125 this._initComments();
126 this._initResponses();
127
128 // add new comment
129 if (this._container.data('canAdd')) {
130 this._initAddComment();
131 }
132
42d7d2cc 133 WCF.DOMNodeInsertedHandler.execute();
285b1d92
MW
134 WCF.DOMNodeInsertedHandler.addCallback('WCF.Comment.Handler', $.proxy(this._domNodeInserted, this));
135 },
136
137 /**
138 * Shows a button to load next comments.
139 */
140 _handleLoadNextComments: function() {
141 if (this._displayedComments < this._container.data('comments')) {
142 if (this._loadNextComments === null) {
9ccc9158 143 this._loadNextComments = $('<li class="commentLoadNext"><button class="small">' + WCF.Language.get('wcf.comment.more') + '</button></li>').appendTo(this._container);
285b1d92
MW
144 this._loadNextComments.children('button').click($.proxy(this._loadComments, this));
145 }
146
147 this._loadNextComments.children('button').enable();
148 }
149 else if (this._loadNextComments !== null) {
150 this._loadNextComments.hide();
151 }
152 },
153
154 /**
155 * Shows a button to load next responses per comment.
156 *
157 * @param integer commentID
158 */
159 _handleLoadNextResponses: function(commentID) {
160 var $comment = this._comments[commentID];
161 $comment.data('displayedResponses', $comment.find('ul.commentResponseList > li').length);
162
163 if ($comment.data('displayedResponses') < $comment.data('responses')) {
164 if (this._loadNextResponses[commentID] === undefined) {
ebe32443
AE
165 var $difference = $comment.data('responses') - $comment.data('displayedResponses');
166 this._loadNextResponses[commentID] = $('<li class="jsCommentLoadNextResponses"><a>' + WCF.Language.get('wcf.comment.response.more', { count: $difference }) + '</a></li>').appendTo(this._commentButtonList[commentID]);
167 this._loadNextResponses[commentID].children('a').data('commentID', commentID).click($.proxy(this._loadResponses, this));
168 this._commentButtonList[commentID].parent().show();
285b1d92 169 }
285b1d92
MW
170 }
171 else if (this._loadNextResponses[commentID] !== undefined) {
ebe32443
AE
172 var $showAddResponse = this._loadNextResponses[commentID].next();
173 this._loadNextResponses[commentID].remove();
174 if ($showAddResponse.length) {
175 $showAddResponse.trigger('click');
176 }
285b1d92
MW
177 }
178 },
179
180 /**
181 * Loads next comments.
182 */
183 _loadComments: function() {
184 this._loadNextComments.children('button').disable();
185
186 this._proxy.setOption('data', {
187 actionName: 'loadComments',
188 className: 'wcf\\data\\comment\\CommentAction',
189 parameters: {
190 data: {
191 objectID: this._container.data('objectID'),
192 objectTypeID: this._container.data('objectTypeID'),
193 lastCommentTime: this._container.data('lastCommentTime')
194 }
195 }
196 });
197 this._proxy.sendRequest();
198 },
199
200 /**
201 * Loads next responses for given comment.
202 *
203 * @param object event
204 */
205 _loadResponses: function(event) {
6f874ba8 206 this._loadResponsesExecute($(event.currentTarget).disable().data('commentID'), false);
285b1d92 207
6f874ba8
AE
208 },
209
210 /**
211 * Executes loading of comments, optionally fetching all at once.
212 *
213 * @param integer commentID
214 * @param boolean loadAllResponses
215 */
216 _loadResponsesExecute: function(commentID, loadAllResponses) {
285b1d92
MW
217 this._proxy.setOption('data', {
218 actionName: 'loadResponses',
219 className: 'wcf\\data\\comment\\response\\CommentResponseAction',
220 parameters: {
221 data: {
6f874ba8
AE
222 commentID: commentID,
223 lastResponseTime: this._comments[commentID].data('lastResponseTime'),
224 loadAllResponses: (loadAllResponses ? 1 : 0)
285b1d92
MW
225 }
226 }
227 });
228 this._proxy.sendRequest();
229 },
230
231 /**
232 * Handles DOMNodeInserted events.
233 */
234 _domNodeInserted: function() {
235 this._initComments();
236 this._initResponses();
237 },
238
239 /**
240 * Initializes available comments.
241 */
242 _initComments: function() {
243 var self = this;
244 var $loadedComments = false;
245 this._container.find('.jsComment').each(function(index, comment) {
246 var $comment = $(comment).removeClass('jsComment');
247 var $commentID = $comment.data('commentID');
248 self._comments[$commentID] = $comment;
249
1968e4a2
AE
250 var $insertAfter = $comment.find('ul.commentResponseList');
251 if (!$insertAfter.length) $insertAfter = $comment.find('.commentContent');
252
253 $container = $('<div class="commentOptionContainer" />').hide().insertAfter($insertAfter);
ebe32443
AE
254 self._commentButtonList[$commentID] = $('<ul />').appendTo($container);
255
6f874ba8 256 self._handleLoadNextResponses($commentID);
285b1d92
MW
257 self._initComment($commentID, $comment);
258 self._displayedComments++;
259
260 $loadedComments = true;
285b1d92
MW
261 });
262
263 if ($loadedComments) {
264 this._handleLoadNextComments();
265 }
266 },
267
268 /**
269 * Initializes a specific comment.
270 *
271 * @param integer commentID
272 * @param jQuery comment
273 */
274 _initComment: function(commentID, comment) {
275 if (this._container.data('canAdd')) {
276 this._initAddResponse(commentID, comment);
277 }
278
279 if (comment.data('canEdit')) {
280 var $editButton = $('<li><a class="jsTooltip" title="' + WCF.Language.get('wcf.global.button.edit') + '"><span class="icon icon16 icon-pencil" /> <span class="invisible">' + WCF.Language.get('wcf.global.button.edit') + '</span></a></li>');
281 $editButton.data('commentID', commentID).appendTo(comment.find('ul.commentOptions:eq(0)')).click($.proxy(this._prepareEdit, this));
282 }
283
284 if (comment.data('canDelete')) {
285 var $deleteButton = $('<li><a class="jsTooltip" title="' + WCF.Language.get('wcf.global.button.delete') + '"><span class="icon icon16 icon-remove" /> <span class="invisible">' + WCF.Language.get('wcf.global.button.delete') + '</span></a></li>');
286 $deleteButton.data('commentID', commentID).appendTo(comment.find('ul.commentOptions:eq(0)')).click($.proxy(this._delete, this));
287 }
288 },
289
290 /**
291 * Initializes available responses.
292 */
293 _initResponses: function() {
294 var self = this;
295 this._container.find('.jsCommentResponse').each(function(index, response) {
296 var $response = $(response).removeClass('jsCommentResponse');
297 var $responseID = $response.data('responseID');
298 self._responses[$responseID] = $response;
299
300 self._initResponse($responseID, $response);
301 });
302 },
303
304 /**
305 * Initializes a specific response.
306 *
307 * @param integer responseID
308 * @param jQuery response
309 */
310 _initResponse: function(responseID, response) {
311 if (response.data('canEdit')) {
312 var $editButton = $('<li><a class="jsTooltip" title="' + WCF.Language.get('wcf.global.button.edit') + '"><span class="icon icon16 icon-pencil" /> <span class="invisible">' + WCF.Language.get('wcf.global.button.edit') + '</span></a></li>');
313
314 var self = this;
315 $editButton.data('responseID', responseID).appendTo(response.find('ul.commentOptions:eq(0)')).click(function(event) { self._prepareEdit(event, true); });
316 }
317
318 if (response.data('canDelete')) {
319 var $deleteButton = $('<li><a class="jsTooltip" title="' + WCF.Language.get('wcf.global.button.delete') + '"><span class="icon icon16 icon-remove" /> <span class="invisible">' + WCF.Language.get('wcf.global.button.delete') + '</span></a></li>');
320
321 var self = this;
322 $deleteButton.data('responseID', responseID).appendTo(response.find('ul.commentOptions:eq(0)')).click(function(event) { self._delete(event, true); });
323 }
324 },
325
326 /**
327 * Initializes the UI components to add a comment.
328 */
329 _initAddComment: function() {
330 // create UI
331 this._commentAdd = $('<li class="box32 jsCommentAdd"><span class="framed">' + this._userAvatar + '</span><div /></li>').prependTo(this._container);
332 var $inputContainer = this._commentAdd.children('div');
333 var $input = $('<input type="text" placeholder="' + WCF.Language.get('wcf.comment.add') + '" maxlength="65535" class="long" />').appendTo($inputContainer);
334 $('<small>' + WCF.Language.get('wcf.comment.description') + '</small>').appendTo($inputContainer);
335
336 $input.keyup($.proxy(this._keyUp, this));
337 },
338
339 /**
340 * Initializes the UI elements to add a response.
341 *
342 * @param integer commentID
343 * @param jQuery comment
344 */
345 _initAddResponse: function(commentID, comment) {
6f874ba8
AE
346 var $placeholder = null;
347 if (!comment.data('responses') || this._loadNextResponses[commentID]) {
ebe32443 348 $placeholder = $('<li class="jsCommentShowAddResponse"><a>' + WCF.Language.get('wcf.comment.button.response.add') + '</a></li>').data('commentID', commentID).click($.proxy(this._showAddResponse, this)).appendTo(this._commentButtonList[commentID]);
6f874ba8
AE
349 }
350
351 var $listItem = $('<div class="box32 commentResponseAdd jsCommentResponseAdd"><span class="framed">' + this._userAvatar + '</span><div /></div>');
352 if ($placeholder !== null) {
353 $listItem.hide();
354 }
1968e4a2
AE
355 else {
356 this._commentButtonList[commentID].parent().addClass('jsAddResponseActive');
357 }
ebe32443 358 $listItem.appendTo(this._commentButtonList[commentID].parent().show());
285b1d92 359
285b1d92
MW
360 var $inputContainer = $listItem.children('div');
361 var $input = $('<input type="text" placeholder="' + WCF.Language.get('wcf.comment.response.add') + '" maxlength="65535" class="long" />').data('commentID', commentID).appendTo($inputContainer);
362 $('<small>' + WCF.Language.get('wcf.comment.description') + '</small>').appendTo($inputContainer);
363
364 var self = this;
6f874ba8 365 $input.keyup(function(event) { self._keyUp(event, true); });
285b1d92
MW
366
367 comment.data('responsePlaceholder', $placeholder).data('responseInput', $listItem);
368 },
369
370 /**
371 * Prepares editing of a comment or response.
372 *
373 * @param object event
374 * @param boolean isResponse
375 */
376 _prepareEdit: function(event, isResponse) {
377 var $button = $(event.currentTarget);
378 var $data = {
379 objectID: this._container.data('objectID'),
380 objectTypeID: this._container.data('objectTypeID')
381 };
382
383 if (isResponse === true) {
384 $data.responseID = $button.data('responseID');
385 }
386 else {
387 $data.commentID = $button.data('commentID');
388 }
389
390 this._proxy.setOption('data', {
391 actionName: 'prepareEdit',
392 className: 'wcf\\data\\comment\\CommentAction',
393 parameters: {
394 data: $data
395 }
396 });
397 this._proxy.sendRequest();
398 },
399
400 /**
401 * Displays the UI elements to create a response.
402 *
403 * @param object event
404 */
405 _showAddResponse: function(event) {
ebe32443
AE
406 var $placeholder = $(event.currentTarget);
407 var $commentID = $placeholder.data('commentID');
408 if ($placeholder.prev().hasClass('jsCommentLoadNextResponses')) {
6f874ba8
AE
409 this._loadResponsesExecute($commentID, true);
410 $placeholder.parent().children('.button').disable();
411 }
412
413 $placeholder.remove();
285b1d92
MW
414
415 var $responseInput = this._comments[$commentID].data('responseInput').show();
416 $responseInput.find('input').focus();
1968e4a2
AE
417
418 $responseInput.parents('.commentOptionContainer').addClass('jsAddResponseActive');
285b1d92
MW
419 },
420
285b1d92
MW
421 /**
422 * Handles the keyup event for comments and responses.
423 *
424 * @param object event
425 * @param boolean isResponse
426 */
427 _keyUp: function(event, isResponse) {
428 // ignore every key except for [Enter] and [Esc]
429 if (event.which !== 13 && event.which !== 27) {
430 return;
431 }
432
433 var $input = $(event.currentTarget);
434
435 // cancel input
436 if (event.which === 27) {
437 $input.val('').trigger('blur', event);
438 return;
439 }
440
441 var $value = $.trim($input.val());
442
443 // ignore empty comments
444 if ($value == '') {
445 return;
446 }
447
448 var $actionName = 'addComment';
449 var $data = {
450 message: $value,
451 objectID: this._container.data('objectID'),
452 objectTypeID: this._container.data('objectTypeID')
453 };
454 if (isResponse === true) {
455 $actionName = 'addResponse';
456 $data.commentID = $input.data('commentID');
457 }
458
b9f4bd69
MS
459 if (!WCF.User.userID) {
460 this._commentData = $data;
461
462 // check if guest dialog has already been loaded
463 if (this._guestDialog === null) {
464 this._proxy.setOption('data', {
465 actionName: 'getGuestDialog',
466 className: 'wcf\\data\\comment\\CommentAction',
467 parameters: {
468 data: {
469 message: $value,
470 objectID: this._container.data('objectID'),
471 objectTypeID: this._container.data('objectTypeID')
472 }
473 }
474 });
475 this._proxy.sendRequest();
285b1d92 476 }
b9f4bd69
MS
477 else {
478 // request a new recaptcha
479 if (this._useRecaptcha) {
480 Recaptcha.reload();
481 }
482
483 this._guestDialog.find('input[type="submit"]').enable();
484
485 this._guestDialog.wcfDialog('open');
486 }
487 }
488 else {
489 this._proxy.setOption('data', {
490 actionName: $actionName,
491 className: 'wcf\\data\\comment\\CommentAction',
492 parameters: {
493 data: $data
494 }
495 });
496 this._proxy.sendRequest();
497 }
285b1d92
MW
498 },
499
500 /**
501 * Shows a confirmation message prior to comment or response deletion.
502 *
503 * @param object event
504 * @param boolean isResponse
505 */
506 _delete: function(event, isResponse) {
507 WCF.System.Confirmation.show(WCF.Language.get('wcf.comment.delete.confirmMessage'), $.proxy(function(action) {
508 if (action === 'confirm') {
509 var $data = {
510 objectID: this._container.data('objectID'),
511 objectTypeID: this._container.data('objectTypeID')
512 };
513 if (isResponse !== true) {
514 $data.commentID = $(event.currentTarget).data('commentID');
515 }
516 else {
517 $data.responseID = $(event.currentTarget).data('responseID');
518 }
519
520 this._proxy.setOption('data', {
521 actionName: 'remove',
522 className: 'wcf\\data\\comment\\CommentAction',
523 parameters: {
524 data: $data
525 }
526 });
527 this._proxy.sendRequest();
528 }
529 }, this));
530 },
531
b9f4bd69
MS
532 /**
533 * Handles a failed AJAX request.
534 *
535 * @param object data
536 * @param object jqXHR
537 * @param string textStatus
538 * @param string errorThrown
539 * @return boolean
540 */
541 _failure: function(data, jqXHR, textStatus, errorThrown) {
542 if (!WCF.User.userID && this._guestDialog) {
543 // enable submit button again
544 this._guestDialog.find('input[type="submit"]').enable();
545 }
546
547 return true;
548 },
549
285b1d92
MW
550 /**
551 * Handles successful AJAX requests.
552 *
553 * @param object data
554 * @param string textStatus
555 * @param jQuery jqXHR
556 */
557 _success: function(data, textStatus, jqXHR) {
558 switch (data.actionName) {
559 case 'addComment':
b9f4bd69
MS
560 if (data.returnValues.errors) {
561 this._handleGuestDialogErrors(data.returnValues.errors);
562 }
563 else {
564 this._commentAdd.find('input').val('').blur();
565 $(data.returnValues.template).insertAfter(this._commentAdd).wcfFadeIn();
566
567 if (!WCF.User.userID) {
568 this._guestDialog.wcfDialog('close');
569 }
570 }
285b1d92
MW
571 break;
572
573 case 'addResponse':
b9f4bd69
MS
574 if (data.returnValues.errors) {
575 this._handleGuestDialogErrors(data.returnValues.errors);
576 }
577 else {
578 var $comment = this._comments[data.returnValues.commentID];
579 $comment.find('.jsCommentResponseAdd input').val('').blur();
580
581 var $responseList = $comment.find('ul.commentResponseList');
582 if (!$responseList.length) $responseList = $('<ul class="commentResponseList" />').insertBefore($comment.find('.commentOptionContainer'));
583 $(data.returnValues.template).appendTo($responseList).wcfFadeIn();
584 }
62b57ad1 585
b9f4bd69
MS
586 if (!WCF.User.userID) {
587 this._guestDialog.wcfDialog('close');
588 }
285b1d92
MW
589 break;
590
591 case 'edit':
592 this._update(data);
593 break;
594
595 case 'loadComments':
596 this._insertComments(data);
597 break;
598
599 case 'loadResponses':
600 this._insertResponses(data);
601 break;
602
603 case 'prepareEdit':
604 this._edit(data);
605 break;
606
607 case 'remove':
608 this._remove(data);
609 break;
b9f4bd69
MS
610
611 case 'getGuestDialog':
612 this._createGuestDialog(data);
613 break;
285b1d92
MW
614 }
615
42d7d2cc 616 WCF.DOMNodeInsertedHandler.execute();
285b1d92
MW
617 },
618
619 /**
620 * Inserts previously loaded comments.
621 *
622 * @param object data
623 */
624 _insertComments: function(data) {
625 // insert comments
626 $(data.returnValues.template).insertBefore(this._loadNextComments);
627
628 // update time of last comment
629 this._container.data('lastCommentTime', data.returnValues.lastCommentTime);
630 },
631
632 /**
633 * Inserts previously loaded responses.
634 *
635 * @param object data
636 */
637 _insertResponses: function(data) {
638 var $comment = this._comments[data.returnValues.commentID];
639
640 // insert responses
641 $(data.returnValues.template).appendTo($comment.find('ul.commentResponseList'));
642
643 // update time of last response
644 $comment.data('lastResponseTime', data.returnValues.lastResponseTime);
645
646 // update button state to load next responses
647 this._handleLoadNextResponses(data.returnValues.commentID);
648 },
649
650 /**
651 * Removes a comment or response from list.
652 *
653 * @param object data
654 */
655 _remove: function(data) {
656 if (data.returnValues.commentID) {
657 this._comments[data.returnValues.commentID].remove();
658 delete this._comments[data.returnValues.commentID];
659 }
660 else {
661 this._responses[data.returnValues.responseID].remove();
662 delete this._responses[data.returnValues.responseID];
663 }
664 },
665
666 /**
667 * Prepares editing of a comment or response.
668 *
669 * @param object data
670 */
671 _edit: function(data) {
672 if (data.returnValues.commentID) {
673 var $content = this._comments[data.returnValues.commentID].find('.commentContent:eq(0) .userMessage:eq(0)');
674 }
675 else {
676 var $content = this._responses[data.returnValues.responseID].find('.commentContent:eq(0) .userMessage:eq(0)');
677 }
678
679 // replace content with input field
680 $content.html($.proxy(function(index, oldHTML) {
681 var $input = $('<input type="text" class="long" maxlength="65535" /><small>' + WCF.Language.get('wcf.comment.description') + '</small>').val(data.returnValues.message);
682 $input.data('__html', oldHTML).keyup($.proxy(this._saveEdit, this));
683
684 if (data.returnValues.commentID) {
685 $input.data('commentID', data.returnValues.commentID);
686 }
687 else {
688 $input.data('responseID', data.returnValues.responseID);
689 }
690
691 return $input;
692 }, this));
693 $content.children('input').focus();
694
695 // hide elements
696 $content.parent().find('.containerHeadline:eq(0)').hide();
697 $content.parent().find('.buttonGroupNavigation:eq(0)').hide();
698 },
699
700 /**
701 * Updates a comment or response.
702 *
703 * @param object data
704 */
705 _update: function(data) {
706 if (data.returnValues.commentID) {
707 var $input = this._comments[data.returnValues.commentID].find('.commentContent:eq(0) .userMessage:eq(0) > input');
708 }
709 else {
710 var $input = this._responses[data.returnValues.responseID].find('.commentContent:eq(0) .userMessage:eq(0) > input');
711 }
712
713 $input.data('__html', data.returnValues.message);
714
715 this._cancelEdit($input);
716 },
717
b9f4bd69
MS
718 /**
719 * Creates the guest dialog based on the given return data from the AJAX
720 * request.
721 *
722 * @param object data
723 */
724 _createGuestDialog: function(data) {
725 this._guestDialog = $('<div id="commentAddGuestDialog" />').append(data.returnValues.template).hide().appendTo(document.body);
726
727 // bind submit event listeners
728 this._guestDialog.find('input[type="submit"]').click($.proxy(this._submit, this));
729
730 this._guestDialog.find('input[type="text"]').keydown($.proxy(this._keyDown, this));
731
732 // check if recaptcha is used
733 this._useRecaptcha = this._guestDialog.find('dl.reCaptcha').length > 0;
734
735 this._guestDialog.wcfDialog({
736 'title': WCF.Language.get('wcf.comment.guestDialog.title')
737 });
738 },
739
740 /**
741 * Handles clicking enter in the input fields of the guest dialog by
742 * submitting it.
743 *
744 * @param Event event
745 */
746 _keyDown: function(event) {
747 if (event.which === $.ui.keyCode.ENTER) {
748 this._submit();
749 }
750 },
751
752 /**
753 * Handles errors during creation of a comment or response due to the input
754 * in the guest dialog.
755 *
756 * @param object errors
757 */
758 _handleGuestDialogErrors: function(errors) {
759 if (errors.username) {
760 var $usernameInput = this._guestDialog.find('input[name="username"]');
761 var $errorMessage = $usernameInput.next('.innerError');
762 if (!$errorMessage.length) {
763 $errorMessage = $('<small class="innerError" />').text(errors.username).insertAfter($usernameInput);
764 }
765 else {
766 $errorMessage.text(errors.username).show();
767 }
768 }
769
770 if (errors.recaptcha) {
771 Recaptcha.reload();
772
773 var $recaptchaInput = this._guestDialog.find('input[name="recaptcha_response_field"]');
774 var $errorMessage = $recaptchaInput.next('.innerError');
775 if (!$errorMessage.length) {
776 $errorMessage = $('<small class="innerError" />').text(errors.recaptcha).insertAfter($recaptchaInput);
777 }
778 else {
779 $errorMessage.text(errors.recaptcha).show();
780 }
781 }
782
783 this._guestDialog.find('input[type="submit"]').enable();
784 },
785
786 /**
787 * Handles submitting the guest dialog.
788 *
789 * @param Event event
790 */
791 _submit: function(event) {
792 var $submit = true;
793
794 this._guestDialog.find('input[type="submit"]').enable();
795
796 // validate username
797 var $usernameInput = this._guestDialog.find('input[name="username"]');
798 var $username = $usernameInput.val();
799 var $usernameErrorMessage = $usernameInput.next('.innerError');
800 if (!$username) {
801 $submit = false;
802 if (!$usernameErrorMessage.length) {
803 $usernameErrorMessage = $('<small class="innerError" />').text(WCF.Language.get('wcf.global.form.error.empty')).insertAfter($usernameInput);
804 }
805 else {
806 $usernameErrorMessage.text(WCF.Language.get('wcf.global.form.error.empty')).show();
807 }
808 }
809
810 // validate recaptcha
811 if (this._useRecaptcha) {
812 var $recaptchaInput = this._guestDialog.find('input[name="recaptcha_response_field"]');
813 var $recaptchaResponse = $recaptchaInput.val();
814 var $recaptchaErrorMessage = $recaptchaInput.next('.innerError');
815 if (!$recaptchaResponse) {
816 $submit = false;
817 if (!$recaptchaErrorMessage.length) {
818 $recaptchaErrorMessage = $('<small class="innerError" />').text(WCF.Language.get('wcf.global.form.error.empty')).insertAfter($recaptchaInput);
819 }
820 else {
821 $recaptchaErrorMessage.text(WCF.Language.get('wcf.global.form.error.empty')).show();
822 }
823 }
824 }
825
826 if ($submit) {
827 if ($usernameErrorMessage.length) {
828 $usernameErrorMessage.hide();
829 }
830
831 if (this._useRecaptcha && $recaptchaErrorMessage.length) {
832 $recaptchaErrorMessage.hide();
833 }
834
835 var $data = this._commentData;
836 $data.username = $username;
837
838 var $parameters = {
839 data: $data
840 };
841
842 if (this._useRecaptcha) {
843 $parameters.recaptchaChallenge = Recaptcha.get_challenge();
844 $parameters.recaptchaResponse = Recaptcha.get_response();
845 }
846
847 this._proxy.setOption('data', {
848 actionName: this._commentData.commentID ? 'addResponse' : 'addComment',
849 className: 'wcf\\data\\comment\\CommentAction',
850 parameters: $parameters
851 });
852 this._proxy.sendRequest();
853
854 this._guestDialog.find('input[type="submit"]').disable();
855 }
856 },
857
285b1d92
MW
858 /**
859 * Saves editing of a comment or response.
860 *
861 * @param object event
862 */
863 _saveEdit: function(event) {
864 var $input = $(event.currentTarget);
865
866 // abort with [Esc]
867 if (event.which === 27) {
868 this._cancelEdit($input);
869 return;
870 }
871 else if (event.which !== 13) {
872 // ignore everything except for [Enter]
873 return;
874 }
875
876 var $message = $.trim($input.val());
877
878 // ignore empty message
879 if ($message === '') {
880 return;
881 }
882
883 var $data = {
884 message: $message,
885 objectID: this._container.data('objectID'),
886 objectTypeID: this._container.data('objectTypeID')
887 };
888 if ($input.data('commentID')) {
889 $data.commentID = $input.data('commentID');
890 }
891 else {
892 $data.responseID = $input.data('responseID');
893 }
894
895 this._proxy.setOption('data', {
896 actionName: 'edit',
897 className: 'wcf\\data\\comment\\CommentAction',
898 parameters: {
899 data: $data
900 }
901 });
902 this._proxy.sendRequest()
903 },
904
905 /**
906 * Cancels editing of a comment or response.
907 *
908 * @param jQuery input
909 */
910 _cancelEdit: function(input) {
911 // restore elements
912 input.parent().prev('.containerHeadline:eq(0)').show();
913 input.parent().next('.buttonGroupNavigation:eq(0)').show();
914
915 // restore HTML
916 input.parent().html(input.data('__html'));
917 }
918});
919
920/**
921 * Like support for comments
922 *
923 * @see WCF.Like
924 */
925WCF.Comment.Like = WCF.Like.extend({
926 /**
927 * @see WCF.Like._getContainers()
928 */
929 _getContainers: function() {
930 return $('.commentList > li.comment');
931 },
932
933 /**
934 * @see WCF.Like._getObjectID()
935 */
936 _getObjectID: function(containerID) {
937 return this._containers[containerID].data('commentID');
938 },
939
940 /**
941 * @see WCF.Like._buildWidget()
942 */
943 _buildWidget: function(containerID, likeButton, dislikeButton, badge, summary) {
944 this._containers[containerID].find('.containerHeadline:eq(0) > h3').append(badge);
945
946 if (this._canLike) {
947 likeButton.appendTo(this._containers[containerID].find('.commentOptions:eq(0)'));
948 dislikeButton.appendTo(this._containers[containerID].find('.commentOptions:eq(0)'));
949 }
950 },
951
952 /**
953 * @see WCF.Like._getWidgetContainer()
954 */
955 _getWidgetContainer: function(containerID) {},
956
957 /**
958 * @see WCF.Like._addWidget()
959 */
960 _addWidget: function(containerID, widget) {}
961});
962
963/**
964 * Namespace for comment responses
965 */
966WCF.Comment.Response = { };
967
968/**
969 * Like support for comments responses.
970 *
971 * @see WCF.Like
972 */
973WCF.Comment.Response.Like = WCF.Like.extend({
974 /**
975 * @see WCF.Like._addWidget()
976 */
977 _addWidget: function(containerID, widget) { },
978
979 /**
980 * @see WCF.Like._buildWidget()
981 */
982 _buildWidget: function(containerID, likeButton, dislikeButton, badge, summary) {
983 this._containers[containerID].find('.containerHeadline:eq(0) > h3').append(badge);
984
985 if (this._canLike) {
986 likeButton.appendTo(this._containers[containerID].find('.commentOptions:eq(0)'));
987 dislikeButton.appendTo(this._containers[containerID].find('.commentOptions:eq(0)'));
988 }
989 },
990
991 /**
992 * @see WCF.Like._getContainers()
993 */
994 _getContainers: function() {
995 return $('.commentResponseList > li.commentResponse');
996 },
997
998 /**
999 * @see WCF.Like._getObjectID()
1000 */
1001 _getObjectID: function(containerID) {
1002 return this._containers[containerID].data('responseID');
1003 },
1004
1005 /**
1006 * @see WCF.Like._getWidgetContainer()
1007 */
1008 _getWidgetContainer: function(containerID) { }
b9f4bd69 1009});