/// /// /// namespace editor { export class Editor { audioManager: audio.AudioManager; audioElement: HTMLInputElement; barElement: HTMLElement; markerListElement: HTMLElement; intervalListElement: HTMLElement; kanaElement: HTMLElement; kanjiElement: HTMLElement; jsonElement: HTMLInputElement; track: audio.Track | null = null; markers: Marker[] = []; rafId: number; constructor() { this.audioManager = new audio.AudioManager(); this.audioElement = document.querySelector('#audio'); this.audioElement.addEventListener('change', event => { this.loadAudio(); }); this.barElement = document.querySelector('.bar-overlay'); this.markerListElement = document.querySelector('.markers'); this.intervalListElement = document.querySelector('#intervals'); this.kanaElement = document.querySelector('#kana'); this.kanjiElement = document.querySelector('#kanji'); this.jsonElement = document.querySelector('#json'); this.markerListElement.addEventListener('click', (event: MouseEvent) => this.markersClick(event)); document.querySelector('#play').addEventListener('click', () => this.play()); document.querySelector('#pause').addEventListener('click', () => this.pause()); document.querySelector('#insert-marker').addEventListener('click', () => this.insertMarker()); document.querySelector('.bar').addEventListener('click', (event: MouseEvent) => this.scrubberClick(event)); document.querySelector('#import').addEventListener('click', () => this.import()); document.querySelector('#export').addEventListener('click', () => this.export()); } loadAudio(): void { let file = this.audioElement.files[0]; if (file != null) { if (this.track != null) { this.track.stop(); } this.clearMarkers(); this.audioManager.loadTrackFromFile(file).then(t => this.track = t); } } update(): void { let percentage = this.track.getTime() / this.track.getDuration() * 100; this.barElement.style.width = `${percentage}%`; if (this.track.isPlaying()) { this.rafId = requestAnimationFrame(() => this.update()); } } scrubberClick(event: MouseEvent): void { let pos = event.clientX - this.markerListElement.offsetLeft; let percentage = pos / this.markerListElement.clientWidth; let targetTime = percentage * this.track.getDuration(); this.track.stop(); this.track.resumeTime = targetTime; this.play(); } markersClick(event: MouseEvent): void { let pos = event.clientX - this.markerListElement.offsetLeft; let percentage = pos / this.markerListElement.clientWidth; let targetTime = percentage * this.track.getDuration(); this.insertMarker(targetTime); } insertMarker(time: number = undefined): void { let marker = new Marker( this.track.getDuration(), (marker: Marker) => this.removeMarker(marker), (marker: Marker) => this.playMarker(marker) ); if (time !== undefined) { marker.time = time; } else { marker.time = this.track.getTime(); } let insertIndex = this.markers.findIndex(m => m.time > marker.time); if (insertIndex >= 0) { this.markers.splice(insertIndex, 0, marker); } else { this.markers.push(marker); } this.markerListElement.appendChild(marker.markerElement); if (insertIndex >= 0) { this.intervalListElement.insertBefore(marker.liElement, this.markers[insertIndex+1].liElement); } else { this.intervalListElement.appendChild(marker.liElement); } } play(duration: number = undefined): void { window.cancelAnimationFrame(this.rafId); this.track.start(duration); this.update(); } pause(): void { this.track.pause(); } playMarker(marker: Marker): void { let start = 0; let index = this.markers.findIndex(m => m == marker); if (index > 0) { start = this.markers[index - 1].time; } let duration = marker.time - start; this.track.stop(); this.track.resumeTime = start; this.play(duration); } removeMarker(marker: Marker): void { let index = this.markers.findIndex(m => m == marker); this.markers.splice(index, 1); this.markerListElement.removeChild(marker.markerElement); this.intervalListElement.removeChild(marker.liElement); } clearMarkers(): void { this.markers.forEach(m => { this.markerListElement.removeChild(m.markerElement); this.intervalListElement.removeChild(m.liElement); }); this.markers = []; } import(): void { this.clearMarkers(); let lines: level.Line[] = JSON.parse(this.jsonElement.value); let kanji = ''; let kana = ''; lines.forEach(line => { kanji += line.kanji + '\n'; kana += line.kana + '\n'; if (line.end != undefined) { this.insertMarker(line.end); } }); this.kanjiElement.value = kanji; this.kanaElement.value = kana; } export(): void { let kanji = this.kanjiElement.value.split('\n'); let kana = this.kanaElement.value.split('\n'); kanji.pop(); kana.pop(); let length = Math.max(kanji.length, kana.length, this.markers.length); let lines = []; let lastStart = 0; for (let i = 0; i < length; ++i) { let data: level.Line = { kanji: kanji[i] || '@', kana: kana[i] || '@', } if (this.markers[i]) { data.start = lastStart; data.end = this.markers[i].time; lastStart = data.end; } lines.push(data); } this.jsonElement.value = JSON.stringify(lines); } start(): void { this.loadAudio(); } } class Marker { markerElement: HTMLElement; liElement: HTMLElement; inputElement: HTMLInputElement; constructor( readonly duration: number, readonly remove: (marker: Marker) => void, readonly play: (marker: Marker) => void ) { this.markerElement = document.createElement('div'); this.markerElement.className = 'marker'; let fragment = util.loadTemplate('interval'); this.liElement = fragment.querySelector('*'); this.inputElement = fragment.querySelector('.interval'); this.inputElement.addEventListener('change', () => { this.time = parseFloat(this.inputElement.value); }); fragment.querySelector('.play-section').addEventListener('click', () => play(this)); fragment.querySelector('.remove-section').addEventListener('click', () => remove(this)); } get time(): number { return parseFloat(this.inputElement.value); } set time(t: number) { this.inputElement.value = t.toFixed(1); let percentage = t * 100 / this.duration; this.markerElement.style.left = `${percentage}%`; } } }