From beaed99ba73107729b4b5a5f839f7914fd3b0ecb Mon Sep 17 00:00:00 2001 From: Shaheed Azaad Date: Mon, 21 Jul 2025 20:12:48 +0200 Subject: [PATCH] added new plugin --- scripts/transcribe-call.js | 275 +++++++++++++++++++++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 scripts/transcribe-call.js diff --git a/scripts/transcribe-call.js b/scripts/transcribe-call.js new file mode 100644 index 0000000..4950b83 --- /dev/null +++ b/scripts/transcribe-call.js @@ -0,0 +1,275 @@ +import { ParameterType } from 'jspsych'; +import html from '../utils/html.js'; + + const info = { + name: "transcribe-call", + parameters: { + + }, + }; + + class jsPsychTranscribeCall { + constructor(jsPsych) { + this.jsPsych = jsPsych; + } + static { + this.info = info; + } + + + trial(display_element, trial) { + // Get the original recording data from the record-call trial + // Look through recent trials to find one with recording response + const allData = this.jsPsych.data.get(); + const recentTrials = allData.trials.slice(-10); // Look at last 10 trials + + let recordingData = null; + for (let i = recentTrials.length - 1; i >= 0; i--) { + const trial = recentTrials[i]; + if (trial.response && typeof trial.response === 'string' && trial.response.length > 100) { + // Found a trial with audio recording data + recordingData = trial; + break; + } + } + + if (!recordingData || !recordingData.response) { + display_element.innerHTML = ` +
+

No recording found from the previous trial.

+ +
+ `; + return; + } + + // Convert base64 back to audio blob for playback + let audioData; + let audioBlob; + try { + const byteCharacters = atob(recordingData.response); + const byteNumbers = new Array(byteCharacters.length); + for (let i = 0; i < byteCharacters.length; i++) { + byteNumbers[i] = byteCharacters.charCodeAt(i); + } + const byteArray = new Uint8Array(byteNumbers); + audioBlob = new Blob([byteArray], { type: 'audio/ogg' }); + audioData = URL.createObjectURL(audioBlob); + } catch (error) { + console.error('Error creating audio blob:', error); + display_element.innerHTML = ` +
+

Error loading audio data.

+ +
+ `; + return; + } + + display_element.innerHTML = ` +
+

Recording Transcription

+ +
+ +
+ +
+

Please answer the following questions:

+ +
+ + +
+ +
+ + +
+ + + + + +
+ + +
+ + +
+
+ `; + + // Set the audio source + const audio = document.getElementById('transcription-playback-audio'); + audio.src = audioData; + + this.setupTranscriptionEvents(recordingData); + } + + setupTranscriptionEvents(recordingData) { + const audio = document.getElementById('transcription-playback-audio'); + const spellingInput = document.getElementById('spelling-input'); + const languageSelect = document.getElementById('language-select'); + const otherLanguageSection = document.getElementById('other-language-section'); + const otherLanguageInput = document.getElementById('other-language-input'); + const translationSection = document.getElementById('translation-section'); + const translationInput = document.getElementById('translation-input'); + const meaningInput = document.getElementById('meaning-input'); + const submitButton = document.getElementById('submit-answers-button'); + + // Language selection logic + languageSelect.addEventListener('change', () => { + const selectedLanguage = languageSelect.value; + + if (selectedLanguage === 'Other') { + otherLanguageSection.style.display = 'block'; + otherLanguageInput.required = true; + } else { + otherLanguageSection.style.display = 'none'; + otherLanguageInput.required = false; + otherLanguageInput.value = ''; + } + + if (selectedLanguage && selectedLanguage !== 'English' && selectedLanguage !== '') { + translationSection.style.display = 'block'; + translationInput.required = true; + } else { + translationSection.style.display = 'none'; + translationInput.required = false; + translationInput.value = ''; + } + }); + + // Form validation and submission + const validateForm = () => { + const spelling = spellingInput.value.trim(); + const language = languageSelect.value; + const otherLanguage = otherLanguageInput.value.trim(); + const translation = translationInput.value.trim(); + const meaning = meaningInput.value.trim(); + + let isValid = true; + + if (!spelling) { + spellingInput.style.borderColor = '#f44336'; + isValid = false; + } else { + spellingInput.style.borderColor = '#ddd'; + } + + if (!language) { + languageSelect.style.borderColor = '#f44336'; + isValid = false; + } else { + languageSelect.style.borderColor = '#ddd'; + } + + if (language === 'Other' && !otherLanguage) { + otherLanguageInput.style.borderColor = '#f44336'; + isValid = false; + } else { + otherLanguageInput.style.borderColor = '#ddd'; + } + + if (language && language !== 'English' && language !== '' && !translation) { + translationInput.style.borderColor = '#f44336'; + isValid = false; + } else { + translationInput.style.borderColor = '#ddd'; + } + + if (!meaning) { + meaningInput.style.borderColor = '#f44336'; + isValid = false; + } else { + meaningInput.style.borderColor = '#ddd'; + } + + return isValid; + }; + + // Submit button event + submitButton.addEventListener('click', () => { + if (!validateForm()) { + alert('Please fill in all required fields.'); + return; + } + + const finalLanguage = languageSelect.value === 'Other' ? otherLanguageInput.value.trim() : languageSelect.value; + + const trialData = { + spelling: spellingInput.value.trim(), + language: finalLanguage, + translation: translationInput.value.trim(), + meaning: meaningInput.value.trim(), + original_recording_data: { + response: recordingData.response, + rt: recordingData.rt, + stimulus: recordingData.stimulus, + audio_duration: recordingData.audio_duration + } + }; + + // Clean up object URL to prevent memory leaks + if (audio.src && audio.src.startsWith('blob:')) { + URL.revokeObjectURL(audio.src); + } + + this.jsPsych.finishTrial(trialData); + }); + + // Auto-focus on first input + spellingInput.focus(); + } + } + +export default jsPsychTranscribeCall; \ No newline at end of file