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'));