]> BookStack Code Mirror - bookstack/blob - resources/assets/js/components/page-comments.js
Update maintenance.php
[bookstack] / resources / assets / js / components / page-comments.js
1 import MarkdownIt from "markdown-it";
2 const md = new MarkdownIt({ html: false });
3
4 class PageComments {
5
6     constructor(elem) {
7         this.elem = elem;
8         this.pageId = Number(elem.getAttribute('page-id'));
9         this.editingComment = null;
10         this.parentId = null;
11
12         this.container = elem.querySelector('[comment-container]');
13         this.formContainer = elem.querySelector('[comment-form-container]');
14
15         if (this.formContainer) {
16             this.form = this.formContainer.querySelector('form');
17             this.formInput = this.form.querySelector('textarea');
18             this.form.addEventListener('submit', this.saveComment.bind(this));
19         }
20
21         this.elem.addEventListener('click', this.handleAction.bind(this));
22         this.elem.addEventListener('submit', this.updateComment.bind(this));
23     }
24
25     handleAction(event) {
26         let actionElem = event.target.closest('[action]');
27         if (event.target.matches('a[href^="#"]')) {
28             let id = event.target.href.split('#')[1];
29             window.scrollAndHighlight(document.querySelector('#' + id));
30         }
31         if (actionElem === null) return;
32         event.preventDefault();
33
34         let action = actionElem.getAttribute('action');
35         if (action === 'edit') this.editComment(actionElem.closest('[comment]'));
36         if (action === 'closeUpdateForm') this.closeUpdateForm();
37         if (action === 'delete') this.deleteComment(actionElem.closest('[comment]'));
38         if (action === 'addComment') this.showForm();
39         if (action === 'hideForm') this.hideForm();
40         if (action === 'reply') this.setReply(actionElem.closest('[comment]'));
41         if (action === 'remove-reply-to') this.removeReplyTo();
42     }
43
44     closeUpdateForm() {
45         if (!this.editingComment) return;
46         this.editingComment.querySelector('[comment-content]').style.display = 'block';
47         this.editingComment.querySelector('[comment-edit-container]').style.display = 'none';
48     }
49
50     editComment(commentElem) {
51         this.hideForm();
52         if (this.editingComment) this.closeUpdateForm();
53         commentElem.querySelector('[comment-content]').style.display = 'none';
54         commentElem.querySelector('[comment-edit-container]').style.display = 'block';
55         let textArea = commentElem.querySelector('[comment-edit-container] textarea');
56         let lineCount = textArea.value.split('\n').length;
57         textArea.style.height = ((lineCount * 20) + 40) + 'px';
58         this.editingComment = commentElem;
59     }
60
61     updateComment(event) {
62         let form = event.target;
63         event.preventDefault();
64         let text = form.querySelector('textarea').value;
65         let reqData = {
66             text: text,
67             html: md.render(text),
68             parent_id: this.parentId || null,
69         };
70         this.showLoading(form);
71         let commentId = this.editingComment.getAttribute('comment');
72         window.$http.put(window.baseUrl(`/ajax/comment/${commentId}`), reqData).then(resp => {
73             let newComment = document.createElement('div');
74             newComment.innerHTML = resp.data;
75             this.editingComment.innerHTML = newComment.children[0].innerHTML;
76             window.$events.emit('success', window.trans('entities.comment_updated_success'));
77             window.components.init(this.editingComment);
78             this.closeUpdateForm();
79             this.editingComment = null;
80             this.hideLoading(form);
81         });
82     }
83
84     deleteComment(commentElem) {
85         let id = commentElem.getAttribute('comment');
86         this.showLoading(commentElem.querySelector('[comment-content]'));
87         window.$http.delete(window.baseUrl(`/ajax/comment/${id}`)).then(resp => {
88             commentElem.parentNode.removeChild(commentElem);
89             window.$events.emit('success', window.trans('entities.comment_deleted_success'));
90             this.updateCount();
91             this.hideForm();
92         });
93     }
94
95     saveComment(event) {
96         event.preventDefault();
97         event.stopPropagation();
98         let text = this.formInput.value;
99         let reqData = {
100             text: text,
101             html: md.render(text),
102             parent_id: this.parentId || null,
103         };
104         this.showLoading(this.form);
105         window.$http.post(window.baseUrl(`/ajax/page/${this.pageId}/comment`), reqData).then(resp => {
106             let newComment = document.createElement('div');
107             newComment.innerHTML = resp.data;
108             let newElem = newComment.children[0];
109             this.container.appendChild(newElem);
110             window.components.init(newElem);
111             window.$events.emit('success', window.trans('entities.comment_created_success'));
112             this.resetForm();
113             this.updateCount();
114         });
115     }
116
117     updateCount() {
118         let count = this.container.children.length;
119         this.elem.querySelector('[comments-title]').textContent = window.trans_choice('entities.comment_count', count, {count});
120     }
121
122     resetForm() {
123         this.formInput.value = '';
124         this.formContainer.appendChild(this.form);
125         this.hideForm();
126         this.removeReplyTo();
127         this.hideLoading(this.form);
128     }
129
130     showForm() {
131         this.formContainer.style.display = 'block';
132         this.formContainer.parentNode.style.display = 'block';
133         this.elem.querySelector('[comment-add-button-container]').style.display = 'none';
134         this.formInput.focus();
135         window.scrollToElement(this.formInput);
136     }
137
138     hideForm() {
139         this.formContainer.style.display = 'none';
140         this.formContainer.parentNode.style.display = 'none';
141         const addButtonContainer = this.elem.querySelector('[comment-add-button-container]');
142         if (this.getCommentCount() > 0) {
143             this.elem.appendChild(addButtonContainer)
144         } else {
145             const countBar = this.elem.querySelector('[comment-count-bar]');
146             countBar.appendChild(addButtonContainer);
147         }
148         addButtonContainer.style.display = 'block';
149     }
150
151     getCommentCount() {
152         return this.elem.querySelectorAll('.comment-box[comment]').length;
153     }
154
155     setReply(commentElem) {
156         this.showForm();
157         this.parentId = Number(commentElem.getAttribute('local-id'));
158         this.elem.querySelector('[comment-form-reply-to]').style.display = 'block';
159         let replyLink = this.elem.querySelector('[comment-form-reply-to] a');
160         replyLink.textContent = `#${this.parentId}`;
161         replyLink.href = `#comment${this.parentId}`;
162     }
163
164     removeReplyTo() {
165         this.parentId = null;
166         this.elem.querySelector('[comment-form-reply-to]').style.display = 'none';
167     }
168
169     showLoading(formElem) {
170         let groups = formElem.querySelectorAll('.form-group');
171         for (let i = 0, len = groups.length; i < len; i++) {
172             groups[i].style.display = 'none';
173         }
174         formElem.querySelector('.form-group.loading').style.display = 'block';
175     }
176
177     hideLoading(formElem) {
178         let groups = formElem.querySelectorAll('.form-group');
179         for (let i = 0, len = groups.length; i < len; i++) {
180             groups[i].style.display = 'block';
181         }
182         formElem.querySelector('.form-group.loading').style.display = 'none';
183     }
184
185 }
186
187 export default PageComments;