diff --git a/chrome_point_plus/js/point-plus.js b/chrome_point_plus/js/point-plus.js
index 44efbd5..17e1c9e 100644
--- a/chrome_point_plus/js/point-plus.js
+++ b/chrome_point_plus/js/point-plus.js
@@ -503,100 +503,10 @@ function PointPlus(ppVersion) {
if (options.is('option_ajax')) {
// Comments
if (options.is('option_ajax_comments')) {
- // Removing old bindings
- // Dirty hack for page context
- $('#comments').replaceWith($('#comments').clone());
-
- // Binding new
- $('#comments').on('keypress.pp', '.reply-form textarea', function (evt) {
- var $textarea = $(this);
- var $post;
- var $form;
- var processClass = 'pp-progress'
- var csRf;
-
- if ((evt.keyCode === 10 || evt.keyCode === 13) && (evt.ctrlKey || evt.metaKey)) {
- evt.stopPropagation();
- evt.preventDefault();
-
- $post = $textarea.parents('.post').first();
- csRf = $textarea.siblings('input[name="csrf_token"]').val();
-
- $textarea.prop('disabled', true);
- $form = $textarea.parent();
- $form.addClass(processClass);
-
- $.ajax({
- type: 'POST',
- url: '/api/post/' + $post.data('id'),
- data: {
- text: $textarea.val(),
- comment_id: $post.data('comment-id')
- },
- error: function(req, status, error) {
- console.error('AJAX request error while sending the comment: %s', error);
- console.log('Status: %s', status);
-
- alert(chrome.i18n.getMessage('msg_comment_send_failed') + '\n' + error);
-
- $textarea.prop('disabled', false);
- $form.removeClass(processClass);
- },
- /**
- * @param {object} data Response data
- * @param {number} data.comment_id ID of the created comment
- * @param {string} data.id ID of the post
- * @param {string} textStatus Text of request status
- */
- success: function(data, textStatus) {
- console.log('data %O', data);
- console.log('status %O', textStatus);
-
- if (textStatus === 'success') {
- // Hiding form
- $('#reply-' + $post.data('id') + '_' + $post.data('comment-id')).prop('checked', false);
-
- // Creating the comment HTML
- create_comment_elements({
- id: data.comment_id,
- toId: $post.data('comment-id') || null,
- postId: $post.data('id'),
- author: $('#name h1').text(),
- text: $textarea.val(),
- fadeOut: true
- }, function($comment) {
- // If list mode or not addressed to other comment
- if ($('#comments #tree-switch a').eq(0).hasClass('active') || ($post.data('comment-id') === undefined)) {
- // Adding to the end of the list
- $('.content-wrap #comments #post-reply').before($comment);
- } else {
- // Check for children
- $parentCommentChildren = $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...');
- $post.after($('
').addClass('comments').append($comment));
- }
- }
- });
-
- // Cleaning textarea
- $textarea.val('');
- $textarea.prop('disabled', false);
-
- $form.removeClass(processClass);
- }
- },
- beforeSend: function (xhr) {
- xhr.setRequestHeader('X-CSRF', csRf);
- }
- });
- }
+ messenger.js({
+ file: 'modules/ajax-comments.js'
+ }, function() {
+ var ajaxComments = new AjaxComments();
});
}
}
diff --git a/chrome_point_plus/modules/ajax-comments.js b/chrome_point_plus/modules/ajax-comments.js
new file mode 100644
index 0000000..61fadde
--- /dev/null
+++ b/chrome_point_plus/modules/ajax-comments.js
@@ -0,0 +1,209 @@
+/**
+ * Находит элемент #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);
+ var proc;
+
+ event.preventDefault();
+ event.stopPropagation();
+
+ if ($form.hasClass('reply-form')) {
+ 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) {
+ proc = new AjaxCommentProcessor($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 Элемент формы, на которой это произошло
+ */
+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._postId,
+ 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') {
+ this.hideForm();
+
+ // Creating the comment HTML
+ 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));
+
+ // Cleaning textarea
+ $textarea.val('');
+ this.setProgress(false);
+ }
+};
+
+/**
+ * Вставляет комментарий в 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(
+ $('
')
+ .addClass('comments')
+ .append($comment)
+ );
+ }
+ }
+};
+
+/**
+ * Показывает алерт с ошибкой и снимает прогресс, если коммент не отправился
+ * @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);
+};