PageSourceSearch

https://gondia.gov.in/wp-content/plugins/bhashini-translation/assets/js/form-voice-input.js

js gondia.gov.in collected 2026-10-02 21:25:38 UTC 10,167 bytes, 248 lines download raw bytes

1jQuery(document).ready(function($) {
2    const fields = [
3        { id: 'your-name', selector: 'input[name="your-name"]', label: bhashiniAjax.nameFieldError },
4        { id: 'your-message', selector: 'textarea[name="your-message"]', label: bhashiniAjax.messageFieldError }
5    ];
6
7    fields.forEach(field => {
8        const $input = $(field.selector);
9        if ($input.length) {
10            $input.wrap('<div class="form-group" style="position: relative; margin-bottom: 15px;"></div>');
11            $input.after(`
12                <button type="button" 
13                    title="${field.label}" 
14                    aria-label="${field.label}" 
15                    class="mic-button" 
16                    data-target="${field.id}" 
17                    style="position: absolute; right: 0px; top: 0; padding: 0; width: 32px; height: 30px; line-height: 32px; background: none; border: none; cursor: pointer; font-size: 16px;">
18                    <i class="fa fa-microphone"></i>
19                </button>
20            `);
21        }
22    });
23
24    const overlay = $('#voice-fullscreen-overlay');
25    const voiceMsg = $('.voice-message');
26    const voiceClose = $('.voice-close');
27    let mediaRecorder = null;
28    let audioChunks = [];
29    let recordingTimeout = null;
30    let targetInput = null;
31    let mediaStream = null;
32
33    function trapFocus(element) {
34        const focusableEls = element.find('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
35        const firstFocusableEl = focusableEls[0];
36        const lastFocusableEl = focusableEls[focusableEls.length - 1];
37
38        element.off('keydown.trapFocus').on('keydown.trapFocus', function(e) {
39            if (e.key === 'Tab' || e.keyCode === 9) {
40                if (e.shiftKey) {
41                    if (document.activeElement === firstFocusableEl) {
42                        e.preventDefault();
43                        lastFocusableEl && lastFocusableEl.focus();
44                    }
45                } else {
46                    if (document.activeElement === lastFocusableEl) {
47                        e.preventDefault();
48                        firstFocusableEl && firstFocusableEl.focus();
49                    }
50                }
51            }
52        });
53
54        if (firstFocusableEl) {
55            setTimeout(() => firstFocusableEl.focus(), 100);
56        }
57    }
58
59    $('.mic-button').on('click', function() {
60        const targetId = $(this).data('target');
61        const field = fields.find(f => f.id === targetId);
62        targetInput = field ? $(field.selector) : null;
63
64        if (!targetInput || !targetInput.length) {
65            console.error('Target input not found for ID:', targetId);
66            return;
67        }
68
69        overlay.fadeIn(200, function() {
70            // overlay.attr('tabindex', '0');
71            // overlay.attr({
72            //     'role': 'dialog',
73            //     'aria-modal': 'true',
74            //     'tabindex': '0'
75            // });            
76            trapFocus(overlay);
77        });
78
79        voiceMsg.text(bhashiniAjax.speak_now_text);
80        startRecording();
81    });
82
83    voiceClose.on('click', function() {
84        stopRecording(true);
85        overlay.fadeOut(200);
86        if (targetInput) targetInput.focus();
87        targetInput = null;
88    });
89    $(document).on('keydown', function(e) {
90        if (e.key === 'Escape' && overlay.is(':visible')) {
91            voiceClose.trigger('click');
92        }
93    });
94
95    function startRecording() {
96        let mimeType = 'audio/webm';
97        if (MediaRecorder.isTypeSupported('audio/wav')) {
98            mimeType = 'audio/wav';
99        } else if (MediaRecorder.isTypeSupported('audio/webm')) {
100            mimeType = 'audio/webm';
101        } else {
102            console.error('No supported audio format (WAV or WebM) found.');
103            voiceMsg.text('Your browser does not support required audio formats.');
104            setTimeout(() => {
105                overlay.fadeOut(300);
106                targetInput = null;
107            }, 2000);
108            return;
109        }
110
111        const language = localStorage.getItem('preferredLanguage') || 'en';
112        // console.log('Selected language:', language);
113
114        navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
115            mediaStream = stream;
116            mediaRecorder = new MediaRecorder(stream, { mimeType: mimeType });
117            audioChunks = [];
118
119            setTimeout(() => {
120                voiceMsg.text(bhashiniAjax.listening_text);
121            }, 1000);
122
123            mediaRecorder.ondataavailable = e => {
124                audioChunks.push(e.data);
125            };
126
127            mediaRecorder.onstop = () => {
128                clearTimeout(recordingTimeout);
129                stopMediaStream();
130
131                const audioBlob = new Blob(audioChunks, { type: mimeType });
132                const reader = new FileReader();
133                reader.readAsDataURL(audioBlob);
134
135                reader.onloadend = () => {
136                    const base64Audio = reader.result.split(',')[1];
137                    const audioFormat = mimeType.includes('wav') ? 'wav' : 'webm';
138
139                    $.ajax({
140                        url: bhashiniAjax.ajaxurl,
141                        type: 'POST',
142                        data: {
143                            action: 'bhashini_asr',
144                            audio_content: base64Audio,
145                            language: language,
146                            audio_format: audioFormat
147                        },
148                        success: (response) => {
149                            let text = response?.data?.text?.trim()?.toLowerCase();
150                            const excludePhrases = [
151                                "close voice search dialogue button listening",
152                                "this page is accessing your microphone change the setting",
153                                "this page is accessing your microphone change this",
154                                "listening",
155                                "speak now",
156                                "search my voice button, dialogue speak now, listening",
157                                "firefox sharing indicator alert",
158                                "dialogue box",
159                                "dialogue speed now listening okay query",
160                                "you",
161                                "firebox sharing educator alert",
162                                "close button",
163                                "dialogue speed now listening",
164                                "dialogue speed, listen",
165                                "dialogue's too pale listen",
166                                "dialogue speak"
167                            ];
168
169                            const isExcluded = excludePhrases.some(phrase => text.includes(phrase));
170
171                            if (response.success && text && !isExcluded) {
172                                if (targetInput.attr('name') === 'your-name') {
173                                    text = text.replace(/\./g, '');
174                                    targetInput.val(text).focus();
175                                } else if (targetInput.attr('name') === 'your-message') {
176                                    const currentText = targetInput.val();
177                                    const newText = currentText ? currentText + ' ' + text : text;
178                                    targetInput.val(newText).focus();
179                                }
180                                setTimeout(() => {
181                                    overlay.fadeOut(1000);
182                                    targetInput = null;
183                                }, 1000);
184                            } else {
185                                console.warn('Text excluded (screen reader or invalid):', text);
186                                voiceMsg.text(bhashiniAjax.client_error);
187                                setTimeout(() => {
188                                    overlay.fadeOut(300);
189                                    targetInput = null;
190                                }, 2000);
191                            }
192                        },
193                        error: (xhr, status, error) => {
194                            console.error('AJAX error details:', {
195                                status: status,
196                                error: error,
197                                responseText: xhr.responseText,
198                                statusCode: xhr.status,
199                                url: bhashiniAjax.ajaxurl,
200                                headers: xhr.getAllResponseHeaders()
201                            });
202                            voiceMsg.text(bhashiniAjax.server_error_text);
203                            setTimeout(() => {
204                                overlay.fadeOut(300);
205                                targetInput = null;
206                            }, 2000);
207                        }
208                    });
209                };
210            };
211
212            mediaRecorder.start();
213            const timeoutDuration = targetInput.attr('name') === 'your-name' ? 2000 : 5000;
214            recordingTimeout = setTimeout(() => {
215                if (mediaRecorder.state !== 'inactive') {
216                    mediaRecorder.stop();
217                }
218            }, timeoutDuration);
219        }).catch((err) => {
220            console.error('Microphone error:', err);
221            voiceMsg.text(bhashiniAjax.no_mic_detected);
222            setTimeout(() => {
223                overlay.fadeOut(300);
224                targetInput = null;
225            }, 2000);
226        });
227    }
228
229    function stopRecording(cancel = false) {
230        if (mediaRecorder && mediaRecorder.state !== 'inactive') {
231            mediaRecorder.stop();
232        }
233        if (cancel) {
234            audioChunks = [];
235            clearTimeout(recordingTimeout);
236        }
237        stopMediaStream();
238    }
239
240    function stopMediaStream() {
241        if (mediaStream) {
242            mediaStream.getTracks().forEach(track => {
243                track.stop();
244            });
245            mediaStream = null;
246        }
247    }
248});

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.