|  | @@ -1,358 +1,387 @@
 | 
	
		
			
				|  |  | -/// <reference path="util.ts" />
 | 
	
		
			
				|  |  | -/// <reference path="level.ts" />
 | 
	
		
			
				|  |  | -/// <reference path="audio.ts" />
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -namespace editor {
 | 
	
		
			
				|  |  | -  export class Editor {
 | 
	
		
			
				|  |  | -    audioManager: audio.AudioManager;
 | 
	
		
			
				|  |  | -    urlElement: HTMLInputElement;
 | 
	
		
			
				|  |  | -    loadElement: HTMLButtonElement;
 | 
	
		
			
				|  |  | -    audioElement: HTMLInputElement;
 | 
	
		
			
				|  |  | -    barElement: HTMLElement;
 | 
	
		
			
				|  |  | -    markerListElement: HTMLElement;
 | 
	
		
			
				|  |  | -    intervalListElement: HTMLElement;
 | 
	
		
			
				|  |  | -    kanaElement: HTMLTextAreaElement;
 | 
	
		
			
				|  |  | -    kanjiElement: HTMLTextAreaElement;
 | 
	
		
			
				|  |  | -    displayElement: HTMLElement;
 | 
	
		
			
				|  |  | -    jsonElement: HTMLInputElement;
 | 
	
		
			
				|  |  | -    waveFormContainer: HTMLDivElement;
 | 
	
		
			
				|  |  | -    track: audio.Track | null = null;
 | 
	
		
			
				|  |  | -    markers: Marker[] = [];
 | 
	
		
			
				|  |  | -    waveForm: WaveForm;
 | 
	
		
			
				|  |  | -    currentMarker: Marker | null = null;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    constructor() {
 | 
	
		
			
				|  |  | -      this.audioManager = new audio.AudioManager();
 | 
	
		
			
				|  |  | -      this.urlElement = util.getElement(document, '#url');
 | 
	
		
			
				|  |  | -      this.loadElement = util.getElement(document, '#load');
 | 
	
		
			
				|  |  | -      this.loadElement.addEventListener('click', event => {
 | 
	
		
			
				|  |  | -        this.loadAudio();
 | 
	
		
			
				|  |  | -      });
 | 
	
		
			
				|  |  | -      this.audioElement = util.getElement(document, '#audio');
 | 
	
		
			
				|  |  | -      this.audioElement.addEventListener('change', event => {
 | 
	
		
			
				|  |  | -        this.urlElement.value = '';
 | 
	
		
			
				|  |  | -        this.loadAudio();
 | 
	
		
			
				|  |  | -      });
 | 
	
		
			
				|  |  | -      this.barElement = util.getElement(document, '.bar-overlay');
 | 
	
		
			
				|  |  | -      this.markerListElement = util.getElement(document, '.markers');
 | 
	
		
			
				|  |  | -      this.intervalListElement = util.getElement(document, '#intervals');
 | 
	
		
			
				|  |  | -      this.kanaElement = util.getElement(document, '#kana');
 | 
	
		
			
				|  |  | -      this.kanjiElement = util.getElement(document, '#kanji');
 | 
	
		
			
				|  |  | -      this.displayElement = util.getElement(document, '#display');
 | 
	
		
			
				|  |  | -      this.jsonElement = util.getElement(document, '#json');
 | 
	
		
			
				|  |  | -      this.waveFormContainer = util.getElement(document, '.waveform-container');
 | 
	
		
			
				|  |  | -      this.waveForm = new WaveForm(
 | 
	
		
			
				|  |  | -        util.getElement(document, '#waveform'),
 | 
	
		
			
				|  |  | -        util.getElement(document, '#waveform-overlay'),
 | 
	
		
			
				|  |  | -        (time: number) => this.play(time)
 | 
	
		
			
				|  |  | +import * as audio from './audio';
 | 
	
		
			
				|  |  | +import * as level from './level';
 | 
	
		
			
				|  |  | +import * as util from './util';
 | 
	
		
			
				|  |  | +import * as youtube from './youtube';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +export class Editor {
 | 
	
		
			
				|  |  | +  audioManager: audio.AudioManager;
 | 
	
		
			
				|  |  | +  urlElement: HTMLInputElement;
 | 
	
		
			
				|  |  | +  loadElement: HTMLButtonElement;
 | 
	
		
			
				|  |  | +  audioElement: HTMLInputElement;
 | 
	
		
			
				|  |  | +  barElement: HTMLElement;
 | 
	
		
			
				|  |  | +  markerListElement: HTMLElement;
 | 
	
		
			
				|  |  | +  intervalListElement: HTMLElement;
 | 
	
		
			
				|  |  | +  kanaElement: HTMLTextAreaElement;
 | 
	
		
			
				|  |  | +  kanjiElement: HTMLTextAreaElement;
 | 
	
		
			
				|  |  | +  displayElement: HTMLElement;
 | 
	
		
			
				|  |  | +  jsonElement: HTMLInputElement;
 | 
	
		
			
				|  |  | +  waveFormContainer: HTMLDivElement;
 | 
	
		
			
				|  |  | +  track: audio.Track | null = null;
 | 
	
		
			
				|  |  | +  markers: Marker[] = [];
 | 
	
		
			
				|  |  | +  waveForm: WaveForm;
 | 
	
		
			
				|  |  | +  currentMarker: Marker | null = null;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  constructor() {
 | 
	
		
			
				|  |  | +    this.audioManager = new audio.AudioManager();
 | 
	
		
			
				|  |  | +    this.urlElement = util.getElement(document, '#url');
 | 
	
		
			
				|  |  | +    this.loadElement = util.getElement(document, '#load');
 | 
	
		
			
				|  |  | +    this.loadElement.addEventListener('click', (event) => {
 | 
	
		
			
				|  |  | +      this.loadAudio();
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +    this.audioElement = util.getElement(document, '#audio');
 | 
	
		
			
				|  |  | +    this.audioElement.addEventListener('change', (event) => {
 | 
	
		
			
				|  |  | +      this.urlElement.value = '';
 | 
	
		
			
				|  |  | +      this.loadAudio();
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +    this.barElement = util.getElement(document, '.bar-overlay');
 | 
	
		
			
				|  |  | +    this.markerListElement = util.getElement(document, '.markers');
 | 
	
		
			
				|  |  | +    this.intervalListElement = util.getElement(document, '#intervals');
 | 
	
		
			
				|  |  | +    this.kanaElement = util.getElement(document, '#kana');
 | 
	
		
			
				|  |  | +    this.kanjiElement = util.getElement(document, '#kanji');
 | 
	
		
			
				|  |  | +    this.displayElement = util.getElement(document, '#display');
 | 
	
		
			
				|  |  | +    this.jsonElement = util.getElement(document, '#json');
 | 
	
		
			
				|  |  | +    this.waveFormContainer = util.getElement(document, '.waveform-container');
 | 
	
		
			
				|  |  | +    this.waveForm = new WaveForm(
 | 
	
		
			
				|  |  | +      util.getElement(document, '#waveform'),
 | 
	
		
			
				|  |  | +      util.getElement(document, '#waveform-overlay'),
 | 
	
		
			
				|  |  | +      (time: number) => this.play(time)
 | 
	
		
			
				|  |  | +    );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    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<HTMLElement>('.bar')!
 | 
	
		
			
				|  |  | +      .addEventListener('click', (event: MouseEvent) =>
 | 
	
		
			
				|  |  | +        this.scrubberClick(event)
 | 
	
		
			
				|  |  |        );
 | 
	
		
			
				|  |  | +    document
 | 
	
		
			
				|  |  | +      .querySelector('#import')!
 | 
	
		
			
				|  |  | +      .addEventListener('click', () => this.import());
 | 
	
		
			
				|  |  | +    document
 | 
	
		
			
				|  |  | +      .querySelector('#export')!
 | 
	
		
			
				|  |  | +      .addEventListener('click', () => this.export());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    this.update();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      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<HTMLElement>('.bar')!.addEventListener('click', (event: MouseEvent) => this.scrubberClick(event));
 | 
	
		
			
				|  |  | -      document.querySelector('#import')!.addEventListener('click', () => this.import());
 | 
	
		
			
				|  |  | -      document.querySelector('#export')!.addEventListener('click', () => this.export());
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      this.update();
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    loadAudio(): void {
 | 
	
		
			
				|  |  | -      const url = this.urlElement.value;
 | 
	
		
			
				|  |  | -      if (url != '') {
 | 
	
		
			
				|  |  | -        const videoId = youtube.getVideoId(url);
 | 
	
		
			
				|  |  | -        if (videoId !== null) {
 | 
	
		
			
				|  |  | -          const element = util.getElement(document, '#youtube');
 | 
	
		
			
				|  |  | -          this.audioManager.loadTrackFromYoutube(videoId, element, () => {}).then(t => {
 | 
	
		
			
				|  |  | +  loadAudio(): void {
 | 
	
		
			
				|  |  | +    const url = this.urlElement.value;
 | 
	
		
			
				|  |  | +    if (url != '') {
 | 
	
		
			
				|  |  | +      const videoId = youtube.getVideoId(url);
 | 
	
		
			
				|  |  | +      if (videoId !== null) {
 | 
	
		
			
				|  |  | +        const element = util.getElement(document, '#youtube');
 | 
	
		
			
				|  |  | +        this.audioManager
 | 
	
		
			
				|  |  | +          .loadTrackFromYoutube(videoId, element, () => {})
 | 
	
		
			
				|  |  | +          .then((t) => {
 | 
	
		
			
				|  |  |              this.track = t;
 | 
	
		
			
				|  |  |              this.waveForm.clear();
 | 
	
		
			
				|  |  |              this.waveFormContainer.style.display = 'none';
 | 
	
		
			
				|  |  |            });
 | 
	
		
			
				|  |  | -          return;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      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;
 | 
	
		
			
				|  |  | -          this.waveForm.setTrack(t);
 | 
	
		
			
				|  |  | -          this.waveFormContainer.style.display = 'block';
 | 
	
		
			
				|  |  | -        });
 | 
	
		
			
				|  |  | +        return;
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    update(): void {
 | 
	
		
			
				|  |  | +    let file = this.audioElement.files![0];
 | 
	
		
			
				|  |  | +    if (file != null) {
 | 
	
		
			
				|  |  |        if (this.track != null) {
 | 
	
		
			
				|  |  | -        let percentage = this.track.getTime() / this.track.getDuration() * 100;
 | 
	
		
			
				|  |  | -        this.barElement.style.width = `${percentage}%`;
 | 
	
		
			
				|  |  | -        if (this.track instanceof audio.FileTrack) {
 | 
	
		
			
				|  |  | -          this.waveForm.update(this.markers);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        if (this.currentMarker) {
 | 
	
		
			
				|  |  | -          this.currentMarker.liElement.className = '';
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        let index = this.markers.findIndex(m => m.time > this.track!.getTime());
 | 
	
		
			
				|  |  | -        if (index < 0) index = 0;
 | 
	
		
			
				|  |  | -        this.currentMarker = this.markers[index - 1];
 | 
	
		
			
				|  |  | -        if (this.currentMarker) {
 | 
	
		
			
				|  |  | -          this.currentMarker.liElement.className = 'highlight';
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        let text = this.kanjiElement.value.split('\n')[index] || '';
 | 
	
		
			
				|  |  | -        this.displayElement.textContent = text;
 | 
	
		
			
				|  |  | +        this.track.stop();
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -      requestAnimationFrame(() => this.update());
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    scrubberClick(event: MouseEvent): void {
 | 
	
		
			
				|  |  | -      let pos = event.clientX - 10;
 | 
	
		
			
				|  |  | -      console.log(pos);
 | 
	
		
			
				|  |  | -      let percentage = pos / this.markerListElement.clientWidth;
 | 
	
		
			
				|  |  | -      let targetTime = percentage * this.track!.getDuration();
 | 
	
		
			
				|  |  | -      this.play(targetTime);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    markersClick(event: MouseEvent): void {
 | 
	
		
			
				|  |  | -      let pos = event.clientX - 10;
 | 
	
		
			
				|  |  | -      let percentage = pos / this.markerListElement.clientWidth;
 | 
	
		
			
				|  |  | -      let targetTime = percentage * this.track!.getDuration();
 | 
	
		
			
				|  |  | -      this.insertMarker(targetTime);
 | 
	
		
			
				|  |  | +      this.clearMarkers();
 | 
	
		
			
				|  |  | +      this.audioManager.loadTrackFromFile(file).then((t) => {
 | 
	
		
			
				|  |  | +        this.track = t;
 | 
	
		
			
				|  |  | +        this.waveForm.setTrack(t);
 | 
	
		
			
				|  |  | +        this.waveFormContainer.style.display = 'block';
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    insertMarker(time?: number): 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();
 | 
	
		
			
				|  |  | +  update(): void {
 | 
	
		
			
				|  |  | +    if (this.track != null) {
 | 
	
		
			
				|  |  | +      let percentage = (this.track.getTime() / this.track.getDuration()) * 100;
 | 
	
		
			
				|  |  | +      this.barElement.style.width = `${percentage}%`;
 | 
	
		
			
				|  |  | +      if (this.track instanceof audio.FileTrack) {
 | 
	
		
			
				|  |  | +        this.waveForm.update(this.markers);
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -      let insertIndex = this.markers.findIndex(m => m.time > marker.time);
 | 
	
		
			
				|  |  | -      if (insertIndex >= 0) {
 | 
	
		
			
				|  |  | -        this.markers.splice(insertIndex, 0, marker);
 | 
	
		
			
				|  |  | -      } else {
 | 
	
		
			
				|  |  | -        this.markers.push(marker);
 | 
	
		
			
				|  |  | +      if (this.currentMarker) {
 | 
	
		
			
				|  |  | +        this.currentMarker.liElement.className = '';
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -      this.markerListElement.appendChild(marker.markerElement);
 | 
	
		
			
				|  |  | -      if (insertIndex >= 0) {
 | 
	
		
			
				|  |  | -        this.intervalListElement.insertBefore(marker.liElement, this.markers[insertIndex+1].liElement);
 | 
	
		
			
				|  |  | -      } else {
 | 
	
		
			
				|  |  | -        this.intervalListElement.appendChild(marker.liElement);
 | 
	
		
			
				|  |  | +      let index = this.markers.findIndex((m) => m.time > this.track!.getTime());
 | 
	
		
			
				|  |  | +      if (index < 0) index = 0;
 | 
	
		
			
				|  |  | +      this.currentMarker = this.markers[index - 1];
 | 
	
		
			
				|  |  | +      if (this.currentMarker) {
 | 
	
		
			
				|  |  | +        this.currentMarker.liElement.className = 'highlight';
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | +      let text = this.kanjiElement.value.split('\n')[index] || '';
 | 
	
		
			
				|  |  | +      this.displayElement.textContent = text;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +    requestAnimationFrame(() => this.update());
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    play(start?: number, duration?: number): void {
 | 
	
		
			
				|  |  | -      this.track!.pause();
 | 
	
		
			
				|  |  | -      this.track!.start(start, duration);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  scrubberClick(event: MouseEvent): void {
 | 
	
		
			
				|  |  | +    let pos = event.clientX - 10;
 | 
	
		
			
				|  |  | +    console.log(pos);
 | 
	
		
			
				|  |  | +    let percentage = pos / this.markerListElement.clientWidth;
 | 
	
		
			
				|  |  | +    let targetTime = percentage * this.track!.getDuration();
 | 
	
		
			
				|  |  | +    this.play(targetTime);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    pause(): void {
 | 
	
		
			
				|  |  | -      this.track!.pause();
 | 
	
		
			
				|  |  | +  markersClick(event: MouseEvent): void {
 | 
	
		
			
				|  |  | +    let pos = event.clientX - 10;
 | 
	
		
			
				|  |  | +    let percentage = pos / this.markerListElement.clientWidth;
 | 
	
		
			
				|  |  | +    let targetTime = percentage * this.track!.getDuration();
 | 
	
		
			
				|  |  | +    this.insertMarker(targetTime);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  insertMarker(time?: number): 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);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    playMarker(marker: Marker): void {
 | 
	
		
			
				|  |  | -      let start = marker.time;
 | 
	
		
			
				|  |  | -      let end = this.track!.getDuration();
 | 
	
		
			
				|  |  | -      let index = this.markers.findIndex(m => m == marker);
 | 
	
		
			
				|  |  | -      if (index < this.markers.length - 1) {
 | 
	
		
			
				|  |  | -        end = this.markers[index + 1].time;
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      let duration = end - start;
 | 
	
		
			
				|  |  | -      this.play(start, duration);
 | 
	
		
			
				|  |  | +  play(start?: number, duration?: number): void {
 | 
	
		
			
				|  |  | +    this.track!.pause();
 | 
	
		
			
				|  |  | +    this.track!.start(start, duration);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      this.highlightLine(this.kanjiElement, index + 1);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  pause(): void {
 | 
	
		
			
				|  |  | +    this.track!.pause();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    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);
 | 
	
		
			
				|  |  | +  playMarker(marker: Marker): void {
 | 
	
		
			
				|  |  | +    let start = marker.time;
 | 
	
		
			
				|  |  | +    let end = this.track!.getDuration();
 | 
	
		
			
				|  |  | +    let index = this.markers.findIndex((m) => m == marker);
 | 
	
		
			
				|  |  | +    if (index < this.markers.length - 1) {
 | 
	
		
			
				|  |  | +      end = this.markers[index + 1].time;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +    let duration = end - start;
 | 
	
		
			
				|  |  | +    this.play(start, duration);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    clearMarkers(): void {
 | 
	
		
			
				|  |  | -      this.markers.forEach(m => {
 | 
	
		
			
				|  |  | -        this.markerListElement.removeChild(m.markerElement);
 | 
	
		
			
				|  |  | -        this.intervalListElement.removeChild(m.liElement);
 | 
	
		
			
				|  |  | -      });
 | 
	
		
			
				|  |  | -      this.markers = [];
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +    this.highlightLine(this.kanjiElement, index + 1);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    highlightLine(element: HTMLTextAreaElement, line: number) {
 | 
	
		
			
				|  |  | -      let text = element.value;
 | 
	
		
			
				|  |  | -      let index = 0;
 | 
	
		
			
				|  |  | -      for (let i = 0; i < line; ++i) {
 | 
	
		
			
				|  |  | -        index = text.indexOf('\n', index + 1);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      let endIndex = text.indexOf('\n', index + 1);
 | 
	
		
			
				|  |  | -      element.focus();
 | 
	
		
			
				|  |  | -      element.setSelectionRange(index, endIndex);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  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);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    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);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -      });
 | 
	
		
			
				|  |  | +  clearMarkers(): void {
 | 
	
		
			
				|  |  | +    this.markers.forEach((m) => {
 | 
	
		
			
				|  |  | +      this.markerListElement.removeChild(m.markerElement);
 | 
	
		
			
				|  |  | +      this.intervalListElement.removeChild(m.liElement);
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +    this.markers = [];
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      this.kanjiElement.value = kanji;
 | 
	
		
			
				|  |  | -      this.kanaElement.value = kana;
 | 
	
		
			
				|  |  | +  highlightLine(element: HTMLTextAreaElement, line: number) {
 | 
	
		
			
				|  |  | +    let text = element.value;
 | 
	
		
			
				|  |  | +    let index = 0;
 | 
	
		
			
				|  |  | +    for (let i = 0; i < line; ++i) {
 | 
	
		
			
				|  |  | +      index = text.indexOf('\n', index + 1);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +    let endIndex = text.indexOf('\n', index + 1);
 | 
	
		
			
				|  |  | +    element.focus();
 | 
	
		
			
				|  |  | +    element.setSelectionRange(index, endIndex);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    export(): void {
 | 
	
		
			
				|  |  | -      let kanji = this.kanjiElement.value.split('\n');
 | 
	
		
			
				|  |  | -      let kana = this.kanaElement.value.split('\n');
 | 
	
		
			
				|  |  | -      let length = Math.max(kanji.length, kana.length, this.markers.length - 1);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      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);
 | 
	
		
			
				|  |  | +  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.jsonElement.value = JSON.stringify(lines);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +    this.kanjiElement.value = kanji;
 | 
	
		
			
				|  |  | +    this.kanaElement.value = kana;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    start(): void {
 | 
	
		
			
				|  |  | -      this.loadAudio();
 | 
	
		
			
				|  |  | +  export(): void {
 | 
	
		
			
				|  |  | +    let kanji = this.kanjiElement.value.split('\n');
 | 
	
		
			
				|  |  | +    let kana = this.kanaElement.value.split('\n');
 | 
	
		
			
				|  |  | +    let length = Math.max(kanji.length, kana.length, this.markers.length - 1);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    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);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  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(document, 'interval');
 | 
	
		
			
				|  |  | -      this.liElement = util.getElement(fragment, '*');
 | 
	
		
			
				|  |  | -      this.inputElement = util.getElement(fragment, '.interval');
 | 
	
		
			
				|  |  | -      this.inputElement.addEventListener('change', () => {
 | 
	
		
			
				|  |  | -        this.time = parseFloat(this.inputElement.value);
 | 
	
		
			
				|  |  | -      });
 | 
	
		
			
				|  |  | +  start(): void {
 | 
	
		
			
				|  |  | +    this.loadAudio();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      fragment.querySelector('.play-section')!.addEventListener('click', () => play(this));
 | 
	
		
			
				|  |  | -      fragment.querySelector('.remove-section')!.addEventListener('click', () => remove(this));
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +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(document, 'interval');
 | 
	
		
			
				|  |  | +    this.liElement = util.getElement(fragment, '*');
 | 
	
		
			
				|  |  | +    this.inputElement = util.getElement(fragment, '.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);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  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}%`;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  set time(t: number) {
 | 
	
		
			
				|  |  | +    this.inputElement.value = t.toFixed(1);
 | 
	
		
			
				|  |  | +    let percentage = (t * 100) / this.duration;
 | 
	
		
			
				|  |  | +    this.markerElement.style.left = `${percentage}%`;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  class WaveForm {
 | 
	
		
			
				|  |  | -    ctx: CanvasRenderingContext2D;
 | 
	
		
			
				|  |  | -    overlayCtx: CanvasRenderingContext2D;
 | 
	
		
			
				|  |  | -    track: audio.FileTrack | null = null;
 | 
	
		
			
				|  |  | -    data: Float32Array | null = null;
 | 
	
		
			
				|  |  | -    stride: number = 0;
 | 
	
		
			
				|  |  | -    currentSection: number = -1;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    constructor(
 | 
	
		
			
				|  |  | -      readonly canvas: HTMLCanvasElement,
 | 
	
		
			
				|  |  | -      readonly overlay: HTMLCanvasElement,
 | 
	
		
			
				|  |  | -      readonly setTime: (time: number) => void
 | 
	
		
			
				|  |  | -    ) {
 | 
	
		
			
				|  |  | -      canvas.height = canvas.clientHeight;
 | 
	
		
			
				|  |  | -      canvas.width = canvas.clientWidth;
 | 
	
		
			
				|  |  | -      overlay.height = overlay.clientHeight;
 | 
	
		
			
				|  |  | -      overlay.width = overlay.clientWidth;
 | 
	
		
			
				|  |  | -      this.ctx = canvas.getContext('2d')!;
 | 
	
		
			
				|  |  | -      this.overlayCtx = overlay.getContext('2d')!;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      this.overlayCtx.fillStyle = 'rgba(255, 0, 0, 0.5)';
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      this.overlay.addEventListener('click', (event: MouseEvent) => {
 | 
	
		
			
				|  |  | -        let pos = event.clientX - this.overlay.offsetLeft;
 | 
	
		
			
				|  |  | -        let percentage = pos / this.overlay.width;
 | 
	
		
			
				|  |  | -        let time = this.currentSection * 5 + percentage * 5;
 | 
	
		
			
				|  |  | -        this.setTime(time);
 | 
	
		
			
				|  |  | -      });
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +class WaveForm {
 | 
	
		
			
				|  |  | +  ctx: CanvasRenderingContext2D;
 | 
	
		
			
				|  |  | +  overlayCtx: CanvasRenderingContext2D;
 | 
	
		
			
				|  |  | +  track: audio.FileTrack | null = null;
 | 
	
		
			
				|  |  | +  data: Float32Array | null = null;
 | 
	
		
			
				|  |  | +  stride: number = 0;
 | 
	
		
			
				|  |  | +  currentSection: number = -1;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  constructor(
 | 
	
		
			
				|  |  | +    readonly canvas: HTMLCanvasElement,
 | 
	
		
			
				|  |  | +    readonly overlay: HTMLCanvasElement,
 | 
	
		
			
				|  |  | +    readonly setTime: (time: number) => void
 | 
	
		
			
				|  |  | +  ) {
 | 
	
		
			
				|  |  | +    canvas.height = canvas.clientHeight;
 | 
	
		
			
				|  |  | +    canvas.width = canvas.clientWidth;
 | 
	
		
			
				|  |  | +    overlay.height = overlay.clientHeight;
 | 
	
		
			
				|  |  | +    overlay.width = overlay.clientWidth;
 | 
	
		
			
				|  |  | +    this.ctx = canvas.getContext('2d')!;
 | 
	
		
			
				|  |  | +    this.overlayCtx = overlay.getContext('2d')!;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    this.overlayCtx.fillStyle = 'rgba(255, 0, 0, 0.5)';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    this.overlay.addEventListener('click', (event: MouseEvent) => {
 | 
	
		
			
				|  |  | +      let pos = event.clientX - this.overlay.offsetLeft;
 | 
	
		
			
				|  |  | +      let percentage = pos / this.overlay.width;
 | 
	
		
			
				|  |  | +      let time = this.currentSection * 5 + percentage * 5;
 | 
	
		
			
				|  |  | +      this.setTime(time);
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    clear(): void {
 | 
	
		
			
				|  |  | -      this.track = null;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  clear(): void {
 | 
	
		
			
				|  |  | +    this.track = null;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    setTrack(track: audio.FileTrack): void {
 | 
	
		
			
				|  |  | -      this.track = track;
 | 
	
		
			
				|  |  | -      this.stride = Math.floor(this.track.buffer.sampleRate / this.canvas.width * 5);
 | 
	
		
			
				|  |  | -      this.currentSection = -1;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  setTrack(track: audio.FileTrack): void {
 | 
	
		
			
				|  |  | +    this.track = track;
 | 
	
		
			
				|  |  | +    this.stride = Math.floor(
 | 
	
		
			
				|  |  | +      (this.track.buffer.sampleRate / this.canvas.width) * 5
 | 
	
		
			
				|  |  | +    );
 | 
	
		
			
				|  |  | +    this.currentSection = -1;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    timeToX(time: number): number {
 | 
	
		
			
				|  |  | -      return (time - this.currentSection * 5) / 5 * this.canvas.width
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  timeToX(time: number): number {
 | 
	
		
			
				|  |  | +    return ((time - this.currentSection * 5) / 5) * this.canvas.width;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    update(markers: Marker[]): void {
 | 
	
		
			
				|  |  | -      let section = Math.floor(this.track!.getTime() / 5);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      let height = this.canvas.height;
 | 
	
		
			
				|  |  | -      if (this.currentSection != section) {
 | 
	
		
			
				|  |  | -        this.data = this.track!.buffer.getChannelData(0);
 | 
	
		
			
				|  |  | -        this.ctx.clearRect(0, 0, this.canvas.width, height);
 | 
	
		
			
				|  |  | -        this.ctx.beginPath();
 | 
	
		
			
				|  |  | -        this.ctx.moveTo(0, height / 2);
 | 
	
		
			
				|  |  | -        let offset = section * this.canvas.width * this.stride;
 | 
	
		
			
				|  |  | -        for (let i = 0; i < this.canvas.width; ++i) {
 | 
	
		
			
				|  |  | -          let index = offset + i * this.stride;
 | 
	
		
			
				|  |  | -          let value = height / 2 + height / 2 * this.data[index];
 | 
	
		
			
				|  |  | -          this.ctx.lineTo(i, value);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        this.ctx.stroke();
 | 
	
		
			
				|  |  | -        this.currentSection = section;
 | 
	
		
			
				|  |  | +  update(markers: Marker[]): void {
 | 
	
		
			
				|  |  | +    let section = Math.floor(this.track!.getTime() / 5);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let height = this.canvas.height;
 | 
	
		
			
				|  |  | +    if (this.currentSection != section) {
 | 
	
		
			
				|  |  | +      this.data = this.track!.buffer.getChannelData(0);
 | 
	
		
			
				|  |  | +      this.ctx.clearRect(0, 0, this.canvas.width, height);
 | 
	
		
			
				|  |  | +      this.ctx.beginPath();
 | 
	
		
			
				|  |  | +      this.ctx.moveTo(0, height / 2);
 | 
	
		
			
				|  |  | +      let offset = section * this.canvas.width * this.stride;
 | 
	
		
			
				|  |  | +      for (let i = 0; i < this.canvas.width; ++i) {
 | 
	
		
			
				|  |  | +        let index = offset + i * this.stride;
 | 
	
		
			
				|  |  | +        let value = height / 2 + (height / 2) * this.data[index];
 | 
	
		
			
				|  |  | +        this.ctx.lineTo(i, value);
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      let marker = this.timeToX(this.track!.getTime());
 | 
	
		
			
				|  |  | -      this.overlayCtx.clearRect(0, 0, this.canvas.width, height);
 | 
	
		
			
				|  |  | -      this.overlayCtx.fillRect(0, 0, marker, height);
 | 
	
		
			
				|  |  | -      markers.forEach(m => {
 | 
	
		
			
				|  |  | -        if (m.time > section * 5 && m.time <= section * 5 + 5) {
 | 
	
		
			
				|  |  | -          let x = this.timeToX(m.time);
 | 
	
		
			
				|  |  | -          this.overlayCtx.beginPath();
 | 
	
		
			
				|  |  | -          this.overlayCtx.moveTo(x, 0);
 | 
	
		
			
				|  |  | -          this.overlayCtx.lineTo(x, height);
 | 
	
		
			
				|  |  | -          this.overlayCtx.stroke();
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -      })
 | 
	
		
			
				|  |  | +      this.ctx.stroke();
 | 
	
		
			
				|  |  | +      this.currentSection = section;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let marker = this.timeToX(this.track!.getTime());
 | 
	
		
			
				|  |  | +    this.overlayCtx.clearRect(0, 0, this.canvas.width, height);
 | 
	
		
			
				|  |  | +    this.overlayCtx.fillRect(0, 0, marker, height);
 | 
	
		
			
				|  |  | +    markers.forEach((m) => {
 | 
	
		
			
				|  |  | +      if (m.time > section * 5 && m.time <= section * 5 + 5) {
 | 
	
		
			
				|  |  | +        let x = this.timeToX(m.time);
 | 
	
		
			
				|  |  | +        this.overlayCtx.beginPath();
 | 
	
		
			
				|  |  | +        this.overlayCtx.moveTo(x, 0);
 | 
	
		
			
				|  |  | +        this.overlayCtx.lineTo(x, height);
 | 
	
		
			
				|  |  | +        this.overlayCtx.stroke();
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +let e = new Editor();
 | 
	
		
			
				|  |  | +e.start();
 |