function findGetParameter(parameterName) { var result = null, tmp = []; location.search .substr(1) .split('&') .forEach(function(item) { tmp = item.split('='); if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]); }); return result; } const Events = [ 'Transition -> slide', 'Transition -> demo', 'Transition -> new viewpoint', 'Build Up', 'Animation', 'Pointer', 'Live annotation', ]; class Annotator { constructor(root, annotations) { this.root = root; this.annotations = annotations; this.maxIndex = Object.keys(this.annotations).length - 1; let img1Id = 'img-1'; let img2Id = 'img-2' let annotationsId = 'annotations'; this.img1Element = document.getElementById(img1Id); this.img1IndexElement = document.getElementById(img1Id + '-index'); this.img2Element = document.getElementById(img2Id); this.img2IndexElement = document.getElementById(img2Id + '-index'); this.annotationElement = document.getElementById(annotationsId); this.navbar = document.getElementById('navbar'); this.shortcutsIndex = document.getElementById('shortcuts-index'); this.shortcutsValue = document.getElementById('shortcuts-value'); this.setNavbar(); this.setShortcutsInfo(); this.setIndex(0); this.addShortcuts(); } setShortcutsInfo() { for (let index = 1; index <= Events.length; index++) { let elt = document.createElement('td'); elt.innerHTML = index; this.shortcutsIndex.appendChild(elt); elt = document.createElement('td'); elt.innerHTML = Events[index - 1]; this.shortcutsValue.appendChild(elt); } } setNavbar() { for (let frame in this.annotations) { let div = document.createElement('div'); div.classList.add('column10'); div.classList.add('notselected'); div.id = 'thumbnail-' + frame; let img = document.createElement('img'); img.setAttribute('src', this.root + '/' + frame + '.jpg'); let text = document.createElement('p'); text.classList.add('centered'); text.innerHTML = frame; div.appendChild(img); div.appendChild(text); div.addEventListener('click', () => { this.setIndex(frame); }); this.navbar.appendChild(div); } } formatIndex(index) { index += ''; return index.padStart(4, '0'); } getUrl(index) { return this.root + '/' + this.formatIndex(index) + '.jpg'; } setIndex(index) { // If index is undefined, we only want a refresh if (index !== undefined) { this.index = index; this.index = Math.max(0, this.index); this.index = Math.min(this.index, this.maxIndex); // Refrsh images this.img1Element.src = this.getUrl(this.index); this.img2Element.src = this.getUrl(this.index + 1); // Refresh headers this.img1IndexElement.innerHTML = this.index + '/' + this.maxIndex; this.img2IndexElement.innerHTML = this.index + 1 + '/' + this.maxIndex; } let formattedIndex = this.formatIndex(this.index + 1); // Refresh navbar for (let key of Object.keys(this.annotations)) { document.getElementById('thumbnail-' + key).classList.remove('selected'); document.getElementById('thumbnail-' + key).classList.add('notselected'); } let current = document.getElementById('thumbnail-' + formattedIndex); current.classList.add('selected'); current.classList.remove('notselected'); current.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'center' }); // Refresh annotations list let currentAnnotations = this.annotations[formattedIndex]; if (currentAnnotations.length === 0) { this.annotationElement.innerHTML = 'There is no annotation for this frame'; } else { this.annotationElement.innerHTML = ''; } } increment(by = 1) { this.setIndex(this.index + by); } decrement(by = 1) { this.setIndex(this.index - by); } addShortcuts() { document.addEventListener('keyup', async (e) => { switch (e.code) { case 'ArrowRight': this.increment(e.ctrlKey ? 10 : 1); break; case 'ArrowLeft': this.decrement(e.ctrlKey ? 10 : 1); break; } if (e.code.startsWith('Numpad') || e.code.startsWith('Digit')) { let key = parseInt(e.code[e.code.length - 1], 10); await this.toggle(key); } }); } async toggle(key) { let event = Events[key]; if (event === undefined) { return; } let events = this.annotations[this.formatIndex(this.index + 1)]; let search = events.indexOf(event); if (search === -1) { events.push(event); } else { events.splice(search, 1); } await this.sync(); this.setIndex(); } async sync() { await fetch('/data', { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ root: this.root, annotations: this.annotations, }), }); } } async function main() { const root = findGetParameter('root'); let annotations = await fetch(root + '/annotations.json'); annotations = await annotations.json(); const annotator = new Annotator(root, annotations, ); } main();