2015-02-05 19:28:01 +00:00
|
|
|
|
/**
|
|
|
|
|
* Находит элемент #comments, и если он есть, начинает слушать на нём события
|
|
|
|
|
* @constructor
|
|
|
|
|
*/
|
|
|
|
|
function AjaxComments() {
|
|
|
|
|
var comments = document.querySelector('#comments');
|
|
|
|
|
|
|
|
|
|
if (comments) {
|
|
|
|
|
this._comments = comments;
|
|
|
|
|
this.listen(comments);
|
|
|
|
|
|
2015-02-07 17:15:20 +00:00
|
|
|
|
this.listenFirstComments();
|
|
|
|
|
}
|
2015-02-05 19:28:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Вешает обработчики. Магия в последнем параметре 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')) {
|
2015-02-07 16:33:18 +00:00
|
|
|
|
this.submit($form);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
2015-02-08 18:01:27 +00:00
|
|
|
|
* Проверяет, выбран ли файл. Если да — отправляет форму с перезагрузкой, иначе — аяксом
|
2015-02-07 16:33:18 +00:00
|
|
|
|
* @param {jQuery} $form Элемент формы
|
|
|
|
|
*/
|
|
|
|
|
AjaxComments.prototype.submit = function($form) {
|
|
|
|
|
var proc;
|
|
|
|
|
|
|
|
|
|
if (this.isFileSelected($form)) {
|
|
|
|
|
$form.submit();
|
|
|
|
|
} else {
|
2015-02-05 19:28:01 +00:00
|
|
|
|
proc = new AjaxCommentProcessor($form);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Обрабатывает нажатия кнопок. Если это сочетание Ctrl|⌘+Enter, отправляет коммент
|
|
|
|
|
* @param {Event} event Событие нажатия кнопки
|
|
|
|
|
*/
|
|
|
|
|
AjaxComments.prototype.onKeypress = function(event) {
|
|
|
|
|
var $form;
|
2015-02-08 18:01:27 +00:00
|
|
|
|
|
2015-02-05 19:28:01 +00:00
|
|
|
|
if (this.isProperKeys(event)) {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
event.stopPropagation();
|
|
|
|
|
|
|
|
|
|
$form = $(event.target).closest('.reply-form');
|
|
|
|
|
|
|
|
|
|
if ($form.length) {
|
2015-02-07 16:33:18 +00:00
|
|
|
|
this.submit($form);
|
2015-02-05 19:28:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Проверяет, что нажато нужное сочетание клавишь
|
|
|
|
|
* @param {Event} event Событие нажатия
|
|
|
|
|
* @return {Boolean}
|
|
|
|
|
*/
|
|
|
|
|
AjaxComments.prototype.isProperKeys = function(event) {
|
|
|
|
|
return (event.keyCode === 10 || event.keyCode === 13) && (event.ctrlKey || event.metaKey);
|
|
|
|
|
};
|
|
|
|
|
|
2015-02-07 16:33:18 +00:00
|
|
|
|
/**
|
|
|
|
|
* @param {jQuery} $form
|
|
|
|
|
* @return {Boolean} Выбран ли файл
|
|
|
|
|
*/
|
|
|
|
|
AjaxComments.prototype.isFileSelected = function($form) {
|
2015-02-08 13:58:55 +00:00
|
|
|
|
var attach = $form.get(0).elements.attach;
|
2015-02-07 16:33:18 +00:00
|
|
|
|
|
2015-02-08 13:58:55 +00:00
|
|
|
|
if (attach) {
|
|
|
|
|
return Boolean(attach.files && attach.files.length);
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2015-02-07 16:33:18 +00:00
|
|
|
|
};
|
|
|
|
|
|
2015-02-05 19:28:01 +00:00
|
|
|
|
/**
|
|
|
|
|
* Создаётся при каждой отправке комментария
|
|
|
|
|
* @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;
|
|
|
|
|
|
2015-02-08 18:01:27 +00:00
|
|
|
|
this._actionUrl = this._$form.attr('action');
|
2015-02-05 19:28:01 +00:00
|
|
|
|
this._postId = this._$post.data('id');
|
|
|
|
|
this._commentId = this._$post.data('comment-id');
|
|
|
|
|
|
|
|
|
|
this.sendComment();
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-08 18:01:27 +00:00
|
|
|
|
/**
|
|
|
|
|
* Регулярка для урла, проверяюшая, не рекомендация ли это
|
|
|
|
|
* @type {RegExp}
|
|
|
|
|
*/
|
|
|
|
|
AjaxCommentProcessor.recommendationReg = /^\/[^/]*\/r$/;
|
|
|
|
|
|
2015-02-05 19:28:01 +00:00
|
|
|
|
/**
|
|
|
|
|
* Отправляет комментарий
|
|
|
|
|
*/
|
|
|
|
|
AjaxCommentProcessor.prototype.sendComment = function() {
|
|
|
|
|
this.setProgress(true);
|
|
|
|
|
|
|
|
|
|
$.ajax({
|
|
|
|
|
type: 'POST',
|
2015-02-08 18:01:27 +00:00
|
|
|
|
url: this.getUrl(),
|
2015-02-05 19:28:01 +00:00
|
|
|
|
data: {
|
|
|
|
|
text: this._text,
|
|
|
|
|
comment_id: this._commentId
|
|
|
|
|
},
|
|
|
|
|
beforeSend: this.beforeSend.bind(this),
|
|
|
|
|
error: this.onError.bind(this),
|
|
|
|
|
success: this.onSuccess.bind(this)
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2015-02-08 18:01:27 +00:00
|
|
|
|
/**
|
|
|
|
|
* @return {Boolean} true — это коммент-рекомендация, fasle — обычный коммент
|
|
|
|
|
*/
|
|
|
|
|
AjaxCommentProcessor.prototype.isRecommendation = function() {
|
|
|
|
|
return this._isRec || (this._isRec = this.constructor.recommendationReg.test(this._actionUrl));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return {String} Адрес, на который слать запрос
|
|
|
|
|
*/
|
|
|
|
|
AjaxCommentProcessor.prototype.getUrl = function() {
|
|
|
|
|
// Если это рекомендация комментария
|
|
|
|
|
if (this.isRecommendation() && this._commentId) {
|
|
|
|
|
return '/api/post/' + this._postId + '/' + this._commentId + '/r';
|
|
|
|
|
} else {
|
|
|
|
|
return '/api/post' + this._actionUrl;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2015-02-05 19:28:01 +00:00
|
|
|
|
/**
|
|
|
|
|
* Подкладывает 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') {
|
2015-02-07 16:03:24 +00:00
|
|
|
|
if (data.error) {
|
|
|
|
|
this.onError(null, null, data.error);
|
|
|
|
|
} else {
|
2015-02-08 19:03:39 +00:00
|
|
|
|
if (this.isRecommendation() && this._text.trim().length === 0) {
|
|
|
|
|
this.showSuccessRecommendation();
|
|
|
|
|
} else {
|
|
|
|
|
this.createComment(data);
|
|
|
|
|
}
|
2015-02-07 16:03:24 +00:00
|
|
|
|
this.hideForm();
|
|
|
|
|
|
|
|
|
|
// Cleaning textarea
|
|
|
|
|
$textarea.val('');
|
|
|
|
|
this.setProgress(false);
|
|
|
|
|
}
|
2015-02-05 19:28:01 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2015-02-07 17:11:23 +00:00
|
|
|
|
AjaxCommentProcessor.prototype.createComment = function(data) {
|
2015-02-08 18:01:27 +00:00
|
|
|
|
/* global create_comment_elements */
|
2015-02-07 17:11:23 +00:00
|
|
|
|
create_comment_elements({
|
|
|
|
|
id: data.comment_id,
|
|
|
|
|
toId: this._commentId || null,
|
|
|
|
|
postId: this._postId,
|
|
|
|
|
author: $('#name h1').text(),
|
|
|
|
|
text: this._text,
|
2015-02-08 18:15:29 +00:00
|
|
|
|
fadeOut: true,
|
|
|
|
|
isRec: this.isRecommendation()
|
2015-02-07 17:11:23 +00:00
|
|
|
|
}, this.insertComment.bind(this));
|
|
|
|
|
};
|
|
|
|
|
|
2015-02-05 19:28:01 +00:00
|
|
|
|
/**
|
|
|
|
|
* Вставляет комментарий в 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)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-02-07 17:11:23 +00:00
|
|
|
|
|
|
|
|
|
this.showComment($comment);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
AjaxCommentProcessor.prototype.showComment = function($comment) {
|
|
|
|
|
$('body').animate({
|
|
|
|
|
scrollTop: $comment.offset().top
|
|
|
|
|
}, 500);
|
2015-02-05 19:28:01 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Показывает алерт с ошибкой и снимает прогресс, если коммент не отправился
|
|
|
|
|
* @param {*} req
|
|
|
|
|
* @param {*} status
|
|
|
|
|
* @param {String} error
|
|
|
|
|
*/
|
|
|
|
|
AjaxCommentProcessor.prototype.onError = function(req, status, error) {
|
2015-02-08 18:01:27 +00:00
|
|
|
|
/* global alert */
|
2015-02-05 19:28:01 +00:00
|
|
|
|
alert(chrome.i18n.getMessage('msg_comment_send_failed') + '\n' + error);
|
|
|
|
|
|
|
|
|
|
this.setProgress(false);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Устанавливает прогресс
|
2015-02-08 18:01:27 +00:00
|
|
|
|
* @param {Boolean} isProgress true — включить прогресс, false — отключить
|
2015-02-05 19:28:01 +00:00
|
|
|
|
*/
|
|
|
|
|
AjaxCommentProcessor.prototype.setProgress = function(isProgress) {
|
|
|
|
|
this._$textarea.prop('disabled', isProgress);
|
|
|
|
|
this._$form.toggleClass('pp-progress', isProgress);
|
|
|
|
|
};
|
2015-02-08 19:03:39 +00:00
|
|
|
|
|
|
|
|
|
AjaxCommentProcessor.prototype.getRecommendationLink = function() {
|
|
|
|
|
var url = '//point.im/' + this._postId;
|
|
|
|
|
var text = '#' + this._postId;
|
|
|
|
|
|
|
|
|
|
if (this._commentId) {
|
|
|
|
|
url += '#' + this._commentId;
|
|
|
|
|
text += '/' + this._commentId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return '<a href="' + url + '">' + text + '</a>';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
AjaxCommentProcessor.prototype.showSuccessRecommendation = function() {
|
|
|
|
|
var $notification = $('<div>')
|
|
|
|
|
.addClass('pp-notification pp-notification-success');
|
|
|
|
|
|
|
|
|
|
$notification.html(this.getRecommendationLink() + ' ' + chrome.i18n.getMessage('msg_success_recommendation'));
|
|
|
|
|
|
|
|
|
|
$notification.on('transitionend', function() {
|
|
|
|
|
$notification.remove();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$('body').append($notification);
|
|
|
|
|
|
|
|
|
|
window.requestAnimationFrame(function() {
|
|
|
|
|
$notification.addClass('pp-fade');
|
|
|
|
|
});
|
|
|
|
|
};
|