OSDN Git Service

js: deprecate ns-util.js, use newslash-util.js
[newslash/newslash.git] / src / newslash_web / public / js / comment-tree.js
1 /* comment-tree.js */
2 var commentTree = {};
3
4 commentTree.run = function (params) {
5   /* define exotic parameters */
6   params = params || {};
7   var userConfig = params.userConfig || {};
8   var siteConfig = params.siteConfig || {};
9   var pageInfo = params.pageInfo || {};
10   var user = params.user || {};
11
12   if (!params.el) {
13     console.log('error in commentTree.run(): no element given');
14     return;
15   }
16
17   /*
18    * register <comment-tree>
19    */
20   Vue.component('comment-tree', {
21     template: '#comment-tree-template',
22     props: [
23     ],
24     data: function () {
25       return {
26         comments: [],
27         commentAllowed: pageInfo.comment_allowed,
28         archived: pageInfo.archived,
29         discussionId: pageInfo.discussion_id,
30         rootId: pageInfo.id,
31         parentId: pageInfo.parent_id || 0,
32       };
33     },
34     created: function commentTreeCreated() {
35       newslash.getComments(this.discussionId, this.parentId).then(
36         (resp) => { // success
37           var comments = resp.comments;
38           
39           // build comment tree
40           var commentIndex = {};
41           comments.forEach(comment => {
42             comment._children = [];
43             commentIndex[comment.cid] = comment;
44             if (comment.pid && commentIndex[comment.pid]) {
45               commentIndex[comment.pid]._children.push(comment);
46             }
47           });
48           this.comments = resp.comments;
49         },
50         (resp) => { // fail
51           statusIndicator.error("comment_loading_error");
52         }
53       );
54     },
55   });
56
57   /*
58    * register <comment-header>
59    */
60   Vue.component('comment-header', {
61     template: '#comment-header-template',
62     data: function () {
63       return {
64         config: userConfig || {},
65         savable: user.is_login || 0,
66         commentAllowed: pageInfo.comment_allowed,
67         archived: pageInfo.archived,
68         showPrefs: false,
69       };
70     },
71     methods: {
72       saveConfig: function saveConfig() {
73         const data = {
74           type: "config",
75           config: this.$data.config,
76         };
77         statusIndicator.loading("saving");
78         this.$newslash.post('user', data,
79                             (response) => { // success
80                               statusIndicator.done("saved");
81                             },
82                             (response) => { // fail
83                               statusIndicator.error("save_failed");
84                             }
85                            );
86       }
87     },
88   });
89   
90   /*
91    * register <comment>
92    */
93   Vue.component('comment', {
94     template: '#comment-template',
95     data: function () {
96       return {
97         modReasons: siteConfig.modReasons,
98         moderateReason: 0,
99         moderateMessage: '',
100         metamoderateMessage: '',
101         displayModInfo: 0,
102         loadingMods: 1,
103         noMods: 0,
104         secToken: '',
105         showShareButtons: false,
106
107         displayForce: false,
108         commentAllowed: pageInfo.comment_allowed,
109         archived: pageInfo.archived || false,
110
111         isFormVisible: false,
112       };
113     },
114     props: {
115       comment: Object,
116       preview: { type: Boolean, default: false },
117     },
118     created: function () {
119       if (this.comment._displayForce) {
120         this.displayForce = true;
121       }
122     },
123     computed: {
124       commentLocalTime: function () {
125         if (this.comment.create_time == "") return "";
126         var dt = newslash.util.decodeMySQLDateTime(this.comment.create_time);
127         return newslash.util.formatToLocalDateTime(dt);
128       },
129       isIpidVisible: function () { return user.is_admin; },
130       isM2able: function () { return user.is_login; },
131       signedPoints: function () {
132         var num = this.comment.points;
133         if (num < 0) {
134           return "-" + num;
135         } else if (num > 0) {
136           return "+" + num;
137         }
138         return "0";
139       },
140       reasonText: function () {
141         if (this.comment.reason != 0) {
142           return ":" + siteConfig.modReasons[this.comment.reason];
143         }
144         return "";
145       },
146       isScoreVisible: function () {
147         return !userConfig.comment.hide_score
148           && this.comment.lastmod != 0
149           && this.comment.points != 0;
150       },
151       isFoldedComment: function () {
152         return this.comment.points < userConfig.comment.fold_threshold
153           && !this.comment.isPreview
154           && !this.displayForce;
155       },
156       isHiddenComment: function () {
157         return this.comment.points < userConfig.comment.show_threshold
158           && !this.comment.isPreview
159           && !this.displayForce;
160       },
161       isHiddenBoxMode: function () {
162         if (this.comment.isPreview
163             || this.displayForce
164             || this.comment.points >= userConfig.comment.show_threshold) {
165           return 0;
166         }
167         // 子アイテムの少なくとも1つが表示なら -> 「1件の非表示コメント」表示
168         var count = _countHiddenItems(this.comment);
169         if (count[0] != count[1]) {
170           return 1;
171         }
172         return 0;
173       },
174       hiddenCount: function () {
175         // when point >= threshold: hide "hidden" box.
176         if (this.comment.points >= userConfig.comment.show_threshold) {
177           return 0;
178         }
179         // 子アイテムの少なくとも1つが表示なら -> 「1件の非表示コメント」表示
180         const count = _countHiddenItems(this.comment);
181         if (count[0] != count[1]) {
182           return 1;
183         }
184         // 子アイテムがすべて非表示なら -> 「n件の非表示コメント」表示
185         return count[0];
186       },
187       isModerateEnabled: function () {
188         return this.comment.cid && (user.is_admin || (user.is_moderator && this.commentAllowed));
189       },
190       isReplyEnabled: function () {
191         return this.commentAllowed && !this.comment.isPreview;
192       },
193       isSignatureVisible: function () {
194         return !userConfig.comment.hide_signature && this.comment.signature;
195       },
196     },
197     methods: {
198       //isM2able: isM2able, 
199       showItem: showItem,
200       showModInfo: showModInfo,
201       moderate: moderate,
202       metamoderate: metamoderate, 
203       showReplyForm: showReplyForm,
204       hideReplyForm: hideReplyForm,
205       toggleShareButtons: toggleShareButtons,
206     },
207   });
208
209   /* methods for comment */
210   /*
211   function isM2able(mod) {
212     return typeof mod.m2val == 'undefined' ? 1 : 0;
213   }
214    */
215
216   function showModInfo() {
217     this.loadingMods = 1;
218     this.displayModInfo = !this.displayModInfo;
219     if (!this.displayModInfo) {
220       return;
221     }
222     // get moderation data
223     //this.$http.get(this.urls.moderate + "?cid=" + this.comment.cid).then(
224     this.$newslash.getModerations(this.comment.cid,
225       (response) => { // success
226         if (response.body.sec_token) {
227           this.secToken = response.body.sec_token;
228         }
229         for (var i = 0; i < response.body.moderations.length; i++) {
230           response.body.moderations[i].status = '';
231         }
232         if (response.body.moderations.length > 0) {
233           Vue.set(this.comment, 'moderations', response.body.moderations);
234         } else {
235           Vue.set(this.comment, 'moderations', []);
236           this.noMods = 1;
237         }
238         this.loadingMods = 0;
239       },
240       (response) => { // fail
241       }
242     );
243   }
244
245   function showReplyForm() {
246     this.isFormVisible = true;
247   }
248
249   function hideReplyForm() {
250     this.isFormVisible = false;
251   }
252
253   function toggleShareButtons() {
254     this.showShareButtons = !this.showShareButtons;
255   }
256
257   function showItem() {
258     this.displayForce = true;
259   }
260
261   function moderate() {
262     var data = {
263       reason: Number(this.moderateReason),
264       discussion_id: pageInfo.discussion_id,
265       cid: Number(this.comment.cid),
266       action: 'moderate',
267     };
268     this.loadingMods = 1;
269     this.$http.post(this.urls.moderate, data).then(
270       (response) => { // success
271         this.moderateMessage = "保存しました";
272       },
273       (response) => { // fail
274         if (response.body.message) {
275           this.moderateMessage = "エラー:" + response.body.message;
276         } else {
277           this.moderateMessage = "エラー";
278         }
279       }
280     );
281   }
282
283   function metamoderate(mod, value) {
284     if (mod.m2val == 1 || mod.m2val == -1) {
285       return;
286     }
287     var data = {
288       value: value,
289       moderation_id: mod.id,
290       action: 'metamoderate',
291       sec_token: this.secToken,
292     };
293
294     mod.status = 'm2saving';
295     this.$http.post(this.urls.metamoderate, data).then(
296       (response) => { // success
297         mod.status = 'm2saved';
298         mod.m2val = value;
299       },
300       (response) => { // fail
301         mod.status = 'error';
302         if (response.body.message) {
303           console.log(response.body.message);
304         }
305       }
306     );
307   }
308   
309   function _countHiddenItems(comment) {
310     if (!comment || !comment._children) {
311       console.log("_countHiddenItems: invalid parameter given");
312       return [0, 0];
313     }
314     var result = [1, 0];
315     for (var i = 0; i < comment._children.length; i++) {
316       var ret = _countHiddenItems(comment._children[i]);
317       result[0] += ret[0];
318       result[1] += ret[1];
319     }
320     if (comment.points < userConfig.comment.show_threshold) {
321       result[1]++;
322     }
323     return result;
324   }
325
326   /*
327    * register <comment-form>
328    */
329   Vue.component('comment-form', {
330     template: '#comment-form-template',
331     data: function () {
332       return {
333         isFormVisible: true,
334         isPreviewVisible: false,
335         cancelable: true,
336         rawTitle: "",
337         rawComment: "",
338         title: "",
339         comment: "",
340         author: "",
341         date: "",
342         signature: "",
343         preview: new Comment(),
344         csrfToken: "",
345       };
346     },
347     props: {
348       replyTo: Object,
349       target: Array,
350     },
351     created: function () {
352       if (this.replyTo) {
353         this.rawTitle = "Re: " + this.replyTo.title;
354       } else {
355         this.cancelable = false;
356       }
357     },
358     computed: {
359       isItemFilled: function isItemFilled () { return this.rawTitle.length && this.rawComment.length;}
360     },
361     methods: {
362       cancelEdit: cancelEdit,
363       doPreview: doPreview,
364       leavePreview: leavePreview,
365       postComment: postComment,
366     },
367     
368   });
369
370   function cancelEdit() {
371     this.$emit("finish-edit");
372   }
373
374   function doPreview() {
375     var data = {
376       title: this.rawTitle,
377       comment: this.rawComment,
378       discussion_id: pageInfo.discussion_id,
379       stoid: pageInfo.stoid,
380       pid: this.replyTo ? this.replyTo.pid : 0,
381       action: 'preview',
382     };
383     this.$newslash.post("comment", data, {noCaptcha: 1},
384                         (response) => { // success
385                           this.preview.title = response.body.title;
386                           this.preview.comment = response.body.comment;
387                           this.preview.author = response.body.author;
388                           this.preview.date = response.body.date;
389                           this.preview.signature = response.body.signature;
390                           this.csrfToken = response.body.csrf_token || "";
391                           this.preview.isPreview = true;
392
393                           this.isFormVisible = false;
394                           this.isPreviewVisible = true;
395
396                         },
397                         (response) => { // fail
398                           if (response.body.message) {
399                             statusIndicator.error(response.body.message);
400                           }
401                         }
402                        );
403   }
404
405   function leavePreview() {
406     this.isFormVisible = true;
407     this.isPreviewVisible = false;
408   }
409
410   function postComment() {
411     var data = {
412       title: this.rawTitle,
413       comment: this.rawComment,
414       discussion_id: pageInfo.discussion_id,
415       stoid: pageInfo.stoid,
416       pid: 0,
417       action: 'post',
418       csrf_token: this.csrfToken,
419     };
420
421     if (this.replyTo) {
422       data.pid = this.replyTo.cid;
423     } else if (pageInfo.comment_id) {
424       data.pid = pageInfo.comment_id;
425     }
426
427     this.$newslash.post("comment", data,
428       (response) => { // success
429         var newComment = response.body.new_comment;
430         newComment._children = [];
431         newComment._displayForce = true;
432         //comments.initComment(newComment, this.reply.parent);
433         
434         this.isFormVisible = true;
435         this.isPreviewVisible = false;
436
437         if (this.replyTo) {
438           this.replyTo._children.unshift(newComment);
439         } else {
440           this.target.push(newComment);
441         }
442         this.rawTitle = this.replyTo ? ("Re: " + this.replyTo.title) : "";
443         this.rawComment = "";
444         this.$emit("finish-edit");
445       },
446       (response) => { // fail
447         if (response.body.message) {
448           statusIndicator.error(response.body.message);
449         }
450       }
451     );
452   }
453
454   this.vm = new Vue({el: params.el});
455 };
456