{ "version": 3, "sources": ["../../javascript/utils/element_i18n.js", "../../javascript/web_components/audio_recorder.js", "../../javascript/utils/time_to_string.js", "../../javascript/web_components/audio_player.js", "../../javascript/web_components/custom_select.js", "../../javascript/web_components/tags_input.js", "../../javascript/web_components/topics_select.js"], "sourcesContent": ["// The copy a web component renders, read from its own `data-i18n` attribute and laid over the\n// Spanish defaults the class ships. A component renders before Rails can reach it, so a missing or\n// malformed attribute must still leave it readable.\nexport default function elementI18n(element, defaults) {\n const attribute = element.getAttribute('data-i18n');\n if (!attribute) return defaults;\n\n try {\n return { ...defaults, ...JSON.parse(attribute) };\n } catch {\n return defaults;\n }\n}\n", "import elementI18n from '../utils/element_i18n';\n\nclass AudioRecorder extends HTMLElement {\n static formAssociated = true;\n\n static isSupported() {\n return (\n typeof window !== \"undefined\" &&\n window.MediaRecorder &&\n MediaRecorder.isTypeSupported(\"audio/webm;codecs=opus\")\n );\n }\n\n static defaultI18n = {\n unsupported: \"Tu navegador no soporta la grabaci\u00F3n de audio. Por favor usa Chrome, Firefox o Edge.\",\n microphoneRequired: \"Se requiere acceso al micr\u00F3fono\",\n microphoneRequiredHelp: \"Por favor permit\u00ED el acceso al micr\u00F3fono en la configuraci\u00F3n de tu navegador y recarg\u00E1 la p\u00E1gina.\",\n microphoneBusy: \"No pudimos usar tu micr\u00F3fono\",\n microphoneBusyHelp: \"Otra aplicaci\u00F3n puede estar us\u00E1ndolo. Cerrala y volv\u00E9 a intentar.\",\n microphoneMissing: \"No encontramos un micr\u00F3fono\",\n microphoneMissingHelp: \"Conect\u00E1 un micr\u00F3fono y volv\u00E9 a intentar.\",\n microphoneFailed: \"No pudimos empezar a grabar\",\n microphoneFailedHelp: \"Volv\u00E9 a intentar. Si sigue fallando, prob\u00E1 con otro navegador.\",\n recordingRequired: \"Por favor grab\u00E1 un audio antes de enviar\",\n record: \"Grabar\",\n cancel: \"Cancelar\",\n stop: \"Detener\",\n discard: \"Descartar\",\n play: \"Reproducir\",\n pause: \"Pausar\",\n reRecord: \"Volver a grabar\"\n };\n\n // getUserMedia rejects with a DOMException whose `name` states the cause. NotReadableError is a\n // device failure and never a permission failure, so sending that student to her browser settings\n // points her at a setting that is already correct.\n // Each value names a copy pair: the title itself and `Help` beside it. A title with no\n // help key renders \"undefined\" to the student.\n static microphoneErrorTitles = {\n NotAllowedError: \"microphoneRequired\",\n PermissionDeniedError: \"microphoneRequired\",\n SecurityError: \"microphoneRequired\",\n NotReadableError: \"microphoneBusy\",\n TrackStartError: \"microphoneBusy\",\n AbortError: \"microphoneBusy\",\n NotFoundError: \"microphoneMissing\",\n DevicesNotFoundError: \"microphoneMissing\",\n OverconstrainedError: \"microphoneMissing\",\n ConstraintNotSatisfiedError: \"microphoneMissing\"\n };\n\n constructor() {\n super();\n\n this.mediaRecorder = null;\n this.chunks = [];\n this.stream = null;\n this.state = \"idle\"; // idle, recording, recorded, playing\n this.timerInterval = null;\n this.elapsedMs = 0;\n this.currentBlob = null;\n this.form = null;\n this.fileInput = null;\n this.maxDurationMs = 30000;\n\n // Audio visualization\n this.audioContext = null;\n this.analyser = null;\n this.animationId = null;\n\n // Playback\n this.audioElement = null;\n this.playbackProgress = 0;\n this.playbackAnimationId = null;\n\n this.internals = this.attachInternals();\n }\n\n get i18n() {\n return elementI18n(this, AudioRecorder.defaultI18n);\n }\n\n connectedCallback() {\n // Check browser support first\n if (!AudioRecorder.isSupported()) {\n this.innerHTML = `\n <div class=\"audio-recorder-unsupported\">\n ${this.i18n.unsupported}\n </div>\n `;\n return;\n }\n\n this.innerHTML = this.createTemplate();\n\n // State containers\n this.idleState = this.querySelector(\".audio-recorder-idle\");\n this.recordingState = this.querySelector(\".audio-recorder-recording\");\n this.playbackState = this.querySelector(\".audio-recorder-playback\");\n this.recorderContainer = this.querySelector(\".audio-recorder-container\");\n\n // Buttons\n this.errorState = this.querySelector(\".audio-recorder-error\");\n this.startRecordBtn = this.querySelector(\".audio-recorder-start-btn\");\n this.cancelRecordBtn = this.querySelector(\".audio-recorder-cancel-btn\");\n this.stopRecordBtn = this.querySelector(\".audio-recorder-stop-btn\");\n this.discardBtn = this.querySelector(\".audio-recorder-discard-btn\");\n this.playPauseBtn = this.querySelector(\".audio-recorder-play-pause-btn\");\n this.reRecordBtn = this.querySelector(\".audio-recorder-re-record-btn\");\n\n // Icons\n this.playIcon = this.querySelector(\".audio-recorder-play-icon\");\n this.pauseIcon = this.querySelector(\".audio-recorder-pause-icon\");\n\n // Timers\n this.recordingTimer = this.querySelector(\".audio-recorder-recording-timer\");\n this.playbackTimer = this.querySelector(\".audio-recorder-playback-timer\");\n\n // Waveform elements\n this.amplitudeBarsContainer = this.querySelector(\".audio-recorder-amplitude-bars\");\n this.bars = this.querySelectorAll(\".audio-recorder-amplitude-bar\");\n this.playbackWaveform = this.querySelector(\".audio-recorder-playback-waveform\");\n this.waveformBars = this.querySelector(\".audio-recorder-waveform-bars\");\n this.progressIndicator = this.querySelector(\".audio-recorder-progress-indicator\");\n\n // Audio player\n this.audioPlayer = this.querySelector(\".audio-recorder-audio\");\n\n // Get max duration from attribute (in seconds), default 30\n const maxDurationAttr = this.getAttribute(\"max-duration\");\n if (maxDurationAttr) {\n this.maxDurationMs = parseInt(maxDurationAttr, 10) * 1000;\n }\n\n // Wire up buttons\n this.startRecordBtn.addEventListener(\"click\", () => this.startRecording());\n this.cancelRecordBtn.addEventListener(\"click\", () => this.cancelRecording());\n this.stopRecordBtn.addEventListener(\"click\", () => this.stopRecording());\n this.discardBtn.addEventListener(\"click\", () => this.discardRecording());\n this.playPauseBtn.addEventListener(\"click\", () => this.togglePlayback());\n this.reRecordBtn.addEventListener(\"click\", () => this.reRecord());\n\n // Playback waveform click to seek\n this.playbackWaveform.addEventListener(\"click\", (e) => this.seekToPosition(e));\n\n // Audio player events\n this.audioPlayer.addEventListener(\"timeupdate\", () => this.updatePlaybackProgress());\n this.audioPlayer.addEventListener(\"ended\", () => this.onPlaybackEnded());\n\n // Generate static waveform bars\n this.generateWaveformBars();\n\n // Set initial form validation - require a recording\n this.setValidityState(false);\n }\n\n disconnectedCallback() {\n this.stopTimer();\n this.stopStream();\n this.stopVisualization();\n this.stopPlaybackAnimation();\n }\n\n createTemplate() {\n const amplitudeBars = Array(40).fill('<div class=\"audio-recorder-amplitude-bar\" style=\"height: 4px;\"></div>').join('');\n const t = this.i18n;\n\n return `\n <div class=\"audio-recorder-container\">\n <!-- Idle State -->\n <div class=\"audio-recorder-idle\">\n <button type=\"button\" class=\"audio-recorder-primary-btn audio-recorder-start-btn\" title=\"${t.record}\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/>\n </svg>\n </button>\n </div>\n\n <!-- Recording State -->\n <div class=\"audio-recorder-recording\" hidden>\n <button type=\"button\" class=\"audio-recorder-icon-btn audio-recorder-delete-btn audio-recorder-cancel-btn\" title=\"${t.cancel}\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M3 6h18\"/>\n <path d=\"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6\"/>\n <path d=\"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2\"/>\n </svg>\n </button>\n <div class=\"audio-recorder-waveform-container\">\n <div class=\"audio-recorder-amplitude-bars\">\n ${amplitudeBars}\n </div>\n </div>\n <span class=\"audio-recorder-rec-dot\"></span>\n <span class=\"audio-recorder-timer audio-recorder-recording-timer\">0:00</span>\n <button type=\"button\" class=\"audio-recorder-primary-btn audio-recorder-stop-btn\" title=\"${t.stop}\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <rect x=\"6\" y=\"6\" width=\"12\" height=\"12\" rx=\"2\"/>\n </svg>\n </button>\n </div>\n\n <!-- Recorded/Playback State -->\n <div class=\"audio-recorder-playback\" hidden>\n <button type=\"button\" class=\"audio-recorder-icon-btn audio-recorder-delete-btn audio-recorder-discard-btn\" title=\"${t.discard}\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M3 6h18\"/>\n <path d=\"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6\"/>\n <path d=\"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"audio-recorder-icon-btn audio-recorder-play-pause-btn\" title=\"${t.play}\">\n <svg class=\"audio-recorder-play-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <polygon points=\"5 3 19 12 5 21 5 3\"/>\n </svg>\n <svg class=\"audio-recorder-pause-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\" style=\"display: none;\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\"/>\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\"/>\n </svg>\n </button>\n <div class=\"audio-recorder-waveform-container\">\n <div class=\"audio-recorder-playback-waveform\">\n <div class=\"audio-recorder-waveform-bars\"></div>\n <div class=\"audio-recorder-progress-indicator\" style=\"left: 0;\"></div>\n </div>\n </div>\n <span class=\"audio-recorder-timer audio-recorder-playback-timer\">0:00</span>\n <button type=\"button\" class=\"audio-recorder-primary-btn audio-recorder-re-record-btn\" title=\"${t.reRecord}\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/>\n </svg>\n </button>\n </div>\n </div>\n\n <!-- Failure state: it sits BELOW the controls, never in place of them. Replacing the\n container left the recorder with zero buttons, and a full page reload as the only way\n out. -->\n <div class=\"audio-recorder-error\" role=\"alert\" hidden></div>\n\n <audio class=\"audio-recorder-audio\" hidden></audio>\n `;\n }\n\n setState(newState) {\n this.state = newState;\n\n // Hide all states\n this.idleState.hidden = true;\n this.recordingState.hidden = true;\n this.playbackState.hidden = true;\n\n // Show current state\n switch (newState) {\n case \"idle\":\n this.idleState.hidden = false;\n this.recorderContainer.classList.remove(\"has-recording\");\n break;\n case \"recording\":\n this.recordingState.hidden = false;\n this.recorderContainer.classList.remove(\"has-recording\");\n break;\n case \"recorded\":\n case \"playing\":\n this.playbackState.hidden = false;\n this.recorderContainer.classList.add(\"has-recording\");\n break;\n }\n }\n\n async startRecording() {\n this.clearRecordingError();\n\n try {\n this.stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n } catch (err) {\n console.error(\"Microphone error:\", err);\n this.showRecordingError(err);\n this.dispatchEvent(\n new CustomEvent(\"recording-error\", { detail: err, bubbles: true })\n );\n return;\n }\n\n this.chunks = [];\n this.mediaRecorder = new MediaRecorder(this.stream, {\n mimeType: this.getAttribute(\"mime-type\") || \"audio/webm\"\n });\n\n this.mediaRecorder.ondataavailable = (e) => {\n if (e.data.size > 0) this.chunks.push(e.data);\n };\n\n this.mediaRecorder.start();\n this.elapsedMs = 0;\n this.recordingTimer.textContent = \"0:00\";\n this.setState(\"recording\");\n this.startTimer();\n this.startVisualization();\n }\n\n stopRecording() {\n if (this.state !== \"recording\" || !this.mediaRecorder) return;\n\n this.mediaRecorder.stop();\n this.stopTimer();\n this.stopVisualization();\n this.stopStream();\n\n // Wait for data to be available\n this.mediaRecorder.onstop = () => {\n const blob = new Blob(this.chunks, { type: this.mediaRecorder.mimeType });\n this.currentBlob = blob;\n const url = URL.createObjectURL(blob);\n\n this.audioPlayer.src = url;\n this.updatePlaybackTimer(this.elapsedMs);\n this.setState(\"recorded\");\n\n // Generate waveform visualization based on duration\n this.generateWaveformBars();\n\n // Set form value and validity so form can be submitted\n this.updateFile();\n\n this.dispatchEvent(\n new CustomEvent(\"recording-complete\", {\n bubbles: true,\n detail: {\n blob,\n url,\n durationMs: this.elapsedMs\n }\n })\n );\n };\n }\n\n cancelRecording() {\n if (this.state !== \"recording\") return;\n\n if (this.mediaRecorder && this.mediaRecorder.state !== \"inactive\") {\n this.mediaRecorder.stop();\n }\n this.stopTimer();\n this.stopVisualization();\n this.stopStream();\n this.chunks = [];\n this.elapsedMs = 0;\n\n this.setState(\"idle\");\n this.dispatchEvent(new CustomEvent(\"recording-cancelled\"));\n }\n\n discardRecording() {\n if (this.currentBlob) {\n URL.revokeObjectURL(this.audioPlayer.src);\n }\n this.currentBlob = null;\n this.audioPlayer.removeAttribute(\"src\");\n this.audioPlayer.load();\n this.elapsedMs = 0;\n this.playbackProgress = 0;\n this.updateWaveformProgress(0);\n\n // Clear form value and reset validation\n this.internals.setFormValue(null);\n this.setValidityState(false);\n\n this.setState(\"idle\");\n this.dispatchEvent(new CustomEvent(\"recording-discarded\"));\n }\n\n reRecord() {\n // Clean up current recording without changing state to idle\n if (this.currentBlob) {\n URL.revokeObjectURL(this.audioPlayer.src);\n }\n this.currentBlob = null;\n this.audioPlayer.removeAttribute(\"src\");\n this.audioPlayer.load();\n this.playbackProgress = 0;\n this.updateWaveformProgress(0);\n this.internals.setFormValue(null);\n\n // Start recording immediately (no flicker)\n this.startRecording();\n }\n\n togglePlayback() {\n if (this.audioPlayer.paused) {\n this.startPlayback();\n } else {\n this.pausePlayback();\n }\n }\n\n startPlayback() {\n this.audioPlayer.play();\n this.showPauseIcon();\n this.setState(\"playing\");\n this.startPlaybackAnimation();\n }\n\n pausePlayback() {\n this.audioPlayer.pause();\n this.showPlayIcon();\n this.setState(\"recorded\");\n this.stopPlaybackAnimation();\n }\n\n startPlaybackAnimation() {\n this.stopPlaybackAnimation();\n const animate = () => {\n if (this.state !== \"playing\") return;\n this.updatePlaybackProgress();\n this.playbackAnimationId = requestAnimationFrame(animate);\n };\n this.playbackAnimationId = requestAnimationFrame(animate);\n }\n\n stopPlaybackAnimation() {\n if (this.playbackAnimationId) {\n cancelAnimationFrame(this.playbackAnimationId);\n this.playbackAnimationId = null;\n }\n }\n\n showPlayIcon() {\n this.playIcon.style.display = \"block\";\n this.pauseIcon.style.display = \"none\";\n this.playPauseBtn.title = this.i18n.play;\n }\n\n showPauseIcon() {\n this.playIcon.style.display = \"none\";\n this.pauseIcon.style.display = \"block\";\n this.playPauseBtn.title = this.i18n.pause;\n }\n\n onPlaybackEnded() {\n this.stopPlaybackAnimation();\n this.showPlayIcon();\n this.setState(\"recorded\");\n this.updateWaveformProgress(0);\n const durationMs = this.audioPlayer.duration && Number.isFinite(this.audioPlayer.duration)\n ? this.audioPlayer.duration * 1000\n : this.elapsedMs;\n this.updatePlaybackTimer(durationMs);\n }\n\n seekToPosition(e) {\n const duration = this.audioPlayer.duration && Number.isFinite(this.audioPlayer.duration)\n ? this.audioPlayer.duration\n : this.elapsedMs / 1000;\n\n if (!duration || duration <= 0) return;\n\n const rect = this.playbackWaveform.getBoundingClientRect();\n const clickX = e.clientX - rect.left;\n const percentage = clickX / rect.width;\n const newTime = percentage * duration;\n\n this.audioPlayer.currentTime = newTime;\n this.updateWaveformProgress(percentage * 100);\n }\n\n updatePlaybackProgress() {\n const duration = this.audioPlayer.duration && Number.isFinite(this.audioPlayer.duration)\n ? this.audioPlayer.duration\n : this.elapsedMs / 1000;\n\n if (!duration || duration <= 0) return;\n\n const progress = (this.audioPlayer.currentTime / duration) * 100;\n this.updateWaveformProgress(progress);\n\n const currentMs = this.audioPlayer.currentTime * 1000;\n this.updatePlaybackTimer(currentMs);\n }\n\n updateWaveformProgress(percentage) {\n this.progressIndicator.style.left = `calc(${percentage}% - 5px)`;\n\n if (this.playbackBars) {\n const totalBars = this.playbackBars.length;\n const playedBars = Math.floor((percentage / 100) * totalBars);\n\n this.playbackBars.forEach((bar, index) => {\n if (index < playedBars) {\n bar.classList.add(\"played\");\n } else {\n bar.classList.remove(\"played\");\n }\n });\n }\n }\n\n updatePlaybackTimer(ms) {\n const totalSeconds = Math.floor(ms / 1000);\n const minutes = Math.floor(totalSeconds / 60);\n const seconds = totalSeconds % 60;\n this.playbackTimer.textContent = `${minutes}:${String(seconds).padStart(2, \"0\")}`;\n }\n\n generateWaveformBars() {\n const barCount = 50;\n let barsHtml = \"\";\n\n for (let i = 0; i < barCount; i++) {\n const height = 4 + Math.sin(i * 0.5) * 8 + Math.cos(i * 0.3) * 6 + Math.random() * 4;\n barsHtml += `<div class=\"audio-recorder-waveform-bar\" data-index=\"${i}\" style=\"height: ${Math.max(4, Math.min(28, height))}px;\"></div>`;\n }\n\n this.waveformBars.innerHTML = barsHtml;\n this.playbackBars = this.waveformBars.querySelectorAll(\".audio-recorder-waveform-bar\");\n }\n\n stopStream() {\n if (this.stream) {\n this.stream.getTracks().forEach((t) => t.stop());\n this.stream = null;\n }\n }\n\n startTimer() {\n this.stopTimer();\n this.timerInterval = setInterval(() => {\n this.elapsedMs += 1000;\n const totalSeconds = Math.floor(this.elapsedMs / 1000);\n const minutes = Math.floor(totalSeconds / 60);\n const seconds = totalSeconds % 60;\n this.recordingTimer.textContent = `${minutes}:${String(seconds).padStart(2, \"0\")}`;\n\n const remainingMs = this.maxDurationMs - this.elapsedMs;\n if (remainingMs <= 5000 && remainingMs > 0) {\n this.recordingTimer.classList.add(\"warning\");\n }\n\n if (this.elapsedMs >= this.maxDurationMs) {\n this.stopRecording();\n }\n }, 1000);\n }\n\n stopTimer() {\n if (this.timerInterval) {\n clearInterval(this.timerInterval);\n this.timerInterval = null;\n }\n if (this.recordingTimer) {\n this.recordingTimer.classList.remove(\"warning\");\n }\n }\n\n async startVisualization() {\n if (!this.stream) return;\n\n try {\n this.audioContext = new (window.AudioContext || window.webkitAudioContext)();\n\n if (this.audioContext.state === \"suspended\") {\n await this.audioContext.resume();\n }\n\n this.analyser = this.audioContext.createAnalyser();\n this.analyser.fftSize = 256;\n this.analyser.smoothingTimeConstant = 0.6;\n\n const source = this.audioContext.createMediaStreamSource(this.stream);\n source.connect(this.analyser);\n\n this.bars = this.querySelectorAll(\".audio-recorder-amplitude-bar\");\n\n this.visualize();\n } catch (err) {\n console.warn(\"Could not initialize audio visualization:\", err);\n }\n }\n\n visualize() {\n if (this.state !== \"recording\") return;\n\n const barCount = this.bars.length;\n if (barCount === 0) {\n this.animationId = requestAnimationFrame(() => this.visualize());\n return;\n }\n\n let volume = 0;\n\n if (this.analyser) {\n const bufferLength = this.analyser.fftSize;\n const dataArray = new Uint8Array(bufferLength);\n this.analyser.getByteTimeDomainData(dataArray);\n\n let sum = 0;\n for (let i = 0; i < bufferLength; i++) {\n const value = (dataArray[i] - 128) / 128;\n sum += value * value;\n }\n const rms = Math.sqrt(sum / bufferLength);\n volume = rms > 0.02 ? Math.min(1, rms * 3) : 0;\n }\n\n const time = Date.now() / 150;\n for (let i = 0; i < barCount; i++) {\n const position = i / barCount;\n const wave = Math.sin(position * Math.PI * 2 + time) * 0.3 + 0.7;\n const height = Math.round(4 + volume * wave * 24);\n this.bars[i].style.setProperty(\"height\", `${height}px`, \"important\");\n }\n\n this.animationId = requestAnimationFrame(() => this.visualize());\n }\n\n stopVisualization() {\n if (this.animationId) {\n cancelAnimationFrame(this.animationId);\n this.animationId = null;\n }\n if (this.audioContext) {\n this.audioContext.close().catch(() => {});\n this.audioContext = null;\n }\n this.analyser = null;\n\n if (this.bars) {\n this.bars.forEach((bar) => {\n bar.style.height = \"4px\";\n });\n }\n }\n\n // The student keeps every control: the idle state still carries the record button, so trying\n // again costs one click. A full page reload was the only exit before, and the walk's own intro\n // (\"Respondes una sola vez cada pregunta\") makes a reload read as destructive.\n showRecordingError(err) {\n if (!this.errorState) return;\n\n const titleKey = AudioRecorder.microphoneErrorTitles[err?.name] || \"microphoneFailed\";\n const title = document.createElement(\"strong\");\n title.textContent = this.i18n[titleKey];\n const help = document.createElement(\"span\");\n help.textContent = this.i18n[`${titleKey}Help`];\n\n this.errorState.replaceChildren(title, help);\n this.errorState.hidden = false;\n this.setState(\"idle\");\n // `reRecord` clears the form value and starts a new recording. If that recording never starts,\n // the element still reports valid and the form submits with no audio.\n this.setValidityState(false);\n }\n\n clearRecordingError() {\n if (!this.errorState) return;\n\n this.errorState.replaceChildren();\n this.errorState.hidden = true;\n }\n\n updateFile() {\n if (!this.currentBlob) return;\n\n const fileName = `recording-${Date.now()}.webm`;\n const fileType = this.currentBlob.type || \"audio/webm\";\n const file = new File([this.currentBlob], fileName, { type: fileType });\n\n this.internals.setFormValue(file);\n this.setValidityState(true);\n }\n\n setValidityState(isValid) {\n if (isValid) {\n this.internals.setValidity({});\n } else {\n // Without an anchor the browser has nothing to focus or point the bubble at, so\n // `reportValidity()` refuses the submit and reports nothing: Submit reads as a dead control.\n this.internals.setValidity(\n { valueMissing: true },\n this.i18n.recordingRequired,\n this.startRecordBtn\n );\n }\n }\n}\n\ncustomElements.define(\"audio-recorder\", AudioRecorder);\n", "export default function timeToString(value) {\n const time = value && !Number.isNaN(value) && Number.isFinite(value) ? value : 0;\n\n const minutes = time / 60;\n const seconds = time % 60;\n const array = [minutes, seconds].map((number) => {\n let str = Math.floor(number).toString();\n\n if (str.length === 1) {\n str = `0${str}`;\n }\n\n return str;\n });\n\n return array.join(':');\n}\n", "import timeToString from '../utils/time_to_string';\nimport elementI18n from '../utils/element_i18n';\n\nclass AudioPlayer extends HTMLElement {\n // A stalled media load reports nothing: the element sits at readyState 0 with `error` null while\n // the pipeline retries a response it cannot parse. `error` and `stalled` never arrive, so the\n // only way to learn the audio is not coming is to stop waiting for it.\n static LOAD_TIMEOUT_MS = 12000;\n\n static defaultI18n = {\n loadFailed: 'No pudimos cargar el audio',\n loadFailedHelp: 'Revis\u00E1 tu conexi\u00F3n y volv\u00E9 a intentar.',\n retry: 'Volver a intentar',\n speed: 'Velocidad de reproducci\u00F3n'\n };\n\n constructor() {\n super();\n this.src = this.getAttribute('src');\n this.speedOptions = JSON.parse(this.getAttribute('speed-options'));\n this.startTime = '00:00';\n this.barCount = 40;\n this.playbackAnimationId = null;\n this.loadTimeoutId = null;\n }\n\n get i18n() {\n return elementI18n(this, AudioPlayer.defaultI18n);\n }\n\n connectedCallback() {\n this.innerHTML = this.createTemplate();\n\n this.audioPlayer = this.querySelector('.audioplayer');\n this.audio = this.querySelector('audio');\n this.stopButton = this.querySelector('.audioplayer-stop-btn');\n this.backwardButton = this.querySelector('.audioplayer-backward-btn');\n this.playPauseButton = this.querySelector('.audioplayer-play-pause-btn');\n this.forwardButton = this.querySelector('.audioplayer-forward-btn');\n this.speedSelect = this.querySelector('.audioplayer-speed');\n this.currentTime = this.querySelector('.audioplayer-time.current');\n this.totalTime = this.querySelector('.audioplayer-time.total');\n this.waveform = this.querySelector('.audioplayer-waveform');\n this.waveformBars = this.querySelector('.audioplayer-waveform-bars');\n this.playhead = this.querySelector('.audioplayer-playhead');\n this.playIcon = this.querySelector('.audioplayer-play-icon');\n this.pauseIcon = this.querySelector('.audioplayer-pause-icon');\n\n this.loadFailure = this.querySelector('.audioplayer-load-failure');\n this.retryButton = this.querySelector('.audioplayer-retry-btn');\n\n this.generateWaveformBars();\n this.bindEventListeners();\n\n this.load();\n }\n\n disconnectedCallback() {\n clearTimeout(this.loadTimeoutId);\n }\n\n load() {\n this.clearLoadFailure();\n this.audio.src = this.src;\n // `preload=\"metadata\"` alone leaves a stalled element idle in some browsers, so the load is\n // asked for explicitly and the watchdog starts with it.\n this.audio.load();\n this.loadTimeoutId = setTimeout(() => this.showLoadFailure(), AudioPlayer.LOAD_TIMEOUT_MS);\n }\n\n bindEventListeners() {\n this.stopButton.addEventListener('click', this.stop.bind(this));\n this.playPauseButton.addEventListener('click', this.togglePlayPause.bind(this));\n this.forwardButton.addEventListener('click', this.forward.bind(this));\n this.backwardButton.addEventListener('click', this.backward.bind(this));\n this.speedSelect.addEventListener('change', this.updateAudioPlaybackRate.bind(this));\n\n this.waveform.addEventListener('click', this.updateAudioCurrentTimeFromPlayheadPosition.bind(this));\n this.playhead.addEventListener('mousedown', this.mouseDown.bind(this));\n\n this.audio.addEventListener('timeupdate', this.updateTime.bind(this));\n this.audio.addEventListener('loadedmetadata', this.onDurationAvailable.bind(this));\n this.audio.addEventListener('durationchange', this.onDurationAvailable.bind(this));\n this.audio.addEventListener('canplaythrough', this.onCanPlayThrough.bind(this));\n this.audio.addEventListener('ended', this.onEnded.bind(this));\n this.audio.addEventListener('error', this.showLoadFailure.bind(this));\n this.retryButton.addEventListener('click', this.retry.bind(this));\n }\n\n // The retry button hides itself, so focus moves first. A hidden button would drop focus to the body.\n retry() {\n this.playPauseButton.focus();\n this.load();\n }\n\n generateWaveformBars() {\n let barsHtml = '';\n for (let i = 0; i < this.barCount; i++) {\n const height = 4 + Math.sin(i * 0.5) * 8 + Math.cos(i * 0.3) * 6 + Math.random() * 4;\n barsHtml += `<div class=\"audioplayer-waveform-bar\" data-index=\"${i}\" style=\"height: ${Math.max(4, Math.min(28, height))}px;\"></div>`;\n }\n this.waveformBars.innerHTML = barsHtml;\n this.bars = this.waveformBars.querySelectorAll('.audioplayer-waveform-bar');\n }\n\n togglePlayPause() {\n if (this.audio.paused) {\n this.play();\n } else {\n this.pause();\n }\n }\n\n updateSpeed(value) {\n this.speedSelect.value = value;\n this.updateAudioPlaybackRate();\n }\n\n updateAudioCurrentTimeFromPlayheadPosition(event) {\n const rect = this.waveform.getBoundingClientRect();\n\n // A hidden player has a zero-width waveform, and duration stays NaN until the metadata loads.\n // The first case clamps the position to the end of the audio, and the second one makes the\n // currentTime assignment throw.\n if (!rect.width || !Number.isFinite(this.audio.duration)) { return; }\n\n const clickX = event.clientX - rect.left;\n const percentage = Math.max(0, Math.min(1, clickX / rect.width));\n\n this.audio.currentTime = this.audio.duration * percentage;\n this.dispatchEvent(new CustomEvent('audio-player:playheadChanged', { detail: { clientX: event.clientX } }));\n }\n\n mouseDown() {\n const mouseMove = this.updateAudioCurrentTimeFromPlayheadPosition.bind(this);\n\n const mouseUp = () => {\n window.removeEventListener('mousemove', mouseMove, true);\n window.removeEventListener('mouseup', mouseUp, false);\n };\n\n window.addEventListener('mousemove', mouseMove, true);\n window.addEventListener('mouseup', mouseUp, false);\n }\n\n updateAudioPlaybackRate() {\n if(this.speedSelect.value !== this.audio.playbackRate) {\n this.audio.playbackRate = this.speedSelect.value;\n this.dispatchEvent(new CustomEvent('audio-player:speedChanged', { detail: { value: this.speedSelect.value } }));\n }\n }\n\n stop() {\n this.audioPlayer.classList.remove('playing');\n this.audio.pause();\n this.audio.currentTime = 0;\n this.stopPlaybackAnimation();\n this.updateTime();\n this.dispatchEvent(new CustomEvent('audio-player:stopped'));\n }\n\n play() {\n // The control claimed playback before any byte arrived, so a stalled load read as \"playing\"\n // and then sat silent. `play()` rejects when the element has nothing to play.\n this.audio\n .play()\n .then(() => {\n this.audioPlayer.classList.add('playing');\n this.startPlaybackAnimation();\n this.dispatchEvent(new CustomEvent('audio-player:played'));\n })\n .catch((error) => {\n // Only `NotSupportedError` means no audio. A pause raises `AbortError`, and a blocked autoplay raises\n // `NotAllowedError`. The load timeout still covers a stall.\n if (error.name === 'NotSupportedError') this.showLoadFailure();\n });\n }\n\n pause() {\n this.haltPlayback();\n this.dispatchEvent(new CustomEvent('audio-player:paused'));\n }\n\n haltPlayback() {\n this.audioPlayer.classList.remove('playing');\n this.audio.pause();\n this.stopPlaybackAnimation();\n }\n\n startPlaybackAnimation() {\n this.stopPlaybackAnimation();\n const animate = () => {\n if (this.audio.paused) return;\n this.updateTime();\n this.playbackAnimationId = requestAnimationFrame(animate);\n };\n this.playbackAnimationId = requestAnimationFrame(animate);\n }\n\n stopPlaybackAnimation() {\n if (this.playbackAnimationId) {\n cancelAnimationFrame(this.playbackAnimationId);\n this.playbackAnimationId = null;\n }\n }\n\n forward() {\n if(this.audio.currentTime !== this.audio.duration) {\n const leftTime = this.audio.duration - this.audio.currentTime;\n const forwardTime = Math.min(leftTime, 3);\n this.audio.currentTime = this.audio.currentTime + forwardTime;\n this.dispatchEvent(new CustomEvent('audio-player:forwarded'));\n }\n }\n\n backward() {\n if(this.audio.currentTime !== 0) {\n const backwardTime = Math.min(this.audio.currentTime, 3);\n this.audio.currentTime = this.audio.currentTime - backwardTime;\n this.dispatchEvent(new CustomEvent('audio-player:backwarded'));\n }\n }\n\n updateTime() {\n if (!this.audio.duration || !Number.isFinite(this.audio.duration)) return;\n\n const percentage = (this.audio.currentTime / this.audio.duration) * 100;\n\n this.currentTime.innerHTML = timeToString(this.audio.currentTime);\n this.playhead.style.left = `calc(${percentage}% - 5px)`;\n\n // Update waveform bar colors\n if (this.bars) {\n const playedBars = Math.floor((percentage / 100) * this.bars.length);\n this.bars.forEach((bar, index) => {\n if (index < playedBars) {\n bar.classList.add('played');\n } else {\n bar.classList.remove('played');\n }\n });\n }\n }\n\n onDurationAvailable() {\n if (this.audio.duration && Number.isFinite(this.audio.duration)) {\n this.totalTime.innerHTML = timeToString(this.audio.duration);\n this.clearLoadFailure();\n }\n }\n\n onCanPlayThrough() {\n this.onDurationAvailable();\n }\n\n // The duration is the proof the audio is really there: the stall the personas met held\n // readyState 0 and duration NaN for a minute with no error of any kind.\n clearLoadFailure() {\n clearTimeout(this.loadTimeoutId);\n if (!this.loadFailure || this.loadFailure.hidden) return;\n\n // The load can recover on its own while she is on the retry button. Hiding the region under\n // her focus drops it to <body> with nothing announced. `retry()` moves focus for the same\n // reason; this is the path she did not start.\n if (this.loadFailure.contains(document.activeElement)) this.playPauseButton.focus();\n this.loadFailure.hidden = true;\n this.audioPlayer.classList.remove('load-failed');\n }\n\n showLoadFailure() {\n clearTimeout(this.loadTimeoutId);\n if (!this.loadFailure || !this.loadFailure.hidden) return;\n\n // `pause()` would post a media-sync `pause`, and a load that fails for one student must not\n // pause the students the teacher tracks.\n this.haltPlayback();\n this.loadFailure.hidden = false;\n this.audioPlayer.classList.add('load-failed');\n }\n\n onEnded() {\n this.audioPlayer.classList.remove('playing');\n this.stopPlaybackAnimation();\n }\n\n createTemplate() {\n return `\n <div class=\"audioplayer-load-failure\" role=\"alert\" hidden>\n <strong>${this.i18n.loadFailed}</strong>\n <span>${this.i18n.loadFailedHelp}</span>\n <button type=\"button\" class=\"audioplayer-retry-btn\">${this.i18n.retry}</button>\n </div>\n\n <div class=\"audioplayer\">\n <audio class=\"audioplayer-music\" preload=\"metadata\"></audio>\n\n <button type=\"button\" class=\"audioplayer-icon-btn audioplayer-stop-btn\" title=\"Stop\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <rect x=\"6\" y=\"6\" width=\"12\" height=\"12\" rx=\"2\"/>\n </svg>\n </button>\n\n <button type=\"button\" class=\"audioplayer-icon-btn audioplayer-backward-btn\" title=\"Rewind 3s\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/>\n <path d=\"M3 3v5h5\"/>\n <text x=\"12\" y=\"15\" font-size=\"7\" fill=\"currentColor\" stroke=\"none\" text-anchor=\"middle\">3</text>\n </svg>\n </button>\n\n <button type=\"button\" class=\"audioplayer-primary-btn audioplayer-play-pause-btn\" title=\"Play\">\n <svg class=\"audioplayer-play-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <polygon points=\"6 3 20 12 6 21 6 3\"/>\n </svg>\n <svg class=\"audioplayer-pause-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\"/>\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\"/>\n </svg>\n </button>\n\n <button type=\"button\" class=\"audioplayer-icon-btn audioplayer-forward-btn\" title=\"Forward 3s\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 12a9 9 0 1 1-9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\"/>\n <path d=\"M21 3v5h-5\"/>\n <text x=\"12\" y=\"15\" font-size=\"7\" fill=\"currentColor\" stroke=\"none\" text-anchor=\"middle\">3</text>\n </svg>\n </button>\n\n <span class=\"audioplayer-time current\">${this.startTime}</span>\n\n <div class=\"audioplayer-waveform-container\">\n <div class=\"audioplayer-waveform\">\n <div class=\"audioplayer-waveform-bars\"></div>\n <div class=\"audioplayer-playhead\" style=\"left: 0;\"></div>\n </div>\n </div>\n\n <span class=\"audioplayer-time total\">${this.startTime}</span>\n\n <select class=\"audioplayer-speed\" aria-label=\"${this.i18n.speed}\">\n ${this.speedOptions.map(([value, label]) => `<option value=\"${value}\" ${value === 1 ? 'selected' : ''}>${label}</option>`).join('')}\n </select>\n </div>\n `;\n }\n}\n\ncustomElements.define('audio-player', AudioPlayer);\n\nexport default AudioPlayer;", "// Latch shared across every enhanced row so that, when a grid quiz submit fires `invalid` on\n// several unanswered rows at once, only the first (topmost) one pulls focus. It resets on the\n// next microtask, after the whole synchronous batch of invalid events has run \u2014 this is more\n// robust than reading document.activeElement, which stays on the last-picked combobox in\n// browsers that do not focus a <button> on click (Safari, Firefox).\nlet focusClaimed = false;\n\n// The namespaces the view uses to address the two nodes it cannot render. See carryData.\nconst ITEM_DATA_PREFIX = 'data-item-';\nconst BUTTON_DATA_PREFIX = 'data-button-';\n\n// <custom-select> progressively enhances the native <select> it wraps into an accessible listbox\n// combobox, so long option text can wrap to several lines (the native control truncates the\n// selected value to a single line). The wrapped <select> stays in the DOM as the source of truth\n// for form submission, validation and scoring \u2014 this only adds a visual button + listbox that stay\n// synced to it, so nothing about how the answer is submitted or graded changes. The element is only\n// rendered by the view when the documents-revamp flag is on (see `custom_select_wrapper`).\n//\n// Follows the WAI-ARIA \"Select-Only Combobox\" pattern: a button with role=combobox that owns a\n// role=listbox popup of role=option items, driven by keyboard and aria-activedescendant.\nclass CustomSelect extends HTMLElement {\n connectedCallback() {\n this.select = this.querySelector('select');\n if (!this.select) return;\n\n // A restored Turbo snapshot (Back/Forward) is the cached DOM taken *after* enhancement, so it\n // already holds the button/listbox/error we built last time \u2014 but this is a fresh element\n // instance whose listeners those stale nodes lost. Strip them before rebuilding, or connect\n // would append a second combobox on top of the dead one.\n this.querySelectorAll('.cs-button, .cs-list, .cs-error').forEach((node) => node.remove());\n\n // App-localized message shown when a required answer is blank, passed from the view. The\n // native select's own validationMessage is localized to the *browser* UI language, not the\n // account locale, so it would read English on a Spanish page; that is the fallback only.\n this.requiredMessage = this.getAttribute('required-message') || '';\n // Whether a collapsed `Enter` submits the owning form, the way the native <select> this\n // replaces does. Opt-in, because only a caller whose form holds this one control wants it \u2014\n // see submitOwningForm.\n this.submitsOnEnter = this.hasAttribute('submit-on-enter');\n this.baseId = this.select.id || `cs-${Math.floor(Math.random() * 1e9)}`;\n this.activeIndex = -1;\n this.buildOptions();\n this.buildButton();\n this.buildListbox();\n this.select.after(this.button, this.listbox);\n this.hideNativeSelect();\n this.syncFromSelect();\n\n // Document-level pointerdown / morph handling is shared across all instances by the static\n // listeners at the bottom of this file, so a page of many comboboxes adds no per-element\n // document listeners. `invalid` stays per-instance \u2014 it's on this row's own <select>.\n this.onInvalid = this.onInvalid.bind(this);\n this.select.addEventListener('invalid', this.onInvalid);\n }\n\n disconnectedCallback() {\n openSelects.delete(this);\n this.select?.removeEventListener('invalid', this.onInvalid);\n this.button?.remove();\n this.listbox?.remove();\n this.errorEl?.remove();\n this.select?.classList.remove('visually-hidden');\n this.select?.removeAttribute('aria-hidden');\n this.select?.removeAttribute('tabindex');\n }\n\n // ----- build -------------------------------------------------------------\n\n buildOptions() {\n // The first empty-valued option is the prompt (\"Eleg\u00ED una respuesta\"): keep its text as the\n // button's placeholder, but don't offer it as a selectable row.\n this.optionEls = Array.from(this.select.options);\n this.prompt = this.optionEls.find((o) => o.value === '')?.text || '';\n this.choices = this.optionEls.filter((o) => o.value !== '');\n }\n\n buildButton() {\n this.button = document.createElement('button');\n this.button.type = 'button';\n this.button.className = 'cs-button';\n this.button.id = `${this.baseId}_button`;\n this.button.setAttribute('role', 'combobox');\n this.button.setAttribute('aria-haspopup', 'listbox');\n this.button.setAttribute('aria-expanded', 'false');\n this.button.setAttribute('aria-controls', `${this.baseId}_listbox`);\n // The `disabled` property reflects only the select's own attribute, but the graded card disables\n // the whole <fieldset>. Read the ancestor too: without it the button of a graded card stays\n // operable, and answers `:enabled` in the selector that card hands focus on with.\n if (this.select.disabled || this.select.closest('fieldset[disabled]')) this.button.disabled = true;\n\n const label = this.select.getAttribute('aria-label');\n const labelledby = this.select.getAttribute('aria-labelledby');\n if (label) this.button.setAttribute('aria-label', label);\n if (labelledby) this.button.setAttribute('aria-labelledby', labelledby);\n\n this.textEl = document.createElement('span');\n this.textEl.className = 'cs-text';\n this.button.append(this.textEl);\n this.carryButtonData();\n this.button.append(this.buildChevron());\n\n this.button.addEventListener('click', () => this.toggle());\n this.button.addEventListener('keydown', (event) => this.onButtonKeydown(event));\n }\n\n // The button is the combobox's stand-in, and ERB can reach only the `<custom-select>`: it renders\n // no button. So every `data-button-*` the view puts on the host lands here as a plain `data-*`.\n // That is how the view binds a Stimulus hotkey to a control it never renders. One set of\n // attributes carries the key's binding and the key's cap, so a printed cap is a bound key.\n //\n // The cap prints for sighted discoverability and takes no `aria-keyshortcuts`. `Space` and `Enter`\n // are the standard activation keys of a `role=combobox` button, so the role already tells a screen\n // reader user the key. The listbox items do carry the attribute, because a digit is not standard\n // for a `role=option`.\n carryButtonData() {\n this.carryData(this, this.button, BUTTON_DATA_PREFIX);\n if (this.button.dataset.badge) this.button.append(this.buildBadge(this.button.dataset.badge));\n }\n\n buildChevron() {\n const span = document.createElement('span');\n span.className = 'cs-chevron';\n span.setAttribute('aria-hidden', 'true');\n span.innerHTML = \"<svg viewBox='0 0 16 16' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='m2 5 6 6 6-6'/></svg>\";\n return span;\n }\n\n buildListbox() {\n this.listbox = document.createElement('ul');\n this.listbox.className = 'cs-list';\n this.listbox.id = `${this.baseId}_listbox`;\n this.listbox.setAttribute('role', 'listbox');\n this.listbox.hidden = true;\n\n this.items = this.choices.map((option, index) => {\n const li = document.createElement('li');\n li.className = 'cs-option';\n li.id = `${this.baseId}_opt_${index}`;\n li.setAttribute('role', 'option');\n li.dataset.value = option.value;\n li.textContent = option.text;\n this.carryItemData(option, li);\n li.addEventListener('click', () => this.pick(index));\n this.listbox.append(li);\n return li;\n });\n }\n\n // The item is the option's stand-in, and ERB can reach only the <option>: it renders no <li>. So\n // every `data-item-*` the view puts on the option lands here as a plain `data-*`.\n carryItemData(option, li) {\n this.carryData(option, li, ITEM_DATA_PREFIX);\n\n if (!li.dataset.badge) return;\n\n li.prepend(this.buildBadge(li.dataset.badge));\n // The cap is aria-hidden, so this is what tells a screen reader the item has a key. One value\n // feeds the cap and the announcement, exactly as it feeds the binding on the option.\n li.setAttribute('aria-keyshortcuts', li.dataset.badge);\n }\n\n // The rename is what keeps the source element inert \u2014 a `data-controller` left on the `<option>`\n // or on the `<custom-select>` host would bind a second Stimulus controller to an element no\n // student can click.\n carryData(source, target, prefix) {\n Array.from(source.attributes).forEach(({ name, value }) => {\n if (name.startsWith(prefix)) target.setAttribute(`data-${name.slice(prefix.length)}`, value);\n });\n }\n\n // The printed key cap. `aria-hidden` keeps the key out of the option's accessible name, and\n // `hidden` is its default state, so an item prints a cap only while its card is the armed one\n // (content/_quiz.scss).\n buildBadge(key) {\n const badge = document.createElement('kbd');\n badge.className = 'quiz-hotkey';\n badge.hidden = true;\n badge.setAttribute('aria-hidden', 'true');\n badge.textContent = key;\n return badge;\n }\n\n hideNativeSelect() {\n this.select.classList.add('visually-hidden');\n this.select.setAttribute('aria-hidden', 'true');\n this.select.setAttribute('tabindex', '-1');\n }\n\n // ----- sync --------------------------------------------------------------\n\n syncFromSelect() {\n const value = this.select.value;\n const hasValue = value !== '';\n this.textEl.textContent = hasValue ? this.select.options[this.select.selectedIndex].text : this.prompt;\n this.textEl.classList.toggle('cs-placeholder', !hasValue);\n this.items.forEach((li) => li.setAttribute('aria-selected', String(li.dataset.value === value && hasValue)));\n }\n\n pick(index) {\n const option = this.choices[index];\n if (this.select.value !== option.value) {\n this.select.value = option.value;\n this.select.dispatchEvent(new Event('change', { bubbles: true }));\n }\n this.clearError();\n this.syncFromSelect();\n this.close();\n this.button.focus();\n }\n\n // ----- validation --------------------------------------------------------\n\n // The wrapped <select> stays the constraint-validation source of truth, but it is visually\n // hidden, so its default validation bubble would anchor off-screen \u2014 detached from the\n // visible combobox, with no cue about which answer is missing. Suppress that bubble and\n // surface the message inline under the button instead. Submission stays blocked: preventDefault\n // on `invalid` only hides the bubble, it does not un-cancel the submit.\n onInvalid(event) {\n event.preventDefault();\n this.showError(this.requiredMessage || this.select.validationMessage);\n // Only the first invalid row in this submit's synchronous batch pulls focus (see the latch\n // note at the top of the file).\n if (focusClaimed) return;\n\n focusClaimed = true;\n Promise.resolve().then(() => { focusClaimed = false; });\n this.button.focus();\n }\n\n showError(message) {\n if (!this.errorEl) {\n this.errorEl = document.createElement('p');\n this.errorEl.className = 'cs-error';\n this.errorEl.id = `${this.baseId}_error`;\n this.errorEl.setAttribute('role', 'alert');\n this.listbox.after(this.errorEl);\n }\n this.errorEl.textContent = message;\n this.button.setAttribute('aria-invalid', 'true');\n this.button.setAttribute('aria-describedby', this.errorEl.id);\n }\n\n clearError() {\n if (!this.errorEl) return;\n\n this.errorEl.textContent = '';\n this.button.removeAttribute('aria-invalid');\n this.button.removeAttribute('aria-describedby');\n }\n\n // ----- open / close ------------------------------------------------------\n\n toggle() {\n if (this.button.disabled) return;\n this.expanded ? this.close() : this.open();\n }\n\n open() {\n this.expanded = true;\n openSelects.add(this);\n this.listbox.hidden = false;\n this.button.setAttribute('aria-expanded', 'true');\n const selected = this.items.findIndex((li) => li.getAttribute('aria-selected') === 'true');\n this.setActive(selected >= 0 ? selected : 0);\n }\n\n close() {\n if (!this.expanded) return;\n this.expanded = false;\n openSelects.delete(this);\n this.listbox.hidden = true;\n this.button.setAttribute('aria-expanded', 'false');\n this.button.removeAttribute('aria-activedescendant');\n this.items.forEach((li) => li.classList.remove('cs-active'));\n this.activeIndex = -1;\n }\n\n setActive(index) {\n this.activeIndex = index;\n this.items.forEach((li, i) => li.classList.toggle('cs-active', i === index));\n const li = this.items[index];\n if (li) {\n this.button.setAttribute('aria-activedescendant', li.id);\n li.scrollIntoView?.({ block: 'nearest' });\n }\n }\n\n onDocumentPointer(event) {\n if (!this.expanded) return;\n if (!this.button.contains(event.target) && !this.listbox.contains(event.target)) this.close();\n }\n\n // ----- keyboard ----------------------------------------------------------\n\n // `Enter` belongs to each state's own job. Collapsed, it sends the answer of a caller that asks,\n // the way the native <select> did, and it opens the listbox for every other caller. `Space` and\n // the arrows open the listbox too, and the button prints `Space` so she can find it. Expanded,\n // `Enter` confirms the highlighted option \u2014 otherwise one press would answer and submit, which a\n // placement can never take back.\n //\n // This runs only while the button holds the focus. A card that carries the `Space` binding answers\n // the key from anywhere inside itself. That path clicks the button, so it toggles: the press opens\n // a shut listbox and shuts an open one. Neither takes an answer.\n onButtonKeydown(event) {\n const { key } = event;\n if (!this.expanded) {\n if (['ArrowDown', 'ArrowUp', ' ', 'Spacebar'].includes(key)) {\n this.consume(event);\n this.open();\n } else if (key === 'Enter') {\n // Consumed in both branches, so the collapsed state answers the key and the key stops here.\n // `stopPropagation` too, because a document-level listener reads no `defaultPrevented` \u2014 a\n // card that binds `Enter` there would act on a press this button already took.\n this.consume(event);\n if (this.submitsOnEnter) {\n this.submitOwningForm(event);\n } else {\n this.open();\n }\n }\n return;\n }\n\n switch (key) {\n case 'ArrowDown':\n this.consume(event);\n this.setActive(Math.min(this.activeIndex + 1, this.items.length - 1));\n break;\n case 'ArrowUp':\n this.consume(event);\n this.setActive(Math.max(this.activeIndex - 1, 0));\n break;\n case 'Home':\n this.consume(event);\n this.setActive(0);\n break;\n case 'End':\n this.consume(event);\n this.setActive(this.items.length - 1);\n break;\n case 'Enter':\n case ' ':\n case 'Spacebar':\n this.consume(event);\n if (this.activeIndex >= 0) this.pick(this.activeIndex);\n break;\n case 'Escape':\n this.consume(event);\n this.close();\n this.button.focus();\n break;\n case 'Tab':\n this.close();\n break;\n default:\n break;\n }\n }\n\n // `Enter` on a collapsed native <select> posts its form, and this component replaces that select\n // with a `type=\"button\"` button, which `Enter` activates instead \u2014 so the form stopped posting.\n // `requestSubmit` restores it: unlike `form.submit()` it runs interactive validation and fires the\n // `submit` event Turbo intercepts, exactly as clicking the submit button does.\n //\n // Only a caller that asks gets it. A grid renders one of these per row inside one form, so a\n // press in row 2 would post rows the student has not reached; it opens its listbox instead, which\n // is the key it has always had.\n submitOwningForm(event) {\n // A held key auto-repeats, and each repeat is another post. The first press is the whole intent.\n if (event.repeat) return;\n\n this.select.form?.requestSubmit();\n }\n\n // A consumed key stops here. `preventDefault` alone is not enough: a document-level listener\n // reads no `defaultPrevented`, so the Enter that confirms an option would also click the card's\n // submit button \u2014 and a placement takes one answer.\n consume(event) {\n event.preventDefault();\n event.stopPropagation();\n }\n}\n\n// One document listener drives every instance, rather than one per <custom-select>: a page can hold\n// many comboboxes (a grid quiz renders one per row), so N per-element listeners would each fire on\n// every pointerdown and every morph. Instances register in `openSelects` only while their listbox\n// is open, so the pointer sweep is over the (usually one) open combobox, not all of them.\nconst openSelects = new Set();\n\ndocument.addEventListener('pointerdown', (event) => {\n openSelects.forEach((select) => select.onDocumentPointer(event));\n});\n\n// A page morph (the document view runs `turbo_refreshes_with method: :morph`) reconciles the DOM to\n// the server HTML, which has only the native <select> \u2014 the JS-built button and listbox aren't in\n// it, so morph would strip the enhancement while connectedCallback never re-fires on the preserved\n// element. Skip morphing the custom-select AND its immediate wrapper: the wrapper carries the\n// grid-quiz verdict class that answer-verdict clears on edit, so morphing it back would re-add the\n// server's stale verdict while the enhanced control keeps the student's new value \u2014 a split state.\n// Preserving the wrapper subtree keeps the whole in-progress answer intact, like data-turbo-permanent.\ndocument.addEventListener('turbo:before-morph-element', (event) => {\n const el = event.target;\n if (el instanceof CustomSelect || el.firstElementChild instanceof CustomSelect) event.preventDefault();\n});\n\ncustomElements.define('custom-select', CustomSelect);\n\nexport default CustomSelect;\n", "class TagsInput extends HTMLElement {\n constructor() {\n super();\n this.items = JSON.parse(this.getAttribute('items') || '[]');\n this.selected = JSON.parse(this.getAttribute('selected') || '[]');\n this.name = this.getAttribute('name') || 'tags[]';\n this.placeholderText = this.getAttribute('placeholder') || '';\n }\n\n connectedCallback() {\n this.innerHTML = this.createTemplate();\n\n this.control = this.querySelector('.tag-input-control');\n this.pillsContainer = this.querySelector('.tag-input-pills');\n this.input = this.querySelector('.tag-input-text');\n this.dropdown = this.querySelector('.tag-input-dropdown');\n this.hiddenInputs = this.querySelector('.tag-input-hidden-inputs');\n\n this.bindEventListeners();\n this.render();\n }\n\n createTemplate() {\n return `\n <div class=\"position-relative\">\n <div class=\"tag-input-control form-control d-flex flex-wrap align-items-center gap-1\">\n <span class=\"tag-input-pills d-contents\"></span>\n <input type=\"text\"\n class=\"tag-input-text\"\n placeholder=\"${this.placeholderText}\"\n autocomplete=\"off\">\n </div>\n <div class=\"tag-input-dropdown dropdown-menu w-100\"></div>\n </div>\n <div class=\"tag-input-hidden-inputs\"></div>\n `;\n }\n\n bindEventListeners() {\n this.control.addEventListener('click', () => this.input.focus());\n this.input.addEventListener('focus', () => this.showDropdown());\n this.input.addEventListener('input', () => this.showDropdown());\n this.input.addEventListener('keydown', (e) => this.onKeydown(e));\n document.addEventListener('click', (e) => {\n if (!this.contains(e.target)) this.hideDropdown();\n });\n }\n\n labelFor(value) {\n const item = this.items.find((i) => i.value === value);\n return item ? item.label : value;\n }\n\n selectedValues() {\n return this.selected.map((s) => s.value || s);\n }\n\n availableItems(query) {\n const selectedValues = this.selectedValues();\n return this.items.filter(\n (item) => !selectedValues.includes(item.value)\n && item.label.toLowerCase().includes(query),\n );\n }\n\n onKeydown(event) {\n if (event.key === 'Escape') {\n this.hideDropdown();\n this.input.blur();\n }\n if (event.key === ',' || event.key === 'Enter') {\n event.preventDefault();\n const query = this.input.value.toLowerCase();\n const available = this.availableItems(query);\n if (available.length > 0) this.selectItem(available[0].value);\n return;\n }\n if (event.key === 'Backspace' && this.input.value === '' && this.selected.length > 0) {\n this.selected = this.selected.slice(0, -1);\n this.render();\n this.showDropdown();\n }\n }\n\n showDropdown() {\n const query = this.input.value.toLowerCase();\n const available = this.availableItems(query);\n\n if (available.length === 0) {\n this.hideDropdown();\n return;\n }\n\n this.dropdown.innerHTML = available\n .map((item) => `<button type=\"button\" class=\"dropdown-item\" data-value=\"${item.value}\">${item.label}</button>`)\n .join('');\n\n this.dropdown.querySelectorAll('.dropdown-item').forEach((btn) => {\n btn.addEventListener('mousedown', (e) => {\n e.preventDefault();\n this.selectItem(btn.dataset.value);\n });\n });\n\n this.dropdown.classList.add('show');\n }\n\n hideDropdown() {\n this.dropdown.classList.remove('show');\n }\n\n selectItem(value) {\n this.selected = [...this.selected, { value, label: this.labelFor(value) }];\n this.input.value = '';\n this.hideDropdown();\n this.render();\n this.input.focus();\n }\n\n removeItem(value) {\n this.selected = this.selected.filter((item) => (item.value || item) !== value);\n this.render();\n this.input.focus();\n }\n\n render() {\n this.renderPills();\n this.renderHiddenInputs();\n this.input.placeholder = this.selected.length === 0 ? this.placeholderText : '';\n }\n\n renderPills() {\n this.pillsContainer.innerHTML = this.selected\n .map((item) => {\n const value = item.value || item;\n const label = item.label || this.labelFor(value);\n return `<span class=\"tag-input-pill badge bg-info text-white\" data-value=\"${value}\">${label}<button type=\"button\" class=\"btn-close btn-close-white btn-close-sm ms-1\" aria-label=\"Remove\"></button></span>`;\n })\n .join('');\n\n this.pillsContainer.querySelectorAll('.btn-close').forEach((btn) => {\n btn.addEventListener('click', (e) => {\n e.stopPropagation();\n const pill = e.target.closest('.tag-input-pill');\n this.removeItem(pill.dataset.value);\n });\n });\n }\n\n renderHiddenInputs() {\n const values = this.selectedValues();\n\n if (values.length === 0) {\n this.hiddenInputs.innerHTML = `<input type=\"hidden\" name=\"${this.name}\" value=\"\">`;\n return;\n }\n\n this.hiddenInputs.innerHTML = values\n .map((value) => `<input type=\"hidden\" name=\"${this.name}\" value=\"${value}\">`)\n .join('');\n }\n}\n\ncustomElements.define('tags-input', TagsInput);\n", "// A multi-select for blog topics: a full-width field that accumulates the chosen\n// topics as chips (order matters \u2014 the first is the \"primary\" one shown on cards),\n// and a panel with a search box + a checkable list that STAYS OPEN so several can\n// be picked in one go. Kept separate from the shared <tags-input> (which closes\n// on each pick and puts the search inline) so competencies are unaffected.\n\nfunction esc(value) {\n const el = document.createElement('div');\n el.textContent = value == null ? '' : String(value);\n return el.innerHTML.replace(/\"/g, '"');\n}\n\nfunction arraysEqual(a, b) {\n return a.length === b.length && a.every((value, index) => value === b[index]);\n}\n\nclass TopicsSelect extends HTMLElement {\n constructor() {\n super();\n this.items = JSON.parse(this.getAttribute('items') || '[]');\n this.selected = JSON.parse(this.getAttribute('selected') || '[]');\n this.name = this.getAttribute('name') || 'topics[]';\n this.placeholderText = this.getAttribute('placeholder') || '';\n this.searchPlaceholder = this.getAttribute('search-placeholder') || '';\n this.manageUrl = this.getAttribute('manage-url') || '';\n this.manageLabel = this.getAttribute('manage-label') || '';\n this.removeLabel = this.getAttribute('remove-label') || '';\n this.onDocumentClick = this.onDocumentClick.bind(this);\n }\n\n connectedCallback() {\n this.innerHTML = this.template();\n this.trigger = this.querySelector('.topics-select-trigger');\n this.caret = this.querySelector('.topics-select-caret');\n this.chipsEl = this.querySelector('.topics-select-chips');\n this.placeholderEl = this.querySelector('.topics-select-placeholder');\n this.panel = this.querySelector('.topics-select-panel');\n this.search = this.querySelector('.topics-select-search');\n this.list = this.querySelector('.topics-select-list');\n this.hiddenInputs = this.querySelector('.topics-select-hidden');\n\n this.bindEventListeners();\n this.render();\n this.savedValues = this.values();\n }\n\n disconnectedCallback() {\n document.removeEventListener('click', this.onDocumentClick);\n }\n\n template() {\n return `\n <div class=\"topics-select-trigger form-control d-flex flex-wrap align-items-center gap-1\">\n <span class=\"topics-select-chips d-flex flex-wrap gap-1\"></span>\n <span class=\"topics-select-placeholder text-muted\"></span>\n <button type=\"button\" class=\"topics-select-caret ms-auto\" aria-haspopup=\"true\" aria-expanded=\"false\" aria-label=\"${esc(this.placeholderText)}\">\n <span aria-hidden=\"true\">▾</span>\n </button>\n </div>\n <div class=\"topics-select-panel\">\n <div class=\"topics-select-search-wrap\">\n <input type=\"text\" class=\"topics-select-search form-control form-control-sm\" placeholder=\"${esc(this.searchPlaceholder)}\" aria-label=\"${esc(this.searchPlaceholder)}\" autocomplete=\"off\">\n </div>\n <div class=\"topics-select-list\"></div>\n ${this.manageUrl ? `<div class=\"topics-select-footer\"><a href=\"${esc(this.manageUrl)}\" class=\"topics-select-manage\">${esc(this.manageLabel)}</a></div>` : ''}\n </div>\n <div class=\"topics-select-hidden\"></div>\n `;\n }\n\n bindEventListeners() {\n this.trigger.addEventListener('click', () => this.toggle());\n this.addEventListener('keydown', (event) => this.onKeydown(event));\n this.search.addEventListener('input', () => this.renderList());\n document.addEventListener('click', this.onDocumentClick);\n\n const manage = this.querySelector('.topics-select-manage');\n if (manage) manage.addEventListener('click', (event) => this.onManageClick(event));\n }\n\n onDocumentClick(event) {\n if (!this.contains(event.target)) this.close();\n }\n\n onKeydown(event) {\n if (event.key !== 'Escape') return;\n if (!this.panel.classList.contains('is-open')) return;\n\n this.close();\n this.caret.focus();\n }\n\n toggle() {\n if (this.panel.classList.contains('is-open')) {\n this.close();\n } else {\n this.open();\n }\n }\n\n open() {\n this.panel.classList.add('is-open');\n this.trigger.classList.add('is-open');\n this.caret.setAttribute('aria-expanded', 'true');\n this.search.value = '';\n this.renderList();\n this.search.focus();\n }\n\n close() {\n this.panel.classList.remove('is-open');\n this.trigger.classList.remove('is-open');\n this.caret.setAttribute('aria-expanded', 'false');\n this.commitIfChanged();\n }\n\n form() {\n return this.closest('form');\n }\n\n // Autosave when the panel closes with a changed selection \u2014 mirrors the Autor\n // and Destacado controls above, so there's nothing \"unsaved\" to lose.\n commitIfChanged() {\n const current = this.values();\n if (arraysEqual(current, this.savedValues)) return;\n\n this.savedValues = current.slice();\n this.form()?.requestSubmit();\n }\n\n // \"Crear tema\": save the current picks first, then let the server send us to\n // the new-topic form (via the create_topic flag), so nothing is lost.\n onManageClick(event) {\n event.preventDefault();\n const form = this.form();\n if (!form) return;\n\n const flag = document.createElement('input');\n flag.type = 'hidden';\n flag.name = 'create_topic';\n flag.value = '1';\n form.appendChild(flag);\n form.requestSubmit();\n }\n\n values() {\n return this.selected.map((item) => item.value || item);\n }\n\n isSelected(value) {\n return this.values().includes(value);\n }\n\n labelFor(value) {\n const item = this.items.find((candidate) => candidate.value === value);\n return item ? item.label : value;\n }\n\n toggleValue(value) {\n if (this.isSelected(value)) {\n this.selected = this.selected.filter((item) => (item.value || item) !== value);\n } else {\n this.selected = [...this.selected, { value, label: this.labelFor(value) }];\n }\n this.render();\n this.renderList();\n }\n\n render() {\n this.renderChips();\n this.renderHiddenInputs();\n this.placeholderEl.textContent = this.selected.length === 0 ? this.placeholderText : '';\n }\n\n renderChips() {\n this.chipsEl.innerHTML = this.selected\n .map((item) => {\n const value = item.value || item;\n const label = item.label || this.labelFor(value);\n return `<span class=\"topics-select-chip badge\" data-value=\"${esc(value)}\">${esc(label)}<button type=\"button\" class=\"btn-close btn-close-white btn-close-sm ms-1\" aria-label=\"${esc(this.removeLabel)}\"></button></span>`;\n })\n .join('');\n\n this.chipsEl.querySelectorAll('.btn-close').forEach((button) => {\n button.addEventListener('click', (event) => {\n event.stopPropagation();\n this.toggleValue(event.target.closest('.topics-select-chip').dataset.value);\n });\n });\n }\n\n renderList() {\n const query = this.search.value.toLowerCase();\n const matches = this.items.filter((item) => item.label.toLowerCase().includes(query));\n\n this.list.innerHTML = matches\n .map((item) => `\n <button type=\"button\" class=\"topics-select-option${this.isSelected(item.value) ? ' is-selected' : ''}\" data-value=\"${esc(item.value)}\" aria-pressed=\"${this.isSelected(item.value)}\">\n <span>${esc(item.label)}</span>\n <span class=\"topics-select-check\" aria-hidden=\"true\">✓</span>\n </button>`)\n .join('');\n\n this.list.querySelectorAll('.topics-select-option').forEach((button) => {\n button.addEventListener('click', (event) => {\n event.stopPropagation();\n this.toggleValue(button.dataset.value);\n });\n });\n }\n\n renderHiddenInputs() {\n const values = this.values();\n\n this.hiddenInputs.innerHTML = values.length === 0\n ? `<input type=\"hidden\" name=\"${esc(this.name)}\" value=\"\">`\n : values.map((value) => `<input type=\"hidden\" name=\"${esc(this.name)}\" value=\"${esc(value)}\">`).join('');\n }\n}\n\ncustomElements.define('topics-select', TopicsSelect);\n"], "mappings": "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", "names": ["elementI18n", "element", "defaults", "attribute", "AudioRecorder", "_AudioRecorder", "elementI18n", "maxDurationAttr", "amplitudeBars", "t", "newState", "err", "e", "blob", "url", "animate", "durationMs", "duration", "rect", "percentage", "newTime", "progress", "currentMs", "totalBars", "playedBars", "bar", "index", "ms", "totalSeconds", "minutes", "seconds", "barsHtml", "height", "remainingMs", "barCount", "volume", "bufferLength", "dataArray", "sum", "i", "value", "rms", "time", "position", "wave", "titleKey", "title", "help", "fileName", "fileType", "file", "isValid", "timeToString", "value", "time", "minutes", "seconds", "number", "str", "AudioPlayer", "_AudioPlayer", "elementI18n", "barsHtml", "i", "height", "value", "event", "rect", "clickX", "percentage", "mouseMove", "mouseUp", "error", "animate", "leftTime", "forwardTime", "backwardTime", "timeToString", "playedBars", "bar", "index", "label", "focusClaimed", "ITEM_DATA_PREFIX", "BUTTON_DATA_PREFIX", "CustomSelect", "node", "openSelects", "o", "label", "labelledby", "event", "span", "option", "index", "li", "source", "target", "prefix", "name", "value", "key", "badge", "hasValue", "message", "selected", "i", "select", "el", "TagsInput", "e", "value", "item", "s", "query", "selectedValues", "event", "available", "btn", "label", "pill", "values", "esc", "value", "el", "arraysEqual", "b", "index", "TopicsSelect", "event", "manage", "current", "form", "flag", "item", "candidate", "label", "button", "query", "matches", "values"] }