Translation

by wio_dude

HTML

<div id="tl"></div>

CSS

/* .tl-group:hover {
  font-weight: bold;
} */
.tl-group.active-group {
  background-color: yellow;
}
.tl-group {
  cursor: pointer;
}
/* 
.tl-group {
  color: red;
} */

JavaScript

const tl = {
  src: [
    ["他", {
      group: 0,
      sound: [0.05, 0.275],
    }],
    ["很", {
      group: 1,
      sound: [0.3, 0.68],
    }],
    ["开心", {
      group: 2,
      sound: [0.68, 1],
    }],
    ["。"]
  ],
  srcAudio: 'https://tatoeba.org/en/audio/download/992961',
  dest: [
    ["He", {
      group: 0
    }],
    [" "],
    ["is", {
      group: 1
    }],
    [" "],
    ["happy", {
      group: 2
    }],
    ["."]
  ]
}

function byId(id) {
  return document.getElementById(id);
}

$elem = {
  src: byId('src'),
  dest: byId('dest'),
  tl: byId('tl')
};

function create(data) {
  const tl = {
    data,
    $src: document.createElement('div'),
    $dest: document.createElement('div'),
    activeGroup: null
  };
  if (data.srcAudio) {
    tl.$srcAudio = new Audio(data.srcAudio);
  }

console.log(tl)
  return tl;
}

function setActiveGroup(tl, group) {
  for (const span of tl.$src.querySelectorAll('span')) {
    if (span.dataset.group === group) {
      span.classList.add('active-group');
    } else {
      span.classList.remove('active-group');
    }
  }

  for (const span of tl.$dest.querySelectorAll('span')) {
    if (span.dataset.group === group) {
      span.classList.add('active-group');
    } else {
      span.classList.remove('active-group');
    }
  }
}

function removeActiveGroup(tl, group) {
  for (const span of tl.$src.querySelectorAll('span')) {
    if (span.dataset.group === group) {
      span.classList.remove('active-group');
    }
  }

  for (const span of tl.$dest.querySelectorAll('span')) {
    if (span.dataset.group === group) {
      span.classList.remove('active-group');
    }
  }
}

const audioCtx = new (window.AudioContext || window.webkitAudioContext)();

let s = null;

async function playAudioSpan(audio, [spanStart, spanEnd]) {
	const start = Math.max(spanStart, 0);
  const end = Math.min(spanEnd, audio.duration);
  const duration = end - start;
  
  if (!s) {
  try {
    const response = await...