PageSourceSearch

https://docs.jaseci.org/js/feedback.js

js jaseci.org collected 2026-10-05 07:21:20 UTC 4,916 bytes, 151 lines download raw bytes

1class FeedbackWidget {
2    constructor() {
3        this.container = document.getElementById('feedbackContainer');
4        this.toggle = document.getElementById('feedbackToggle');
5        this.form = document.getElementById('feedbackForm');
6        this.formElement = document.getElementById('feedbackFormElement');
7        this.ratingBtns = document.querySelectorAll('.rating-btn');
8        this.submitBtn = document.getElementById('submitBtn');
9        this.cancelBtn = document.getElementById('cancelBtn');
10        this.successMessage = document.getElementById('successMessage');
11        this.commentInput = document.getElementById('commentInput');
12
13        this.selectedRating = null;
14        this.isSubmitting = false;
15
16        this.init();
17    }
18
19    init() {
20        // Check if we should hide the form on main domain
21        this.checkDomain();
22
23        // Set up event listeners
24        this.toggle.addEventListener('click', () => this.toggleForm());
25        this.cancelBtn.addEventListener('click', () => this.closeForm());
26
27        this.ratingBtns.forEach(btn => {
28            btn.addEventListener('click', () => this.selectRating(btn));
29        });
30
31        // Add input listener for comment field
32        this.commentInput.addEventListener('input', () => this.updateSubmitButton());
33
34        this.formElement.addEventListener('submit', (e) => this.handleSubmit(e));
35
36        // Close form when clicking outside
37        document.addEventListener('click', (e) => {
38            if (!this.container.contains(e.target)) {
39                this.closeForm();
40            }
41        });
42
43        // Populate hidden fields
44        this.populateHiddenFields();
45    }
46
47    checkDomain() {
48        const currentUrl = window.location.href;
49        const mainDomainUrl = 'https://www.jac-lang.org/';
50
51        // Only hide if we're exactly on the main domain (no path)
52        if (currentUrl === mainDomainUrl) {
53            this.container.style.display = 'none';
54            return;
55        }
56    }
57
58    toggleForm() {
59        this.form.classList.toggle('active');
60        if (this.form.classList.contains('active')) {
61            this.populateHiddenFields();
62        }
63    }
64
65    closeForm() {
66        this.form.classList.remove('active');
67        this.resetForm();
68    }
69
70    selectRating(btn) {
71        // Remove selected class from all buttons
72        this.ratingBtns.forEach(b => b.classList.remove('selected'));
73
74        // Add selected class to clicked button
75        btn.classList.add('selected');
76
77        // Store rating
78        this.selectedRating = btn.dataset.rating;
79        document.getElementById('ratingInput').value = this.selectedRating;
80
81        // Update submit button immediately after rating selection
82        this.updateSubmitButton();
83    }
84
85    updateSubmitButton() {
86        const comment = this.commentInput.value.trim();
87        const hasRating = this.selectedRating !== null;
88        const hasComment = comment.length > 0;
89
90        console.log('Update Submit Button:', { hasRating, hasComment, comment: comment.length, rating: this.selectedRating });
91
92        this.submitBtn.disabled = !(hasRating && hasComment);
93    }
94
95    populateHiddenFields() {
96        document.getElementById('urlInput').value = window.location.href;
97        document.getElementById('timestampInput').value = new Date().toISOString();
98        document.getElementById('userAgentInput').value = navigator.userAgent;
99        document.getElementById('pageTitleInput').value = document.title;
100    }
101
102    async handleSubmit(e) {
103        e.preventDefault();
104
105        if (this.isSubmitting) return;
106
107        this.isSubmitting = true;
108        this.submitBtn.disabled = true;
109        this.submitBtn.textContent = 'Submitting...';
110
111        try {
112            const formData = new FormData(this.formElement);
113
114            const response = await fetch(this.formElement.action, {
115                method: 'POST',
116                mode: 'no-cors',
117                body: formData
118            });
119
120            // Show success message
121            this.showSuccess();
122
123        } catch (error) {
124            console.error('Error submitting feedback:', error);
125            // Still show success since no-cors mode doesn't give us response details
126            this.showSuccess();
127        }
128    }
129
130    showSuccess() {
131        this.successMessage.classList.add('show');
132        setTimeout(() => {
133            this.successMessage.classList.remove('show');
134            this.closeForm();
135        }, 2000);
136    }
137
138    resetForm() {
139        this.formElement.reset();
140        this.ratingBtns.forEach(btn => btn.classList.remove('selected'));
141        this.selectedRating = null;
142        this.submitBtn.disabled = true;
143        this.submitBtn.textContent = 'Submit';
144        this.isSubmitting = false;
145    }
146}
147
148// Initialize when DOM is loaded
149document.addEventListener('DOMContentLoaded', () => {
150    window.feedbackWidget = new FeedbackWidget();
151});

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.