2023-02-23 11:56:58 +01:00
|
|
|
function findGetParameter(parameterName) {
|
|
|
|
var result = null, tmp = [];
|
|
|
|
|
|
|
|
location.search
|
|
|
|
.substr(1)
|
2023-02-23 16:04:55 +01:00
|
|
|
.split('&')
|
2023-02-23 11:56:58 +01:00
|
|
|
.forEach(function(item) {
|
2023-02-23 16:04:55 +01:00
|
|
|
tmp = item.split('=');
|
2023-02-23 11:56:58 +01:00
|
|
|
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 {
|
2023-02-23 16:04:55 +01:00
|
|
|
constructor(root, annotations) {
|
2023-02-23 11:56:58 +01:00
|
|
|
this.root = root;
|
|
|
|
this.annotations = annotations;
|
|
|
|
|
|
|
|
this.maxIndex = Object.keys(this.annotations).length - 1;
|
|
|
|
|
2023-02-23 16:04:55 +01:00
|
|
|
let img1Id = 'img-1';
|
|
|
|
let img2Id = 'img-2'
|
|
|
|
let annotationsId = 'annotations';
|
|
|
|
|
2023-02-23 11:56:58 +01:00
|
|
|
this.img1Element = document.getElementById(img1Id);
|
|
|
|
this.img1IndexElement = document.getElementById(img1Id + '-index');
|
|
|
|
this.img2Element = document.getElementById(img2Id);
|
|
|
|
this.img2IndexElement = document.getElementById(img2Id + '-index');
|
2023-02-23 16:04:55 +01:00
|
|
|
this.annotationElement = document.getElementById(annotationsId);
|
|
|
|
this.navbar = document.getElementById('navbar');
|
|
|
|
this.shortcutsIndex = document.getElementById('shortcuts-index');
|
|
|
|
this.shortcutsValue = document.getElementById('shortcuts-value');
|
2023-02-23 11:56:58 +01:00
|
|
|
|
2023-02-23 16:04:55 +01:00
|
|
|
this.setNavbar();
|
|
|
|
this.setShortcutsInfo();
|
2023-02-23 11:56:58 +01:00
|
|
|
this.setIndex(0);
|
|
|
|
this.addShortcuts();
|
|
|
|
}
|
|
|
|
|
2023-02-23 16:04:55 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-23 11:56:58 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-02-23 16:04:55 +01:00
|
|
|
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' });
|
|
|
|
|
2023-02-23 11:56:58 +01:00
|
|
|
// Refresh annotations list
|
2023-02-23 16:04:55 +01:00
|
|
|
let currentAnnotations = this.annotations[formattedIndex];
|
|
|
|
|
2023-02-23 11:56:58 +01:00
|
|
|
if (currentAnnotations.length === 0) {
|
2023-02-23 16:04:55 +01:00
|
|
|
this.annotationElement.innerHTML = 'There is no annotation for this frame';
|
2023-02-23 11:56:58 +01:00
|
|
|
} else {
|
|
|
|
this.annotationElement.innerHTML = '<ul>';
|
|
|
|
for (let annotation of currentAnnotations) {
|
|
|
|
this.annotationElement.innerHTML += '<li>' + annotation + '</li>';
|
|
|
|
}
|
|
|
|
this.annotationElement.innerHTML += '</ul>';
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
increment(by = 1) {
|
|
|
|
this.setIndex(this.index + by);
|
|
|
|
}
|
|
|
|
|
|
|
|
decrement(by = 1) {
|
|
|
|
this.setIndex(this.index - by);
|
|
|
|
}
|
|
|
|
|
|
|
|
addShortcuts() {
|
2023-02-23 16:04:55 +01:00
|
|
|
document.addEventListener('keyup', async (e) => {
|
2023-02-23 11:56:58 +01:00
|
|
|
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);
|
2023-02-23 16:04:55 +01:00
|
|
|
await this.toggle(key);
|
2023-02-23 11:56:58 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-02-23 16:04:55 +01:00
|
|
|
async toggle(key) {
|
2023-02-23 11:56:58 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-02-23 16:04:55 +01:00
|
|
|
await this.sync();
|
2023-02-23 11:56:58 +01:00
|
|
|
this.setIndex();
|
|
|
|
}
|
|
|
|
|
2023-02-23 16:04:55 +01:00
|
|
|
async sync() {
|
|
|
|
await fetch('/data', {
|
2023-02-23 11:56:58 +01:00
|
|
|
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();
|
2023-02-23 16:04:55 +01:00
|
|
|
const annotator = new Annotator(root, annotations, );
|
2023-02-23 11:56:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
main();
|