chrome_point_plus/chrome_point_plus/modules/ajax-comments.js

252 lines
7.4 KiB
JavaScript
Raw Normal View History

/**
* Находит элемент #comments, и если он есть, начинает слушать на нём события
* @constructor
*/
function AjaxComments() {
var comments = document.querySelector('#comments');
if (comments) {
this._comments = comments;
this.listen(comments);
this.listenFirstComments();
}
}
/**
* Вешает обработчики. Магия в последнем параметре addEventListener
*/
AjaxComments.prototype.listen = function(elem) {
elem.addEventListener('submit', this.onSubmit.bind(this), true);
elem.addEventListener('keypress', this.onKeypress.bind(this), true);
};
/**
* Слушает отправки первых комментариев
*/
AjaxComments.prototype.listenFirstComments = function() {
var posts = document.querySelectorAll('.post-content');
// Чтобы не ловить события на чём попало и мочь использовать useCapture, приходится
// получать все post-content и на каждый вешать обработчики.
Array.prototype.forEach.call(posts, this.listen.bind(this));
};
/**
* Обрабатывает событие отправки формы
* @param {Event} event Событие отправки
*/
AjaxComments.prototype.onSubmit = function(event) {
var $form = $(event.target);
event.preventDefault();
event.stopPropagation();
if ($form.hasClass('reply-form')) {
this.submit($form);
}
};
/**
* Проверяет, выбран ли файл. Если да отправляет форму с перезагрузкой, иначе аяксом 
* @param {jQuery} $form Элемент формы
*/
AjaxComments.prototype.submit = function($form) {
var proc;
if (this.isFileSelected($form)) {
$form.submit();
} else {
proc = new AjaxCommentProcessor($form);
}
};
/**
* Обрабатывает нажатия кнопок. Если это сочетание Ctrl|+Enter, отправляет коммент
* @param {Event} event Событие нажатия кнопки
*/
AjaxComments.prototype.onKeypress = function(event) {
var $form;
var proc;
if (this.isProperKeys(event)) {
event.preventDefault();
event.stopPropagation();
$form = $(event.target).closest('.reply-form');
if ($form.length) {
this.submit($form);
}
}
};
/**
* Проверяет, что нажато нужное сочетание клавишь
* @param {Event} event Событие нажатия
* @return {Boolean}
*/
AjaxComments.prototype.isProperKeys = function(event) {
return (event.keyCode === 10 || event.keyCode === 13) && (event.ctrlKey || event.metaKey);
};
/**
* @param {jQuery} $form
* @return {Boolean} Выбран ли файл
*/
AjaxComments.prototype.isFileSelected = function($form) {
var attach = $form.get(0).elements.attach;
if (attach) {
return Boolean(attach.files && attach.files.length);
} else {
return false;
}
};
/**
* Создаётся при каждой отправке комментария
* @param {jQuery} $form Элемент формы, на которой это произошло
*/
function AjaxCommentProcessor($form) {
this._$form = $form;
this._$post = $form.closest('.post');
this._$textarea = $form.find('textarea');
this._text = this._$textarea.val();
this._CSRF = $form.get(0).elements.csrf_token.value;
this._postId = this._$post.data('id');
this._commentId = this._$post.data('comment-id');
this.sendComment();
}
/**
* Отправляет комментарий
*/
AjaxCommentProcessor.prototype.sendComment = function() {
this.setProgress(true);
$.ajax({
type: 'POST',
url: '/api/post' + this._$form.attr('action'),
data: {
text: this._text,
comment_id: this._commentId
},
beforeSend: this.beforeSend.bind(this),
error: this.onError.bind(this),
success: this.onSuccess.bind(this)
});
};
/**
* Подкладывает CSRF-токен в заголовки запроса
* @param {XMLHttpRequest} xhr Объект запроса
*/
AjaxCommentProcessor.prototype.beforeSend = function(xhr) {
xhr.setRequestHeader('X-CSRF', this._CSRF);
};
/**
* Скрывает форму отправки комментария
* @return {[type]} [description]
*/
AjaxCommentProcessor.prototype.hideForm = function() {
this._$form.prev().prop('checked', false);
};
/**
* Создаёт новый комментарий, скрывает форму, снимает прогресс.
* @param {Object} data Ответ сервера
* @param {String} textStatus Статус ответа
*/
AjaxCommentProcessor.prototype.onSuccess = function(data, textStatus) {
var $textarea = this._$textarea;
if (textStatus === 'success') {
if (data.error) {
this.onError(null, null, data.error);
} else {
this.createComment(data);
this.hideForm();
// Cleaning textarea
$textarea.val('');
this.setProgress(false);
}
}
};
AjaxCommentProcessor.prototype.createComment = function(data) {
create_comment_elements({
id: data.comment_id,
toId: this._commentId || null,
postId: this._postId,
author: $('#name h1').text(),
text: this._text,
fadeOut: true
}, this.insertComment.bind(this));
};
/**
* Вставляет комментарий в DOM
* @param {jQuery} $comment
*/
AjaxCommentProcessor.prototype.insertComment = function($comment) {
var $parentCommentChildren;
if ($('#comments #tree-switch a').eq(0).hasClass('active') || (this._commentId === undefined)) {
// Adding to the end of the list
$('.content-wrap #comments #post-reply').before($comment);
} else {
// Check for children
$parentCommentChildren = this._$post.next('.comments');
// @fixme Find a bug with lost indentation of new comment
// If child comment already exist
if ($parentCommentChildren.length) {
console.log('Child comments found. Appending...');
$parentCommentChildren.append($comment);
} else {
console.log('No child comments found. Creating...');
this._$post.after(
$('<div>')
.addClass('comments')
.append($comment)
);
}
}
this.showComment($comment);
};
AjaxCommentProcessor.prototype.showComment = function($comment) {
$('body').animate({
scrollTop: $comment.offset().top
}, 500);
};
/**
* Показывает алерт с ошибкой и снимает прогресс, если коммент не отправился
* @param {*} req
* @param {*} status
* @param {String} error
*/
AjaxCommentProcessor.prototype.onError = function(req, status, error) {
alert(chrome.i18n.getMessage('msg_comment_send_failed') + '\n' + error);
this.setProgress(false);
};
/**
* Устанавливает прогресс
* @param {Boolean} isProgress true включить прогресс, false  отключить
*/
AjaxCommentProcessor.prototype.setProgress = function(isProgress) {
this._$textarea.prop('disabled', isProgress);
this._$form.toggleClass('pp-progress', isProgress);
};