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.