2 * Namespace for conversations.
4 * @author Alexander Ebert
5 * @copyright 2001-2018 WoltLab GmbH
6 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
8 WCF
.Conversation
= { };
11 * Core editor handler for conversations.
13 WCF
.Conversation
.EditorHandler
= Class
.extend({
15 * list of attributes per conversation
21 * list of conversations
27 * list of permissions per conversation
33 * Initializes the core editor handler for conversations.
35 init: function(availableLabels
) {
36 this._conversations
= { };
39 $('.conversation').each(function(index
, conversation
) {
40 var $conversation
= $(conversation
);
41 var $conversationID
= $conversation
.data('conversationID');
43 if (!self
._conversations
[$conversationID
]) {
44 self
._conversations
[$conversationID
] = $conversation
;
45 var $labelIDs
= eval($conversation
.data('labelIDs'));
48 self
._attributes
[$conversationID
] = {
49 isClosed
: ($conversation
.data('isClosed') ? true : false),
54 self
._permissions
[$conversationID
] = {
55 canAddParticipants
: ($conversation
.data('canAddParticipants') ? true : false),
56 canCloseConversation
: ($conversation
.data('canCloseConversation') ? true : false)
63 * Returns a permission's value for given conversation id.
65 * @param integer conversationID
66 * @param string permission
69 getPermission: function(conversationID
, permission
) {
70 if (this._permissions
[conversationID
][permission
] === undefined) {
74 return (this._permissions
[conversationID
][permission
]) ? true : false;
78 * Returns an attribute's value for given conversation id.
80 * @param integer conversationID
84 getValue: function(conversationID
, key
) {
87 if (this._attributes
[conversationID
].labelIDs
=== undefined) {
88 this._attributes
[conversationID
].labelIDs
= [ ];
91 return this._attributes
[conversationID
].labelIDs
;
95 return (this._attributes
[conversationID
].isClosed
) ? true : false;
101 * Counts available labels.
105 countAvailableLabels: function() {
106 return (this.getAvailableLabels()).length
;
110 * Returns a list with the data of the available labels.
112 * @return array<object>
114 getAvailableLabels: function() {
117 WCF
.Dropdown
.getDropdownMenu('conversationLabelFilter').children('.scrollableDropdownMenu').children('li').each(function(index
, listItem
) {
118 var $listItem
= $(listItem
);
119 if ($listItem
.hasClass('dropdownDivider')) {
123 var $span
= $listItem
.find('span');
125 cssClassName
: $span
.data('cssClassName'),
126 labelID
: $span
.data('labelID'),
135 * Updates conversation data.
137 * @param integer conversationID
140 update: function(conversationID
, key
, data
) {
141 if (!this._conversations
[conversationID
]) {
142 console
.debug("[WCF.Conversation.EditorHandler] Unknown conversation id '" + conversationID
+ "'");
145 var $conversation
= this._conversations
[conversationID
];
149 $('<li><span class="icon icon16 fa-lock jsTooltip jsIconLock" title="' + WCF
.Language
.get('wcf.global.state.closed') + '" /></li>').prependTo($conversation
.find('.statusIcons'));
151 this._attributes
[conversationID
].isClosed
= 1;
156 WCF
.Dropdown
.getDropdownMenu('conversationLabelFilter').find('li > a > span').each(function(index
, span
) {
159 $labels
[$span
.data('labelID')] = {
160 cssClassName
: $span
.data('cssClassName'),
162 url
: $span
.parent().attr('href')
166 var $labelList
= $conversation
.find('.columnSubject > .labelList');
168 if ($labelList
.length
) $labelList
.remove();
171 // create label list if missing
172 if (!$labelList
.length
) {
173 $labelList
= $('<ul class="labelList" />').prependTo($conversation
.find('.columnSubject'));
176 // remove all existing labels
180 for (var $i
= 0, $length
= data
.length
; $i
< $length
; $i
++) {
181 var $label
= $labels
[data
[$i
]];
182 $('<li><a href="' + $label
.url
+ '" class="badge label' + ($label
.cssClassName
? " " + $label
.cssClassName
: "") + '">' + WCF
.String
.escapeHTML($label
.label
) + '</a></li>').appendTo($labelList
);
188 $conversation
.find('.statusIcons li').each(function(index
, listItem
) {
189 var $listItem
= $(listItem
);
190 if ($listItem
.children('span.jsIconLock').length
) {
196 this._attributes
[conversationID
].isClosed
= 0;
200 WCF
.Clipboard
.reload();
205 * Conversation editor handler for conversation page.
207 * @see WCF.Conversation.EditorHandler
208 * @param array<object> availableLabels
210 WCF
.Conversation
.EditorHandlerConversation
= WCF
.Conversation
.EditorHandler
.extend({
212 * list of available labels
215 _availableLabels
: null,
218 * @see WCF.Conversation.EditorHandler.init()
220 * @param array<object> availableLabels
222 init: function(availableLabels
) {
223 this._availableLabels
= availableLabels
|| [ ];
229 * @see WCF.Conversation.EditorHandler.getAvailableLabels()
231 getAvailableLabels: function() {
232 return this._availableLabels
;
236 * @see WCF.Conversation.EditorHandler.update()
238 update: function(conversationID
, key
, data
) {
239 if (!this._conversations
[conversationID
]) {
240 console
.debug("[WCF.Conversation.EditorHandler] Unknown conversation id '" + conversationID
+ "'");
244 var container
= $('.contentHeaderTitle > .contentHeaderMetaData');
248 $('<li><span class="icon icon16 fa-lock jsIconLock" /> ' + WCF
.Language
.get('wcf.global.state.closed') + '</li>').appendTo(container
);
250 this._attributes
[conversationID
].isClosed
= 1;
254 var labelList
= container
.find('.labelList');
256 labelList
.parent().remove();
259 var availableLabels
= this.getAvailableLabels();
261 if (!labelList
.length
) {
262 labelList
= $('<li><span class="icon icon16 fa-tags"></span> <ul class="labelList"></ul></li>').prependTo(container
);
263 labelList
= labelList
.children('ul');
267 data
.forEach(function(labelId
) {
268 availableLabels
.forEach(function(label
) {
269 if (label
.labelID
== labelId
) {
270 html
+= '<li><span class="label badge' + (label
.cssClassName
? ' ' + label
.cssClassName
: '') + '">' + label
.label
+ '</span></li>';
275 labelList
[0].innerHTML
= html
;
280 container
.find('.jsIconLock').parent().remove();
282 this._attributes
[conversationID
].isClosed
= 0;
289 * Provides extended actions for conversation clipboard actions.
291 WCF
.Conversation
.Clipboard
= Class
.extend({
294 * @var WCF.Conversation.EditorHandler
296 _editorHandler
: null,
299 * Initializes a new WCF.Conversation.Clipboard object.
301 * @param {WCF.Conversation.EditorHandler} editorHandler
303 init: function(editorHandler
) {
304 this._editorHandler
= editorHandler
;
306 WCF
.System
.Event
.addListener('com.woltlab.wcf.clipboard', 'com.woltlab.wcf.conversation.conversation', (function (data
) {
307 if (data
.responseData
=== null) {
308 this._execute(data
.data
.actionName
, data
.data
.parameters
);
311 this._evaluateResponse(data
.data
.actionName
, data
.responseData
);
317 * Handles clipboard actions.
319 * @param {string} actionName
320 * @param {Object} parameters
322 _execute: function(actionName
, parameters
) {
323 if (actionName
=== 'com.woltlab.wcf.conversation.conversation.assignLabel') {
324 new WCF
.Conversation
.Label
.Editor(this._editorHandler
, null, parameters
.objectIDs
);
329 * Evaluates AJAX responses.
331 * @param {Object} data
332 * @param {string} actionName
334 _evaluateResponse: function(actionName
, data
) {
335 switch (actionName
) {
336 case 'com.woltlab.wcf.conversation.conversation.leave':
337 case 'com.woltlab.wcf.conversation.conversation.leavePermanently':
338 case 'com.woltlab.wcf.conversation.conversation.markAsRead':
339 case 'com.woltlab.wcf.conversation.conversation.restore':
340 window
.location
.reload();
343 case 'com.woltlab.wcf.conversation.conversation.close':
344 case 'com.woltlab.wcf.conversation.conversation.open':
345 //noinspection JSUnresolvedVariable
346 for (var conversationId
in data
.returnValues
.conversationData
) {
347 //noinspection JSUnresolvedVariable
348 if (data
.returnValues
.conversationData
.hasOwnProperty(conversationId
)) {
349 //noinspection JSUnresolvedVariable
350 var $data
= data
.returnValues
.conversationData
[conversationId
];
352 this._editorHandler
.update(conversationId
, ($data
.isClosed
? 'close' : 'open'), $data
);
361 * Inline editor implementation for conversations.
363 * @see WCF.Inline.Editor
365 WCF
.Conversation
.InlineEditor
= WCF
.InlineEditor
.extend({
367 * editor handler object
368 * @var WCF.Conversation.EditorHandler
370 _editorHandler
: null,
373 * execution environment
376 _environment
: 'conversation',
379 * @see WCF.InlineEditor._setOptions()
381 _setOptions: function() {
384 { label
: WCF
.Language
.get('wcf.conversation.edit.subject'), optionName
: 'editSubject' },
387 { label
: WCF
.Language
.get('wcf.conversation.edit.close'), optionName
: 'close' },
388 { label
: WCF
.Language
.get('wcf.conversation.edit.open'), optionName
: 'open' },
391 { label
: WCF
.Language
.get('wcf.conversation.edit.assignLabel'), optionName
: 'assignLabel' },
394 { optionName
: 'divider' },
397 { label
: WCF
.Language
.get('wcf.conversation.edit.addParticipants'), optionName
: 'addParticipants' },
399 // leave conversation
400 { label
: WCF
.Language
.get('wcf.conversation.edit.leave'), optionName
: 'leave' },
403 { label
: WCF
.Language
.get('wcf.global.button.edit'), optionName
: 'edit', isQuickOption
: true }
408 * Sets editor handler object.
410 * @param WCF.Conversation.EditorHandler editorHandler
411 * @param string environment
413 setEditorHandler: function(editorHandler
, environment
) {
414 this._editorHandler
= editorHandler
;
415 this._environment
= (environment
== 'list') ? 'list' : 'conversation';
419 * @see WCF.InlineEditor._getTriggerElement()
421 _getTriggerElement: function(element
) {
422 return element
.find('.jsConversationInlineEditor');
426 * @see WCF.InlineEditor._validate()
428 _validate: function(elementID
, optionName
) {
429 var $conversationID
= $('#' + elementID
).data('conversationID');
431 switch (optionName
) {
432 case 'addParticipants':
433 return (this._editorHandler
.getPermission($conversationID
, 'canAddParticipants'));
437 return (this._editorHandler
.countAvailableLabels()) ? true : false;
441 return (!!this._editorHandler
.getPermission($conversationID
, 'canCloseConversation'));
446 if (!this._editorHandler
.getPermission($conversationID
, 'canCloseConversation')) {
450 if (optionName
=== 'close') return !(this._editorHandler
.getValue($conversationID
, 'isClosed'));
451 else return (this._editorHandler
.getValue($conversationID
, 'isClosed'));
459 return ($('#' + elementID
).data('isDraft') ? true : false);
467 * @see WCF.InlineEditor._execute()
469 _execute: function(elementID
, optionName
) {
470 // abort if option is invalid or not accessible
471 if (!this._validate(elementID
, optionName
)) {
475 switch (optionName
) {
476 case 'addParticipants':
477 require(['WoltLabSuite/Core/Conversation/Ui/Participant/Add'], function(UiParticipantAdd
) {
478 new UiParticipantAdd(elData(elById(elementID
), 'conversation-id'));
483 new WCF
.Conversation
.Label
.Editor(this._editorHandler
, elementID
);
487 require(['WoltLabSuite/Core/Conversation/Ui/Subject/Editor'], function (UiSubjectEditor
) {
488 UiSubjectEditor
.beginEdit(elData(elById(elementID
), 'conversation-id'));
494 this._updateConversation(elementID
, optionName
, { isClosed
: (optionName
=== 'close' ? 1 : 0) });
498 new WCF
.Conversation
.Leave([ $('#' + elementID
).data('conversationID') ], this._environment
);
502 window
.location
= this._getTriggerElement($('#' + elementID
)).prop('href');
508 * Updates conversation properties.
510 * @param string elementID
511 * @param string optionName
514 _updateConversation: function(elementID
, optionName
, data
) {
515 var $conversationID
= this._elements
[elementID
].data('conversationID');
517 switch (optionName
) {
521 this._updateData
.push({
522 elementID
: elementID
,
523 optionName
: optionName
,
527 this._proxy
.setOption('data', {
528 actionName
: optionName
,
529 className
: 'wcf\\data\\conversation\\ConversationAction',
530 objectIDs
: [ $conversationID
]
532 this._proxy
.sendRequest();
538 * @see WCF.InlineEditor._updateState()
540 _updateState: function() {
541 for (var $i
= 0, $length
= this._updateData
.length
; $i
< $length
; $i
++) {
542 var $data
= this._updateData
[$i
];
543 var $conversationID
= this._elements
[$data
.elementID
].data('conversationID');
545 switch ($data
.optionName
) {
549 this._editorHandler
.update($conversationID
, $data
.optionName
, $data
.data
);
557 * Provides a dialog for leaving or restoring conversation.
559 * @param array<integer> conversationIDs
561 WCF
.Conversation
.Leave
= Class
.extend({
563 * list of conversation ids
564 * @var array<integer>
566 _conversationIDs
: [ ],
582 * @var WCF.Action.Proxy
587 * Initializes the leave/restore dialog for conversations.
589 * @param array<integer> conversationIDs
590 * @param string environment
592 init: function(conversationIDs
, environment
) {
593 this._conversationIDs
= conversationIDs
;
595 this._environment
= environment
;
597 this._proxy
= new WCF
.Action
.Proxy({
598 success
: $.proxy(this._success
, this)
605 * Loads the dialog overlay.
607 _loadDialog: function() {
608 this._proxy
.setOption('data', {
609 actionName
: 'getLeaveForm',
610 className
: 'wcf\\data\\conversation\\ConversationAction',
611 objectIDs
: this._conversationIDs
613 this._proxy
.sendRequest();
617 * Handles successful AJAX requests.
620 * @param string textStatus
621 * @param jQuery jqXHR
623 _success: function(data
, textStatus
, jqXHR
) {
624 switch (data
.returnValues
.actionName
) {
626 this._showDialog(data
);
629 case 'hideConversation':
630 if (this._environment
=== 'conversation') {
631 window
.location
= data
.returnValues
.redirectURL
;
634 window
.location
.reload();
641 * Displays the leave/restore conversation dialog overlay.
645 _showDialog: function(data
) {
646 if (this._dialog
=== null) {
647 this._dialog
= $('#leaveDialog');
648 if (!this._dialog
.length
) {
649 this._dialog
= $('<div id="leaveDialog" />').hide().appendTo(document
.body
);
654 this._dialog
.html(data
.returnValues
.template
);
655 this._dialog
.wcfDialog({
656 title
: WCF
.Language
.get('wcf.conversation.leave.title')
659 this._dialog
.wcfDialog('render');
661 // bind event listener
662 this._dialog
.find('#hideConversation').click($.proxy(this._click
, this));
666 * Handles conversation state changes.
669 var $input
= this._dialog
.find('input[type=radio]:checked');
670 if ($input
.length
=== 1) {
671 this._proxy
.setOption('data', {
672 actionName
: 'hideConversation',
673 className
: 'wcf\\data\\conversation\\ConversationAction',
674 objectIDs
: this._conversationIDs
,
676 hideConversation
: $input
.val()
679 this._proxy
.sendRequest();
685 * Provides methods to remove participants from conversations.
687 * @see WCF.Action.Delete
689 WCF
.Conversation
.RemoveParticipant
= WCF
.Action
.Delete
.extend({
697 * @see WCF.Action.Delete.init()
699 init: function(conversationID
) {
700 this._conversationID
= conversationID
;
701 this._super('wcf\\data\\conversation\\ConversationAction', '.jsParticipant');
705 * @see WCF.Action.Delete._sendRequest()
707 _sendRequest: function(object
) {
708 this.proxy
.setOption('data', {
709 actionName
: 'removeParticipant',
710 className
: this._className
,
711 objectIDs
: [ this._conversationID
],
713 userID
: $(object
).data('objectID')
717 this.proxy
.sendRequest();
721 * @see WCF.Action.Delete._success()
723 _success: function(data
, textStatus
, jqXHR
) {
724 var $userID
= data
.returnValues
.userID
;
726 for (var $index
in this._containers
) {
727 var $container
= $('#' + this._containers
[$index
]);
728 if ($container
.find('.jsDeleteButton').data('objectID') == $userID
) {
729 $container
.find('.userLink').addClass('conversationLeft');
730 $container
.find('.jsDeleteButton').remove();
737 * Namespace for label-related classes.
739 WCF
.Conversation
.Label
= { };
742 * Providers an editor for conversation labels.
744 * @param WCF.Conversation.EditorHandler editorHandler
745 * @param string elementID
746 * @param array<integer> conversationIDs
748 WCF
.Conversation
.Label
.Editor
= Class
.extend({
750 * list of conversation id
751 * @var array<integer>
762 * editor handler object
763 * @var WCF.Conversation.EditorHandler
765 _editorHandler
: null,
768 * system notification object
769 * @var WCF.System.Notification
774 * action proxy object
775 * @var WCF.Action.Proxy
780 * Initializes the label editor for given conversation.
782 * @param WCF.Conversation.EditorHandler editorHandler
783 * @param string elementID
784 * @param array<integer> conversationIDs
786 init: function(editorHandler
, elementID
, conversationIDs
) {
788 this._conversationIDs
= [ $('#' + elementID
).data('conversationID') ];
791 this._conversationIDs
= conversationIDs
;
795 this._editorHandler
= editorHandler
;
797 this._notification
= new WCF
.System
.Notification(WCF
.Language
.get('wcf.global.success.edit'));
798 this._proxy
= new WCF
.Action
.Proxy({
799 success
: $.proxy(this._success
, this)
806 * Loads label assignment dialog.
808 _loadDialog: function() {
809 this._proxy
.setOption('data', {
810 actionName
: 'getLabelForm',
811 className
: 'wcf\\data\\conversation\\label\\ConversationLabelAction',
813 conversationIDs
: this._conversationIDs
816 this._proxy
.sendRequest();
820 * Handles successful AJAX requests.
823 * @param string textStatus
824 * @param jQuery jqXHR
826 _success: function(data
, textStatus
, jqXHR
) {
827 switch (data
.returnValues
.actionName
) {
829 this._assignLabels(data
);
833 this._renderDialog(data
);
839 * Renders the label assignment form overlay.
843 _renderDialog: function(data
) {
844 if (this._dialog
=== null) {
845 this._dialog
= $('#conversationLabelForm');
846 if (!this._dialog
.length
) {
847 this._dialog
= $('<div id="conversationLabelForm" />').hide().appendTo(document
.body
);
851 this._dialog
.html(data
.returnValues
.template
);
852 this._dialog
.wcfDialog({
853 title
: WCF
.Language
.get('wcf.conversation.label.assignLabels')
855 this._dialog
.wcfDialog('render');
857 $('#assignLabels').click($.proxy(this._save
, this));
861 * Saves label assignments for current conversation id.
865 this._dialog
.find('input').each(function(index
, checkbox
) {
866 var $checkbox
= $(checkbox
);
867 if ($checkbox
.is(':checked')) {
868 $labelIDs
.push($checkbox
.data('labelID'));
872 this._proxy
.setOption('data', {
873 actionName
: 'assignLabel',
874 className
: 'wcf\\data\\conversation\\label\\ConversationLabelAction',
876 conversationIDs
: this._conversationIDs
,
880 this._proxy
.sendRequest();
884 * Updates conversation labels.
888 _assignLabels: function(data
) {
889 // update conversation
890 for (var $i
= 0, $length
= this._conversationIDs
.length
; $i
< $length
; $i
++) {
891 this._editorHandler
.update(this._conversationIDs
[$i
], 'labelIDs', data
.returnValues
.labelIDs
);
894 // close dialog and show a 'success' notice
895 this._dialog
.wcfDialog('close');
896 this._notification
.show();
901 * Label manager for conversations.
905 WCF
.Conversation
.Label
.Manager
= Class
.extend({
931 * system notification object
932 * @var WCF.System.Notification
937 * action proxy object
938 * @var WCF.Action.Proxy
943 * maximum number of labels the user may create
949 * number of labels the user created
955 * Initializes the label manager for conversations.
959 init: function(link
) {
960 this._deletedLabelID
= 0;
962 this._labelCount
= 0;
965 this._labels
= WCF
.Dropdown
.getDropdownMenu('conversationLabelFilter').children('.scrollableDropdownMenu');
966 $('#manageLabel').click($.proxy(this._click
, this));
968 this._notification
= new WCF
.System
.Notification(WCF
.Language
.get('wcf.conversation.label.management.addLabel.success'));
969 this._proxy
= new WCF
.Action
.Proxy({
970 success
: $.proxy(this._success
, this)
975 * Handles clicks on the 'manage labels' button.
978 this._proxy
.setOption('data', {
979 actionName
: 'getLabelManagement',
980 className
: 'wcf\\data\\conversation\\ConversationAction'
982 this._proxy
.sendRequest();
986 * Handles successful AJAX requests.
989 * @param string textStatus
990 * @param jQuery jqXHR
992 _success: function(data
, textStatus
, jqXHR
) {
993 if (this._dialog
=== null) {
994 this._dialog
= $('<div id="labelManagement" />').hide().appendTo(document
.body
);
997 if (data
.returnValues
&& data
.returnValues
.actionName
) {
998 switch (data
.returnValues
.actionName
) {
1000 this._insertLabel(data
);
1003 case 'getLabelManagement':
1004 this._maxLabels
= parseInt(data
.returnValues
.maxLabels
);
1005 this._labelCount
= parseInt(data
.returnValues
.labelCount
);
1008 this._dialog
.empty().html(data
.returnValues
.template
);
1009 this._dialog
.wcfDialog({
1010 title
: WCF
.Language
.get('wcf.conversation.label.management')
1012 this._dialog
.wcfDialog('render');
1014 // bind action listeners
1015 this._bindListener();
1020 // check if delete label id is present within URL (causing an IllegalLinkException if reloading)
1021 if (this._deletedLabelID
) {
1022 var $regex
= new RegExp('(\\?|&)labelID=' + this._deletedLabelID
);
1023 window
.location
= window
.location
.toString().replace($regex
, '');
1027 window
.location
.reload();
1033 * Inserts a previously created label.
1035 * @param object data
1037 _insertLabel: function(data
) {
1038 var $listItem
= $('<li><a href="' + this._link
+ '&labelID=' + data
.returnValues
.labelID
+ '"><span class="badge label' + (data
.returnValues
.cssClassName
? ' ' + data
.returnValues
.cssClassName
: '') + '">' + data
.returnValues
.label
+ '</span></a></li>');
1039 $listItem
.find('a > span').data('labelID', data
.returnValues
.labelID
).data('cssClassName', data
.returnValues
.cssClassName
);
1041 this._labels
.append($listItem
);
1043 this._notification
.show();
1047 if (this._labelCount
>= this._maxLabels
) {
1048 $('#conversationLabelManagementForm').hide();
1053 * Binds event listener for label management.
1055 _bindListener: function() {
1056 $('#labelName').on('keyup keydown keypress', $.proxy(this._updateLabels
, this));
1057 if ($.browser
.mozilla
&& $.browser
.touch
) {
1058 $('#labelName').on('input', $.proxy(this._updateLabels
, this));
1061 if (this._labelCount
>= this._maxLabels
) {
1062 $('#conversationLabelManagementForm').hide();
1063 this._dialog
.wcfDialog('render');
1066 $('#addLabel').disable().click($.proxy(this._addLabel
, this));
1067 $('#editLabel').disable();
1069 this._dialog
.find('.conversationLabelList a.label').click($.proxy(this._edit
, this));
1073 * Prepares a label for editing.
1075 * @param object event
1077 _edit: function(event
) {
1078 if (this._labelCount
>= this._maxLabels
) {
1079 $('#conversationLabelManagementForm').show();
1080 this._dialog
.wcfDialog('render');
1083 var $label
= $(event
.currentTarget
);
1086 var $legend
= WCF
.Language
.get('wcf.conversation.label.management.editLabel').replace(/#labelName#/, WCF
.String
.escapeHTML($label
.text()));
1087 $('#conversationLabelManagementForm').data('labelID', $label
.data('labelID')).children('.sectionTitle').html($legend
);
1089 // update text input
1090 $('#labelName').val($label
.text()).trigger('keyup');
1092 // select css class name
1093 var $cssClassName
= $label
.data('cssClassName');
1094 $('#labelManagementList input').each(function(index
, input
) {
1095 var $input
= $(input
);
1097 if ($input
.val() == $cssClassName
) {
1098 $input
.attr('checked', 'checked');
1103 $('#addLabel').hide();
1104 $('#editLabel').show().click($.proxy(this._editLabel
, this));
1105 $('#deleteLabel').show().click($.proxy(this._deleteLabel
, this));
1111 _editLabel: function() {
1112 this._proxy
.setOption('data', {
1113 actionName
: 'update',
1114 className
: 'wcf\\data\\conversation\\label\\ConversationLabelAction',
1115 objectIDs
: [ $('#conversationLabelManagementForm').data('labelID') ],
1118 cssClassName
: $('#labelManagementList input:checked').val(),
1119 label
: $('#labelName').val()
1123 this._proxy
.sendRequest();
1129 _deleteLabel: function() {
1130 var $title
= WCF
.Language
.get('wcf.conversation.label.management.deleteLabel.confirmMessage').replace(/#labelName#/, $('#labelName').val());
1131 WCF
.System
.Confirmation
.show($title
, $.proxy(function(action
) {
1132 if (action
=== 'confirm') {
1133 this._proxy
.setOption('data', {
1134 actionName
: 'delete',
1135 className
: 'wcf\\data\\conversation\\label\\ConversationLabelAction',
1136 objectIDs
: [ $('#conversationLabelManagementForm').data('labelID') ]
1138 this._proxy
.sendRequest();
1140 this._deletedLabelID
= $('#conversationLabelManagementForm').data('labelID');
1142 }, this), undefined, undefined, true);
1146 * Updates label text within label management.
1148 _updateLabels: function() {
1149 var $value
= $.trim($('#labelName').val());
1151 $('#addLabel, #editLabel').enable();
1154 $('#addLabel, #editLabel').disable();
1155 $value
= WCF
.Language
.get('wcf.conversation.label.placeholder');
1158 $('#labelManagementList').find('span.label').text($value
);
1162 * Sends an AJAX request to add a new label.
1164 _addLabel: function() {
1165 var $labelName
= $('#labelName').val();
1166 var $cssClassName
= $('#labelManagementList input:checked').val();
1168 this._proxy
.setOption('data', {
1170 className
: 'wcf\\data\\conversation\\label\\ConversationLabelAction',
1173 cssClassName
: $cssClassName
,
1174 labelName
: $labelName
1178 this._proxy
.sendRequest();
1181 this._dialog
.wcfDialog('close');
1186 * Provides a flexible conversation preview.
1190 WCF
.Conversation
.Preview
= WCF
.Popover
.extend({
1193 * @var WCF.Action.Proxy
1198 * @see WCF.Popover.init()
1201 this._super('.conversationLink');
1204 this._proxy
= new WCF
.Action
.Proxy({
1205 showLoadingOverlay
: false
1208 WCF
.DOMNodeInsertedHandler
.addCallback('WCF.Conversation.Preview', $.proxy(this._initContainers
, this));
1212 * @see WCF.Popover._loadContent()
1214 _loadContent: function() {
1215 var $link
= $('#' + this._activeElementID
);
1217 this._proxy
.setOption('data', {
1218 actionName
: 'getMessagePreview',
1219 className
: 'wcf\\data\\conversation\\ConversationAction',
1220 objectIDs
: [ $link
.data('conversationID') ]
1223 var $elementID
= this._activeElementID
;
1225 this._proxy
.setOption('success', function(data
, textStatus
, jqXHR
) {
1226 self
._insertContent($elementID
, data
.returnValues
.template
, true);
1228 this._proxy
.sendRequest();
1233 * User Panel implementation for conversations.
1235 * @see WCF.User.Panel.Abstract
1237 WCF
.User
.Panel
.Conversation
= WCF
.User
.Panel
.Abstract
.extend({
1239 * @see WCF.User.Panel.Abstract.init()
1241 init: function(options
) {
1242 options
.enableMarkAsRead
= true;
1244 this._super($('#unreadConversations'), 'unreadConversations', options
);
1246 WCF
.System
.Event
.addListener('com.woltlab.wcf.conversation.userPanel', 'reset', (function() {
1248 this.updateBadge(0);
1249 this._loadData
= true;
1252 require(['EventHandler'], (function(EventHandler
) {
1253 EventHandler
.add('com.woltlab.wcf.UserMenuMobile', 'more', (function(data
) {
1254 if (data
.identifier
=== 'com.woltlab.wcf.conversation') {
1262 * @see WCF.User.Panel.Abstract._initDropdown()
1264 _initDropdown: function() {
1265 var $dropdown
= this._super();
1267 if (this._options
.canStartConversation
) {
1268 $('<li><a href="' + this._options
.newConversationLink
+ '" title="' + this._options
.newConversation
+ '" class="jsTooltip"><span class="icon icon24 fa-plus" /></a></li>').appendTo($dropdown
.getLinkList());
1275 * @see WCF.User.Panel.Abstract._load()
1278 this._proxy
.setOption('data', {
1279 actionName
: 'getMixedConversationList',
1280 className
: 'wcf\\data\\conversation\\ConversationAction'
1282 this._proxy
.sendRequest();
1286 * @see WCF.User.Panel.Abstract._markAsRead()
1288 _markAsRead: function(event
, objectID
) {
1289 this._proxy
.setOption('data', {
1290 actionName
: 'markAsRead',
1291 className
: 'wcf\\data\\conversation\\ConversationAction',
1292 objectIDs
: [ objectID
]
1294 this._proxy
.sendRequest();
1298 * @see WCF.User.Panel.Abstract._markAllAsRead()
1300 _markAllAsRead: function(event
) {
1301 this._proxy
.setOption('data', {
1302 actionName
: 'markAllAsRead',
1303 className
: 'wcf\\data\\conversation\\ConversationAction'
1305 this._proxy
.sendRequest();
1310 * Marks one conversation as read.
1312 WCF
.Conversation
.MarkAsRead
= Class
.extend({
1315 * @var WCF.Action.Proxy
1320 * Initializes the mark as read for conversations.
1323 this._proxy
= new WCF
.Action
.Proxy({
1324 success
: $.proxy(this._success
, this)
1327 $(document
).on('dblclick', '.conversationList .new .columnAvatar', $.proxy(this._dblclick
, this));
1331 * Handles double clicks on avatar.
1333 * @param object event
1335 _dblclick: function(event
) {
1336 this._proxy
.setOption('data', {
1337 actionName
: 'markAsRead',
1338 className
: 'wcf\\data\\conversation\\ConversationAction',
1339 objectIDs
: [ $(event
.currentTarget
).parents('ol:eq(0)').data('conversationID') ]
1341 this._proxy
.sendRequest();
1345 * Handles successful AJAX requests.
1347 * @param object data
1348 * @param string textStatus
1349 * @param jQuery jqXHR
1351 _success: function(data
, textStatus
, jqXHR
) {
1352 $('.conversationList .new').each(function(index
, element
) {
1353 var $element
= $(element
);
1354 if (WCF
.inArray($element
.data('conversationID'), data
.objectIDs
)) {
1356 $element
.removeClass('new');
1359 $element
.find('.firstNewPost').parent().remove();
1362 $element
.find('.columnAvatar').off('dblclick');
1369 * Marks all conversations as read.
1371 WCF
.Conversation
.MarkAllAsRead
= Class
.extend({
1374 * @var WCF.Action.Proxy
1379 * Initializes the WCF.Conversation.MarkAllAsRead class.
1382 this._proxy
= new WCF
.Action
.Proxy({
1383 success
: $.proxy(this._success
, this)
1386 $('.markAllAsReadButton').click($.proxy(this._click
, this));
1392 * @param object event
1394 _click: function(event
) {
1395 event
.preventDefault();
1397 this._proxy
.setOption('data', {
1398 actionName
: 'markAllAsRead',
1399 className
: 'wcf\\data\\conversation\\ConversationAction'
1401 this._proxy
.sendRequest();
1405 * Marks all conversations as read.
1407 * @param object data
1408 * @param string textStatus
1409 * @param jQuery jqXHR
1411 _success: function(data
, textStatus
, jqXHR
) {
1413 WCF
.System
.Event
.fireEvent('com.woltlab.wcf.conversation.userPanel', 'reset');
1415 // fix conversation list
1416 var $conversationList
= $('.conversationList');
1417 $conversationList
.find('.new').removeClass('new');
1418 $conversationList
.find('.columnAvatar').off('dblclick');
1423 * Namespace for conversation messages.
1425 WCF
.Conversation
.Message
= { };
1428 * Provides an inline editor for conversation messages.
1430 * @see WCF.Message.InlineEditor
1432 WCF
.Conversation
.Message
.InlineEditor
= WCF
.Message
.InlineEditor
.extend({
1434 * @see WCF.Message.InlineEditor.init()
1436 init: function(containerID
, quoteManager
) {
1437 this._super(containerID
, true, quoteManager
);
1441 * @see WCF.Message.InlineEditor._getClassName()
1443 _getClassName: function() {
1444 return 'wcf\\data\\conversation\\message\\ConversationMessageAction';
1449 * Provides the quote manager for conversation messages.
1451 * @param WCF.Message.Quote.Manager quoteManager
1452 * @see WCF.Message.Quote.Handler
1454 WCF
.Conversation
.Message
.QuoteHandler
= WCF
.Message
.Quote
.Handler
.extend({
1456 * @see WCF.Message.QuoteManager.init()
1458 init: function(quoteManager
) {
1461 'wcf\\data\\conversation\\message\\ConversationMessageAction',
1462 'com.woltlab.wcf.conversation.message',
1465 '.messageBody > div > div.messageText',