Team Ai
Apppublic

FelixFahne/Annotation_Tool

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
script.js277 linesDownload Raw Back to root
1// script.js2 3// --- state ---4let dialogues = [];5let selectedColor = '';6let selectedLabelName = '';7 8// --- predefined colors for each label ---9const labelColors = {10  'reference word': '#ffadad',11  'noun & verb collocation in proper form': '#ffd6a5',12  'code-switching for communicative purposes': '#fdffb6',13  'negotiation of meaning': '#caffbf',14  'tense choice to indicate interactive aims': '#9bf6ff',15  'routinized resources': '#a0c4ff',16  'subordinate clauses': '#bdb2ff',17  'backchannels': '#ff7eb9',18  'question-based responses': '#ff65a3',19  'formulaic responses': '#7afcff',20  'collaborative finishes': '#feff9c',21  'epistemic copulas': '#fff740',22  'epistemic modals': '#ff9ee7',23  'adjectives/adverbs expressing possibility': '#ffd700',24  'non-factive verb phrase structure': '#c1aff0',25  'impersonal subject + non-factive verb + NP': '#6eb5ff',26  'feedback in the next turn': '#ffc8a2',27  'topic extension with clear new context': '#a79aff',28  'topic extension under the previous direction': '#ffaaa5',29  'topic extension with the same content': '#a8e6cf',30  'repeat and no topic extension': '#dcedc1',31  'no topic extension and stop the topic at this point': '#ff8b94',32  'conversation opening': '#ffcccb',33  'conversation closing': '#f3d1f4',34  'overall tone choice: very formal': '#d5a6bd',35  'overall tone choice: quite formal and some expressions are not that formal': '#cfe2f3',36  'overall tone choice: relatively not formal, most expressions are quite informal': '#d9ead3',37  'overall tone choice: quite informal, but some expressions are still formal': '#fce5cd',38  'overall tone choice: very informal': '#fff2cc'39};40 41// --- helper to create one label button + wrapper ---42function addLabelButton(labelName) {43  const list = document.getElementById('labels-list');44  const wrapper = document.createElement('div');45  wrapper.className = 'label-wrapper';46 47  const btn = document.createElement('button');48  btn.className = 'label-btn';49  btn.textContent = labelName;50  const color = labelColors[labelName] || '#cccccc';51  btn.style.backgroundColor = color;52  // Schriftgröße, Innenabstand und Zeilenhöhe setzen für bessere Lesbarkeit53  btn.style.fontSize   = '1.1rem';       // ca. 18px54  btn.style.lineHeight = '1.4';          // für mehr Abstand zwischen den Zeilen55  btn.style.padding    = '0.5rem 0.8rem'; // oben/unten 8px, links/rechts 12px56  btn.style.fontWeight = '600';          // etwas fetter57  btn.dataset.color = color;58  btn.addEventListener('click', () => {59    selectedColor = color;60    selectedLabelName = labelName;61  });62 63 64 65  const deleteBtn = document.createElement('button');66  //deleteBtn.className = 'delete-label-btn';67  //deleteBtn.innerHTML = '<i class="fas fa-times"></i>';68  //deleteBtn.addEventListener('click', () => {69    //list.removeChild(wrapper);70    //if (selectedLabelName === labelName) {71      //selectedLabelName = '';72    //  selectedColor = '';73  //  }74  //});75 76  wrapper.append(btn, deleteBtn);77  list.appendChild(wrapper);78}79 80// --- build the three label sections ---81function addLabelsSection(titleText, labels) {82  const list = document.getElementById('labels-list');83  const title = document.createElement('h2');84  title.textContent = titleText;85  list.appendChild(title);86  labels.forEach(addLabelButton);87}88 89// --- show / hide the sidebar ---90document.addEventListener('DOMContentLoaded', () => {91  const labelsList = document.getElementById('labels-list');92  const toggleBtn = document.getElementById('toggle-labels-btn');93  let isVisible = true;94 95  labelsList.style.display = 'block';96  toggleBtn.textContent = 'Hide Labels';97 98  toggleBtn.addEventListener('click', () => {99    isVisible = !isVisible;100    labelsList.style.display = isVisible ? 'block' : 'none';101    toggleBtn.textContent = isVisible ? 'Hide Labels' : 'Show Labels';102  });103 104  // add all predefined labels105  addLabelsSection('Token level labels', [106    'reference word',107    'noun & verb collocation in proper form',108    'code-switching for communicative purposes',109    'negotiation of meaning',110    'tense choice to indicate interactive aims',111    'routinized resources',112    'subordinate clauses'113  ]);114  addLabelsSection('Utterance level labels', [115    'backchannels',116    'question-based responses',117    'formulaic responses',118    'collaborative finishes',119    'epistemic copulas',120    'epistemic modals',121    'adjectives/adverbs expressing possibility',122    'non-factive verb phrase structure',123    'impersonal subject + non-factive verb + NP',124    'feedback in the next turn'125  ]);126  addLabelsSection('Dialogue level labels', [127    'topic extension with clear new context',128    'topic extension under the previous direction',129    'topic extension with the same content',130    'repeat and no topic extension',131    'no topic extension and stop the topic at this point',132    'conversation opening',133    'conversation closing',134    'overall tone choice: very formal',135    'overall tone choice: quite formal and some expressions are not that formal',136    'overall tone choice: relatively not formal, most expressions are quite informal',137    'overall tone choice: quite informal, but some expressions are still formal',138    'overall tone choice: very informal'139  ]);140});141 142// --- file upload & parsing ---143document.getElementById('file-input').addEventListener('change', (e) => {144  const file = e.target.files[0];145  if (!file) return;146  const reader = new FileReader();147  reader.onload = evt => {148    try {149      const data = JSON.parse(evt.target.result);150      dialogues = [];151      data.forEach(item => {152        if (Array.isArray(item.messages)) {153          item.messages.forEach(m => {154            if (m.role && m.content && m.time) {155              dialogues.push({ role: m.role, content: m.content, time: m.time });156            }157          });158        }159      });160    } catch (err) {161      alert('Failed to parse JSON: ' + err.message);162    }163  };164  reader.readAsText(file);165});166 167// --- render table ---168document.getElementById('confirm-button').addEventListener('click', () => {169  const container = document.getElementById('dialogue-content');170  container.innerHTML = '';171 172  const table = document.createElement('table');173  const thead = document.createElement('thead');174  const headerRow = document.createElement('tr');175  ['role','content','time'].forEach(text => {176    const th = document.createElement('th');177    th.textContent = text;178    headerRow.appendChild(th);179  });180  thead.appendChild(headerRow);181  table.appendChild(thead);182 183  const tbody = document.createElement('tbody');184  dialogues.forEach(d => {185    const tr = document.createElement('tr');186    ['role','content','time'].forEach(key => {187      const td = document.createElement('td');188      td.textContent = d[key];189      tr.appendChild(td);190    });191    tbody.appendChild(tr);192  });193  table.appendChild(tbody);194  container.appendChild(table);195 196  document.getElementById('download-button').style.display = 'inline-block';197});198 199// --- highlighting logic ---200function clearHighlights() {201  document.querySelectorAll('.highlight').forEach(el => {202    const txt = document.createTextNode(el.textContent);203    el.parentNode.replaceChild(txt, el);204  });205}206 207document.getElementById('clear-highlights').addEventListener('click', clearHighlights);208 209let deleteMode = false;210function cancelHighlight(e) {211  if (e.target.classList.contains('highlight')) {212    const txt = document.createTextNode(e.target.textContent);213    e.target.parentNode.replaceChild(txt, e.target);214  }215}216 217document.getElementById('delete-highlight').addEventListener('click', function(){218  const container = document.getElementById('dialogue-content');219  deleteMode = !deleteMode;220  if (deleteMode) {221    container.addEventListener('click', cancelHighlight, true);222    this.textContent = 'Click highlighted text to cancel highlighting';223    this.style.backgroundColor = '#aaa';224  } else {225    container.removeEventListener('click', cancelHighlight, true);226    this.textContent = 'Cancel annotation';227    this.style.backgroundColor = '#0056b3';228  }229});230 231// apply highlight on mouseup232document.getElementById('dialogue-content').addEventListener('mouseup', () => {233  const sel = window.getSelection();234  if (!sel.isCollapsed && selectedColor && sel.toString().trim()) {235    const range = sel.getRangeAt(0);236    const span = document.createElement('span');237    span.className = 'highlight';238    span.style.backgroundColor = selectedColor;239    span.dataset.label = selectedLabelName;240    range.surroundContents(span);241    sel.removeAllRanges();242  }243});244 245// --- collect annotated data & download XLSX ---246function collectAnnotatedData() {247  const levels = {248    /* same mapping as before, omitted for brevity */249  };250  const out = [];251  document.querySelectorAll('.highlight').forEach(h => {252    const tr = h.closest('tr');253    if (!tr) return;254    const role = tr.cells[0].textContent;255    const label = h.dataset.label;256    const level = levels[label] || 'Unknown';257    const full = tr.cells[1].textContent;258    const snippet = h.textContent;259    const marked = full.replace(snippet, `&&&&${snippet}&&&&`);260    out.push({ Role: role, Label: label, LabelLevel: level, Content: marked });261  });262  return out;263}264 265function downloadExcel(data) {266  const ws = XLSX.utils.json_to_sheet(data);267  const wb = XLSX.utils.book_new();268  XLSX.utils.book_append_sheet(wb, ws, 'Annotations');269  XLSX.writeFile(wb, 'annotations.xlsx');270}271 272document.getElementById('download-button').addEventListener('click', () => {273  const data = collectAnnotatedData();274  if (data.length) downloadExcel(data);275  else alert('No annotations to download.');276});277