Commit | Line | Data |
---|---|---|
285b1d92 MW |
1 | /** |
2 | * Namespace for comments | |
3 | */ | |
b9f4bd69 | 4 | WCF.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 | */ | |
13 | WCF.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 | */ | |
925 | WCF.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 | */ | |
966 | WCF.Comment.Response = { }; | |
967 | ||
968 | /** | |
969 | * Like support for comments responses. | |
970 | * | |
971 | * @see WCF.Like | |
972 | */ | |
973 | WCF.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 | }); |