var HideableWidget = function() {
Widget.call(this);
};
inherits(HideableWidget, Widget);
HideableWidget.prototype.setState = function(state) {
this._state = state;
if (this._element) {
if (state === 'shown') {
this._element.show();
} else if (state === 'hidden') {
this._element.hide();
}
}
};
HideableWidget.prototype.onAfterShow = function() {};
HideableWidget.prototype.show = function() {
this.setState('shown');
this.onAfterShow();
};
HideableWidget.prototype.hide = function() {
this.setState('hidden');
};
HideableWidget.prototype.decorate = function(element) {
this._element = element;
};
/**
* @constructor
*/
var MessageComposer = function() {
HideableWidget.call(this);
};
inherits(MessageComposer, HideableWidget);
MessageComposer.prototype.onAfterCancel = function(handler) {
if (handler) {
this._onAfterCancel = handler;
} else {
return this._onAfterCancel();
}
};
/** override these two
* @param {object} data - the response data
* these functions will run after .send() receives
* the response
*/
MessageComposer.prototype.onSendSuccessInternal = function(data) {};
MessageComposer.prototype.onSendErrorInternal = function(data) {};
MessageComposer.prototype.onSendSuccess = function(callback) {
if (callback) {
this._onSendSuccess = callback;
} else if (this._onSendSuccess) {
this._onSendSuccess();
}
};
MessageComposer.prototype.onSendError = function(callback) {
if (callback) {
this._onSendError = callback;
} else if (this._onSendError) {
this._onSendError();
}
};
MessageComposer.prototype.onAfterShow = function() {
this._textarea.focus();
};
MessageComposer.prototype.cancel = function() {
this._textarea.val('');
this._textareaError.html('');
this.hide();
this.onAfterCancel();
};
MessageComposer.prototype.setPostData = function(data) {
this._postData = data;
};
MessageComposer.prototype.getPostData = function() {
return this._postData;
};
MessageComposer.prototype.setSendUrl = function(url) {
this._sendUrl = url;
};
MessageComposer.prototype.getInputData = function() {
return {'text': this._textarea.val()};
};
MessageComposer.prototype.dataIsValid = function() {
var text = $.trim(this._textarea.val());
if (text === '') {
this._textareaError.html(gettext('required'));
return false;
}
return true;
};
MessageComposer.prototype.send = function() {
var url = this._sendUrl;
var data = this.getPostData() || {};
var inputData = this.getInputData();
$.extend(data, inputData);
var me = this;
data['text'] = this._textarea.val();
$.ajax({
type: 'POST',
dataType: 'json',
url: url,
data: data,
cache: false,
success: function(data) {
if (data['success']) {
me.onSendSuccessInternal(data);
me.onSendSuccess();
} else {
me.onSendErrorInternal(data);
me.onSendError();
}
}
});
};
MessageComposer.prototype.createDom = function() {
this._element = this.makeElement('div');
this.hide();
this._element.addClass('message-composer');
//create textarea
var label = this.makeElement('label');
label.html(gettext('Your message:'));
this._element.append(label);
var error = this.makeElement('label');
error.addClass('errors');
this._element.append(error);
this._element.append($('
'));
this._textareaError = error;
var textarea = this.makeElement('textarea');
this._element.append(textarea);
this._textarea = textarea;
//send button
var me = this;
var sendBtn = this.makeButton(
gettext('send'),
function() {
if (me.dataIsValid()){
me.send();
}
}
);
sendBtn.addClass('submit');
this._element.append(sendBtn);
//cancel button
var cancelBtn = this.makeButton(
gettext('cancel'),
function() { me.cancel(); }
);
cancelBtn.addClass('submit');
this._element.append(cancelBtn);
};
var ReplyMessageComposer = function() {
MessageComposer.call(this);
};
inherits(ReplyMessageComposer, MessageComposer);
ReplyMessageComposer.prototype.setParent = function(elem) {
this._parent = elem;
};
ReplyMessageComposer.prototype.onSendSuccessInternal = function(data) {
var message = new Message();
message.decorate($(data['html']));
this._parent.addMessage(message);
};
/**
* @constructor
* same as message composer, but initially
* hidden and presented by a "reply" link
*/
var ReplyComposer = function() {
HideableWidget.call(this);
};
inherits(ReplyComposer, HideableWidget);
ReplyComposer.prototype.open = function() {
this._opener.hide();
this._editor.show();
};
ReplyComposer.prototype.close = function() {
this._opener.show();
this._editor.hide();
}
ReplyComposer.prototype.setSendUrl = function(url) {
this._sendUrl = url;
};
ReplyComposer.prototype.setPostData = function(data) {
this._editor.setPostData(data);
};
ReplyComposer.prototype.setThread = function(thread) {
this._thread = thread;
};
ReplyComposer.prototype.addMessage = function(message) {
this._thread.addMessage(message);
};
ReplyComposer.prototype.createDom = function() {
this._element = this.makeElement('div');
this._element.addClass('reply-composer');
var opener = this.makeElement('a');
opener.html(gettext('Reply'));
this._opener = opener;
this._element.append(opener);
var editor = new ReplyMessageComposer();
editor.setSendUrl(this._sendUrl);
editor.setParent(this);
editor.onSendSuccess(function() {
editor.cancel();
notify.show(gettext('message sent'), true);
});
this._editor = editor;
this._element.append(editor.getElement());
var me = this;
setupButtonEventHandlers(opener, function() { me.open() });
editor.onAfterCancel(function() { me.close() });
this.hide();
};
/**
* @constructor
*/
var NewThreadComposer = function() {
MessageComposer.call(this);
};
inherits(NewThreadComposer, MessageComposer);
NewThreadComposer.prototype.cancel = function() {
this._toInput.val('');
this._toInputError.html('');
NewThreadComposer.superClass_.cancel.call(this);
};
NewThreadComposer.prototype.onAfterShow = function() {
this._toInput.focus();
};
NewThreadComposer.prototype.onSendErrorInternal = function(data) {
var missingUsers = data['missing_users']
var errors = [];
if (missingUsers) {
var errorTpl = ngettext(
'user {{str}} does not exist',
'users {{str}} do not exist',
missingUsers.length
)
errors.push(errorTpl.replace('{{str}}', joinAsPhrase(missingUsers)));
}
if (data['self_message']) {
errors.push(gettext('cannot send message to yourself'));
}
this._toInputError.html(errors.join(', '));
};
NewThreadComposer.prototype.getInputData = function() {
var data = NewThreadComposer.superClass_.getInputData.call(this);
data['to_usernames'] = $.trim(this._toInput.val());
return data;
};
NewThreadComposer.prototype.dataIsValid = function() {
var superIsValid = NewThreadComposer.superClass_.dataIsValid.call(this);
var to = $.trim(this._toInput.val());
if (to === '') {
this._toInputError.html(gettext('required'));
return false;
}
return superIsValid;
};
NewThreadComposer.prototype.createDom = function() {
NewThreadComposer.superClass_.createDom.call(this);
var element = this.getElement();
var toInput = this.makeElement('input');
toInput.addClass('recipients');
element.prepend(toInput);
this._toInput = toInput;
var userSelectHandler = function() {};
var usersAc = new AutoCompleter({
url: '/get-users-info/',//askbot['urls']['get_users_info'],
minChars: 1,
useCache: true,
matchInside: true,
maxCacheLength: 100,
delay: 10,
onItemSelect: userSelectHandler
});
usersAc.decorate(toInput);
var label = this.makeElement('label');
label.html(gettext('Recipient:'));
element.prepend(label);
var error = this.makeElement('label');
this._element.append($('
'));
error.addClass('errors');
this._toInputError = error;
label.after(error);
};
var ThreadHeading = function() {
SimpleControl.call(this);
};
inherits(ThreadHeading, SimpleControl);
ThreadHeading.prototype.setParent = function(elem) {
this._threadsList = elem;
};
ThreadHeading.prototype.getParent = function() {
return this._threadsList;
};
ThreadHeading.prototype.getId = function() {
return this._id;
};
ThreadHeading.prototype.decorate = function(element) {
this._element = element;
this._id = element.data('threadId');
var deleter = element.find('.delete-or-restore');
var me = this;
setupButtonEventHandlers($(deleter), function() {
me.getParent().deleteOrRestoreThread(me.getId());
return false;
});
};
/**
* @constructor
*/
var ThreadsList = function() {
HideableWidget.call(this);
};
inherits(ThreadsList, HideableWidget);
ThreadsList.prototype.setMessageCenter = function(ctr) {
this._messageCenter = ctr;
};
ThreadsList.prototype.getOpenThreadHandler = function(threadId) {
var messageCenter = this._messageCenter;
return function() {
messageCenter.openThread(threadId);
};
};
ThreadsList.prototype.deleteOrRestoreThread = function(threadId) {
var ctr = this._messageCenter;
ctr.deleteOrRestoreThread(threadId, this._senderId);
};
ThreadsList.prototype.getThreadsCount = function() {
if (self._threads) {
return self._threads.length;
} else {
return 0;
}
};
ThreadsList.prototype.decorate = function(element) {
this._element = element;
var headingElements = element.find('tr.thread-heading');
var me = this;
var threads = [];
$.each(headingElements, function(idx, headingElement) {
var heading = new ThreadHeading();
heading.setParent(me);
heading.decorate($(headingElement));
var threadId = heading.getId();
heading.setHandler(me.getOpenThreadHandler(threadId));
threads.push(heading);
});
this._threads = threads;
this._senderId = element.data('senderId');
}
/**
* @constructor
*/
var Message = function() {
Widget.call(this);
};
inherits(Message, Widget);
Message.prototype.getId = function() {
return this._id;
};
Message.prototype.decorate = function(element) {
this._element = element;
this._id = element.data('messageId');
};
/**
* @constructor
*/
var ThreadContainer = function() {
HideableWidget.call(this);
};
inherits(ThreadContainer, HideableWidget);
ThreadContainer.prototype.show = function() {
ThreadContainer.superClass_.show.call(this);
this._editor.close();
this._editor.show();
};
ThreadContainer.prototype.hide = function() {
ThreadContainer.superClass_.hide.call(this);
this._editor.close();
this._editor.hide();
};
/**
* sets html content part of the thread
* and re-decorates it
*/
ThreadContainer.prototype.setContent = function(html) {
if (this._thread) {
this._thread.dispose();
}
var thread = new Thread();
thread.decorate($(html));
this._thread = thread;
this._contentElement.empty();
this._contentElement.append(thread.getElement());
var postData = {parent_id: thread.getLastMessageId()};
this._editor.setPostData(postData);
this._editor.setThread(thread);
};
ThreadContainer.prototype.setReplyUrl = function(url) {
this._replyUrl = url;
};
ThreadContainer.prototype.appendEditor = function() {
var editor = new ReplyComposer();
editor.setSendUrl(this._replyUrl);
this._element.append(editor.getElement());
this._editor = editor;
editor.show();
};
ThreadContainer.prototype.createDom = function() {
this._element = this.makeElement('div');
var content = this.makeElement('div');
this._contentElement = content;
this._element.append(content);
this.appendEditor();
};
ThreadContainer.prototype.decorate = function(element) {
this._element = element;
this._contentElement = $(element.children()[0]);
var thread = new Thread();
thread.decorate(element.find('.thread'));
this.appendEditor();
var postData = {parent_id: thread.getLastMessageId()};
this._editor.setPostData(postData);
this._editor.setThread(thread);
};
/**
* @constructor
*/
var Thread = function() {
WrappedElement.call(this);
};
inherits(Thread, WrappedElement);
Thread.prototype.getLastMessageId = function() {
return this._messages.slice(-1)[0].getId();
};
Thread.prototype.dispose = function() {
$.each(this._messages, function(idx, message) {
message.dispose()
});
Thread.superClass_.dispose.call(this);
};
Thread.prototype.addMessage = function(message) {
var li = this.makeElement('li');
this._element.append(li);
li.append(message.getElement());
};
Thread.prototype.decorate = function(element) {
this._element = element;
var messages = [];
$.each(element.find('.message'), function(idx, item) {
var message = new Message();
message.decorate($(item));
messages.push(message);
});
this._messages = messages;
};
/**
* @constructor
*/
var Sender = function() {
SimpleControl.call(this);
};
inherits(Sender, SimpleControl);
Sender.prototype.getId = function() {
return this._id;
};
Sender.prototype.select = function() {
this._element.addClass('selected');
};
Sender.prototype.unselect = function() {
this._element.removeClass('selected');
};
Sender.prototype.decorate = function(element) {
Sender.superClass_.decorate.call(this, element);
this._id = element.data('senderId');
};
/**
* @constructor
* list of senders in the first column of inbox
*/
var SendersList = function() {
WrappedElement.call(this);
this._messageCenter = undefined;
};
inherits(SendersList, WrappedElement);
SendersList.prototype.setMessageCenter = function(ctr) {
this._messageCenter = ctr;
};
SendersList.prototype.getSenders = function() {
return this._senders;
};
SendersList.prototype.getSenderSelectHandler = function(sender) {
var messageCenter = this._messageCenter;
var me = this;
return function() {
$.map(me.getSenders(), function(s){ s.unselect() });
sender.select();
messageCenter.loadThreadsForSender(sender.getId());
};
};
SendersList.prototype.decorate = function(element) {
this._element = element;
var senders = [];
$.each(element.find('a'), function(idx, item) {
var sender = new Sender();
sender.decorate($(item));
senders.push(sender);
});
this._senders = senders;
var me = this;
$.each(senders, function(idx, sender) {
sender.setHandler(me.getSenderSelectHandler(sender));
});
};
/**
* @contsructor
*/
var MessageCenter = function() {
Widget.call(this);
this._loadingStatus = false;//true when loading in is process
};
inherits(MessageCenter, Widget);
MessageCenter.prototype.setState = function(state) {
this._editor.hide();
this._threadsList.hide();
this._threadContainer.hide();
if (state === 'compose') {
this._editor.show();
} else if (state === 'show-list') {
this._threadsList.show();
} else if (state === 'show-thread') {
this._threadContainer.show();
}
};
MessageCenter.prototype.openThread = function(threadId) {
var url = this._urls['getThreads'] + threadId + '/';
var me = this;
var threadContainer = this._threadContainer;
$.ajax({
type: 'GET',
dataType: 'json',
url: url,
cache: false,
success: function(data) {
if (data['success']) {
threadContainer.setContent(data['html']);
me.setState('show-thread');
}
}
});
};
MessageCenter.prototype.setThreadsList = function(list) {
this._threadsList = list;
this._secondCol.prepend(list.getElement());
};
MessageCenter.prototype.setLoadingStatus = function(loadingStatus) {
this._loadingStatus = loadingStatus;
};
MessageCenter.prototype.hitThreadsList = function(url, senderId, requestMethod) {
if (this._loadingStatus === true) {
return;
};
var threadsList = this._threadsList;
var me = this;
$.ajax({
type: requestMethod,
dataType: 'json',
url: url,
cache: false,
data: {sender_id: senderId},
success: function(data) {
if (data['success']) {
threadsList.dispose();
var threads = new ThreadsList();
threads.setMessageCenter(me);
threads.decorate($(data['html']));
me.setThreadsList(threads);
me.setState('show-list');
me.setLoadingStatus(false);
},
error: function() {
me.setLoadingStatus(false);
}
}
});
this.setLoadingStatus(true);
};
MessageCenter.prototype.deleteOrRestoreThread = function(threadId, senderId) {
var url = this._urls['getThreads'] + threadId + '/delete-or-restore/';
this.hitThreadsList(url, senderId, 'POST');
};
MessageCenter.prototype.loadThreadsForSender = function(senderId) {
var url = this._urls['getThreads'];
this.hitThreadsList(url, senderId, 'GET');
};
MessageCenter.prototype.decorate = function(element) {
this._element = element;
this._firstCol = element.find('.first-col');
this._secondCol = element.find('.second-col');
this._urls = {
getThreads: element.data('getThreadsUrl'),
getThreadDetails: element.data('getThreadDetailsUrl'),
reply: element.data('replyUrl')
};
//read sender list
var senders = new SendersList();
senders.setMessageCenter(this);
senders.decorate($('.senders-list'));
this._sendersList = senders;
//read message list
var threads = new ThreadsList();
threads.setMessageCenter(this);
threads.decorate($('.threads-list'));
this._threadsList = threads;
//add empty thread container or decorate existing one
var threadContainer = new ThreadContainer();
this._threadContainer = threadContainer;
threadContainer.setReplyUrl(this._urls['reply']);
var threadElement = $('.thread').parent().parent();
if (threadElement.length) {
threadContainer.decorate(threadElement);
} else {
this._secondCol.append(threadContainer.getElement());
threadContainer.hide();
}
var me = this;
//create editor
var editor = new NewThreadComposer();
this._secondCol.append(editor.getElement());
editor.setSendUrl(element.data('createThreadUrl'));
editor.onAfterCancel(function() {
me.setState('show-list')
});
editor.onSendSuccess(function() {
if (threads.getThreadsCount() === 0) {
me.loadThreadsForSender(-1);
}
editor.cancel();
notify.show(gettext('message sent'), true);
});
this._editor = editor;
//activate compose button
var btn = element.find('button.compose');
this._composeBtn = btn;
setupButtonEventHandlers(btn, function(){ me.setState('compose') });
};
var msgCtr = new MessageCenter();
msgCtr.decorate($('.group-messaging'));