PageSourceSearch

https://sinistrainrete.info/components/com_jcomment/js/main.js

js sinistrainrete.info collected 2026-09-24 09:19:59 UTC 72,037 bytes, 2,101 lines download raw bytes

1/**
2 * JCOMMENT
3 *
4 * @version Joomla!
5 * @package JCOMMENT
6 * @author Joomla! Extensions Store
7 * @copyright (C) 2021 Joomla! Extensions Store
8 * @license GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html
9 */
10//'use strict';
11function JCommentEvents() {}
12
13function JCommentInput() {}
14
15function JCommentIndicator() {
16    this.init();
17}
18
19function JCommentForm(id, editor) {
20    this.init(id, editor);
21}
22
23function JCommentEditor(textarea, resizable) {
24    this.init(textarea, resizable);
25}
26
27function JComment(oi, og, r) {
28    this.init(oi, og, r);
29}
30
31JCommentEvents.prototype = {
32    add: function(o, e, f) {
33        if (o.addEventListener) {
34            o.addEventListener(e, f, false);
35            return true;
36        } else if (o.attachEvent) {
37            return o.attachEvent("on" + e, f);
38        } else {
39            return false;
40        }
41    },
42    remove: function(o, e, f) {
43        if (o.removeEventListener) {
44            o.removeEventListener(e, f, false);
45        } else if (o.detachEvent) {
46            o.detachEvent("on" + e, o[e + f]);
47            o[e + f] = null;
48            o["e" + e + f] = null;
49        }
50    },
51    cancel: function(e) {
52        if (e.stopPropagation) {
53            e.cancelBubble = true;
54            e.preventDefault();
55            e.stopPropagation();
56        }
57        e.returnValue = false;
58        return false;
59    },
60    target: function(e) {
61        var t;
62        if (!e) e = window.event;
63        if (e.target) t = e.target;
64        else if (e.srcElement) t = e.srcElement;
65        if (t.nodeType == 3) t = t.parentNode;
66        return t;
67    }
68};
69
70JCommentInput.prototype = {
71    register: function(el) {
72        if (el) {
73            var th = this,
74                events = new JCommentEvents();
75            events.add(el, 'focus', function(e) {
76                return th.onFocus(e);
77            });
78            events.add(el, 'blur', function(e) {
79                return th.onBlur(e);
80            });
81        }
82    },
83    unregister: function(el) {
84        if (el) {
85            var th = this,
86                events = new JCommentEvents();
87            events.remove(el, 'focus', function(e) {
88                return th.onFocus(e);
89            });
90            events.remove(el, 'blur', function(e) {
91                return th.onBlur(e);
92            });
93        }
94    },
95    onFocus: function(e) {
96        var t = JCommentEvents.prototype.target(e);
97        if (t != null) {
98            t.className = t.className.replace('error', '') + ' selected';
99        }
100    },
101    onBlur: function(e) {
102        var t = JCommentEvents.prototype.target(e);
103        if (t != null) {
104            var c = t.className.replace('error', '');
105            c = c.replace('error', '');
106            c = c.replace('selected', '');
107            t.className = c.replace(/^\s+/g, "");
108        }
109    }
110};
111
112JCommentEditor.prototype = {
113    ta: null,
114    l10n: {},
115    tags: {},
116    smiles: {},
117    events: null,
118    counter: null,
119    focused: false,
120    resizable: true,
121
122    init: function(textareaID, r) {
123        this.ta = JComment.prototype.$(textareaID);
124        this.l10n = {
125            counterPre: '',
126            counterPost: ' symbols left'
127        };
128        this.resizable = r;
129        this.events = new JCommentEvents();
130        this.defaultHeight = this.ta.clientHeight;
131        this.defaultRows = this.ta.rows;
132        if (this.resizable) {
133            this.addGrippie();
134        }
135
136        var th = this;
137        this.events.add(document, !window.opera && document.all ? 'keydown' : 'keypress', function(e) {
138            return th.onKeyPress(e);
139        });
140        this.events.add(this.ta, 'click', function(e) {
141            th.closeSmiles();
142            return th.storeCaret(this.ta);
143        });
144        this.events.add(this.ta, 'select', function(e) {
145            return th.storeCaret(this.ta);
146        });
147        this.events.add(this.ta, 'change', function(e) {
148            th.onChange();
149            return th.storeCaret(this.ta);
150        });
151        this.events.add(this.ta, 'keyup', function(e) {
152            return th.onChange();
153        });
154        this.events.add(this.ta, 'focus', function(e) {
155            th.closeSmiles();
156            return th.focused = true;
157        });
158        this.events.add(this.ta, 'blur', function(e) {
159            return th.focused = false;
160        });
161    },
162    onKeyPress: function(e) {
163        if (!this.focused) {
164            return;
165        }
166        var k = String.fromCharCode(e.keyCode ? e.keyCode : e.charCode),
167            t;
168        for (var id in this.tags) {
169            t = this.tags[id];
170            if (!t.key || k.toUpperCase() != t.key.toUpperCase()) continue;
171            if (t.ctrl && !e[t.ctrl + "Key"]) continue;
172            this.insertTag(id);
173            return this.events.cancel(e);
174        }
175        return e.returnValue;
176    },
177    defined: function(v) {
178        return (typeof(v) != "undefined");
179    },
180    clear: function() {
181        this.ta.value = '';
182        if (this.defaultHeight) {
183            this.ta.style.height = this.defaultHeight + 'px';
184        }
185        this.updateCounter();
186    },
187    focus: function() {
188        this.ta.focus();
189    },
190    storeCaret: function() {
191        var ta = this.ta;
192        if (ta.createTextRange) ta.caretPos = document.selection.createRange().duplicate();
193    },
194    getElementPos: function(e) {
195        var p = {
196            left: 0,
197            top: 0,
198            right: 0,
199            bottom: 0
200        };
201        while (e != null) {
202            p.left += e.offsetLeft;
203            p.top += e.offsetTop;
204            e = e.offsetParent;
205        }
206        p.right += p.left;
207        p.bottom += p.top;
208        return p;
209    },
210    getSelection: function() {
211        var ta = this.ta,
212            s = '';
213        if (document.selection && document.selection.createRange) {
214            s = document.selection.createRange().text;
215        } else {
216            s = ta.value.substr(ta.selectionStart, ta.selectionEnd - ta.selectionStart);
217        }
218        return s;
219    },
220    insertText: function(text) {
221        var ta = this.ta;
222        if (this.defined(ta.caretPos) && ta.createTextRange) {
223            ta.focus();
224            var sel = document.selection.createRange();
225            sel.text = sel.text + text;
226            ta.focus();
227        } else if (this.defined(ta.selectionStart)) {
228            var ss = ta.value.substr(0, ta.selectionStart);
229            var se = ta.value.substr(ta.selectionEnd),
230                sp = ta.scrollTop;
231            ta.value = ss + text + se;
232            if (ta.setSelectionRange) {
233                ta.focus();
234                ta.setSelectionRange(ss.length + text.length, ss.length + text.length);
235            }
236            ta.scrollTop = sp;
237        } else {
238            ta.value += text;
239            ta.focus(ta.value.length - 1);
240        }
241    },
242    surroundText: function(t1, t2) {
243        var ta = this.ta;
244        if (this.defined(ta.caretPos) && ta.createTextRange) {
245            var cp = ta.caretPos,
246                tl = cp.text.length;
247            cp.text = cp.text.charAt(cp.text.length - 1) == ' ' ? t1 + cp.text + t2 + ' ' : t1 + cp.text + t2;
248            if (tl == 0) {
249                cp.moveStart("character", -t2.length);
250                cp.moveEnd("character", -t2.length);
251                cp.select();
252            } else {
253                ta.focus(cp);
254            }
255        } else if (this.defined(ta.selectionStart)) {
256            var ss = ta.value.substr(0, ta.selectionStart),
257                se = ta.value.substr(ta.selectionEnd);
258            var sl = ta.value.substr(ta.selectionStart, ta.selectionEnd - ta.selectionStart);
259            var nc = ta.selectionStart,
260                sp = ta.scrollTop;
261            ta.value = ss + t1 + sl + t2 + se;
262            if (ta.setSelectionRange) {
263                if (sl.length == 0) {
264                    ta.setSelectionRange(nc + t1.length, nc + t1.length);
265                } else {
266                    ta.setSelectionRange(nc, nc + t1.length + sl.length + t2.length);
267                }
268                ta.focus();
269            }
270            ta.scrollTop = sp;
271        } else {
272            ta.value += t1 + t2;
273            ta.focus(ta.value.length - 1);
274        }
275    },
276    insertTag: function(id) {
277        var tag = this.tags[id];
278        if (!tag) return;
279        var s = this.getSelection();
280        if (s.length > 0) {
281            this.surroundText(tag.open, tag.close);
282        }
283    },
284    initTags: function() {
285        if (this.bbc == null || this.bbc.length == 0) {
286            this.bbc = {};
287            this.bbc['b'] = {
288                id: 'bbcode-b',
289                open: '[b]',
290                close: '[/b]',
291                key: 'B',
292                ctrl: 'ctrl',
293                hint: 'Bold'
294            };
295            this.bbc['i'] = {
296                id: 'bbcode-i',
297                open: '[i]',
298                close: '[/i]',
299                key: 'I',
300                ctrl: 'ctrl',
301                hint: 'Italic'
302            };
303            this.bbc['u'] = {
304                id: 'bbcode-u',
305                open: '[u]',
306                close: '[/u]',
307                key: 'U',
308                ctrl: 'ctrl',
309                hint: 'Underline'
310            };
311            this.bbc['s'] = {
312                id: 'bbcode-s',
313                open: '[s]',
314                close: '[/s]',
315                key: null,
316                ctrl: null,
317                hint: 'Strikeout'
318            };
319            this.bbc['img'] = {
320                id: 'bbcode-img',
321                open: '[img]',
322                close: '[/img]',
323                key: null,
324                ctrl: null,
325                hint: 'Image'
326            };
327            this.bbc['uploadimg'] = {
328                id: 'bbcode-uploadimg',
329                open: '[uploadimg]',
330                close: '[/uploadimg]',
331                key: null,
332                ctrl: null,
333                hint: 'Image'
334            };
335            this.bbc['file'] = {
336                id: 'bbcode-file',
337                open: '[file]',
338                close: '[/file]',
339                key: null,
340                ctrl: null,
341                hint: 'File'
342            };
343            this.bbc['url'] = {
344                id: 'bbcode-url',
345                open: '[url]',
346                close: '[/url]',
347                key: null,
348                ctrl: null,
349                hint: 'Link'
350            };
351            this.bbc['hide'] = {
352                id: 'bbcode-hide',
353                open: '[hide]',
354                close: '[/hide]',
355                key: null,
356                ctrl: null,
357                hint: 'Hidden'
358            };
359            this.bbc['quote'] = {
360                id: 'bbcode-quote',
361                open: '[quote]',
362                close: '[/quote]',
363                key: null,
364                ctrl: null,
365                hint: 'Quote'
366            };
367            this.bbc['list'] = {
368                id: 'bbcode-list',
369                open: '[list][*]',
370                close: '[/list]',
371                key: null,
372                ctrl: null,
373                hint: 'List'
374            };
375        }
376    },
377
378    createButton: function(i, t, c, f, img) {
379        var e;
380        if (img == null || img == "") {
381            e = document.createElement('a');
382            e.style.display = 'block';
383            e.setAttribute('href', 'javascript:void(0)');
384        } else {
385            e = document.createElement('img');
386            if (t) {
387                e.setAttribute('alt', t);
388            }
389            e.setAttribute('src', img);
390            if (!c) {
391                c = 'custombbcode';
392            }
393        }
394        if (i) {
395            e.setAttribute('id', i);
396        }
397        if (t) {
398            e.setAttribute('title', t);
399        }
400        if (c) {
401            e.className = c;
402        }
403        var ee = e;
404        e.onclick = (f != null ? function() {
405            f(ee);
406            return false;
407        } : function() {
408            return false;
409        });
410        return e;
411    },
412
413    addButton: function(id, h, p, ot, ct, css, img) {
414        if (this.ta) {
415            this.initTags();
416            var tag = this.bbc[id],
417                th = this;
418            if (!tag) {
419                if (ot && ct) {
420                    this.bbc[id] = {
421                        id: id,
422                        open: ot,
423                        close: ct,
424                        key: null,
425                        ctrl: null,
426                        hint: h
427                    };
428                    tag = this.bbc[id];
429                } else {
430                    return;
431                }
432            }
433            if (this.bbcPanel == null) {
434                this.bbcPanel = document.createElement('span');
435                this.bbcPanel.className = 'bbcode';
436                this.bbcPanel.style.display = 'block';
437                this.ta.parentNode.insertBefore(this.bbcPanel, this.ta);
438            }
439            var f = function() {
440                var s = th.getSelection();
441                if (s.length > 0) {
442                    th.surroundText(tag.open, tag.close);
443                } else {
444                    var v = prompt(p, '');
445                    if (null != v && '' != v) {
446                        th.insertText(tag.open + v + tag.close);
447                    }
448                }
449                return false;
450            };
451            
452            var textareaReference = this.ta;
453            var uploadImgFunction = function(button) {
454            	var imageFormField = document.querySelector('#imagecomment');
455            	var spanArrow = document.querySelector('#imagecommentarrow');
456            	if(imageFormField) {
457            		imageFormField.remove();
458            		spanArrow.remove();
459            	} else {
460            		var imgInputField = document.createElement('input');
461            		imgInputField.id = 'imagecomment';
462            		imgInputField.name = 'imagecomment';
463            		imgInputField.type = 'file';
464            		document.querySelector('span.bbcode').insertBefore(imgInputField, this.ta);
465            		
466            		var arrowSpan = document.createElement('div');
467            		arrowSpan.id = 'imagecommentarrow';
468            		arrowSpan.classList.add('jcomment-arrow-popover');
469            		arrowSpan.classList.add('jcomment-arrow-popover-fileinput');
470            		document.querySelector('span.bbcode').insertBefore(arrowSpan, this.ta);
471            		
472            		imgInputField.style.left = `${button.offsetLeft - 48}px`;
473            		arrowSpan.style.left = `${button.offsetLeft + 12}px`;
474            		
475            		// Rileva se il dispositivo è touch (mobile/tablet)
476                    var isTouchDevice = (
477                        'ontouchstart' in window ||
478                        navigator.maxTouchPoints > 0 ||
479                        navigator.msMaxTouchPoints > 0
480                    );
481
482                    if (isTouchDevice) {
483                        imgInputField.click();
484                    }
485            	}
486            }
487            var uploadFileFunction = function(button) {
488            	var fileFormField = document.querySelector('#filecomment');
489            	var spanArrow = document.querySelector('#filecommentarrow');
490            	if(fileFormField) {
491            		fileFormField.remove();
492            		spanArrow.remove();
493            	} else {
494            		var fileInputField = document.createElement('input');
495            		fileInputField.id = 'filecomment';
496            		fileInputField.name = 'filecomment';
497            		fileInputField.type = 'file';
498            		document.querySelector('span.bbcode').insertBefore(fileInputField, this.ta);
499            		
500            		var arrowSpan = document.createElement('div');
501            		arrowSpan.id = 'filecommentarrow';
502            		arrowSpan.classList.add('jcomment-arrow-popover');
503            		arrowSpan.classList.add('jcomment-arrow-popover-fileinput');
504            		document.querySelector('span.bbcode').insertBefore(arrowSpan, this.ta);
505            		
506            		fileInputField.style.left = `${button.offsetLeft - 48}px`;
507            		arrowSpan.style.left = `${button.offsetLeft + 12}px`;
508            	}
509            }
510            if(id != 'uploadimg') {
511            	if(id == 'file') {
512            		tag.e = this.createButton(tag.id, (h != null ? h : tag.hint), (css ? css : tag.id), uploadFileFunction, img);
513            	} else {
514            		tag.e = this.createButton(tag.id, (h != null ? h : tag.hint), (css ? css : tag.id), f, img);
515            	}
516            } else {
517        		tag.e = this.createButton(tag.id, (h != null ? h : tag.hint), (css ? css : tag.id), uploadImgFunction, img);
518            }
519            this.bbcPanel.appendChild(tag.e);
520            this.tags[tag.id] = tag;
521        }
522    },
523
524    initSmiles: function() {
525        if (this.ta) {
526            this.smilesPanel = document.createElement('div');
527            if (this.bbcPanel) {
528                document.body.appendChild(this.smilesPanel);
529                this.smilesPanel.id = 'comments-form-smilespanel';
530                this.smilesPanel.setAttribute('class', 'comment-form-close');
531                var jc = this,
532                    f = function(e) {
533                        var sp = jc.smilesPanel,
534                            p = jc.getElementPos(e);
535                        if (sp) {
536                        	sp.className = (sp.className == '' || sp.className == 'comment-form-close'  ? 'comment-form-open' : 'comment-form-close');
537                            var sps = sp.style;
538                            sps.left = p.left + "px";
539                            sps.top = p.bottom + e.offsetHeight + "px";
540                            sps.zIndex = 99;
541                        }
542                        return false;
543                    };
544                this.bbcPanel.appendChild(this.createButton(null, 'Emojis', 'bbcode-smile', f));
545            } else {
546                this.smilesPanel.className = 'smiles';
547                this.ta.parentNode.insertBefore(this.smilesPanel, this.ta);
548            }
549        }
550    },
551    closeSmiles: function() {
552        if (this.smilesPanel && this.bbcPanel) {
553            this.smilesPanel.setAttribute('class', 'comment-form-close');
554        }
555    },
556
557    addSmile: function(code, image) {
558        if (this.ta) {
559            if (!this.smilesPanel) {
560                return;
561            }
562            var th = this,
563                e = document.createElement('img');
564            
565            function imageExists(image_url){
566                var http = new XMLHttpRequest();
567                http.open('HEAD', image_url, false);
568                http.send();
569                return http.status != 404;
570            }
571        	e.setAttribute('src', image);
572            e.setAttribute('alt', code);
573            e.setAttribute('title', code);
574            e.className = 'smile';
575            e.onclick = function() {
576                th.insertText(' ' + code + ' ');
577            };
578            this.smilesPanel.appendChild(e);
579        }
580    },
581    initEmojis: function() {
582        if (this.ta) {
583            this.emojisPanel = document.createElement('div');
584            this.emojisPanel.id = 'comments-form-emojispanel';
585            this.emojisPanel.setAttribute('class', 'comment-form-close');
586            this.emojisPanel.style.maxHeight = '300px';
587            this.emojisPanel.style.overflowY = 'auto';
588
589            if (this.bbcPanel) {
590                document.body.appendChild(this.emojisPanel);
591                var jc = this,
592                    f = function(e) {
593                        var ep = jc.emojisPanel,
594                            p = jc.getElementPos(e);
595                        if (ep) {
596                            ep.className = (ep.className === '' || ep.className === 'comment-form-close') ? 'comment-form-open' : 'comment-form-close';
597                            var eps = ep.style;
598                            eps.left = p.left + "px";
599                            eps.top = p.bottom + e.offsetHeight + "px";
600                            eps.zIndex = 99;
601                        }
602                        return false;
603                    };
604                this.bbcPanel.appendChild(this.createButton(null, 'Unicode emojis', 'bbcode-emoji', f));
605            } else {
606                this.emojisPanel.className = 'emojis';
607                this.ta.parentNode.insertBefore(this.emojisPanel, this.ta);
608            }
609
610        }
611    },
612    addEmojiCategory: function(label) {
613        if (!this.emojisPanel) return;
614
615        var title = document.createElement('div');
616        title.className = 'emoji-category-title';
617        title.textContent = label;
618        this.emojisPanel.appendChild(title);
619    },
620    addEmoji: function(char, title) {
621        if (this.ta) {
622            if (!this.emojisPanel) return;
623
624            var th = this,
625                e = document.createElement('span');
626
627			var textarea = this.ta;
628            e.textContent = char;
629            e.setAttribute('title', title);
630            e.className = 'emoji-item';
631            e.style.cursor = 'pointer';
632            e.style.fontSize = '20px';
633            e.style.padding = '2px';
634            e.onclick = function() {
635                th.insertText(char);
636				textarea.dispatchEvent(new Event('input', {
637				  bubbles: true,
638				  cancelable: true
639				}));
640            };
641
642            this.emojisPanel.appendChild(e);
643        }
644    },
645    closeEmojis: function() {
646        if (this.emojisPanel && this.bbcPanel) {
647            this.emojisPanel.setAttribute('class', 'comment-form-close');
648        }
649    },
650	initBackgrounds: function () {
651		// Manage styles apply syncronously to server side loaded messages into HTML
652		if(typeof(window.jcommentStyleMap) !== 'undefined') {
653			const styleMap = window.jcommentStyleMap || {};
654			const comments = document.querySelectorAll('.comment-body[data-style]');
655		
656			comments.forEach(comment => {
657				const styleKey = comment.getAttribute('data-style');
658				const styleDef = styleMap[styleKey];
659		
660				if (styleDef) {
661					if (styleDef.style.includes('linear-gradient') || styleDef.style.includes('radial-gradient')) {
662						const gradientMatch = styleDef.style.match(/(linear|radial)-gradient\([^;]+?\)/i);
663						if (gradientMatch) {
664							comment.style.backgroundImage = gradientMatch[0];
665						}
666					} else {
667						const colorMatch = styleDef.style.match(/background-color:\s*([^;]+);?/i);
668						if (colorMatch && colorMatch[1]) {
669							comment.style.backgroundColor = colorMatch[1];
670						}
671					}
672		
673					if (styleDef.textColor) {
674						comment.style.color = styleDef.textColor;
675						comment.style.caretColor = styleDef.textColor;
676						
677						// Apply also to inner comment title and quote elements if any
678						const spanCommentTitle = comment.querySelector('span.comment-title');
679						if(spanCommentTitle) {
680							spanCommentTitle.style.color = styleDef.textColor;
681						}
682						const spanCommentQuote = comment.querySelector('span.quote');
683						if(spanCommentQuote) {
684							spanCommentQuote.style.color = styleDef.textColor;
685						}
686						const spanCommentBlockquote = comment.querySelector('blockquote > div');
687						if(spanCommentBlockquote) {
688							spanCommentBlockquote.style.color = styleDef.textColor;
689						}
690					}
691		
692					if (styleDef.textAlign) {
693						comment.style.textAlign = styleDef.textAlign;
694					}
695		
696					// Default styles
697					comment.style.fontSize = '30px';
698					comment.style.padding = '10%';
699					comment.style.lineHeight = 'normal';
700					comment.style.borderRadius = '6px';
701				}
702			});
703		}
704						
705        if (this.ta) {
706            this.backgroundsPanel = document.createElement('div');
707            this.backgroundsPanel.id = 'comments-form-backgroundspanel';
708            this.backgroundsPanel.setAttribute('class', 'comment-form-close');
709            this.backgroundsPanel.style.maxHeight = '300px';
710            this.backgroundsPanel.style.overflowY = 'auto';
711
712            if (this.bbcPanel) {
713                document.body.appendChild(this.backgroundsPanel);
714                var jc = this,
715                    f = function(e) {
716                        var bp = jc.backgroundsPanel,
717                            p = jc.getElementPos(e);
718                        if (bp) {
719                            bp.className = (bp.className === '' || bp.className === 'comment-form-close') ? 'comment-form-open' : 'comment-form-close';
720                            var bps = bp.style;
721                            bps.left = p.left + "px";
722                            bps.top = p.bottom + e.offsetHeight + "px";
723                            bps.zIndex = 99;
724                        }
725                        return false;
726                    };
727                this.bbcPanel.appendChild(this.createButton(null, 'Styles', 'bbcode-background', f));
728            } else {
729                this.backgroundsPanel.className = 'backgrounds';
730                this.ta.parentNode.insertBefore(this.backgroundsPanel, this.ta);
731            }
732        }
733    },
734    addCounter: function(m, pre, post, className) {
735        if (this.ta) {
736            if (pre != null) {
737                this.l10n.counterPre = pre;
738            }
739            if (post != null) {
740                this.l10n.counterPost = post;
741            }
742            var ch = document.createElement('span');
743            ch.className = className != null ? className : '';
744            var t1 = document.createTextNode(this.l10n.counterPre + ' '),
745                t2 = document.createTextNode(' ' + this.l10n.counterPost);
746            var c = document.createElement('span');
747            ch.appendChild(t1);
748            ch.appendChild(c);
749            ch.appendChild(t2);
750            if (this.resizable) {
751                if (this.grippie != null) {
752                    this.grippie.appendChild(ch);
753                }
754            } else {
755                var d = document.createElement('div');
756                d.className = 'counterpanel';
757                this.ta.parentNode.insertBefore(d, this.ta.nextSibling);
758                d.appendChild(ch);
759            }
760            this.counter = {
761                e: c,
762                max: m
763            };
764            this.updateCounter();
765        }
766    },
767    addGrippie: function() {
768        this.offset = null;
769        this.dragging = false;
770        this.grippie = document.createElement('div');
771        this.grippie.className = 'grippie';
772        this.ta.parentNode.insertBefore(this.grippie, this.ta.nextSibling);
773        var th = this;
774        this.events.add(this.grippie, 'mousedown', function(e) {
775            return th.onMouseDown(e);
776        });
777    },
778    updateCounter: function() {
779        if (this.counter != null) {
780            var ta = this.ta,
781                e = this.counter.e;
782            try {
783                var n = document.createElement(e.tagName),
784                    v = this.counter.max;
785                if (ta.value.length > 0) {
786                    v = v - ta.value.length;
787                    var ln = ta.value.match(/[^\r]\n/g);
788                    if (ln) {
789                        v = v - ln.length;
790                    }
791                }
792                n.innerHTML = (v >= 0) ? v : 0;
793                e.parentNode.replaceChild(n, e);
794                this.counter.e = n;
795            } catch (ex) {}
796        }
797    },
798    mousePosition: function(e) {
799        var px = 0,
800            py = 0;
801        if (!e) {
802            e = window.event;
803        }
804        if (e.pageX || e.pageY) {
805            px = e.pageX;
806            py = e.pageY;
807        } else if (e.clientX || e.clientY) {
808            px = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
809            py = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
810        }
811        return {
812            x: px,
813            y: py
814        };
815    },
816    onChange: function() {
817        this.updateCounter();
818        return false;
819    },
820    onMouseDown: function(e) {
821        this.offset = this.mousePosition(e).y;
822        this.height = this.ta.clientHeight;
823        this.dragging = true;
824        var th = this;
825        this.events.add(document, 'mousemove', function(e) {
826            return th.onMouseMove(e);
827        });
828        this.events.add(document, 'mouseup', function(e) {
829            return th.onMouseUp(e);
830        });
831        return false;
832    },
833    onMouseUp: function(e) {
834        this.dragging = false;
835    },
836    onMouseMove: function(e) {
837        if (this.dragging) this.ta.style.height = Math.max(this.defaultHeight, this.height + this.mousePosition(e).y - this.offset) + 'px';
838        return false;
839    }
840};
841
842var JCommentScroll = JCommentScroll || {
843    getElementRect: function(e) {
844        var x = 0,
845            y = 0,
846            w = e.offsetWidth,
847            h = e.offsetHeight;
848        if (e.getBoundingClientRect) {
849            var b = e.getBoundingClientRect(),
850                de = document.documentElement,
851                db = document.body;
852            var st = window.pageYOffset || de.scrollTop || db.scrollTop;
853            var sl = window.pageXOffset || de.scrollLeft || db.scrollLeft;
854            var ct = de.clientTop || db.clientTop || 0;
855            var cl = de.clientLeft || db.clientLeft || 0;
856            x = b.left + sl - cl;
857            y = b.top + st - ct;
858        } else {
859            while (e.offsetParent !== null) {
860                x += e.offsetLeft;
861                y += e.offsetTop;
862                e = e.offsetParent;
863            }
864            x = Math.round(x);
865            y = Math.round(y);
866        }
867        return [x, y, x + w, y + h];
868    },
869    getPageRect: function() {
870        var de = document.documentElement,
871            db = document.body;
872        var x = window.pageXOffset || de.scrollLeft || db.scrollLeft;
873        var y = window.pageYOffset || de.scrollTop || db.scrollTop;
874        var w = 'innerWidth' in window ? window.innerWidth : (de.clientWidth || db.clientWidth);
875        var h = 'innerHeight' in window ? window.innerHeight : (de.clientHeight || db.clientHeight);
876        return [x, y, x + w, y + h];
877    },
878    scroll: function(element) {
879        var a = this.getElementRect(element),
880            b = this.getPageRect();
881        if (!(a[0] < b[2] && a[2] > b[0] && a[1] < b[3] && a[3] > b[1])) {
882            window.scrollTo(a[0], a[1]);
883        }
884    }
885};
886
887JCommentForm.prototype = {
888    id: null,
889    form: null,
890    events: null,
891    editor: null,
892    elements: {},
893    store: new Array('name', 'email', 'homepage'),
894    fadeTimer: null,
895
896    init: function(id, editor) {
897        this.id = id;
898        this.form = this.$(id);
899        this.editor = editor;
900        this.events = new JCommentEvents();
901        this.setElements(new Array('name', 'email', 'homepage', 'title', 'comment', 'privacy'));
902        this.restoreValues();
903        this.clear('captcha');
904        this.setCaptcha();
905    },
906    setCaptcha: function() {
907        this.captchaImage = this.$(this.id + '-captcha-image');
908        this.register('captcha');
909    },
910    setEditor: function(e) {
911        this.editor = e;
912    },
913    setElements: function(e) {
914        for (var i = 0; i < e.length; i++) {
915            try {
916                this.register(e[i]);
917            } catch (e) {}
918        }
919    },
920    $: function(e) {
921        return JComment.prototype.$(e);
922    },
923    register: function(n) {
924        var f = this,
925            e = this.$(this.id + '-' + n);
926        if (e) {
927            this.elements[n] = e;
928            JCommentInput.prototype.register(e);
929            this.events.add(e, !window.opera && document.all ? 'keydown' : 'keypress', function(e) {
930                return f.keypress(e);
931            });
932        }
933    },
934    error: function(n) {
935        var e = this.elements[n];
936        if (e) {
937            e.focus();
938            var c = e.className.replace('/\berror\b/g', '') + ' error';
939            e.className = c.replace(/\s{2,}/g, ' ');
940        }
941    },
942
943    clear: function(n) {
944        if (n == null) {
945            if (this.form != null) {
946                this.form.reset();
947            }
948            return;
949        }
950        switch (n) {
951            case 'comment':
952                if (this.editor != null) {
953                    this.editor.clear();
954                }
955                break;
956            case 'captcha':
957                var cim = this.captchaImage,
958                    cin = this.elements['captcha'];
959                if (cim) {
960                    var r = new String(Math.floor(Math.random() * 100000));
961                    if (/\d+$/g.test(cim.src)) {
962                        cim.src = cim.src.replace(/\d+$/g, r);
963                    } else if (/\?/g.test(cim.src)) {
964                        cim.src = cim.src + '&r=' + r;
965                    } else {
966                        cim.src = cim.src + '?r=' + new String(Math.floor(Math.random() * 100000));
967                    }
968                }
969                if (cin) {
970                    cin.value = '';
971                }
972                break;
973            default:
974                var e = this.elements[n];
975                if (e) {
976                    e.value = '';
977                }
978                break;
979        }
980    },
981    focus: function(n) {
982        var e;
983        if (n == null) {
984            for (var nm in this.elements) {
985                e = this.elements[nm];
986                if (e) {
987                    e.focus();
988                    break;
989                }
990            }
991        } else {
992            e = this.elements[n];
993            if (e) {
994                e.focus();
995            }
996        }
997    },
998    keypress: function(e) {
999        if (e.ctrlKey && (e.keyCode == 13 || (e.type == 'keypress' && e.keyCode == 10))) {
1000            this.submit();
1001            e.returnValue = false;
1002            return this.events.cancel(e);
1003        }
1004        return this.keypressed(e);
1005    },
1006    add: function(n, i, v) {
1007        if (this.form != null) {
1008            var e = document.createElement('input');
1009            e.setAttribute('type', 'hidden');
1010            e.setAttribute('name', n);
1011            e.setAttribute('id', i);
1012            e.setAttribute('value', v);
1013            this.form.appendChild(e);
1014        }
1015    },
1016    remove: function(i) {
1017        var e = this.$(i);
1018        if (e) {
1019            e.value = 0;
1020            e.parentNode.removeChild(e);
1021        }
1022    },
1023    setText: function(n, t) {
1024        if (n == 'comment') {
1025            if (this.editor != null) {
1026                this.editor.clear();
1027                this.editor.insertText(t);
1028            }
1029        } else {
1030            var e = this.elements[n];
1031            if (e) {
1032                e.value = t;
1033            }
1034        }
1035    },
1036    insertText: function(n, t) {
1037        if (n == 'comment') {
1038            if (this.editor != null) {
1039                this.editor.insertText(t);
1040            }
1041        } else {
1042            var e = this.elements[n];
1043            if (e) {
1044                e.value += t;
1045            }
1046        }
1047    },
1048    storeValues: function() {
1049        for (var i = 0; i < this.store.length; i++) {
1050            try {
1051                var el = JComment.prototype.$(this.id + '-' + this.store[i]);
1052                if (el) {
1053                    JComment.prototype.setCookie(this.store[i], encodeURIComponent(el.value), 14);
1054                }
1055            } catch (e) {}
1056        }
1057    },
1058    restoreValues: function() {
1059        for (var i = 0; i < this.store.length; i++) {
1060            try {
1061                var el = JComment.prototype.$(this.id + '-' + this.store[i]);
1062                if (el) {
1063                    if (el.type && el.type == 'hidden') {
1064                        return;
1065                    } else {
1066                        el.value = decodeURIComponent(JComment.prototype.getCookie(this.store[i]));
1067                    }
1068                }
1069            } catch (e) {}
1070        }
1071    },
1072    submit: function() {},
1073    keypressed: function(e) {}
1074};
1075
1076JCommentIndicator.prototype = {
1077    e: null,
1078    init: function() {
1079        if (this.e == null) {
1080            this.e = document.createElement('div');
1081            this.e.className = 'busy';
1082        }
1083    },
1084    move: function(p, b) {
1085        if (p) {
1086            if (this.e.parentNode) {
1087                this.e.parentNode.removeChild(this.e);
1088            }
1089            if (b) {
1090                p.insertBefore(this.e, b);
1091            } else {
1092                p.appendChild(this.e);
1093            }
1094        }
1095    },
1096    show: function() {
1097        this.e.style.display = 'block';
1098    },
1099    hide: function() {
1100        this.e.style.display = 'none';
1101    },
1102    start: function(p, b) {
1103        this.move(p, b);
1104        this.show();
1105    },
1106    stop: function() {
1107        this.hide();
1108    }
1109};
1110
1111JComment.prototype = {
1112    oi: null,
1113    og: null,
1114    requestURI: '',
1115    busy: null,
1116    form: null,
1117    cache: {},
1118    mode: 'add',
1119    readyList: [],
1120    isReady: false,
1121
1122    init: function(oi, og, r) {
1123        this.oi = oi;
1124        this.og = og;
1125        this.busy = new JCommentIndicator();
1126        this.requestURI = r;
1127        var th = this;
1128        jtajax.startLoading = function() {
1129            th.busy.show();
1130        };
1131        jtajax.finishLoading = function() {
1132            th.busy.hide();
1133        };
1134    },
1135    reinit: function(oi, og) {
1136        this.oi = oi;
1137        this.og = og;
1138        var foi = this.$('object_id');
1139        if (foi) {
1140            foi.value = oi;
1141        }
1142        var fog = this.$('object_group');
1143        if (fog) {
1144            fog.value = og;
1145        }
1146        this.showPage(oi, og, 0);
1147    },
1148    setForm: function(f) {
1149        this.form = f;
1150        this.form_id = f.id;
1151        this.setMode('add', null);
1152        var jc = this;
1153        this.form.submit = function() {
1154            jc.saveComment();
1155        };
1156        this.form.keypressed = function(e) {
1157            if (e.keyCode == 27 && jc.mode == 'reply') {
1158                jc.restoreForm(false);
1159            }
1160        };
1161        f.form.classList.add('jcomment-form-initialized');
1162        this.formLoaded();
1163    },
1164    setList: function(l) {
1165        this.list_id = l;
1166    },
1167    setMode: function(m, i) {
1168        var b = this.$('comments-form-cancel'),
1169            jc = this;
1170        if (b != null) {
1171            b.style.display = (m != 'add') ? '' : 'none';
1172            b.onclick = (m == 'edit' ? function() {
1173                jc.cancelEdit(i);
1174            } : (m == 'reply' ? function() {
1175                jc.cancelReply();
1176            } : null));
1177        }
1178        this.mode = m;
1179    },
1180    $: function(id) {
1181        if (!id) {
1182            return null;
1183        }
1184        var e = document.getElementById(id);
1185        if (!e && document.all) {
1186            e = document.all[id];
1187        }
1188        return e;
1189    },
1190    ajax: function(f, a, fid) {
1191        var r, prevURI = '';
1192        try {
1193            prevURI = jtajax.options.url;
1194            jtajax.setup({
1195                url: this.requestURI
1196            });
1197            r = jtajax.call(f, a, 'post', fid);
1198            jtajax.options.url = prevURI;
1199        } catch (e) {
1200            jtajax.options.url = prevURI;
1201        }
1202        return r;
1203    },
1204    initOnReady: function() {
1205        if (this.isReadyInited) return;
1206        this.isReadyInited = 1;
1207        var jc = this;
1208        jc.ready(jc);
1209    },
1210    onReady: function(f) {
1211        if (this.isReady) {
1212            f();
1213        } else {
1214            var jc = this;
1215            jc.readyList.push(f);
1216            jc.initOnReady();
1217        }
1218    },
1219    ready: function() {
1220        var jc = window.jcomment;
1221        if (jc.isReady || jc.readyList == null) return;
1222        jc.isReady = 1;
1223        for (var i = 0, len = jc.readyList.length; i < len; i++) {
1224            try {
1225                jc.readyList[i]();
1226            } catch (e) {}
1227        }
1228        jc.readyList = null;
1229    },
1230    setAntiCache: function(c, p, f) {
1231    	jc.isReady = 1;
1232    	this.isReady = 1;
1233        this.aca = c;
1234        this.acp = p;
1235        this.acf = f;
1236        this.onReady(this.loadComments);
1237    },
1238    loadComments: function() {
1239        var jc = window.jcomment;
1240        var l = document.location.href,
1241            lc = true,
1242            i = l.lastIndexOf('#comment-');
1243        jc.clear('captcha');
1244        if (jc.aca) {
1245            if (i != 0) {
1246                var c = l.substring(i + 9, l.length);
1247                if (!isNaN(c)) {
1248                    lc = false;
1249                    jc.showComment(c);
1250                }
1251            }
1252        }
1253        if (jc.acp) {
1254            if (lc) {
1255                jc.showPage(jc.oi, jc.og, 0);
1256            }
1257        }
1258        if (jc.acf) {
1259            if (l.lastIndexOf('#addcomments') != -1) {
1260                jc.showForm(jc.oi, jc.og, 'comments-form-link');
1261            }
1262        }
1263    },
1264    setCookie: function (n, v, d) {
1265        sessionStorage.setItem("jc_" + n, v);
1266    },
1267    getCookie: function (n) {
1268        var value = sessionStorage.getItem("jc_" + n);
1269        return value !== null ? value : '';
1270    },
1271    removeCookie: function (n) {
1272        sessionStorage.removeItem("jc_" + n);
1273    },
1274    scrollTo: function(n) {
1275        var e = this.$(n);
1276        if (e) {
1277            JCommentScroll.scroll(e);
1278        }
1279    },
1280    scrollToList: function() {
1281        this.scrollTo(this.list_id);
1282    },
1283    scrollToForm: function() {
1284        this.scrollTo(this.form_id);
1285    },
1286    scrollToComment: function(id) {
1287        this.scrollTo('comment-' + id);
1288    },
1289    moveElement: function(e, p, b) {
1290        if (e) {
1291            if (p) {
1292                if (e.parentNode) {
1293                    e.parentNode.removeChild(e);
1294                }
1295                if (b) {
1296                    p.insertBefore(e, b);
1297                } else {
1298                    p.appendChild(e);
1299                }
1300            }
1301        }
1302    },
1303    createElement: function(t, i, c) {
1304        var e = document.createElement(t);
1305        if (i) {
1306            e.setAttribute('id', i);
1307        }
1308        if (c) {
1309            e.className = c;
1310        }
1311        return e;
1312    },
1313    fade: function(id, s, e, m) {
1314        var speed = Math.round(m / 100),
1315            timer = 0;
1316        if (s > e) {
1317            for (var i = s; i >= e; i--) {
1318                setTimeout("JComment.prototype.setOpacity('" + id + "'," + i + ")", (timer * speed));
1319                timer++;
1320            }
1321            var o = JComment.prototype.$(id);
1322            if (o) {
1323                setTimeout(function() {
1324                    o.style.display = 'none';
1325                }, ((s - e) * speed));
1326            }
1327        } else if (s < e) {
1328            for (var i = s; i <= e; i++) {
1329                setTimeout("JComment.prototype.setOpacity('" + id + "'," + i + ")", (timer * speed));
1330                timer++;
1331            }
1332        }
1333    },
1334    setOpacity: function(id, opacity) {
1335        var e = this.$(id);
1336        if (e) {
1337            var s = e.style;
1338            s.opacity = (opacity / 100);
1339            s.MozOpacity = (opacity / 100);
1340            s.KhtmlOpacity = (opacity / 100);
1341            s.filter = "alpha(opacity=" + opacity + ")";
1342        }
1343    },
1344    clear: function(n) {
1345        if (this.form != null) {
1346            this.form.clear(n);
1347        }
1348    },
1349    insertText: function(t) {
1350        if (this.form != null) {
1351            this.form.insertText('comment', t);
1352        } else {
1353            var jc = this;
1354            window.setTimeout(function() {
1355                jc.insertText(t);
1356            }, 500);
1357        }
1358    },
1359    busyList: function() {
1360        if (this.list_id) {
1361            var l = this.$(this.list_id);
1362            if (l) {
1363                this.busy.move(l.parentNode, l);
1364            }
1365        }
1366    },
1367    busyForm: function() {
1368        if (this.form_id) {
1369            var f = this.$(this.form_id);
1370            if (f) {
1371                this.busy.move(f.parentNode, f);
1372            }
1373        }
1374    },
1375    busyComment: function(i) {
1376        this.busy.move(this.$('comment-item-' + i), null);
1377    },
1378    saveComment: function() {
1379        var f = '';
1380		// Auto-close all advanced panels if any open
1381		['comments-form-backgroundspanel', 'comments-form-emojispanel'].forEach(function(id) {
1382			var panel = document.getElementById(id);
1383	      	if (panel && panel.classList.contains('comment-form-open')) {
1384		        if (panel.classList.replace) {
1385					panel.classList.replace('comment-form-open', 'comment-form-close');
1386		        }
1387	      	}
1388	    });
1389						
1390        // JS Captcha validation
1391        var $field = jQuery('#jcomment_recaptcha_control');
1392        if ($field.length && !window.jcommentrhvvalidation) {
1393            // Add error class
1394            jQuery('#jcomment_recaptcha_label').addClass('jcomment_label_validate_recaptcha');
1395
1396            //Bind blur event
1397            $field.on('change', function() {
1398                jQuery('#jcomment_recaptcha_label').removeClass('jcomment_label_validate_recaptcha');
1399            });
1400
1401            jQuery('label.jcomment_label_validation_recaptcha').remove();
1402            jQuery('#jcomment_recaptcha_container').after('<label class="jcomment_label_validation jcomment_label_validation_recaptcha">* ' + 
1403									            				jQuery('#jcomment_recaptcha_control').data('validation-label') + 
1404									            		   '</label>');
1405            return false;
1406        }
1407        
1408        // Validate rating
1409    	if(document.querySelector('#jcomment-star-rating')) {
1410    		if(document.querySelector('#jcomment-star-rating div.stars') && document.querySelectorAll('#jcomment-star-rating div.stars a.star.fullStar').length == 0 && 
1411			  (jcommentRatingRequired == 1 || (jcommentRatingRequired == 2 && this.mode != 'reply'))) {
1412    			if(!document.querySelector('label.jcomment_label_validation_rating')) {
1413    				document.querySelector('#jcomment-star-rating div.stars').insertAdjacentHTML('afterend', '<label class="jcomment_label_validation jcomment_label_validation_rating">* ' + 
1414    						document.querySelector('#jcomment-star-rating').dataset.validationLabel + 
1415    				'</label>');
1416    			}
1417    			return false;
1418    		}
1419    	}
1420    	
1421        if (this.mode != 'edit') {
1422            f = 'JCommentAddComment';
1423            this.busyForm();
1424            if (this.form) {
1425                this.form.storeValues();
1426            }
1427            this.busy.show();
1428        } else {
1429            f = 'JCommentSaveComment';
1430            this.busy.show();
1431        }
1432        var emptyState = document.querySelector('div.jcomment-empty-state');
1433        if(emptyState) {
1434        	emptyState.remove();
1435        	document.querySelector('h4.jcomment-empty-state').remove();
1436        }
1437        
1438        return this.ajax(f, null, this.form_id);
1439    },
1440    editComment: function(i) {
1441        this.busyComment(i);
1442        var a = arguments;
1443        if (this.form == null) {
1444            a = new Array(i, 1);
1445        }
1446        return this.ajax('JCommentEditComment', a);
1447    },
1448    cancelEdit: function(i) {
1449        if ((!this.cache[i]) || (this.cache[i] == '')) {
1450            this.$('comment-body-' + i).innerHTML = this.cache[i];
1451            this.cache[i] = '';
1452        }
1453        if (this.form) {
1454            this.form.remove('comment-id-hidden-' + i);
1455            this.restoreForm(true);
1456        }
1457        var t = this.$('comment-toolbar-' + i);
1458        if (t) {
1459            t.style.display = '';
1460        }
1461        
1462        // Show the 'Add comments' line
1463        var addCommentLine = document.querySelector('div.jcomment-container-form h4');
1464        if (addCommentLine) {
1465        	addCommentLine.style.display = '';
1466        }
1467        
1468        // Reset rating
1469        if(typeof(jQuery.fn.rating) !== 'undefined') {
1470    		// Check for reviews and reset/add full stars
1471    		var stars = document.querySelectorAll('#comments-form div.stars a.star');
1472    		if(stars && stars.length) {
1473    			for(var i = 1; i <= 5; i++) {
1474    				var starToReset = document.querySelector('#comments-form div.stars a.star[data-title="' + i + '"]');
1475    				starToReset.classList.remove('fullStar');
1476    			}
1477    		}
1478        }
1479        
1480        return this.ajax('JCommentCancelComment', arguments);
1481    },
1482    cancelReply: function() {
1483        if (this.form) {
1484            this.form.remove('comment-parent-id-hidden');
1485            this.restoreForm(false);
1486            
1487            // Show the 'Add comments' line
1488            var addCommentLine = document.querySelector('div.jcomment-container-form h4');
1489            if (addCommentLine) {
1490            	addCommentLine.style.display = '';
1491            }
1492            
1493            // Reset rating
1494            if(typeof(jQuery.fn.rating) !== 'undefined') {
1495        		// Check for reviews and reset/add full stars
1496        		var stars = document.querySelectorAll('#comments-form div.stars a.star');
1497        		if(stars && stars.length) {
1498        			for(var i = 1; i <= 5; i++) {
1499        				var starToReset = document.querySelector('#comments-form div.stars a.star[data-title="' + i + '"]');
1500        				starToReset.classList.remove('fullStar');
1501        			}
1502        		}
1503            }
1504        }
1505    },
1506    quoteComment: function(i) {
1507        var a = arguments;
1508        if (this.form == null) {
1509        	var ff = document.querySelector('#comments-form');
1510        	if(ff && !ff.classList.contains('jcomment-form-initialized')) {
1511            	return;
1512            }
1513            a = new Array(i, 1);
1514        }
1515        return this.ajax('JCommentQuoteComment', a);
1516    },
1517    publishComment: function(i) {
1518        if (this.form) {
1519            this.restoreForm();
1520        }
1521        this.busyComment(i);
1522        return this.ajax('JCommentPublishComment', arguments);
1523    },
1524    deleteComment: function(i) {
1525        this.busyComment(i);
1526        return this.ajax('JCommentDeleteComment', arguments);
1527    },
1528    jump2email: function(i) {
1529        return this.ajax('JCommentJump2email', arguments);
1530    },
1531    updateList: function(t, m) {
1532        if (this.list_id) {
1533            var l = this.$(this.list_id);
1534            if (!l) {
1535                l = this.$('comments');
1536                m = 'a';
1537            }
1538            switch (m) {
1539                case 'a':
1540                    l.innerHTML = l.innerHTML + t;
1541                    break;
1542                case 'p':
1543                    l.innerHTML = t + l.innerHTML;
1544                    break;
1545                case 'r':
1546                    l.parentNode.innerHTML = t;
1547                    break;
1548            }
1549        }
1550        this.restoreForm(true);
1551    },
1552    updateTree: function(t, r, m) {
1553        var l;
1554        if (r == null) {
1555            l = this.$('comments');
1556            if (l) {
1557                l.innerHTML = t;
1558            }
1559            return;
1560        }
1561        l = this.$('comments-list-' + r);
1562        if (!l) {
1563            var p = this.$('comment-item-' + r);
1564            if (p) {
1565                this.busyComment(r);
1566                l = this.createElement('div', 'comments-list-' + r, 'comments-list');
1567                l.innerHTML = t;
1568                p.parentNode.insertBefore(l, p.nextSibling);
1569            }
1570        } else {
1571            l.innerHTML = (m == 'b' ? t + l.innerHTML : l.innerHTML + t);
1572        }
1573        this.restoreForm(true);
1574    },
1575    updateComment: function(id, t) {
1576        if (t == '') {
1577            var c = this.$('comment-item-' + id);
1578            c.parentNode.removeChild(c);
1579            var l = this.$('comments-list-' + id);
1580            if (l) {
1581                l.parentNode.removeChild(l);
1582            }
1583            return;
1584        }
1585        this.$('comment-item-' + id).innerHTML = t;
1586        var te = this.$('comment-toolbar-' + id);
1587        if (te) {
1588            te.style.display = '';
1589        }
1590        if (this.form) {
1591            this.form.remove('comment-id-hidden-' + id);
1592            this.restoreForm(true);
1593        }
1594    },
1595    voteComment: function(i) {
1596        var v = this.$('comment-vote-holder-' + i);
1597        if (v) {
1598            v.innerHTML = '';
1599            this.busy.start(v, null);
1600        }
1601        return this.ajax('JCommentVoteComment', arguments);
1602    },
1603    reactComment: function(i) {
1604        return this.ajax('JCommentReactComment', arguments);
1605    },
1606    updateVote: function(i, t) {
1607        this.busy.stop();
1608        var c = this.$('comment-vote-holder-' + i);
1609        if (c) {
1610            c.innerHTML = t;
1611        }
1612    },
1613    showComment: function(id) {
1614        return this.ajax('JCommentShowComment', arguments);
1615    },
1616    showPage: function(i, g, p, search) {
1617        if (this.form) {
1618            this.restoreForm();
1619        }
1620        var l = this.$(this.list_id);
1621        if (!l) {
1622            l = this.$(this.list_id + '-0');
1623            if (l) {
1624                this.list_id = this.list_id + '-0';
1625            }
1626        }
1627        
1628        // Manage show comments/form by button
1629        var addCommentsElementHidden = document.querySelectorAll('.showsearch-comments-load-bybutton');
1630        if(addCommentsElementHidden && addCommentsElementHidden.length) {
1631        	for(commentElementHidden of addCommentsElementHidden) {
1632        		commentElementHidden.classList.remove('showsearch-comments-load-bybutton');
1633        	}
1634        }
1635        
1636        this.busyList();
1637        return this.ajax('JCommentShowPage', arguments);
1638    },
1639    showForm: function(i, g, t, viewname) {
1640        if (this.form) {
1641            this.moveElement(this.form.form, this.$(t));
1642            return;
1643        }
1644        this.busyForm();
1645        return this.ajax('JCommentShowForm', arguments);
1646    },
1647    showEdit: function(id, n, e, h, t, txt) {
1648        var jc = this;
1649        if (this.form == null) {
1650            window.setTimeout(function() {
1651                jc.showEdit(id, n, e, h, t, txt);
1652            }, 500);
1653            return;
1654        }
1655        if ((!this.cache[id]) || (this.cache[id] == '')) {
1656            this.cache[id] = this.$('comment-body-' + id).innerHTML;
1657        }
1658        this.busy.stop();
1659        var f = this.form,
1660            ff = this.form.form,
1661            c = this.$('comment-item-' + id);
1662        if (ff != null && c != null) {
1663			var root = document.documentElement;
1664		    var computedBehavior = window.getComputedStyle(root).scrollBehavior;
1665		    root.style.scrollBehavior = 'auto';
1666			
1667            f.add('id', 'comment-id-hidden-' + id, id);
1668            f.setText('name', n);
1669            f.setText('email', e);
1670            f.setText('homepage', h);
1671            f.setText('title', t);
1672            f.setText('comment', txt);
1673            
1674        	if(typeof(jQuery.fn.rating) !== 'undefined') {
1675        		// Check for reviews and reset/add full stars
1676        		var stars = document.querySelectorAll('#comment-item-' + id + ' div.jcomment_review_rating span.star');
1677        		if(stars && stars.length) {
1678        			for(var i = 1; i <= 5; i++) {
1679        				var starToReset = document.querySelector('#comments-form div.stars a.star[data-title="' + i + '"]');
1680        				starToReset.classList.remove('fullStar');
1681        			}
1682        		}
1683        		var ratings = document.querySelectorAll('#comment-item-' + id + ' div.jcomment_review_rating span.star.fullStar');
1684        		if(ratings && ratings.length) {
1685        			var numberOfRatings = ratings.length;
1686        			for(var i = 1; i <= numberOfRatings; i++) {
1687        				var starToLight = document.querySelector('#comments-form div.stars a.star[data-title="' + i + '"]');
1688        				starToLight.classList.add('fullStar');
1689        			}
1690        		}
1691        	}
1692            
1693            var d = this.$('comments-inline-edit');
1694            if (d) {
1695                d.parentNode.removeChild(d);
1696            } else {
1697                d = this.createElement('div', 'comments-inline-edit', 'comments-inline-edit');
1698            }
1699            c.appendChild(d);
1700            this.moveElement(ff, d);
1701            this.setMode('edit', id);
1702
1703            // Ensure all other toolbars are visible as there could a direct edit between messages
1704            var allMessagesHiddenToolbar = document.querySelectorAll('#comments-list p.toolbar[style*=none]');
1705            if(allMessagesHiddenToolbar && allMessagesHiddenToolbar.length) {
1706            	var numberOfHiddenToolbars = allMessagesHiddenToolbar.length;
1707            	for(var m = 0; m < numberOfHiddenToolbars; m++) {
1708            		allMessagesHiddenToolbar[m].removeAttribute('style');
1709    			}
1710            }
1711            
1712            
1713            // Hide the current edited message toolbar
1714            var te = this.$('comment-toolbar-' + id);
1715            if (te) {
1716                te.style.display = 'none';
1717            }
1718            
1719            // Hide the 'Add comments' line
1720            var addCommentLine = document.querySelector('div.jcomment-container-form h4');
1721            if (addCommentLine) {
1722            	addCommentLine.style.display = 'none';
1723            }
1724			
1725			// Check if there are styles enabled and if the comment has a style to be reapplied to the textarea
1726			var backgroundStylesPanel = document.querySelector('#comments-form-backgroundspanel');
1727			if(backgroundStylesPanel) {
1728				var editedCommentBody = c.querySelector('div.comment-body');
1729				if(editedCommentBody.dataset.style) {
1730					var editedStyle = editedCommentBody.dataset.style;
1731					var buttonStyle = document.querySelector(`button.bg-style-btn[data-name="${editedStyle}"]`);
1732					if(buttonStyle) {
1733						setTimeout(function(){
1734							buttonStyle.click();
1735						}, 1)
1736					}
1737				}
1738			}
1739            
1740            this.scrollTo('comments-inline-edit');
1741            this.form.focus('comment');
1742			root.style.scrollBehavior = computedBehavior;
1743        }
1744    },
1745    showReply: function(id, q) {
1746        this.cancelReport();
1747        var jc = this,
1748            c = this.$('comment-item-' + id),
1749            d = this.$('comments-inline-edit');
1750        if (d) {
1751            d.parentNode.removeChild(d);
1752        } else {
1753            d = this.createElement('div', 'comments-inline-edit', 'comments-inline-edit');
1754        }
1755        c.appendChild(d);
1756        
1757        // Hide the 'Add comments' line
1758        var addCommentLine = document.querySelector('div.jcomment-container-form h4');
1759        if (addCommentLine) {
1760        	addCommentLine.style.display = 'none';
1761        }
1762        
1763        if (!this.form) {
1764        	var ff = document.querySelector('#comments-form');
1765        	if(ff && !ff.classList.contains('jcomment-form-initialized')) {
1766            	return;
1767            }
1768        	this.busyComment(id);
1769            var t = 'comments-inline-edit',
1770                h = this.$('comments-form-link');
1771            if (h) {
1772                t = 'comments-form-link';
1773            }
1774            this.showForm(this.oi, this.og, t);
1775            var pid = id;
1776            this.formLoaded = function() {
1777                var f = jc.form;
1778                if (f != null) {
1779                    f.add('parent', 'comment-parent-id-hidden', pid);
1780                }
1781                jc.setMode('reply', pid);
1782                jc.moveElement(jc.form.form, jc.$('comments-inline-edit'));
1783                jc.form.focus();
1784                if (q) {
1785                    jc.quoteComment(id);
1786                }
1787            };
1788        } else {
1789            var f = this.form,
1790                ff = this.form.form,
1791                p = this.$('comment-parent-id-hidden');
1792            if (ff != null && c != null) {
1793                if (!p) {
1794                    f.add('parent', 'comment-parent-id-hidden', id);
1795                } else {
1796                    p.value = id;
1797                }
1798                this.moveElement(ff, d);
1799                this.setMode('reply', id);
1800                this.form.focus();
1801                if (q) {
1802                    this.quoteComment(id);
1803                }
1804            }
1805        }
1806    },
1807    formLoaded: function() {},
1808    restoreForm: function(c) {
1809        var f = this.form;
1810        if (f != null) {
1811            var ff = this.form.form;
1812            this.busy.stop();
1813            if (ff != null) {
1814                if (c) {
1815                    f.clear(null);
1816                }
1817                f.restoreValues();
1818                var a = this.$('addcomments'),
1819                    p = this.$('comment-parent-id-hidden');
1820                if (p) {
1821                    p.value = 0;
1822                }
1823                this.moveElement(ff, a.parentNode, a);
1824                var d = this.$('comments-inline-edit');
1825                if (d) {
1826                    d.parentNode.removeChild(d);
1827                }
1828                this.setMode('add', null);
1829            }
1830            
1831            // Reset Captcha state
1832            var $field = jQuery('#jcomment_recaptcha_control');
1833            if ($field.length) {
1834            	window.jcommentrhvvalidation = false;
1835            	jQuery('#jcomment_recaptcha_control').removeAttr('style');
1836            	jQuery('#jcomment_recaptcha_success').remove();
1837            	jQuery('#jcomment_recaptcha_label').removeClass('jcomment_label_validate_recaptcha');
1838            	jQuery('label.jcomment_label_validation.jcomment_label_validation_recaptcha').remove();
1839            }
1840			
1841			// Reset textarea background style if any
1842			const ta = document.getElementById('comments-form-comment');
1843			if (ta && ta.classList.contains('bg-colored')) {
1844			    // Reset all styles applied to the textarea
1845			    ta.removeAttribute('style');
1846			    ta.removeAttribute('data-active-style');
1847			    ta.classList.remove('bg-colored');
1848
1849			    // Reset placeholder and caret color
1850			    ta.style.setProperty('--placeholder-color', '');
1851			    ta.style.caretColor = '';
1852
1853			    // Remove prepended line breaks if present
1854			    ta.value = ta.value.replace(/^\n{3}/, '');
1855
1856			    // Clear hidden input field value
1857			    const hiddenInput = document.getElementById('comment-style-name');
1858			    if (hiddenInput) {
1859			        hiddenInput.value = '';
1860			    }
1861
1862			    // Trigger input event to update placeholder behavior
1863			    ta.dispatchEvent(new Event('input'));
1864			}
1865        }
1866    },
1867    reportComment: function(id) {
1868        this.busyComment(id);
1869        this.cancelReply();
1870        var c = this.$('comment-item-' + id),
1871            d = this.$('comments-inline-report');
1872        if (d) {
1873            d.parentNode.removeChild(d);
1874        } else {
1875            d = this.createElement('div', 'comments-inline-report', 'comments-inline-report');
1876        }
1877        c.appendChild(d);
1878        var a = new Array(id, 'comments-inline-report');
1879        return this.ajax('JCommentShowReportForm', a);
1880    },
1881    saveReport: function() {
1882        this.ajax('JCommentReportComment', null, 'comments-report-form');
1883        return true;
1884    },
1885    cancelReport: function() {
1886        var d = this.$('comments-inline-report');
1887        if (d) {
1888            d.innerHTML = '';
1889            d.parentNode.removeChild(d);
1890        }
1891    },
1892    closeReport: function(m) {
1893        this.cancelReport();
1894        if (m) {
1895            this.showMessage(m, 'info', 'comments-inline-report');
1896        }
1897    },
1898    banIP: function(id) {
1899        this.ajax('JCommentBanIP', arguments)
1900    },
1901    error: function(m, t, n) {
1902        if (!t || t == '') {
1903            t = this.form_id;
1904        }
1905        this.showMessage(m, 'error', t);
1906        if (this.form != null) {
1907            this.form.error(n);
1908        }
1909    },
1910    message: function(m, t) {
1911        if (!t || t == '') {
1912            t = this.form_id;
1913        }
1914        this.showMessage(m, 'info', t);
1915    },
1916    showMessage: function(m, c, t) {
1917        clearTimeout(this.fadeTimer);
1918        var fe = this.$('comments-form-message');
1919        var af = this.$(t);
1920        if (fe) {
1921            fe.parentNode.removeChild(fe);
1922        }
1923        fe = JComment.prototype.createElement('div', 'comments-form-message', '');
1924        if (af) {
1925            JComment.prototype.moveElement(fe, af, af.firstChild);
1926        } else {
1927            alert(m);
1928            return;
1929        }
1930        if (!c) {
1931            c = 'info';
1932        }
1933        fe.className = 'comments-form-message-' + c;
1934        fe.innerHTML = m;
1935        fe.style.display = 'block';
1936        JComment.prototype.setOpacity(fe.id, 100);
1937        this.fadeTimer = setTimeout(function() {
1938            JComment.prototype.fade('comments-form-message', 100, 0, 1000);
1939        }, 6000);
1940        JCommentScroll.scroll(fe);
1941    },
1942
1943    subscribe: function(o, g) {
1944        return this.ajax('JCommentSubscribe', arguments);
1945    },
1946    unsubscribe: function(o, g) {
1947        return this.ajax('JCommentUnsubscribe', arguments);
1948    },
1949    updateSubscription: function(m, t) {
1950        var e = this.$('comments-subscription');
1951        if (e) {
1952            var jc = this;
1953            e.innerHTML = t;
1954            e.onclick = m ? function() {
1955                jc.unsubscribe(jc.oi, jc.og);
1956                return false;
1957            } : function() {
1958                jc.subscribe(jc.oi, jc.og);
1959                return false;
1960            };
1961            e.blur();
1962        }
1963    },
1964    go: function(l) {
1965        window.open(l);
1966        return false;
1967    }
1968};
1969
1970JComment.reloadGravatar = function(image) {
1971	var urlWithoutQueryString = image.src.split("?")[0];
1972	image.src = urlWithoutQueryString;
1973}
1974
1975JComment.initGRecaptcha = function(){
1976	var $ = jQuery;
1977	if ($('#jcomment_recaptcha_control').length) {
1978        $('#jcomment_recaptcha_control').on('click change pointerdown', function(jqEvent) {
1979            // Detect human
1980            var eventX = jqEvent.clientX;
1981            var eventY = jqEvent.clientY;
1982            var pageX = jqEvent.pageX;
1983            var pageY = jqEvent.pageY;
1984            var isTrusted = false;
1985            if (typeof(jqEvent.originalEvent) !== 'undefined') {
1986                isTrusted = jqEvent.originalEvent.isTrusted;
1987            }
1988            if (eventX && eventY && pageX && pageY && isTrusted && !$(this).is(':checked')) {
1989                var animationObject = {
1990                    width: 0,
1991                    'margin-left': '18px'
1992                };
1993                // Get if it's FF
1994                if (window.mozInnerScreenX || navigator.userAgent.indexOf('Firefox') > -1) {
1995                    animationObject = {
1996                        'opacity': 0
1997                    }
1998                }
1999
2000                $(this).animate(animationObject, 500, function() {
2001                    // Animation complete.
2002                    $(this).after('<span id="jcomment_recaptcha_success"></span>');
2003                    $(this).hide();
2004                    window.jcommentrhvvalidation = true;
2005                    $('#jcomment_recaptcha_label').removeClass('jcomment_label_validate_recaptcha');
2006                    setTimeout(function(element) {
2007                        $('#jcomment_recaptcha_success').addClass('jcomment_animate');
2008                    }, 1);
2009                    $('label.jcomment_label_validation_recaptcha').remove();
2010                });
2011
2012                return true;
2013            }
2014
2015            return false;
2016        });
2017    }
2018}
2019
2020//Manage the optional ReCaptcha control
2021jQuery(function($) {
2022	JComment.initGRecaptcha();
2023
2024    $(document).on('click', 'a.toolbar-button-ip', function(jqEvent) {
2025        // Toggle the tooltip
2026        var existingTooltip = $(this).next('div.jcomment_ip_popover');
2027        if (existingTooltip.length) {
2028            existingTooltip.remove();
2029            return true;
2030        }
2031
2032        var ipToLookup = $(this).data('ipaddress');
2033        var webServiceEndpoint = 'https://json.geoiplookup.io/' + ipToLookup;
2034        $.get(webServiceEndpoint, function(response) {
2035            var emptyResponse = true;
2036            var marginOffset = 0;
2037            if (response) {
2038                var htmlIpInfo = '<ul class="jcomment_ipgeolocation">';
2039                if (response.city) {
2040                    htmlIpInfo += '<li>' + response.city + '</li>';
2041                    emptyResponse = false;
2042                    marginOffset += 4;
2043                }
2044
2045                if (response.country_name) {
2046                    htmlIpInfo += '<li>' + response.country_name + '</li>';
2047                    emptyResponse = false;
2048                    marginOffset += 4;
2049                }
2050
2051                if (response.region) {
2052                    htmlIpInfo += '<li>' + response.region + '</li>';
2053                    emptyResponse = false;
2054                    marginOffset += 4;
2055                }
2056            } else {
2057                // Empty response
2058                htmlIpInfo = '-';
2059            }
2060            // Empty response
2061            if (emptyResponse) {
2062                htmlIpInfo = '-';
2063            } else {
2064                htmlIpInfo += '</ul>';
2065            }
2066
2067            $(jqEvent.target).after('<div class="jcomment_ip_popover">' + htmlIpInfo + '<div class="jcomment-arrow-popover"></div></div>');
2068        }, 'jsonp');
2069    });
2070	// Ensure closing popovers when click on other DOM elements
2071	$(document).on('click', 'body', function(jqEvent){
2072		if(!$(jqEvent.target).hasClass('toolbar-button-ip') && !$(jqEvent.target).parents('.jcomment_ip_popover').length) {
2073			$('div.jcomment_ip_popover').remove();
2074		}
2075	});
2076	
2077	// Manage collapsing
2078	$(document).on('click', 'div.comments-list-collapsed div.comments-list-toggler, div.comments-list-expanded div.comments-list-toggler', function(jqEvent){
2079		if($(jqEvent.target).parent('.comments-list').hasClass('comments-list-collapsed')) {
2080			var oppositeTitle = $(jqEvent.target).data('title-expanded');
2081			$(jqEvent.target).text(oppositeTitle);
2082			$(jqEvent.target).removeClass('comments-list-collapsed').addClass('comments-list-expanded');
2083			$(jqEvent.target).parent('.comments-list').removeClass('comments-list-collapsed').addClass('comments-list-expanded');
2084		} else if($(jqEvent.target).parent('.comments-list').hasClass('comments-list-expanded')) {
2085			var oppositeTitle = $(jqEvent.target).data('title-collapsed');
2086			$(jqEvent.target).text(oppositeTitle);
2087			$(jqEvent.target).removeClass('comments-list-expanded').addClass('comments-list-collapsed');
2088			$(jqEvent.target).parent('.comments-list').removeClass('comments-list-expanded').addClass('comments-list-collapsed');
2089		}
2090		
2091		return false;
2092	});
2093	
2094	// Review instantiation
2095	if(typeof(jQuery.fn.rating) !== 'undefined') {
2096		$('#jcomment-star-rating').rating(function(vote, event) {
2097			$('input[name="comment_reviews_rating"]').val(vote);
2098			$('label.jcomment_label_validation.jcomment_label_validation_rating').remove();
2099		});
2100	}
2101});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.