From 2b883421d84d78e42c72bd1f19cca7aee39e8241 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 10:04:46 +0200 Subject: [PATCH 01/31] var -> let --- .../static/annotations/js/annotations.js | 128 ++++++++---------- .../static/annotations/js/boundingboxes.js | 32 ++--- 2 files changed, 75 insertions(+), 85 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index fc5bc965..2d7fe3cb 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -39,28 +39,19 @@ function calculateImageScale() { const PRELOAD_FORWARD = 5; const STATIC_ROOT = '/static/'; - // TODO: Find a solution for url resolvings - - var gCsrfToken; - var gHeaders; - var gHideFeedbackTimeout; - var gImageCache = {}; - var gImageId; - var gImageSetId; - var gImageList; - var gMousepos; + let gCsrfToken; + let gHeaders; + let gHideFeedbackTimeout; + let gImageCache = {}; + let gImageId; + let gImageSetId; + let gImageList; + let gMousepos; let gAnnotationType = -1; let gCurrentAnnotations = []; let gHighlightedAnnotation; - - var gShiftDown; - - // a threshold for editing an annotation if you select a small rectangle - var gSelectionThreshold = 5; - - // save the current annotations of the image, so we can draw and hide the - - var tool; + let gShiftDown; + let tool; function shorten(string, length) { let threshold = length || 30; @@ -163,8 +154,8 @@ function calculateImageScale() { event.preventDefault(); } - var annotationTypeId = parseInt($('#annotation_type_id').val()); - var vector = null; + let annotationTypeId = parseInt($('#annotation_type_id').val()); + let vector = null; if (isNaN(annotationTypeId)) { displayFeedback($('#feedback_annotation_type_missing')); @@ -205,15 +196,15 @@ function calculateImageScale() { globals.restoreSelectionNodeCount = node_count; } - var action = 'create'; - var data = { + let action = 'create'; + let data = { annotation_type_id: annotationTypeId, image_id: gImageId, vector: vector, concealed: concealed, blurred: blurred }; - var editing = false; + let editing = false; if (globals.editedAnnotationsId !== undefined) { // edit instead of create action = 'update'; @@ -338,7 +329,7 @@ function calculateImageScale() { } } $('.js_feedback').stop().addClass('hidden'); - var params = { + let params = { annotation_id: annotationId }; $.ajax(API_ANNOTATIONS_BASE_URL + 'annotation/delete/?' + $.param(params), { @@ -369,8 +360,8 @@ function calculateImageScale() { * @param annotations */ function displayExistingAnnotations(annotations) { - var existingAnnotations = $('#existing_annotations'); - var noAnnotations = $('#no_annotations'); + let existingAnnotations = $('#existing_annotations'); + let noAnnotations = $('#no_annotations'); existingAnnotations.addClass('hidden'); @@ -383,14 +374,14 @@ function calculateImageScale() { existingAnnotations.html(''); // display new annotations - for (var i = 0; i < annotations.length; i++) { - var annotation = annotations[i]; + for (let i = 0; i < annotations.length; i++) { + let annotation = annotations[i]; - var alertClass = ''; + let alertClass = ''; if (globals.editedAnnotationsId === annotation.id) { alertClass = ' alert-info'; } - var newAnnotation = $( + let newAnnotation = $( '
'); @@ -422,14 +413,14 @@ function calculateImageScale() { newAnnotation.append(annotation.annotation_type.name + ':'); - var annotationLinks = $('
'); - var verifyButton = $('' + + let annotationLinks = $('
'); + let verifyButton = $('' + 'verify' + ''); - var editButton = $('' + + let editButton = $('' + 'edit' + ''); - var deleteButton = $('' + + let deleteButton = $('' + 'delete' + ''); const annotationId = annotation.id; @@ -469,7 +460,7 @@ function calculateImageScale() { function handleMouseClick(e) { if (e && (e.target.id === 'image' || e.target.id === 'image_canvas')) { - var position = globals.image.offset(); + let position = globals.image.offset(); globals.mouseClickX = Math.round((e.pageX - position.left)); globals.mouseClickY = Math.round((e.pageY - position.top)); tool.handleMouseClick(e); @@ -527,8 +518,8 @@ function calculateImageScale() { } // image is in cache. - var currentImage = globals.image; - var newImage = gImageCache[imageId]; + let currentImage = globals.image; + let newImage = gImageCache[imageId]; currentImage.attr('id', ''); newImage.attr('id', 'image'); @@ -553,16 +544,16 @@ function calculateImageScale() { * @param imageList */ function displayImageList(imageList) { - var oldImageList = $('#image_list'); - var result = $('
'); - var imageContained = false; + let oldImageList = $('#image_list'); + let result = $('
'); + let imageContained = false; oldImageList.html(''); - for (var i = 0; i < imageList.length; i++) { - var image = imageList[i]; + for (let i = 0; i < imageList.length; i++) { + let image = imageList[i]; - var link = $(''); + let link = $(''); link.attr('id', 'annotate_image_link_' + image.id); link.attr('href', ANNOTATE_URL.replace('%s', image.id)); link.addClass('annotate_image_link'); @@ -631,11 +622,11 @@ function calculateImageScale() { event.preventDefault(); } $('.js_feedback').stop().addClass('hidden'); - var params = { + let params = { annotation_id: annotationId }; - var annotationData = annotationElem.data('vector'); + let annotationData = annotationElem.data('vector'); if (annotationData === undefined) { annotationData = annotationElem.data('escapedvector'); } @@ -644,7 +635,7 @@ function calculateImageScale() { $('.annotation').removeClass('alert-info'); annotationElem.parent().parent().addClass('alert-info'); - var notInImage = $('#not_in_image'); + let notInImage = $('#not_in_image'); if (annotationData === null) { // not in image notInImage.prop('checked', true).change(); @@ -665,9 +656,9 @@ function calculateImageScale() { * Get the image list from all .annotate_image_link within #image_list. */ function getImageList() { - var imageList = []; + let imageList = []; $('#image_list').find('.annotate_image_link').each(function(key, elem) { - var imageId = parseInt($(elem).data('imageid')); + let imageId = parseInt($(elem).data('imageid')); if (imageList.indexOf(imageId) === -1) { imageList.push(imageId); } @@ -799,8 +790,8 @@ function calculateImageScale() { */ function handleSelection(event) { calculateImageScale(); - var cH = $('#crosshair-h'), cV = $('#crosshair-v'); - var position = globals.image.offset(); + let cH = $('#crosshair-h'), cV = $('#crosshair-v'); + let position = globals.image.offset(); if (event.pageX > position.left && event.pageX < position.left + globals.image.width() && event.pageY > position.top && @@ -862,10 +853,9 @@ function calculateImageScale() { return; } - var noAnnotations = $('#no_annotations'); - var notInImage = $('#not_in_image'); - var existingAnnotations = $('#existing_annotations'); - var loading = $('#annotations_loading'); + let noAnnotations = $('#no_annotations'); + let existingAnnotations = $('#existing_annotations'); + let loading = $('#annotations_loading'); existingAnnotations.addClass('hidden'); noAnnotations.addClass('hidden'); loading.removeClass('hidden'); @@ -879,7 +869,7 @@ function calculateImageScale() { scrollImageList(); $('.annotate_image_link').removeClass('active'); - var link = $('#annotate_image_link_' + imageId); + let link = $('#annotate_image_link_' + imageId); link.addClass('active'); $('#active_image_name').text(link.text().trim()); let next_image_id = gImageList[gImageList.indexOf(imageId) + 1]; @@ -994,7 +984,7 @@ function calculateImageScale() { return; } - var params = { + let params = { image_id: imageId }; $.ajax(API_ANNOTATIONS_BASE_URL + 'annotation/load/?' + $.param(params), { @@ -1019,7 +1009,7 @@ function calculateImageScale() { * @param offset integer to add to the current image index */ function loadAdjacentImage(offset) { - var imageIndex = gImageList.indexOf(gImageId); + let imageIndex = gImageList.indexOf(gImageId); if (imageIndex < 0) { console.log('current image is not referenced from page!'); return; @@ -1040,8 +1030,8 @@ function calculateImageScale() { * Preload next and previous images to cache. */ function preloadImages() { - var keepImages = []; - for (var imageId = gImageId - PRELOAD_BACKWARD; + let keepImages = []; + for (let imageId = gImageId - PRELOAD_BACKWARD; imageId <= gImageId + PRELOAD_FORWARD; imageId++) { keepImages.push(imageId); @@ -1056,7 +1046,7 @@ function calculateImageScale() { * @param keep Array of the image ids which should be kept in the cache. */ function pruneImageCache(keep) { - for (var imageId in gImageCache) { + for (let imageId in gImageCache) { imageId = parseInt(imageId); if (gImageCache[imageId] !== undefined && keep.indexOf(imageId) === -1) { delete gImageCache[imageId]; @@ -1068,11 +1058,11 @@ function calculateImageScale() { * Scroll image list to make current image visible. */ function scrollImageList() { - var imageLink = $('#annotate_image_link_' + gImageId); - var list = $('#image_list'); + let imageLink = $('#annotate_image_link_' + gImageId); + let list = $('#image_list'); - var offset = list.offset().top; - var linkTop = imageLink.offset().top; + let offset = list.offset().top; + let linkTop = imageLink.offset().top; // link should be (roughly) in the middle of the element offset += parseInt(list.height() / 2); @@ -1097,7 +1087,7 @@ function calculateImageScale() { if (!$('#draw_annotations').is(':checked')) return; - var position = globals.image.offset(); + let position = globals.image.offset(); if (event.pageX > position.left && event.pageX < position.left + globals.image.width() && event.pageY > position.top && event.pageY < position.top + globals.image.height()) { @@ -1115,7 +1105,7 @@ function calculateImageScale() { if (!$('#draw_annotations').is(':checked')) return; - var position = globals.image.offset(); + let position = globals.image.offset(); globals.mouseUpX = Math.round((event.pageX - position.left)/* * globals.imageScaleWidth*/); globals.mouseUpY = Math.round((event.pageY - position.top)/* * globals.imageScaleHeight*/); @@ -1134,8 +1124,8 @@ function calculateImageScale() { } function selectAnnotationType(annotationTypeNumber) { - var annotationTypeId = '#annotation_type_' + annotationTypeNumber; - var option = $(annotationTypeId); + let annotationTypeId = '#annotation_type_' + annotationTypeNumber; + let option = $(annotationTypeId); if(option.length) { $('#annotation_type_id').val(option.val()); } diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js b/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js index 272e302a..13f85e3c 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js @@ -36,11 +36,11 @@ class BoundingBoxes { } // clear all boxes - var boundingBoxes = document.getElementById('boundingBoxes'); + let boundingBoxes = document.getElementById('boundingBoxes'); - for (var a in annotations) { + for (let a in annotations) { - var annotation = annotations[a]; + let annotation = annotations[a]; let color = colors[a]; if (annotation.annotation_type.id !== this.annotationTypeId) { continue; @@ -49,7 +49,7 @@ class BoundingBoxes { continue; } - var boundingBox = document.createElement('div'); + let boundingBox = document.createElement('div'); boundingBox.setAttribute('class', 'boundingBox'); boundingBox.setAttribute('id', 'boundingBox' + annotation.id); $(boundingBox).data('annotationid', annotation.id); @@ -96,7 +96,7 @@ class BoundingBoxes { } clear() { - var boundingBoxes = document.getElementById('boundingBoxes'); + let boundingBoxes = document.getElementById('boundingBoxes'); while (boundingBoxes.firstChild) { boundingBoxes.removeChild(boundingBoxes.firstChild); @@ -375,23 +375,23 @@ class BoundingBoxes { handleMouseClick(event) { // get current annotation type id - var annotationType = parseInt($('#annotation_type_id').val()); + let annotationType = parseInt($('#annotation_type_id').val()); // array with all matching annotations - var matchingAnnotations = []; + let matchingAnnotations = []; - for (var a in globals.currentAnnotationsOfSelectedType) { - var annotation = globals.currentAnnotationsOfSelectedType[a]; + for (let a in globals.currentAnnotationsOfSelectedType) { + let annotation = globals.currentAnnotationsOfSelectedType[a]; if (annotation.annotation_type.id !== annotationType) { continue; } if (annotation.vector === null) continue; - var left = annotation.vector.x1 / globals.imageScaleWidth; - var right = annotation.vector.x2 / globals.imageScaleWidth; - var top = annotation.vector.y1 / globals.imageScaleHeight; - var bottom = annotation.vector.y2 / globals.imageScaleHeight; + let left = annotation.vector.x1 / globals.imageScaleWidth; + let right = annotation.vector.x2 / globals.imageScaleWidth; + let top = annotation.vector.y1 / globals.imageScaleHeight; + let bottom = annotation.vector.y2 / globals.imageScaleHeight; // check if we clicked inside that annotation if (globals.mouseClickX >= left && globals.mouseClickX <= right && globals.mouseClickY >= top && globals.mouseClickY <= bottom) { @@ -404,7 +404,7 @@ class BoundingBoxes { return; } - annotation = matchingAnnotations[0]; + let annotation = matchingAnnotations[0]; // a single match if (matchingAnnotations.length === 1) { @@ -419,8 +419,8 @@ class BoundingBoxes { // 1. prefer annotation lying inside another one completely // 2. prefer annotation, which left border is to the left of another ones // 3. prefer annotation, which top border is above another ones - for (var a1 in matchingAnnotations) { - var annotation1 = matchingAnnotations[a1]; + for (let a1 in matchingAnnotations) { + let annotation1 = matchingAnnotations[a1]; if (annotation.id === annotation1.id) continue; From 1209c88bd150fc961f56b0ca76f692bed127ab19 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 10:21:10 +0200 Subject: [PATCH 02/31] only call event.preventDefault() when necessary --- .../static/annotations/js/annotations.js | 60 +++++++------------ 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index 2d7fe3cb..41841d0c 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -144,16 +144,10 @@ function calculateImageScale() { * Create an annotation using the form data from the current page. * If an annotation is currently edited, an update is triggered instead. * - * @param event * @param successCallback a function to be executed on success * @param markForRestore */ - function createAnnotation(event, successCallback, markForRestore, reload_list) { - if (event !== undefined) { - // triggered using an event handler - event.preventDefault(); - } - + function createAnnotation(successCallback, markForRestore, reload_list) { let annotationTypeId = parseInt($('#annotation_type_id').val()); let vector = null; @@ -309,25 +303,20 @@ function calculateImageScale() { /** * Delete an annotation. * - * @param event * @param annotationId */ - function deleteAnnotation(event, annotationId) { + function deleteAnnotation(annotationId) { if (globals.editedAnnotationsId === annotationId) { // stop editing tool.resetSelection(true); $('#not_in_image').prop('checked', false).change(); } - if (event !== undefined) { - // triggered using an event handler - event.preventDefault(); - - // TODO: Do not use a primitive js confirm - if (!confirm('Do you really want to delete the annotation?')) { - return; - } + // TODO: Do not use a primitive js confirm + if (!confirm('Do you really want to delete the annotation?')) { + return; } + $('.js_feedback').stop().addClass('hidden'); let params = { annotation_id: annotationId @@ -426,7 +415,7 @@ function calculateImageScale() { const annotationId = annotation.id; editButton.attr('id', 'annotation_edit_button_' + annotationId); editButton.click(function(event) { - editAnnotation(event, this, annotationId); + editAnnotation(this, annotationId); }); editButton.data('annotationtypeid', annotation.annotation_type.id); editButton.data('annotationid', annotation.id); @@ -434,7 +423,8 @@ function calculateImageScale() { editButton.data('blurred', annotation.blurred); editButton.data('concealed', annotation.concealed); deleteButton.click(function(event) { - deleteAnnotation(event, annotationId); + event.preventDefault(); // the button is a link, don't follow it + deleteAnnotation(annotationId); }); annotationLinks.append(verifyButton); annotationLinks.append(' '); @@ -564,7 +554,7 @@ function calculateImageScale() { link.text(image.name); link.data('imageid', image.id); link.click(function(event) { - event.preventDefault(); + event.preventDefault(); // do not let the browser follow the link loadAnnotateView($(this).data('imageid')); }); @@ -605,11 +595,10 @@ function calculateImageScale() { /** * Edit an annotation. * - * @param event * @param annotationElem the element which stores the edit button of the annotation * @param annotationId */ - function editAnnotation(event, annotationElem, annotationId) { + function editAnnotation(annotationElem, annotationId) { annotationElem = $(annotationElem); let annotationTypeId = annotationElem.data('annotationtypeid'); $('#annotation_type_id').val(annotationTypeId); @@ -617,10 +606,6 @@ function calculateImageScale() { globals.editedAnnotationsId = annotationId; globals.editActiveContainer.removeClass('hidden'); - if (event !== undefined) { - // triggered using an event handler - event.preventDefault(); - } $('.js_feedback').stop().addClass('hidden'); let params = { annotation_id: annotationId @@ -826,8 +811,6 @@ function calculateImageScale() { /** * Handle a resize event of the window. - * - * @param event */ function handleResize() { tool.cancelSelection(); @@ -1120,7 +1103,7 @@ function calculateImageScale() { if (globals.editedAnnotationsId === undefined) return; - deleteAnnotation(event, globals.editedAnnotationsId); + deleteAnnotation(globals.editedAnnotationsId); } function selectAnnotationType(annotationTypeNumber) { @@ -1188,13 +1171,14 @@ function calculateImageScale() { $('#cancel_edit_button').click(function() { tool.resetSelection(true); }); - $('#save_button').click(createAnnotation); + $('#save_button').click(function (event) { + createAnnotation(); + }); $('#reset_button').click(function() { tool.resetSelection(true); }); $('#last_button').click(function(event) { - event.preventDefault(); - createAnnotation(undefined, function() { + createAnnotation(function() { loadAdjacentImage(-1); }, true, true); if (tool instanceof BoundingBoxes) { @@ -1202,22 +1186,19 @@ function calculateImageScale() { } }); $('#back_button').click(function(event) { - event.preventDefault(); if (tool instanceof BoundingBoxes) { tool.cancelSelection(); } loadAdjacentImage(-1); }); $('#skip_button').click(function(event) { - event.preventDefault(); if (tool instanceof BoundingBoxes) { tool.cancelSelection(); } loadAdjacentImage(1); }); $('#next_button').click(function(event) { - event.preventDefault(); - createAnnotation(undefined, function() { + createAnnotation(function() { loadAdjacentImage(1); }, true, true); if (tool instanceof BoundingBoxes) { @@ -1228,7 +1209,7 @@ function calculateImageScale() { $(this).addClass('hidden'); }); $('.annotate_image_link').click(function(event) { - event.preventDefault(); + event.preventDefault(); // do not let the browser load the link loadAnnotateView($(this).data('imageid')); }); @@ -1236,13 +1217,14 @@ function calculateImageScale() { $('.annotation_edit_button').each(function(key, elem) { elem = $(elem); elem.click(function(event) { - editAnnotation(event, this, parseInt(elem.data('annotationid'))); + editAnnotation(this, parseInt(elem.data('annotationid'))); }); }); $('.annotation_delete_button').each(function(key, elem) { elem = $(elem); elem.click(function(event) { - deleteAnnotation(event, parseInt(elem.data('annotationid'))); + event.preventDefault(); // the button is a link, don't follow it + deleteAnnotation(parseInt(elem.data('annotationid'))); }); }); From 2f8f913cd53c0d37e51be15a9abb8a33138561c0 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 10:48:56 +0200 Subject: [PATCH 03/31] make createAnnotation async --- .../static/annotations/js/annotations.js | 126 +++++++++--------- 1 file changed, 61 insertions(+), 65 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index 41841d0c..c1b89eef 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -144,14 +144,13 @@ function calculateImageScale() { * Create an annotation using the form data from the current page. * If an annotation is currently edited, an update is triggered instead. * - * @param successCallback a function to be executed on success - * @param markForRestore + * @param markForRestore if the annotation should be saved to be reused for the next image */ - function createAnnotation(successCallback, markForRestore, reload_list) { + async function createAnnotation(markForRestore) { let annotationTypeId = parseInt($('#annotation_type_id').val()); let vector = null; - if (isNaN(annotationTypeId)) { + if (annotationTypeId === -1) { displayFeedback($('#feedback_annotation_type_missing')); return; } @@ -164,7 +163,7 @@ function calculateImageScale() { vector["y" + i] = parseInt($('#y' + i + 'Field').val()); } // Swap points if the second one is left of the first one - if (Object.keys(vector).length == 4) { + if (Object.keys(vector).length === 4) { if (vector.x1 > vector.x2 || vector.x1 === vector.x2 && vector.y1 > vector.y2) { let tmp_x1 = vector.x2; let tmp_y1 = vector.y2; @@ -191,7 +190,7 @@ function calculateImageScale() { } let action = 'create'; - let data = { + let body = { annotation_type_id: annotationTypeId, image_id: gImageId, vector: vector, @@ -202,65 +201,55 @@ function calculateImageScale() { if (globals.editedAnnotationsId !== undefined) { // edit instead of create action = 'update'; - data.annotation_id = globals.editedAnnotationsId; + body.annotation_id = globals.editedAnnotationsId; editing = true; } $('.js_feedback').stop().addClass('hidden'); $('.annotate_button').prop('disabled', true); - $.ajax(API_ANNOTATIONS_BASE_URL + 'annotation/' + action + '/', { - type: 'POST', - headers: gHeaders, - dataType: 'json', - data: JSON.stringify(data), - success: function(data, textStatus, jqXHR) { - if (jqXHR.status === 200) { - if (editing) { - if (data.detail === 'similar annotation exists.') { - displayFeedback($('#feedback_annotation_exists_deleted')); - $('#annotation_edit_button_' + globals.editedAnnotationsId).parent().parent( - ).fadeOut().remove(); - } else { - displayFeedback($('#feedback_annotation_updated')); - displayExistingAnnotations(data.annotations); - if (reload_list === true) { - loadImageList(); - } - tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType); - } + try { + let response = await fetch(API_ANNOTATIONS_BASE_URL + 'annotation/' + action + '/', { + method: 'POST', + headers: gHeaders, + body: JSON.stringify(body), + }); + + let data = await response.json(); + if (response.status === 200) { + if (editing) { + if (data.detail === 'similar annotation exists.') { + displayFeedback($('#feedback_annotation_exists_deleted')); + $('#annotation_edit_button_' + globals.editedAnnotationsId).parent().parent().fadeOut().remove(); } else { - displayFeedback($('#feedback_annotation_exists')); + displayFeedback($('#feedback_annotation_updated')); + displayExistingAnnotations(data.annotations); + tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType); } - } else if (jqXHR.status === 201) { - displayFeedback($('#feedback_annotation_created')); - displayExistingAnnotations(data.annotations); - if (reload_list === true) { - loadImageList(); - } + } else { + displayFeedback($('#feedback_annotation_exists')); } - // update current annotations - gCurrentAnnotations = data.annotations; - globals.currentAnnotationsOfSelectedType = gCurrentAnnotations.filter(function(e) { - return e.annotation_type.id === gAnnotationType; - }); - - tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType); + } else if (response.status === 201) { + displayFeedback($('#feedback_annotation_created')); + displayExistingAnnotations(data.annotations); + } + // update current annotations + gCurrentAnnotations = data.annotations; + globals.currentAnnotationsOfSelectedType = gCurrentAnnotations.filter(function (e) { + return e.annotation_type.id === gAnnotationType; + }); - globals.editedAnnotationsId = undefined; - $('.annotation').removeClass('alert-info'); - globals.editActiveContainer.addClass('hidden'); - tool.resetSelection(true); + tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType); - if (typeof(successCallback) === "function") { - successCallback(); - } - $('.annotate_button').prop('disabled', false); - }, - error: function() { - $('.annotate_button').prop('disabled', false); - displayFeedback($('#feedback_connection_error')); - } - }); + globals.editedAnnotationsId = undefined; + $('.annotation').removeClass('alert-info'); + globals.editActiveContainer.addClass('hidden'); + tool.resetSelection(true); + } catch (e) { + console.log(e); + displayFeedback($('#feedback_connection_error')); + } finally { + $('.annotate_button').prop('disabled', false); + } } function loadAnnotationTypeList() { @@ -1172,18 +1161,22 @@ function calculateImageScale() { tool.resetSelection(true); }); $('#save_button').click(function (event) { + event.preventDefault(); createAnnotation(); }); $('#reset_button').click(function() { tool.resetSelection(true); }); $('#last_button').click(function(event) { - createAnnotation(function() { - loadAdjacentImage(-1); - }, true, true); - if (tool instanceof BoundingBoxes) { + createAnnotation(true).then(r => { + if (tool instanceof BoundingBoxes) { tool.cancelSelection(); - } + } + }).then(r => { + loadImageList(); + }).then(r => { + loadAdjacentImage(-1); + }); }); $('#back_button').click(function(event) { if (tool instanceof BoundingBoxes) { @@ -1198,12 +1191,15 @@ function calculateImageScale() { loadAdjacentImage(1); }); $('#next_button').click(function(event) { - createAnnotation(function() { - loadAdjacentImage(1); - }, true, true); - if (tool instanceof BoundingBoxes) { + createAnnotation(true).then(r => { + if (tool instanceof BoundingBoxes) { tool.cancelSelection(); - } + } + }).then(r => { + loadImageList(); + }).then(r => { + loadAdjacentImage(1); + }); }); $('.js_feedback').mouseover(function() { $(this).addClass('hidden'); From a41932b761e1734dfa050e4c6ae3b8dd4b5609c9 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 11:17:47 +0200 Subject: [PATCH 04/31] less code duplication for resetSelection --- .../static/annotations/js/annotations.js | 63 +++++++++++-------- .../static/annotations/js/boundingboxes.js | 21 ++----- .../static/annotations/js/canvas.js | 13 +--- 3 files changed, 45 insertions(+), 52 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index c1b89eef..fa3f2e07 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -2,8 +2,6 @@ globals = { image: {}, imageScaleWidth: 1, imageScaleHeight: 1, - editedAnnotationsId: undefined, - editActiveContainer: {}, restoreSelection: undefined, restoreSelectionVectorType: 1, restoreSelectionNodeCount: 0, @@ -52,6 +50,7 @@ function calculateImageScale() { let gHighlightedAnnotation; let gShiftDown; let tool; + let gEditAnnotationId; // id of the currently edited annotation, or undefined function shorten(string, length) { let threshold = length || 30; @@ -117,7 +116,7 @@ function calculateImageScale() { cancelSelection: function() {}, reset: function() {}, drawExistingAnnotations: function() {}, - handleEscape: function() {}, + closeDrawing: function() {}, handleMousemove: function() {}, handleMouseDown: function() {}, handleMouseUp: function() {}, @@ -198,10 +197,10 @@ function calculateImageScale() { blurred: blurred }; let editing = false; - if (globals.editedAnnotationsId !== undefined) { + if (gEditAnnotationId !== undefined) { // edit instead of create action = 'update'; - body.annotation_id = globals.editedAnnotationsId; + body.annotation_id = gEditAnnotationId; editing = true; } @@ -219,7 +218,7 @@ function calculateImageScale() { if (editing) { if (data.detail === 'similar annotation exists.') { displayFeedback($('#feedback_annotation_exists_deleted')); - $('#annotation_edit_button_' + globals.editedAnnotationsId).parent().parent().fadeOut().remove(); + $('#annotation_edit_button_' + gEditAnnotationId).parent().parent().fadeOut().remove(); } else { displayFeedback($('#feedback_annotation_updated')); displayExistingAnnotations(data.annotations); @@ -240,10 +239,7 @@ function calculateImageScale() { tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType); - globals.editedAnnotationsId = undefined; - $('.annotation').removeClass('alert-info'); - globals.editActiveContainer.addClass('hidden'); - tool.resetSelection(true); + resetSelection(); } catch (e) { console.log(e); displayFeedback($('#feedback_connection_error')); @@ -295,9 +291,9 @@ function calculateImageScale() { * @param annotationId */ function deleteAnnotation(annotationId) { - if (globals.editedAnnotationsId === annotationId) { + if (gEditAnnotationId === annotationId) { // stop editing - tool.resetSelection(true); + resetSelection(); $('#not_in_image').prop('checked', false).change(); } @@ -323,7 +319,7 @@ function calculateImageScale() { tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType); displayExistingAnnotations(gCurrentAnnotations); displayFeedback($('#feedback_annotation_deleted')); - globals.editedAnnotationsId = undefined; + gEditAnnotationId = undefined; }, error: function() { $('.annotate_button').prop('disabled', false); @@ -356,7 +352,7 @@ function calculateImageScale() { let annotation = annotations[i]; let alertClass = ''; - if (globals.editedAnnotationsId === annotation.id) { + if (gEditAnnotationId === annotation.id) { alertClass = ' alert-info'; } let newAnnotation = $( @@ -509,7 +505,7 @@ function calculateImageScale() { globals.image = newImage; calculateImageScale(); tool.initSelection(); - tool.resetSelection(); + resetSelection(); if (currentImage.data('imageid') !== undefined) { // add previous image to cache @@ -517,6 +513,16 @@ function calculateImageScale() { } } + /** + * Delete the current selection + */ + function resetSelection() { + tool.resetSelection(); + gEditAnnotationId = undefined; + $('.annotation').removeClass('alert-info'); + $('#edit_active').addClass('hidden'); + } + /** * Display the images of an image list. * @@ -592,8 +598,8 @@ function calculateImageScale() { let annotationTypeId = annotationElem.data('annotationtypeid'); $('#annotation_type_id').val(annotationTypeId); handleAnnotationTypeChange(); - globals.editedAnnotationsId = annotationId; - globals.editActiveContainer.removeClass('hidden'); + gEditAnnotationId = annotationId; + $('#edit_active').removeClass('hidden'); $('.js_feedback').stop().addClass('hidden'); let params = { @@ -735,7 +741,7 @@ function calculateImageScale() { if ($('#not_in_image').is(':checked')) { // hide the coordinate selection. - tool.resetSelection(true); + resetSelection(); coordinate_table.hide(); } else { coordinate_table.show(); @@ -814,7 +820,7 @@ function calculateImageScale() { * @param fromHistory */ function loadAnnotateView(imageId, fromHistory) { - globals.editedAnnotationsId = undefined; + gEditAnnotationId = undefined; imageId = parseInt(imageId); @@ -870,7 +876,7 @@ function calculateImageScale() { if (globals.restoreSelection !== undefined) { tool.restoreSelection(); } else { - tool.resetSelection(); + resetSelection(); } }; @@ -1089,10 +1095,10 @@ function calculateImageScale() { // handle DEL key press function handleDelete(event) { - if (globals.editedAnnotationsId === undefined) + if (gEditAnnotationId === undefined) return; - deleteAnnotation(globals.editedAnnotationsId); + deleteAnnotation(gEditAnnotationId); } function selectAnnotationType(annotationTypeNumber) { @@ -1115,7 +1121,6 @@ function calculateImageScale() { break; } } - globals.editActiveContainer = $('#edit_active'); globals.image = $('#image'); globals.drawAnnotations = $('#draw_annotations').is(':checked'); gMousepos = $('#mousepos'); @@ -1158,14 +1163,14 @@ function calculateImageScale() { handleMouseClick(e); }); $('#cancel_edit_button').click(function() { - tool.resetSelection(true); + resetSelection(); }); $('#save_button').click(function (event) { event.preventDefault(); createAnnotation(); }); $('#reset_button').click(function() { - tool.resetSelection(true); + resetSelection(); }); $('#last_button').click(function(event) { createAnnotation(true).then(r => { @@ -1243,7 +1248,13 @@ function calculateImageScale() { gShiftDown = true; break; case 27: // Escape - tool.handleEscape(); + if (globals.annotation_type_id === 4) { + // special case multiline: close drawing + tool.closeDrawing(); + } else { + // normally, just delete the selection + resetSelection(); + } break; case 73: //i if(gShiftDown) { diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js b/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js index 13f85e3c..93063187 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js @@ -80,16 +80,9 @@ class BoundingBoxes { } } - unsetHighlightColor(id) { - let highlightBox; + unsetHighlightColor() { for (let box of $('.boundingBox')) { - if ($(box).data('annotationid') === id) { - highlightBox = box; - break; - } - } - if (highlightBox) { - $(highlightBox).css({ + $(box).css({ 'border': '2px solid ' + globals.stdColor }); } @@ -156,17 +149,13 @@ class BoundingBoxes { /** * Delete current selection. */ - resetSelection(abortEdit) { - this.unsetHighlightColor(globals.editedAnnotationsId); + resetSelection() { + this.unsetHighlightColor(); $('.annotation_value').val(0); if (this.selection !== undefined) { this.selection.cancelSelection(); } - - globals.editedAnnotationsId = undefined; - $('.annotation').removeClass('alert-info'); - globals.editActiveContainer.addClass('hidden'); } /** @@ -371,7 +360,7 @@ class BoundingBoxes { handleMouseDown(event) { } handleMouseUp(event) { } - handleEscape() { this.resetSelection(true); } + closeDrawing() { } handleMouseClick(event) { // get current annotation type id diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js b/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js index 299e6b97..e011de3d 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js @@ -532,32 +532,25 @@ class Canvas { } } - handleEscape() { + closeDrawing() { if (this.currentDrawing && this.annotationTypeId === 4 && !this.currentDrawing.closed) { this.currentDrawing.close(); - } else { - this.resetSelection(true); } } - resetSelection(abortEdit) { + resetSelection() { $('.annotation_value').val(0); // Make every drawing immutable for (let drawing of this.drawings) { drawing.setMutable(false); } - globals.editedAnnotationsId = undefined; - $('.annotation').removeClass('alert-info'); - globals.editActiveContainer.addClass('hidden'); - if (abortEdit === true) { - if (this.old && this.currentDrawing) { + if (this.old && this.currentDrawing) { this.currentDrawing.setPoints(this.old); this.old = undefined; } else if (this.currentDrawing) { this.currentDrawing.remove(); } this.currentDrawing = undefined; - } } cancelSelection() { From 682f6c1a92f4514bc05b2ad9c758158373d495c1 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 11:29:23 +0200 Subject: [PATCH 05/31] make loadAdjacentImage async --- .../static/annotations/js/annotations.js | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index fa3f2e07..d425ae98 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -151,7 +151,7 @@ function calculateImageScale() { if (annotationTypeId === -1) { displayFeedback($('#feedback_annotation_type_missing')); - return; + throw new Error('annotation type missing'); } let blurred = $('#blurred').is(':checked'); let concealed = $('#concealed').is(':checked'); @@ -179,7 +179,7 @@ function calculateImageScale() { let node_count = selected_annotation.nodeCount; if (!validate_vector(vector, vector_type, node_count)) { displayFeedback($('#feedback_annotation_invalid')); - return; + throw new Error('annotation is invalid'); } if (markForRestore === true) { @@ -985,12 +985,12 @@ function calculateImageScale() { * Load the previous or the next image * * @param offset integer to add to the current image index + * @return index of the requested image in gImageList */ - function loadAdjacentImage(offset) { + async function loadAdjacentImage(offset) { let imageIndex = gImageList.indexOf(gImageId); if (imageIndex < 0) { - console.log('current image is not referenced from page!'); - return; + throw new Error('current image is not referenced from page!') } imageIndex += offset; @@ -1000,8 +1000,7 @@ function calculateImageScale() { while (imageIndex > imageIndex.length) { imageIndex -= imageIndex.length; } - - loadAnnotateView(gImageList[imageIndex]); + return imageIndex; } /** @@ -1180,20 +1179,26 @@ function calculateImageScale() { }).then(r => { loadImageList(); }).then(r => { - loadAdjacentImage(-1); + return loadAdjacentImage(-1); + }).then(imageIndex => { + loadAnnotateView(gImageList[imageIndex]); }); }); $('#back_button').click(function(event) { if (tool instanceof BoundingBoxes) { tool.cancelSelection(); } - loadAdjacentImage(-1); + loadAdjacentImage(-1).then(imageIndex => { + loadAnnotateView(gImageList[imageIndex]); + }); }); $('#skip_button').click(function(event) { if (tool instanceof BoundingBoxes) { tool.cancelSelection(); } - loadAdjacentImage(1); + loadAdjacentImage(1).then(imageIndex => { + loadAnnotateView(gImageList[imageIndex]); + }); }); $('#next_button').click(function(event) { createAnnotation(true).then(r => { @@ -1203,7 +1208,9 @@ function calculateImageScale() { }).then(r => { loadImageList(); }).then(r => { - loadAdjacentImage(1); + return loadAdjacentImage(1); + }).then(imageIndex => { + loadAnnotateView(gImageList[imageIndex]); }); }); $('.js_feedback').mouseover(function() { From fef88c73695d4e1d010ad1cf552fe21ae680f6e6 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 11:34:09 +0200 Subject: [PATCH 06/31] use async/await syntax instead of promise syntax --- .../static/annotations/js/annotations.js | 54 +++++++------------ 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index d425ae98..2ff9c139 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -1171,54 +1171,40 @@ function calculateImageScale() { $('#reset_button').click(function() { resetSelection(); }); - $('#last_button').click(function(event) { - createAnnotation(true).then(r => { - if (tool instanceof BoundingBoxes) { - tool.cancelSelection(); - } - }).then(r => { - loadImageList(); - }).then(r => { - return loadAdjacentImage(-1); - }).then(imageIndex => { - loadAnnotateView(gImageList[imageIndex]); - }); + $('#last_button').click(async function(event) { + await createAnnotation(true); + if (tool instanceof BoundingBoxes) { + tool.cancelSelection(); + } + loadImageList(); + await loadAdjacentImage(-1); }); - $('#back_button').click(function(event) { + $('#back_button').click(async function(event) { if (tool instanceof BoundingBoxes) { tool.cancelSelection(); } - loadAdjacentImage(-1).then(imageIndex => { - loadAnnotateView(gImageList[imageIndex]); - }); + await loadAdjacentImage(-1); }); - $('#skip_button').click(function(event) { + $('#skip_button').click(async function(event) { if (tool instanceof BoundingBoxes) { tool.cancelSelection(); } - loadAdjacentImage(1).then(imageIndex => { - loadAnnotateView(gImageList[imageIndex]); - }); + await loadAdjacentImage(1) }); - $('#next_button').click(function(event) { - createAnnotation(true).then(r => { - if (tool instanceof BoundingBoxes) { - tool.cancelSelection(); - } - }).then(r => { - loadImageList(); - }).then(r => { - return loadAdjacentImage(1); - }).then(imageIndex => { - loadAnnotateView(gImageList[imageIndex]); - }); + $('#next_button').click(async function(event) { + await createAnnotation(true); + if (tool instanceof BoundingBoxes) { + tool.cancelSelection(); + } + await loadImageList(); + await loadAdjacentImage(1); }); $('.js_feedback').mouseover(function() { $(this).addClass('hidden'); }); - $('.annotate_image_link').click(function(event) { + $('.annotate_image_link').click(async function(event) { event.preventDefault(); // do not let the browser load the link - loadAnnotateView($(this).data('imageid')); + await loadAnnotateView($(this).data('imageid')); }); // annotation buttons From 17ed6ec5a374cd1224871c03adcafae67ea31ff2 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 11:44:55 +0200 Subject: [PATCH 07/31] make loadAnnotateView and loadAnnotations async --- .../static/annotations/js/annotations.js | 59 ++++++++----------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index 2ff9c139..1c5e4731 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -548,9 +548,9 @@ function calculateImageScale() { } link.text(image.name); link.data('imageid', image.id); - link.click(function(event) { + link.click(async function(event) { event.preventDefault(); // do not let the browser follow the link - loadAnnotateView($(this).data('imageid')); + await loadAnnotateView($(this).data('imageid')); }); result.append(link); @@ -819,7 +819,7 @@ function calculateImageScale() { * @param imageId * @param fromHistory */ - function loadAnnotateView(imageId, fromHistory) { + async function loadAnnotateView(imageId, fromHistory) { gEditAnnotationId = undefined; imageId = parseInt(imageId); @@ -864,11 +864,10 @@ function calculateImageScale() { }, document.title, '/annotations/' + imageId + '/'); } - let handleNewAnnotations = function() { - // image is in cache. - globals.currentAnnotationsOfSelectedType = gCurrentAnnotations.filter(function(e) { - return e.annotation_type.id === gAnnotationType; - }); + try { + // load existing annotations for this image + await loadAnnotations(imageId); + loading.addClass('hidden'); displayExistingAnnotations(gCurrentAnnotations); tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType); @@ -878,10 +877,9 @@ function calculateImageScale() { } else { resetSelection(); } - }; - - // load existing annotations for this image - loadAnnotations(imageId, handleNewAnnotations); + } catch { + console.log("Unable to load annotations for image" + imageId); + } } /** @@ -950,35 +948,30 @@ function calculateImageScale() { * Load the annotations of an image to the cache if they are not in it already. * * @param imageId - * @param cb callback for success */ - function loadAnnotations(imageId, cb) { + async function loadAnnotations(imageId) { imageId = parseInt(imageId); if (gImageList.indexOf(imageId) === -1) { - console.log( - 'skiping request to load annotations of image ' + imageId + - ' as it is not in current image list.'); - return; + throw new Error( + 'skipping request to load annotations of image ' + imageId + + ' as it is not in current image list.'); } let params = { image_id: imageId }; - $.ajax(API_ANNOTATIONS_BASE_URL + 'annotation/load/?' + $.param(params), { - type: 'GET', + let response = await fetch(API_ANNOTATIONS_BASE_URL + 'annotation/load/?' + $.param(params), { + method: 'GET', headers: gHeaders, - dataType: 'json', - success: function(data) { - // save the current annotations - gCurrentAnnotations = data.annotations; - console.log("Saving annotations for", imageId); - cb(); - }, - error: function() { - console.log("Unable to load annotations for image" + imageId); - } }); + let data = await response.json(); + // save the current annotations + gCurrentAnnotations = data.annotations; + globals.currentAnnotationsOfSelectedType = gCurrentAnnotations.filter(function (e) { + return e.annotation_type.id === gAnnotationType; + }); + console.log("Fetched annotations for", imageId); } /** @@ -1000,7 +993,7 @@ function calculateImageScale() { while (imageIndex > imageIndex.length) { imageIndex -= imageIndex.length; } - return imageIndex; + await loadAnnotateView(gImageList[imageIndex]); } /** @@ -1224,9 +1217,9 @@ function calculateImageScale() { $(document).on('mousemove touchmove', handleSelection); $(window).on('resize', handleResize); - window.onpopstate = function(event) { + window.onpopstate = async function(event) { if (event.state !== undefined && event.state !== null && event.state.imageId !== undefined) { - loadAnnotateView(event.state.imageId, true); + await loadAnnotateView(event.state.imageId, true); } }; From c199a1439b40b090f915e44c0b00a2ff80e73a92 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 11:46:40 +0200 Subject: [PATCH 08/31] remove tool.initialized (value is never accessed) --- .../annotations/static/annotations/js/boundingboxes.js | 3 --- .../imagetagger/annotations/static/annotations/js/canvas.js | 4 +--- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js b/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js index 93063187..475f9102 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js @@ -2,7 +2,6 @@ class BoundingBoxes { constructor(annotationTypeId, noSelection) { - this.initialized = false; this.selection = undefined; this.vector_type = 1; if (globals.image === '') { @@ -100,8 +99,6 @@ class BoundingBoxes { * Initialize the selection. */ initSelection() { - this.initialized = true; - this.selection = globals.image.imgAreaSelect({ instance: true, show: true, diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js b/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js index e011de3d..e440f81a 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js @@ -557,9 +557,7 @@ class Canvas { this.resetSelection(true) } - initSelection() { - this.initialized = true; - } + initSelection() { } /** * Restore the selection. From 906f67099e2ff1fec3e2a4880b3406c15c3c900c Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 12:00:51 +0200 Subject: [PATCH 09/31] make deleteAnnotation async --- .../static/annotations/js/annotations.js | 70 ++++++++++--------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index 1c5e4731..e8dafeab 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -290,42 +290,44 @@ function calculateImageScale() { * * @param annotationId */ - function deleteAnnotation(annotationId) { + async function deleteAnnotation(annotationId) { + // TODO: Do not use a primitive js confirm + if (!confirm('Do you really want to delete the annotation?')) { + return; + } + if (gEditAnnotationId === annotationId) { // stop editing resetSelection(); $('#not_in_image').prop('checked', false).change(); } - // TODO: Do not use a primitive js confirm - if (!confirm('Do you really want to delete the annotation?')) { - return; - } - $('.js_feedback').stop().addClass('hidden'); let params = { annotation_id: annotationId }; - $.ajax(API_ANNOTATIONS_BASE_URL + 'annotation/delete/?' + $.param(params), { - type: 'DELETE', - headers: gHeaders, - dataType: 'json', - success: function(data) { - gCurrentAnnotations = data.annotations; - globals.currentAnnotationsOfSelectedType = gCurrentAnnotations.filter(function(e) { - return e.annotation_type.id === gAnnotationType; - }); - // redraw the annotations - tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType); - displayExistingAnnotations(gCurrentAnnotations); - displayFeedback($('#feedback_annotation_deleted')); - gEditAnnotationId = undefined; - }, - error: function() { - $('.annotate_button').prop('disabled', false); - displayFeedback($('#feedback_connection_error')); - } - }); + + $('.annotate_button').prop('disabled', true); + try { + let response = await fetch(API_ANNOTATIONS_BASE_URL + 'annotation/delete/?' + $.param(params), { + method: 'DELETE', + headers: gHeaders, + }); + let data = await response.json(); + gCurrentAnnotations = data.annotations; + globals.currentAnnotationsOfSelectedType = gCurrentAnnotations.filter(function (e) { + return e.annotation_type.id === gAnnotationType; + }); + // redraw the annotations + tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType); + displayExistingAnnotations(gCurrentAnnotations); + displayFeedback($('#feedback_annotation_deleted')); + gEditAnnotationId = undefined; + } catch { + displayFeedback($('#feedback_connection_error')); + } finally { + $('.annotate_button').prop('disabled', false); + } } /** @@ -407,9 +409,9 @@ function calculateImageScale() { editButton.data('vector', annotation.vector); editButton.data('blurred', annotation.blurred); editButton.data('concealed', annotation.concealed); - deleteButton.click(function(event) { + deleteButton.click(async function(event) { event.preventDefault(); // the button is a link, don't follow it - deleteAnnotation(annotationId); + await deleteAnnotation(annotationId); }); annotationLinks.append(verifyButton); annotationLinks.append(' '); @@ -1086,11 +1088,11 @@ function calculateImageScale() { } // handle DEL key press - function handleDelete(event) { + async function handleDelete() { if (gEditAnnotationId === undefined) return; - deleteAnnotation(gEditAnnotationId); + await deleteAnnotation(gEditAnnotationId); } function selectAnnotationType(annotationTypeNumber) { @@ -1209,9 +1211,9 @@ function calculateImageScale() { }); $('.annotation_delete_button').each(function(key, elem) { elem = $(elem); - elem.click(function(event) { + elem.click(async function(event) { event.preventDefault(); // the button is a link, don't follow it - deleteAnnotation(parseInt(elem.data('annotationid'))); + await deleteAnnotation(parseInt(elem.data('annotationid'))); }); }); @@ -1302,7 +1304,7 @@ function calculateImageScale() { break; } }); - $(document).keyup(function(event) { + $(document).keyup(async function(event) { switch (event.keyCode){ case 16: // Shift gShiftDown = false; @@ -1329,7 +1331,7 @@ function calculateImageScale() { $('#save_button').click(); break; case 46: //'DEL' - handleDelete(event); + await handleDelete(); break; case 66: //b $('#blurred').click(); From 3b273506067eb09008bbb60d43e030d37e0a7399 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 12:06:15 +0200 Subject: [PATCH 10/31] make loadImageList async --- .../static/annotations/js/annotations.js | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index e8dafeab..10a95a24 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -887,7 +887,7 @@ function calculateImageScale() { /** * Load the image list from tye server applying a new filter. */ - function loadImageList() { + async function loadImageList() { let filterElem = $('#filter_annotation_type'); let filter = filterElem.val(); let params = { @@ -901,24 +901,25 @@ function calculateImageScale() { handleAnnotationTypeChange(); } - $.ajax(API_IMAGES_BASE_URL + 'imageset/load/?' + $.param(params), { - type: 'GET', - headers: gHeaders, - dataType: 'json', - success: function(data, textStatus, jqXHR) { - if (data.image_set.images.length === 0) { - // redirect to image set view. - displayFeedback($('#feedback_image_set_empty')); - filterElem.val('').change(); - return; - } + $('.annotate_button').prop('disabled', true); + try { + let response = await fetch(API_IMAGES_BASE_URL + 'imageset/load/?' + $.param(params), { + method: 'GET', + headers: gHeaders, + }); + let data = await response.json(); + if (data.image_set.images.length === 0) { + // set is empty, reset filter + displayFeedback($('#feedback_image_set_empty')); + filterElem.val('').change(); + } else { displayImageList(data.image_set.images); - }, - error: function() { - $('.annotate_button').prop('disabled', false); - displayFeedback($('#feedback_connection_error')); } - }); + } catch { + displayFeedback($('#feedback_connection_error')); + } finally { + $('.annotate_button').prop('disabled', false); + } } /** @@ -1171,7 +1172,7 @@ function calculateImageScale() { if (tool instanceof BoundingBoxes) { tool.cancelSelection(); } - loadImageList(); + await loadImageList(); await loadAdjacentImage(-1); }); $('#back_button').click(async function(event) { From 632f5dd69473267e7a206d66a59822415b02a6d8 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 12:19:18 +0200 Subject: [PATCH 11/31] make loadAnnotationTypeList async --- .../static/annotations/js/annotations.js | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index 10a95a24..3f013742 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -248,18 +248,17 @@ function calculateImageScale() { } } - function loadAnnotationTypeList() { - $.ajax(API_ANNOTATIONS_BASE_URL + 'annotation/loadannotationtypes/', { - type: 'GET', - headers: gHeaders, - dataType: 'json', - success: function (data) { - displayAnnotationTypeOptions(data.annotation_types); - }, - error: function () { - displayFeedback($('#feedback_connection_error')) - } - }) + async function loadAnnotationTypeList() { + try { + let response = await fetch(API_ANNOTATIONS_BASE_URL + 'annotation/loadannotationtypes/', { + method: 'GET', + headers: gHeaders, + }); + let data = await response.json(); + displayAnnotationTypeOptions(data.annotation_types); + } catch { + displayFeedback($('#feedback_connection_error')) + } } function displayAnnotationTypeOptions(annotationTypeList) { From 8c23e281d9ddc1941accd98af410b27a70a60cd0 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 12:34:07 +0200 Subject: [PATCH 12/31] make image loading async --- .../static/annotations/js/annotations.js | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index 3f013742..f0f78501 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -478,7 +478,7 @@ function calculateImageScale() { * * @param imageId */ - function displayImage(imageId) { + async function displayImage(imageId) { imageId = parseInt(imageId); if (gImageList.indexOf(imageId) === -1) { @@ -490,7 +490,7 @@ function calculateImageScale() { if (gImageCache[imageId] === undefined) { // image is not available in cache. Load it. - loadImageToCache(imageId); + await loadImageToCache(imageId); } // image is in cache. @@ -500,7 +500,7 @@ function calculateImageScale() { currentImage.attr('id', ''); newImage.attr('id', 'image'); gImageId = imageId; - preloadImages(); + let loadImages = preloadImages(); currentImage.replaceWith(newImage); globals.image = newImage; @@ -512,6 +512,7 @@ function calculateImageScale() { // add previous image to cache gImageCache[currentImage.data('imageid')] = currentImage; } + await loadImages; } /** @@ -529,7 +530,7 @@ function calculateImageScale() { * * @param imageList */ - function displayImageList(imageList) { + async function displayImageList(imageList) { let oldImageList = $('#image_list'); let result = $('
'); let imageContained = false; @@ -565,7 +566,7 @@ function calculateImageScale() { // load first image if current image is not within image set if (!imageContained) { - loadAnnotateView(imageList[0].id); + await loadAnnotateView(imageList[0].id); } scrollImageList(); @@ -840,12 +841,12 @@ function calculateImageScale() { loading.removeClass('hidden'); $('#annotation_type_id').val(gAnnotationType); - displayImage(imageId); + let loadImage = displayImage(imageId).then(scrollImageList); + if (!$('#keep_selection').prop('checked')) { $('#concealed').prop('checked', false); $('#blurred').prop('checked', false); } - scrollImageList(); $('.annotate_image_link').removeClass('active'); let link = $('#annotate_image_link_' + imageId); @@ -881,6 +882,7 @@ function calculateImageScale() { } catch { console.log("Unable to load annotations for image" + imageId); } + await loadImage; } /** @@ -912,7 +914,7 @@ function calculateImageScale() { displayFeedback($('#feedback_image_set_empty')); filterElem.val('').change(); } else { - displayImageList(data.image_set.images); + await displayImageList(data.image_set.images); } } catch { displayFeedback($('#feedback_connection_error')); @@ -926,7 +928,7 @@ function calculateImageScale() { * * @param imageId */ - function loadImageToCache(imageId) { + async function loadImageToCache(imageId) { imageId = parseInt(imageId); if (gImageList.indexOf(imageId) === -1) { @@ -1001,14 +1003,16 @@ function calculateImageScale() { /** * Preload next and previous images to cache. */ - function preloadImages() { + async function preloadImages() { let keepImages = []; + let cacheLoadings = []; for (let imageId = gImageId - PRELOAD_BACKWARD; imageId <= gImageId + PRELOAD_FORWARD; imageId++) { keepImages.push(imageId); - loadImageToCache(imageId); + cacheLoadings.push(loadImageToCache(imageId)); } + await Promise.all(cacheLoadings); pruneImageCache(keepImages); } @@ -1105,7 +1109,7 @@ function calculateImageScale() { } - $(function() { + $(document).ready(function() { let get_params = decodeURIComponent(window.location.search.substring(1)).split('&'); let editAnnotationId = undefined; for (let i = 0; i < get_params.length; i++) { @@ -1129,9 +1133,9 @@ function calculateImageScale() { "X-CSRFTOKEN": gCsrfToken }; gImageList = getImageList(); + scrollImageList(); loadAnnotationTypeList(); preloadImages(); - scrollImageList(); // W3C standards do not define the load event on images, we therefore need to use // it from window (this should wait for all external sources including images) From 7e3d5e592a4e5e20921b97f5d60c2dc5ba29c7f7 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 12:45:50 +0200 Subject: [PATCH 13/31] execute annotation creation and loading of next image in parallel --- .../static/annotations/js/annotations.js | 84 +++++++++++++------ 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index f0f78501..d75cd9a1 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -140,12 +140,11 @@ function calculateImageScale() { } /** - * Create an annotation using the form data from the current page. - * If an annotation is currently edited, an update is triggered instead. - * + * Check whether the current state allows to create an annotation (i.e. a valid selection was made) * @param markForRestore if the annotation should be saved to be reused for the next image + * @return the valid vector or null */ - async function createAnnotation(markForRestore) { + function getValidAnnotation(markForRestore) { let annotationTypeId = parseInt($('#annotation_type_id').val()); let vector = null; @@ -188,19 +187,33 @@ function calculateImageScale() { globals.restoreSelectionNodeCount = node_count; } - let action = 'create'; - let body = { + let annotation = { annotation_type_id: annotationTypeId, image_id: gImageId, vector: vector, concealed: concealed, blurred: blurred }; - let editing = false; if (gEditAnnotationId !== undefined) { + annotation.annotation_id = gEditAnnotationId; + } + return annotation; + } + + /** + * Create an annotation using the form data from the current page. + * If an annotation is currently edited, an update is triggered instead. + * + * @param annotation the annotation to be created, as returned by getValidAnnotation + */ + async function createAnnotation(annotation) { + + let action = 'create'; + let body = annotation; + let editing = false; + if (body.annotation_id !== undefined) { // edit instead of create action = 'update'; - body.annotation_id = gEditAnnotationId; editing = true; } @@ -1163,40 +1176,59 @@ function calculateImageScale() { $('#cancel_edit_button').click(function() { resetSelection(); }); - $('#save_button').click(function (event) { + $('#save_button').click(async function (event) { event.preventDefault(); - createAnnotation(); + try { + let annotation = getValidAnnotation() + await createAnnotation(annotation); + } catch (e) { + console.log(e); + } }); $('#reset_button').click(function() { resetSelection(); }); - $('#last_button').click(async function(event) { - await createAnnotation(true); - if (tool instanceof BoundingBoxes) { - tool.cancelSelection(); + $('#last_button').click(async function (event) { + try { + let annotation = getValidAnnotation(true); + let annotationPromise = createAnnotation(annotation); + let imagePromise = loadImageList().then(r => { + if (tool instanceof BoundingBoxes) { + tool.cancelSelection(); + } + return loadAdjacentImage(-1); + }) + await Promise.all([annotationPromise, imagePromise]); + } catch (e) { + console.log(e); } - await loadImageList(); - await loadAdjacentImage(-1); }); - $('#back_button').click(async function(event) { + $('#back_button').click(async function (event) { if (tool instanceof BoundingBoxes) { - tool.cancelSelection(); + tool.cancelSelection(); } await loadAdjacentImage(-1); }); - $('#skip_button').click(async function(event) { + $('#skip_button').click(async function (event) { if (tool instanceof BoundingBoxes) { - tool.cancelSelection(); + tool.cancelSelection(); } await loadAdjacentImage(1) }); - $('#next_button').click(async function(event) { - await createAnnotation(true); - if (tool instanceof BoundingBoxes) { - tool.cancelSelection(); + $('#next_button').click(async function (event) { + try { + let annotation = getValidAnnotation(true); + let annotationPromise = createAnnotation(annotation); + let imagePromise = loadImageList().then(r => { + if (tool instanceof BoundingBoxes) { + tool.cancelSelection(); + } + return loadAdjacentImage(1); + }) + await Promise.all([annotationPromise, imagePromise]); + } catch (e) { + console.log(e); } - await loadImageList(); - await loadAdjacentImage(1); }); $('.js_feedback').mouseover(function() { $(this).addClass('hidden'); From 4faf28fb4c0c6b8d929c697d2ffe4e1be01a525e Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 13:09:33 +0200 Subject: [PATCH 14/31] move cancelSelection for bounding boxes to the correct place Closes #175. --- .../static/annotations/js/annotations.js | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index d75cd9a1..8ae1d7ac 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -854,6 +854,9 @@ function calculateImageScale() { loading.removeClass('hidden'); $('#annotation_type_id').val(gAnnotationType); + if (tool instanceof BoundingBoxes) { + tool.cancelSelection(); + } let loadImage = displayImage(imageId).then(scrollImageList); if (!$('#keep_selection').prop('checked')) { @@ -1193,9 +1196,6 @@ function calculateImageScale() { let annotation = getValidAnnotation(true); let annotationPromise = createAnnotation(annotation); let imagePromise = loadImageList().then(r => { - if (tool instanceof BoundingBoxes) { - tool.cancelSelection(); - } return loadAdjacentImage(-1); }) await Promise.all([annotationPromise, imagePromise]); @@ -1204,15 +1204,9 @@ function calculateImageScale() { } }); $('#back_button').click(async function (event) { - if (tool instanceof BoundingBoxes) { - tool.cancelSelection(); - } await loadAdjacentImage(-1); }); $('#skip_button').click(async function (event) { - if (tool instanceof BoundingBoxes) { - tool.cancelSelection(); - } await loadAdjacentImage(1) }); $('#next_button').click(async function (event) { @@ -1220,9 +1214,6 @@ function calculateImageScale() { let annotation = getValidAnnotation(true); let annotationPromise = createAnnotation(annotation); let imagePromise = loadImageList().then(r => { - if (tool instanceof BoundingBoxes) { - tool.cancelSelection(); - } return loadAdjacentImage(1); }) await Promise.all([annotationPromise, imagePromise]); From 260278b68d1f26d25894a9d4c238e4f4a3a3da74 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 13:32:42 +0200 Subject: [PATCH 15/31] call loadAnnotateView before preloadImages --- .../annotations/static/annotations/js/annotations.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index 8ae1d7ac..50c35488 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -61,12 +61,6 @@ function calculateImageScale() { } } - function initTool() { - setTool(); - tool.initSelection(); - loadAnnotateView(gImageId); - } - function setTool() { let selected_annotation = $('#annotation_type_id').children(':selected').data(); let vector_type = selected_annotation.vectorType; @@ -1151,12 +1145,14 @@ function calculateImageScale() { gImageList = getImageList(); scrollImageList(); loadAnnotationTypeList(); - preloadImages(); // W3C standards do not define the load event on images, we therefore need to use // it from window (this should wait for all external sources including images) $(window).on('load', function() { - initTool(); + setTool(); + tool.initSelection(); + loadAnnotateView(gImageId); + preloadImages(); }()); $('.annotation_value').on('input', function() { From 6ab2279536fd709b47c21ef2de0670a98399e976 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 15:08:52 +0200 Subject: [PATCH 16/31] cleanup the mess of resetting/reloading the tool --- .../static/annotations/js/annotations.js | 43 +++++++------------ .../static/annotations/js/boundingboxes.js | 42 +++++++----------- .../static/annotations/js/canvas.js | 40 +++++++++-------- 3 files changed, 53 insertions(+), 72 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index 50c35488..a79181ad 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -72,8 +72,6 @@ function calculateImageScale() { } if (tool) { tool.resetSelection(); - tool.cancelSelection(); - tool.initSelection(); tool.reset(); delete tool; } @@ -104,11 +102,11 @@ function calculateImageScale() { default: // Dummytool tool = { - initSelection: function() {}, resetSelection: function() {}, restoreSelection: function() {}, cancelSelection: function() {}, reset: function() {}, + clear: function() {}, drawExistingAnnotations: function() {}, closeDrawing: function() {}, handleMousemove: function() {}, @@ -228,15 +226,12 @@ function calculateImageScale() { $('#annotation_edit_button_' + gEditAnnotationId).parent().parent().fadeOut().remove(); } else { displayFeedback($('#feedback_annotation_updated')); - displayExistingAnnotations(data.annotations); - tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType); } } else { displayFeedback($('#feedback_annotation_exists')); } } else if (response.status === 201) { displayFeedback($('#feedback_annotation_created')); - displayExistingAnnotations(data.annotations); } // update current annotations gCurrentAnnotations = data.annotations; @@ -244,9 +239,6 @@ function calculateImageScale() { return e.annotation_type.id === gAnnotationType; }); - tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType); - - resetSelection(); } catch (e) { console.log(e); displayFeedback($('#feedback_connection_error')); @@ -512,8 +504,6 @@ function calculateImageScale() { currentImage.replaceWith(newImage); globals.image = newImage; calculateImageScale(); - tool.initSelection(); - resetSelection(); if (currentImage.data('imageid') !== undefined) { // add previous image to cache @@ -559,6 +549,7 @@ function calculateImageScale() { link.data('imageid', image.id); link.click(async function(event) { event.preventDefault(); // do not let the browser follow the link + globals.restoreSelection = undefined; await loadAnnotateView($(this).data('imageid')); }); @@ -611,9 +602,6 @@ function calculateImageScale() { $('#edit_active').removeClass('hidden'); $('.js_feedback').stop().addClass('hidden'); - let params = { - annotation_id: annotationId - }; let annotationData = annotationElem.data('vector'); if (annotationData === undefined) { @@ -635,7 +623,6 @@ function calculateImageScale() { notInImage.prop('checked', false).change(); - tool.reloadSelection(annotationId, annotationData); $('#concealed').prop('checked', annotationElem.data('concealed')).change(); $('#blurred').prop('checked', annotationElem.data('blurred')).change(); @@ -848,9 +835,6 @@ function calculateImageScale() { loading.removeClass('hidden'); $('#annotation_type_id').val(gAnnotationType); - if (tool instanceof BoundingBoxes) { - tool.cancelSelection(); - } let loadImage = displayImage(imageId).then(scrollImageList); if (!$('#keep_selection').prop('checked')) { @@ -868,7 +852,13 @@ function calculateImageScale() { } $('#next-image-id').attr('value', next_image_id || ''); $('#delete-image-form').attr('action', DELETE_IMAGE_URL.replace('%s', imageId)); - tool.restoreSelection(false); + + tool.clear(); + if (globals.restoreSelection !== undefined) { + tool.restoreSelection(); + } else { + resetSelection(); + } if (fromHistory !== true) { history.pushState({ @@ -883,12 +873,6 @@ function calculateImageScale() { loading.addClass('hidden'); displayExistingAnnotations(gCurrentAnnotations); tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType); - - if (globals.restoreSelection !== undefined) { - tool.restoreSelection(); - } else { - resetSelection(); - } } catch { console.log("Unable to load annotations for image" + imageId); } @@ -1150,7 +1134,6 @@ function calculateImageScale() { // it from window (this should wait for all external sources including images) $(window).on('load', function() { setTool(); - tool.initSelection(); loadAnnotateView(gImageId); preloadImages(); }()); @@ -1178,8 +1161,13 @@ function calculateImageScale() { $('#save_button').click(async function (event) { event.preventDefault(); try { - let annotation = getValidAnnotation() + let annotation = getValidAnnotation(); + globals.restoreSelection = undefined; + tool.clear(); + resetSelection(); await createAnnotation(annotation); + tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType); + displayExistingAnnotations(gCurrentAnnotations); } catch (e) { console.log(e); } @@ -1222,6 +1210,7 @@ function calculateImageScale() { }); $('.annotate_image_link').click(async function(event) { event.preventDefault(); // do not let the browser load the link + globals.restoreSelection = undefined; await loadAnnotateView($(this).data('imageid')); }); diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js b/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js index 475f9102..d7bf80df 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js @@ -1,20 +1,17 @@ // JS file for bounding box internals class BoundingBoxes { - constructor(annotationTypeId, noSelection) { + constructor(annotationTypeId) { this.selection = undefined; this.vector_type = 1; if (globals.image === '') { globals.image = $('#image'); } this.annotationTypeId = annotationTypeId; - if (!noSelection) { - this.initSelection(); - } + this.resetSelection(); } drawExistingAnnotations(annotations, color) { - this.clear(); calculateImageScale(); color = color || globals.stdColor; let colors = []; @@ -93,21 +90,8 @@ class BoundingBoxes { while (boundingBoxes.firstChild) { boundingBoxes.removeChild(boundingBoxes.firstChild); } - } - /** - * Initialize the selection. - */ - initSelection() { - this.selection = globals.image.imgAreaSelect({ - instance: true, - show: true, - minHeight: 2, - minWidth: 2, - onSelectChange: this.updateAnnotationFields, - resizeMargin: 3 - }); - this.selection.cancelSelection(); + this.cancelSelection(); } /** @@ -117,7 +101,8 @@ class BoundingBoxes { this.setHighlightColor(annotationId); this.selection = globals.image.imgAreaSelect({ instance: true, - show: true + show: true, + onSelectChange: this.updateAnnotationFields, }); if (!annotationData) { annotationData = { @@ -150,15 +135,21 @@ class BoundingBoxes { this.unsetHighlightColor(); $('.annotation_value').val(0); - if (this.selection !== undefined) { - this.selection.cancelSelection(); - } + this.selection = globals.image.imgAreaSelect({ + instance: true, + show: true, + minHeight: 2, + minWidth: 2, + onSelectChange: this.updateAnnotationFields, + resizeMargin: 3 + }); + this.selection.cancelSelection(); } /** * Restore the selection. */ - restoreSelection(reset) { + restoreSelection() { if (!$('#keep_selection').prop('checked')) { return; } @@ -174,9 +165,6 @@ class BoundingBoxes { this.reloadSelection(0, globals.restoreSelection); } } - if (reset !== false) { - globals.restoreSelection = undefined; - } } moveSelectionUp() { diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js b/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js index e440f81a..54af972c 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js @@ -371,6 +371,11 @@ class Canvas { for (let d of this.drawings) { d.remove(); } + this.unsetAnnotationFields(); + this.currentDrawing = undefined; + } + + unsetAnnotationFields() { switch (this.vector_type) { case 2: // Point this.updateAnnotationFields({x1: 0, y1: 0}); @@ -425,7 +430,8 @@ class Canvas { } drawExistingAnnotations(annotations, color) { - this.clear(); + let currentDrawing = this.currentDrawing; + this.currentDrawing = undefined; color = color || globals.stdColor; let colors = []; if (color.constructor === Array) { @@ -445,8 +451,6 @@ class Canvas { for (let i in annotations) { let annotation = annotations[i]; let color = colors[i]; - console.log(annotation); - console.log(color); if (annotation.annotation_type.id !== this.annotationTypeId) { continue; } @@ -485,7 +489,14 @@ class Canvas { console.log("Unknown vector type: " + annotation.annotation_type.vector_type); } } - this.currentDrawing = undefined; + // completely restore the drawing from before drawing the existing annotations, + // especially the annotation fields are overwritten during the drawing + this.currentDrawing = currentDrawing; + if (currentDrawing) { + this.reloadSelection(currentDrawing.id); + } else { + this.unsetAnnotationFields(); + } } updateAnnotationFields(drawing) { @@ -545,24 +556,22 @@ class Canvas { drawing.setMutable(false); } if (this.old && this.currentDrawing) { - this.currentDrawing.setPoints(this.old); - this.old = undefined; - } else if (this.currentDrawing) { - this.currentDrawing.remove(); - } - this.currentDrawing = undefined; + this.currentDrawing.setPoints(this.old); + this.old = undefined; + } else if (this.currentDrawing) { + this.currentDrawing.remove(); + } + this.currentDrawing = undefined; } cancelSelection() { this.resetSelection(true) } - initSelection() { } - /** * Restore the selection. */ - restoreSelection(reset) { + restoreSelection() { if (!$('#keep_selection').prop('checked')) { return; } @@ -606,11 +615,6 @@ class Canvas { this.old = undefined; } } - if (reset !== false) { - globals.restoreSelection = undefined; - globals.restoreSelectionNodeCount = 0; - globals.restoreSelectionVectorType = 1; - } } setHighlightColor(id) { From 28eea089ed21700a8613335ebe2961000bae7d6a Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 17:19:17 +0200 Subject: [PATCH 17/31] clean up some deep and confusing call stacks --- .../static/annotations/js/annotations.js | 101 +++++++++--------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index a79181ad..75fdcb75 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -560,14 +560,7 @@ function calculateImageScale() { result.attr('id', 'image_list'); oldImageList.replaceWith(result); - gImageList = getImageList(); - - // load first image if current image is not within image set - if (!imageContained) { - await loadAnnotateView(imageList[0].id); - } - - scrollImageList(); + gImageList = imageList.map(image => { return image.id }); } /** @@ -628,21 +621,6 @@ function calculateImageScale() { $('#blurred').prop('checked', annotationElem.data('blurred')).change(); } - /** - * Get the image list from all .annotate_image_link within #image_list. - */ - function getImageList() { - let imageList = []; - $('#image_list').find('.annotate_image_link').each(function(key, elem) { - let imageId = parseInt($(elem).data('imageid')); - if (imageList.indexOf(imageId) === -1) { - imageList.push(imageId); - } - }); - - return imageList; - } - /** * Validate a vector. * @@ -816,6 +794,11 @@ function calculateImageScale() { * @param fromHistory */ async function loadAnnotateView(imageId, fromHistory) { + // load first image if current image is not within image set + if (!gImageList.includes(imageId)) { + console.log('Loading first image because ' + imageId + ' is not contained in the selection'); + imageId = gImageList[0]; + } gEditAnnotationId = undefined; imageId = parseInt(imageId); @@ -835,7 +818,7 @@ function calculateImageScale() { loading.removeClass('hidden'); $('#annotation_type_id').val(gAnnotationType); - let loadImage = displayImage(imageId).then(scrollImageList); + let loadImage = displayImage(imageId); if (!$('#keep_selection').prop('checked')) { $('#concealed').prop('checked', false); @@ -880,9 +863,9 @@ function calculateImageScale() { } /** - * Load the image list from tye server applying a new filter. + * Get the image list from the server applying a new filter. */ - async function loadImageList() { + async function getImageList() { let filterElem = $('#filter_annotation_type'); let filter = filterElem.val(); let params = { @@ -908,7 +891,7 @@ function calculateImageScale() { displayFeedback($('#feedback_image_set_empty')); filterElem.val('').change(); } else { - await displayImageList(data.image_set.images); + return data.image_set.images; } } catch { displayFeedback($('#feedback_connection_error')); @@ -976,7 +959,7 @@ function calculateImageScale() { * Load the previous or the next image * * @param offset integer to add to the current image index - * @return index of the requested image in gImageList + * @return id of the requested image */ async function loadAdjacentImage(offset) { let imageIndex = gImageList.indexOf(gImageId); @@ -991,7 +974,8 @@ function calculateImageScale() { while (imageIndex > imageIndex.length) { imageIndex -= imageIndex.length; } - await loadAnnotateView(gImageList[imageIndex]); + gImageId = gImageList[imageIndex]; + return gImageList[imageIndex]; } /** @@ -1026,9 +1010,10 @@ function calculateImageScale() { /** * Scroll image list to make current image visible. + * @param imageId id of the image to scroll to */ - function scrollImageList() { - let imageLink = $('#annotate_image_link_' + gImageId); + function scrollImageList(imageId) { + let imageLink = $('#annotate_image_link_' + imageId); let list = $('#image_list'); let offset = list.offset().top; @@ -1102,6 +1087,17 @@ function calculateImageScale() { handleAnnotationTypeChange(); } + async function updateFilter() { + handleAnnotationTypeChange(); + let imageList = await getImageList(); + await displayImageList(imageList); + if (!gImageList.includes(gImageId)) { + gImageId = gImageList[0]; + } + scrollImageList(gImageId); + await loadAnnotateView(gImageId); + } + $(document).ready(function() { let get_params = decodeURIComponent(window.location.search.substring(1)).split('&'); @@ -1126,16 +1122,17 @@ function calculateImageScale() { "Content-Type": 'application/json', "X-CSRFTOKEN": gCsrfToken }; - gImageList = getImageList(); - scrollImageList(); - loadAnnotationTypeList(); // W3C standards do not define the load event on images, we therefore need to use // it from window (this should wait for all external sources including images) $(window).on('load', function() { setTool(); - loadAnnotateView(gImageId); - preloadImages(); + getImageList() + .then(displayImageList) + .then(r => { scrollImageList(gImageId) }) + .then(r => { loadAnnotateView(gImageId) }) + .then(r => { preloadImages() }); + loadAnnotationTypeList(); }()); $('.annotation_value').on('input', function() { @@ -1143,8 +1140,8 @@ function calculateImageScale() { }); $('#not_in_image').on('change', handleNotInImageToggle); handleNotInImageToggle(); - $('select#filter_annotation_type').on('change', loadImageList); - $('#filter_update_btn').on('click', loadImageList); + $('select#filter_annotation_type').on('change', updateFilter); + $('#filter_update_btn').on('click', updateFilter); $('select').on('change', function() { document.activeElement.blur(); }); @@ -1178,29 +1175,33 @@ function calculateImageScale() { $('#last_button').click(async function (event) { try { let annotation = getValidAnnotation(true); - let annotationPromise = createAnnotation(annotation); - let imagePromise = loadImageList().then(r => { - return loadAdjacentImage(-1); - }) - await Promise.all([annotationPromise, imagePromise]); + await createAnnotation(annotation); + let imageId = await loadAdjacentImage(-1); + let imageList = await getImageList(); + await displayImageList(imageList); + scrollImageList(imageId); + await loadAnnotateView(imageId); } catch (e) { console.log(e); } }); $('#back_button').click(async function (event) { - await loadAdjacentImage(-1); + let imageId = await loadAdjacentImage(-1); + await loadAnnotateView(imageId); }); $('#skip_button').click(async function (event) { - await loadAdjacentImage(1) + let imageId = await loadAdjacentImage(1); + await loadAnnotateView(imageId); }); $('#next_button').click(async function (event) { try { let annotation = getValidAnnotation(true); - let annotationPromise = createAnnotation(annotation); - let imagePromise = loadImageList().then(r => { - return loadAdjacentImage(1); - }) - await Promise.all([annotationPromise, imagePromise]); + await createAnnotation(annotation); + let imageId = await loadAdjacentImage(1); + let imageList = await getImageList(); + await displayImageList(imageList); + scrollImageList(imageId); + await loadAnnotateView(imageId); } catch (e) { console.log(e); } From 4e3ff438d2b0b78f177933e17a6517cad5eb9a89 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 17:19:31 +0200 Subject: [PATCH 18/31] improve logic of image cache --- .../static/annotations/js/annotations.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index 75fdcb75..c01d5087 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -984,11 +984,14 @@ function calculateImageScale() { async function preloadImages() { let keepImages = []; let cacheLoadings = []; - for (let imageId = gImageId - PRELOAD_BACKWARD; - imageId <= gImageId + PRELOAD_FORWARD; - imageId++) { - keepImages.push(imageId); - cacheLoadings.push(loadImageToCache(imageId)); + let currentImageIndex = gImageList.indexOf(gImageId); + for (let index = currentImageIndex - PRELOAD_BACKWARD; + index <= currentImageIndex + PRELOAD_FORWARD; + index++) { + if (gImageList[index] !== undefined) { + keepImages.push(gImageList[index]); + cacheLoadings.push(loadImageToCache(gImageList[index])); + } } await Promise.all(cacheLoadings); pruneImageCache(keepImages); From c75ebb259049ff77bd910b37df47987613b15eea Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 17:17:41 +0200 Subject: [PATCH 19/31] fix some comments --- .../annotations/static/annotations/js/annotations.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index c01d5087..d7ec5251 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -482,7 +482,7 @@ function calculateImageScale() { if (gImageList.indexOf(imageId) === -1) { console.log( - 'skiping request to load image ' + imageId + + 'skipping request to display image ' + imageId + ' as it is not in current image list.'); return; } @@ -805,7 +805,7 @@ function calculateImageScale() { if (gImageList.indexOf(imageId) === -1) { console.log( - 'skiping request to load image ' + imageId + + 'skipping request to load annotation view for image ' + imageId + ' as it is not in current image list.'); return; } @@ -910,8 +910,8 @@ function calculateImageScale() { if (gImageList.indexOf(imageId) === -1) { console.log( - 'skiping request to load image ' + imageId + - ' as it is not in current image list.'); + 'skipping request to load image ' + imageId + + ' to cache as it is not in current image list.'); return; } From 35248c77d90b3add7f9ac6c57ab6efd851e67b1e Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 19:32:34 +0200 Subject: [PATCH 20/31] new function changeImage --- .../static/annotations/js/annotations.js | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index d7ec5251..34b7a558 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -550,7 +550,7 @@ function calculateImageScale() { link.click(async function(event) { event.preventDefault(); // do not let the browser follow the link globals.restoreSelection = undefined; - await loadAnnotateView($(this).data('imageid')); + await changeImage($(this).data('imageid'), false); }); result.append(link); @@ -1101,6 +1101,22 @@ function calculateImageScale() { await loadAnnotateView(gImageId); } + /** + * Change the image that is currently shown, i.e. update the image list, the displayed image and the annotation view + * @param imageId the id of the image to change to + * @param keepSelection whether the current selection should be kept on the next image + */ + async function changeImage(imageId, keepSelection) { + if (!keepSelection) { + // unset restoreSelection to avoid restoring a selection that we do not want to keep + globals.restoreSelection = undefined; + } + let imageList = await getImageList(); + await displayImageList(imageList); + scrollImageList(imageId); + await loadAnnotateView(imageId); + } + $(document).ready(function() { let get_params = decodeURIComponent(window.location.search.substring(1)).split('&'); @@ -1180,31 +1196,27 @@ function calculateImageScale() { let annotation = getValidAnnotation(true); await createAnnotation(annotation); let imageId = await loadAdjacentImage(-1); - let imageList = await getImageList(); - await displayImageList(imageList); - scrollImageList(imageId); - await loadAnnotateView(imageId); + let keepSelection = $('#keep_selection').prop('checked'); + await changeImage(imageId, keepSelection); } catch (e) { console.log(e); } }); $('#back_button').click(async function (event) { let imageId = await loadAdjacentImage(-1); - await loadAnnotateView(imageId); + await changeImage(imageId, false); }); $('#skip_button').click(async function (event) { let imageId = await loadAdjacentImage(1); - await loadAnnotateView(imageId); + await changeImage(imageId, false) }); $('#next_button').click(async function (event) { try { let annotation = getValidAnnotation(true); await createAnnotation(annotation); let imageId = await loadAdjacentImage(1); - let imageList = await getImageList(); - await displayImageList(imageList); - scrollImageList(imageId); - await loadAnnotateView(imageId); + let keepSelection = $('#keep_selection').prop('checked') + await changeImage(imageId, keepSelection); } catch (e) { console.log(e); } @@ -1215,7 +1227,7 @@ function calculateImageScale() { $('.annotate_image_link').click(async function(event) { event.preventDefault(); // do not let the browser load the link globals.restoreSelection = undefined; - await loadAnnotateView($(this).data('imageid')); + await changeImage($(this).data('imageid'), false); }); // annotation buttons From f6c8c77b54b9ba31d4e5155a03f3ed75314b7c58 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 30 Mar 2021 20:49:57 +0200 Subject: [PATCH 21/31] cleanup setting of blurred/concealed check boxes --- .../static/annotations/js/annotations.js | 15 +++++++++------ .../static/annotations/js/boundingboxes.js | 5 ----- .../annotations/static/annotations/js/canvas.js | 15 --------------- 3 files changed, 9 insertions(+), 26 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index 34b7a558..0d1739d4 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -520,6 +520,8 @@ function calculateImageScale() { gEditAnnotationId = undefined; $('.annotation').removeClass('alert-info'); $('#edit_active').addClass('hidden'); + $('#concealed').prop('checked', false); + $('#blurred').prop('checked', false); } /** @@ -820,11 +822,6 @@ function calculateImageScale() { let loadImage = displayImage(imageId); - if (!$('#keep_selection').prop('checked')) { - $('#concealed').prop('checked', false); - $('#blurred').prop('checked', false); - } - $('.annotate_image_link').removeClass('active'); let link = $('#annotate_image_link_' + imageId); link.addClass('active'); @@ -837,8 +834,14 @@ function calculateImageScale() { $('#delete-image-form').attr('action', DELETE_IMAGE_URL.replace('%s', imageId)); tool.clear(); - if (globals.restoreSelection !== undefined) { + if (globals.restoreSelection !== undefined && $('#keep_selection').prop('checked')) { tool.restoreSelection(); + if (globals.restoreSelection === null) { + // not in image + $('#not_in_image').prop('checked', true); + $('#coordinate_table').hide(); + setupCBCheckboxes(); + } } else { resetSelection(); } diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js b/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js index d7bf80df..ee1f0743 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js @@ -150,13 +150,8 @@ class BoundingBoxes { * Restore the selection. */ restoreSelection() { - if (!$('#keep_selection').prop('checked')) { - return; - } if (globals.restoreSelection !== undefined) { if (globals.restoreSelection === null) { - $('#not_in_image').prop('checked', true); - $('#coordinate_table').hide(); } else { $('#x1Field').val(globals.restoreSelection.x1); $('#x2Field').val(globals.restoreSelection.x2); diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js b/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js index 54af972c..d3e29ace 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js @@ -572,23 +572,8 @@ class Canvas { * Restore the selection. */ restoreSelection() { - if (!$('#keep_selection').prop('checked')) { - return; - } if (globals.restoreSelection !== undefined) { if (globals.restoreSelection === null) { - $('#not_in_image').prop('checked', true); - $('#coordinate_table').hide(); - let concealed = $('#concealed'); - let concealedP = $('#concealed_p'); - let blurred = $('#blurred'); - let blurredP = $('#blurred_p'); - concealedP.hide(); - concealed.prop('checked', false); - concealed.prop('disabled', true); - blurredP.hide(); - blurred.prop('checked', false); - blurred.prop('disabled', true); } else if (globals.restoreSelectionVectorType === 4) { // this is not implemented for multilines, so just do nothing this.old = undefined; From c542ad03751e5dd6788a8673c9d42019e9e15af5 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Wed, 31 Mar 2021 23:05:28 +0200 Subject: [PATCH 22/31] do not keep every image in the browser history --- .../static/annotations/js/annotations.js | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index 0d1739d4..2effe32d 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -793,9 +793,8 @@ function calculateImageScale() { * Load the annotation view for another image. * * @param imageId - * @param fromHistory */ - async function loadAnnotateView(imageId, fromHistory) { + async function loadAnnotateView(imageId) { // load first image if current image is not within image set if (!gImageList.includes(imageId)) { console.log('Loading first image because ' + imageId + ' is not contained in the selection'); @@ -846,12 +845,6 @@ function calculateImageScale() { resetSelection(); } - if (fromHistory !== true) { - history.pushState({ - imageId: imageId - }, document.title, '/annotations/' + imageId + '/'); - } - try { // load existing annotations for this image await loadAnnotations(imageId); @@ -1110,6 +1103,7 @@ function calculateImageScale() { * @param keepSelection whether the current selection should be kept on the next image */ async function changeImage(imageId, keepSelection) { + window.history.replaceState(null, document.title, ANNOTATE_URL.replace('%s', imageId)); if (!keepSelection) { // unset restoreSelection to avoid restoring a selection that we do not want to keep globals.restoreSelection = undefined; @@ -1250,11 +1244,6 @@ function calculateImageScale() { $(document).on('mousemove touchmove', handleSelection); $(window).on('resize', handleResize); - window.onpopstate = async function(event) { - if (event.state !== undefined && event.state !== null && event.state.imageId !== undefined) { - await loadAnnotateView(event.state.imageId, true); - } - }; // attach listeners for mouse events $(document).on('mousedown.annotation_edit', handleMouseDown); From 19e0311d3558e53fd460e8e7e57fab2f6046821b Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Wed, 31 Mar 2021 23:09:09 +0200 Subject: [PATCH 23/31] remove restoreSelection from global variables --- .../static/annotations/js/annotations.js | 52 +++++++----------- .../static/annotations/js/boundingboxes.js | 20 +++---- .../static/annotations/js/canvas.js | 55 +++++++++++-------- 3 files changed, 62 insertions(+), 65 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index 2effe32d..e136c03c 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -2,9 +2,6 @@ globals = { image: {}, imageScaleWidth: 1, imageScaleHeight: 1, - restoreSelection: undefined, - restoreSelectionVectorType: 1, - restoreSelectionNodeCount: 0, moveSelectionStepSize: 2, drawAnnotations: true, mouseUpX: undefined, @@ -173,12 +170,6 @@ function calculateImageScale() { throw new Error('annotation is invalid'); } - if (markForRestore === true) { - globals.restoreSelection = vector; - globals.restoreSelectionVectorType = vector_type; - globals.restoreSelectionNodeCount = node_count; - } - let annotation = { annotation_type_id: annotationTypeId, image_id: gImageId, @@ -551,8 +542,7 @@ function calculateImageScale() { link.data('imageid', image.id); link.click(async function(event) { event.preventDefault(); // do not let the browser follow the link - globals.restoreSelection = undefined; - await changeImage($(this).data('imageid'), false); + await changeImage($(this).data('imageid')); }); result.append(link); @@ -793,8 +783,9 @@ function calculateImageScale() { * Load the annotation view for another image. * * @param imageId + * @param restoreAnnotation an annotation to restore */ - async function loadAnnotateView(imageId) { + async function loadAnnotateView(imageId, restoreAnnotation) { // load first image if current image is not within image set if (!gImageList.includes(imageId)) { console.log('Loading first image because ' + imageId + ' is not contained in the selection'); @@ -833,9 +824,16 @@ function calculateImageScale() { $('#delete-image-form').attr('action', DELETE_IMAGE_URL.replace('%s', imageId)); tool.clear(); - if (globals.restoreSelection !== undefined && $('#keep_selection').prop('checked')) { - tool.restoreSelection(); - if (globals.restoreSelection === null) { + if ($('#keep_selection').prop('checked') && restoreAnnotation) { + let annotation_type = $('#annotation_type_id').children('[value=' + restoreAnnotation.annotation_type_id + ']').data(); + let vector_type = annotation_type.vectorType; + let node_count = annotation_type.nodeCount; + tool.restoreSelection({ + vector_type: vector_type, + node_count: node_count, + vector: restoreAnnotation.vector, + }); + if (restoreAnnotation.vector === null) { // not in image $('#not_in_image').prop('checked', true); $('#coordinate_table').hide(); @@ -1100,18 +1098,14 @@ function calculateImageScale() { /** * Change the image that is currently shown, i.e. update the image list, the displayed image and the annotation view * @param imageId the id of the image to change to - * @param keepSelection whether the current selection should be kept on the next image + * @param annotation the annotation to restore at the next image if the option is selected. undefined for no restoring */ - async function changeImage(imageId, keepSelection) { + async function changeImage(imageId, annotation) { window.history.replaceState(null, document.title, ANNOTATE_URL.replace('%s', imageId)); - if (!keepSelection) { - // unset restoreSelection to avoid restoring a selection that we do not want to keep - globals.restoreSelection = undefined; - } let imageList = await getImageList(); await displayImageList(imageList); scrollImageList(imageId); - await loadAnnotateView(imageId); + await loadAnnotateView(imageId, annotation); } @@ -1175,7 +1169,6 @@ function calculateImageScale() { event.preventDefault(); try { let annotation = getValidAnnotation(); - globals.restoreSelection = undefined; tool.clear(); resetSelection(); await createAnnotation(annotation); @@ -1193,27 +1186,25 @@ function calculateImageScale() { let annotation = getValidAnnotation(true); await createAnnotation(annotation); let imageId = await loadAdjacentImage(-1); - let keepSelection = $('#keep_selection').prop('checked'); - await changeImage(imageId, keepSelection); + await changeImage(imageId, annotation); } catch (e) { console.log(e); } }); $('#back_button').click(async function (event) { let imageId = await loadAdjacentImage(-1); - await changeImage(imageId, false); + await changeImage(imageId); }); $('#skip_button').click(async function (event) { let imageId = await loadAdjacentImage(1); - await changeImage(imageId, false) + await changeImage(imageId); }); $('#next_button').click(async function (event) { try { let annotation = getValidAnnotation(true); await createAnnotation(annotation); let imageId = await loadAdjacentImage(1); - let keepSelection = $('#keep_selection').prop('checked') - await changeImage(imageId, keepSelection); + await changeImage(imageId, annotation); } catch (e) { console.log(e); } @@ -1223,8 +1214,7 @@ function calculateImageScale() { }); $('.annotate_image_link').click(async function(event) { event.preventDefault(); // do not let the browser load the link - globals.restoreSelection = undefined; - await changeImage($(this).data('imageid'), false); + await changeImage($(this).data('imageid')); }); // annotation buttons diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js b/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js index ee1f0743..7cfb20b8 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js @@ -148,17 +148,17 @@ class BoundingBoxes { /** * Restore the selection. + * @param selection the selection to restore, object containing the vector */ - restoreSelection() { - if (globals.restoreSelection !== undefined) { - if (globals.restoreSelection === null) { - } else { - $('#x1Field').val(globals.restoreSelection.x1); - $('#x2Field').val(globals.restoreSelection.x2); - $('#y1Field').val(globals.restoreSelection.y1); - $('#y2Field').val(globals.restoreSelection.y2); - this.reloadSelection(0, globals.restoreSelection); - } + restoreSelection(selection) { + let vector = selection.vector; + if (vector === null) { + } else { + $('#x1Field').val(vector.x1); + $('#x2Field').val(vector.x2); + $('#y1Field').val(vector.y1); + $('#y2Field').val(vector.y2); + this.reloadSelection(0, vector); } } diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js b/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js index d3e29ace..3327a9cc 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js @@ -570,35 +570,42 @@ class Canvas { /** * Restore the selection. + * @param selection the selection to restore, object containing vector, vector_type and node_count */ - restoreSelection() { - if (globals.restoreSelection !== undefined) { - if (globals.restoreSelection === null) { - } else if (globals.restoreSelectionVectorType === 4) { - // this is not implemented for multilines, so just do nothing - this.old = undefined; - } else { - let vector = {}; - for (let key in globals.restoreSelection) { - if (key[0] === "y") { - vector[key] = globals.restoreSelection[key] / globals.imageScaleHeight; - } else { - vector[key] = globals.restoreSelection[key] / globals.imageScaleWidth; - } + restoreSelection(selection) { + let vector = selection.vector; + let vectorType = selection.vector_type; + let nodeCount = selection.node_count; + if (vector === null) { + } else if (vectorType === 4) { + // this is not implemented for multilines, so just do nothing + this.old = undefined; + } else { + let scaledVector = {}; + for (let key in vector) { + if (key[0] === "y") { + scaledVector[key] = vector[key] / globals.imageScaleHeight; + } else { + scaledVector[key] = vector[key] / globals.imageScaleWidth; } - this.updateAnnotationFields(globals.restoreSelection); - switch(globals.restoreSelectionVectorType) { - case 2: this.drawPoint(vector, 0, true); break; - case 3: this.drawLine(vector, 0, true); break; - case 5: if (globals.restoreSelectionNodeCount === 0) { - this.drawArbitraryPolygon(vector, 0, true, true); + } + this.updateAnnotationFields(vector); + switch (vectorType) { + case 2: + this.drawPoint(scaledVector, 0, true); + break; + case 3: + this.drawLine(scaledVector, 0, true); + break; + case 5: + if (nodeCount === 0) { + this.drawArbitraryPolygon(scaledVector, 0, true, true); } else { - this.drawPolygon(vector, 0, true, globals.restoreSelectionNodeCount, true); + this.drawPolygon(scaledVector, 0, true, nodeCount, true); } - } - this.reloadSelection(0); - this.old = undefined; } + this.reloadSelection(0); + this.old = undefined; } } From 2dcae963e9aba28e7983292fbf5f6ccfb6f036d1 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Wed, 31 Mar 2021 00:44:06 +0200 Subject: [PATCH 24/31] remove mouse event details from global variables --- .../static/annotations/js/annotations.js | 30 ++++++------------- .../static/annotations/js/boundingboxes.js | 8 ++--- .../static/annotations/js/canvas.js | 24 +++++++-------- 3 files changed, 25 insertions(+), 37 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index e136c03c..2df79312 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -4,12 +4,6 @@ globals = { imageScaleHeight: 1, moveSelectionStepSize: 2, drawAnnotations: true, - mouseUpX: undefined, - mouseUpY: undefined, - mouseClickX: undefined, - mouseClickY: undefined, - mouseDownX: undefined, - mouseDownY: undefined, currentAnnotationsOfSelectedType: undefined, stdColor: '#CC4444', mutColor: '#CC0000' @@ -418,18 +412,12 @@ function calculateImageScale() { existingAnnotations.removeClass('hidden'); } - /** - * Highlight one annotation in a different color - * @param annotationTypeId - * @param annotationId - */ - function handleMouseClick(e) { if (e && (e.target.id === 'image' || e.target.id === 'image_canvas')) { let position = globals.image.offset(); - globals.mouseClickX = Math.round((e.pageX - position.left)); - globals.mouseClickY = Math.round((e.pageY - position.top)); - tool.handleMouseClick(e); + let x = Math.round((e.pageX - position.left)); + let y = Math.round((e.pageY - position.top)); + tool.handleMouseClick(e, x, y); } // remove any existing highlight @@ -1047,9 +1035,9 @@ function calculateImageScale() { displayFeedback($('#feedback_annotation_type_missing')); return; } - globals.mouseDownX = Math.round((event.pageX - position.left) * globals.imageScaleWidth); - globals.mouseDownY = Math.round((event.pageY - position.top) * globals.imageScaleHeight); - tool.handleMouseDown(event); + let x = Math.round((event.pageX - position.left) * globals.imageScaleWidth); + let y = Math.round((event.pageY - position.top) * globals.imageScaleHeight); + tool.handleMouseDown(event, x, y); } } @@ -1058,12 +1046,12 @@ function calculateImageScale() { return; let position = globals.image.offset(); - globals.mouseUpX = Math.round((event.pageX - position.left)/* * globals.imageScaleWidth*/); - globals.mouseUpY = Math.round((event.pageY - position.top)/* * globals.imageScaleHeight*/); + let x = Math.round((event.pageX - position.left)/* * globals.imageScaleWidth*/); + let y = Math.round((event.pageY - position.top)/* * globals.imageScaleHeight*/); if (event.pageX > position.left && event.pageX < position.left + globals.image.width() && event.pageY > position.top && event.pageY < position.top + globals.image.height()) { - tool.handleMouseUp(event); + tool.handleMouseUp(event, x, y); } } diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js b/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js index 7cfb20b8..1b99154d 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/boundingboxes.js @@ -338,11 +338,11 @@ class BoundingBoxes { this.clear(); } - handleMouseDown(event) { } - handleMouseUp(event) { } + handleMouseDown() { } + handleMouseUp() { } closeDrawing() { } - handleMouseClick(event) { + handleMouseClick(event, x, y) { // get current annotation type id let annotationType = parseInt($('#annotation_type_id').val()); @@ -363,7 +363,7 @@ class BoundingBoxes { let bottom = annotation.vector.y2 / globals.imageScaleHeight; // check if we clicked inside that annotation - if (globals.mouseClickX >= left && globals.mouseClickX <= right && globals.mouseClickY >= top && globals.mouseClickY <= bottom) { + if (x >= left && x <= right && y >= top && y <= bottom) { matchingAnnotations.push(annotation); } } diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js b/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js index 3327a9cc..31041543 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js @@ -332,11 +332,11 @@ class Canvas { } } - mouseTooClose() { + mouseTooClose(x, y) { for (let drawing of this.drawings) { let points = drawing.getPointTuples(); for (let point of points) { - if (Math.abs(point[0] * globals.imageScaleWidth - globals.mouseDownX) < threshold && Math.abs(point[1] * globals.imageScaleHeight - globals.mouseDownY) < threshold) { + if (Math.abs(point[0] * globals.imageScaleWidth - x) < threshold && Math.abs(point[1] * globals.imageScaleHeight - y) < threshold) { return true; } } @@ -647,7 +647,7 @@ class Canvas { increaseSelectionSizeRight() {} increaseSelectionSizeUp() {} - handleMouseClick(event) { + handleMouseClick(event, x, y) { let position = globals.image.offset(); if (!(event.pageX > position.left && event.pageX < position.left + globals.image.width() && event.pageY > position.top && event.pageY < position.top + globals.image.height()) && @@ -656,20 +656,20 @@ class Canvas { } } - handleMouseDown() { + handleMouseDown(event, x, y) { // Check if we are close enough to move the point, not draw a new drawing // we use the variable locked which is checked when we can create a new line - if (this.mouseTooClose()) { + if (this.mouseTooClose(x, y)) { this.locked = true; } } - handleMouseUp() { + handleMouseUp(event, x, y) { if (this.inline && this.currentDrawing) { // We are currently drawing a drawing // and we clicked inside of the canvas: // add a point - this.currentDrawing.addPoint(globals.mouseUpX, globals.mouseUpY); + this.currentDrawing.addPoint(x, y); } else if (this.locked) { // we do not create a drawing because we are // only moving an existing one @@ -683,19 +683,19 @@ class Canvas { this.inline = true; switch (this.vector_type) { case 2: // Point - this.drawPoint({x1: globals.mouseUpX, y1: globals.mouseUpY}, 0, true); + this.drawPoint({x1: x, y1: y}, 0, true); break; case 3: // Line - this.drawLine({x1: globals.mouseUpX, y1: globals.mouseUpY}, 0, true); + this.drawLine({x1: x, y1: y}, 0, true); break; case 4: // Multiline - this.drawMultiline({x1: globals.mouseUpX, y1: globals.mouseUpY}, 0, true); + this.drawMultiline({x1: x, y1: y}, 0, true); break; case 5: // Polygon if (this.node_count === 0) { - this.drawArbitraryPolygon({x1: globals.mouseUpX, y1: globals.mouseUpY}, 0, true, false); + this.drawArbitraryPolygon({x1: x, y1: y}, 0, true, false); } else { - this.drawPolygon({x1: globals.mouseUpX, y1: globals.mouseUpY}, 0, true, this.node_count, false); + this.drawPolygon({x1: x, y1: y}, 0, true, this.node_count, false); } break; default: From 877c2510e2208759e9bc0c5cf6245aeed712404a Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Wed, 31 Mar 2021 00:59:44 +0200 Subject: [PATCH 25/31] fix escape for multiline --- .../annotations/static/annotations/js/annotations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index 2df79312..f1711380 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -1234,7 +1234,7 @@ function calculateImageScale() { gShiftDown = true; break; case 27: // Escape - if (globals.annotation_type_id === 4) { + if (gAnnotationType === 4) { // special case multiline: close drawing tool.closeDrawing(); } else { From ffc6aea517a247643d238fbab2f98536fc3201b0 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Wed, 31 Mar 2021 18:36:08 +0200 Subject: [PATCH 26/31] do not reload image list every time --- .../annotations/static/annotations/js/annotations.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index f1711380..e2c29def 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -1090,8 +1090,6 @@ function calculateImageScale() { */ async function changeImage(imageId, annotation) { window.history.replaceState(null, document.title, ANNOTATE_URL.replace('%s', imageId)); - let imageList = await getImageList(); - await displayImageList(imageList); scrollImageList(imageId); await loadAnnotateView(imageId, annotation); } From 6b720a5ad7d689c16e14fbf301843a372768b4d9 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Wed, 31 Mar 2021 18:59:17 +0200 Subject: [PATCH 27/31] always show scroll bar in annotation view --- .../annotations/templates/annotations/annotate.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/imagetagger/imagetagger/annotations/templates/annotations/annotate.html b/imagetagger/imagetagger/annotations/templates/annotations/annotate.html index 7f2a9c8c..fd7329c5 100644 --- a/imagetagger/imagetagger/annotations/templates/annotations/annotate.html +++ b/imagetagger/imagetagger/annotations/templates/annotations/annotate.html @@ -23,6 +23,10 @@ display: none; } {% endif %} + body { + /* this is necessary to avoid a jumping image when the scrollbar appears */ + overflow-y: scroll; + } {% endblock additional_annotation_css %} From 65d50837dd2d453e82092087d5731be9587843dc Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Wed, 31 Mar 2021 23:28:18 +0200 Subject: [PATCH 28/31] call resetSelection when restoring an annotation that is not in image --- .../static/annotations/js/annotations.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index e2c29def..d4705757 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -813,19 +813,21 @@ function calculateImageScale() { tool.clear(); if ($('#keep_selection').prop('checked') && restoreAnnotation) { - let annotation_type = $('#annotation_type_id').children('[value=' + restoreAnnotation.annotation_type_id + ']').data(); - let vector_type = annotation_type.vectorType; - let node_count = annotation_type.nodeCount; - tool.restoreSelection({ - vector_type: vector_type, - node_count: node_count, - vector: restoreAnnotation.vector, - }); if (restoreAnnotation.vector === null) { // not in image + tool.resetSelection(); $('#not_in_image').prop('checked', true); $('#coordinate_table').hide(); setupCBCheckboxes(); + } else { + let annotation_type = $('#annotation_type_id').children('[value=' + restoreAnnotation.annotation_type_id + ']').data(); + let vector_type = annotation_type.vectorType; + let node_count = annotation_type.nodeCount; + tool.restoreSelection({ + vector_type: vector_type, + node_count: node_count, + vector: restoreAnnotation.vector, + }); } } else { resetSelection(); From a0bce569671045cbfe04e749b8a35665dc14a0f0 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Wed, 31 Mar 2021 23:28:58 +0200 Subject: [PATCH 29/31] fix race condition by waiting for the image before drawing on it --- .../annotations/static/annotations/js/annotations.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index d4705757..a1482663 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -478,7 +478,6 @@ function calculateImageScale() { currentImage.attr('id', ''); newImage.attr('id', 'image'); gImageId = imageId; - let loadImages = preloadImages(); currentImage.replaceWith(newImage); globals.image = newImage; @@ -488,7 +487,6 @@ function calculateImageScale() { // add previous image to cache gImageCache[currentImage.data('imageid')] = currentImage; } - await loadImages; } /** @@ -798,7 +796,6 @@ function calculateImageScale() { loading.removeClass('hidden'); $('#annotation_type_id').val(gAnnotationType); - let loadImage = displayImage(imageId); $('.annotate_image_link').removeClass('active'); let link = $('#annotate_image_link_' + imageId); @@ -812,6 +809,10 @@ function calculateImageScale() { $('#delete-image-form').attr('action', DELETE_IMAGE_URL.replace('%s', imageId)); tool.clear(); + + await displayImage(imageId); + let loadImages = preloadImages(); + if ($('#keep_selection').prop('checked') && restoreAnnotation) { if (restoreAnnotation.vector === null) { // not in image @@ -843,7 +844,7 @@ function calculateImageScale() { } catch { console.log("Unable to load annotations for image" + imageId); } - await loadImage; + await loadImages; } /** From a7c23e0d3a5be56399ce700ce4ced274d63972c1 Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Thu, 1 Apr 2021 18:05:44 +0200 Subject: [PATCH 30/31] better error handling when creating an annotation --- .../static/annotations/js/annotations.js | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js index a1482663..73a1ed39 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/annotations.js @@ -182,6 +182,7 @@ function calculateImageScale() { * If an annotation is currently edited, an update is triggered instead. * * @param annotation the annotation to be created, as returned by getValidAnnotation + * @return whether the annotation was successfully created */ async function createAnnotation(annotation) { @@ -194,6 +195,8 @@ function calculateImageScale() { editing = true; } + let success = true; + $('.js_feedback').stop().addClass('hidden'); $('.annotate_button').prop('disabled', true); try { @@ -209,11 +212,13 @@ function calculateImageScale() { if (data.detail === 'similar annotation exists.') { displayFeedback($('#feedback_annotation_exists_deleted')); $('#annotation_edit_button_' + gEditAnnotationId).parent().parent().fadeOut().remove(); + success = false; } else { displayFeedback($('#feedback_annotation_updated')); } } else { displayFeedback($('#feedback_annotation_exists')); + success = false; } } else if (response.status === 201) { displayFeedback($('#feedback_annotation_created')); @@ -227,9 +232,11 @@ function calculateImageScale() { } catch (e) { console.log(e); displayFeedback($('#feedback_connection_error')); + success = false; } finally { $('.annotate_button').prop('disabled', false); } + return success; } async function loadAnnotationTypeList() { @@ -1173,9 +1180,15 @@ function calculateImageScale() { $('#last_button').click(async function (event) { try { let annotation = getValidAnnotation(true); - await createAnnotation(annotation); - let imageId = await loadAdjacentImage(-1); - await changeImage(imageId, annotation); + if (await createAnnotation(annotation)) { + let imageId = await loadAdjacentImage(-1); + await changeImage(imageId, annotation); + } else { + tool.clear(); + resetSelection(); + tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType); + displayExistingAnnotations(gCurrentAnnotations); + } } catch (e) { console.log(e); } @@ -1191,9 +1204,15 @@ function calculateImageScale() { $('#next_button').click(async function (event) { try { let annotation = getValidAnnotation(true); - await createAnnotation(annotation); - let imageId = await loadAdjacentImage(1); - await changeImage(imageId, annotation); + if (await createAnnotation(annotation)) { + let imageId = await loadAdjacentImage(1); + await changeImage(imageId, annotation); + } else { + tool.clear(); + resetSelection(); + tool.drawExistingAnnotations(globals.currentAnnotationsOfSelectedType); + displayExistingAnnotations(gCurrentAnnotations); + } } catch (e) { console.log(e); } From 1259800c7c018229c8dc2e84d58c8dccb9d852ac Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Tue, 6 Apr 2021 12:34:42 +0200 Subject: [PATCH 31/31] better support for points --- .../static/annotations/js/canvas.js | 29 ++----------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js b/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js index 31041543..06538ba3 100644 --- a/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js +++ b/imagetagger/imagetagger/annotations/static/annotations/js/canvas.js @@ -180,16 +180,10 @@ class Drawing { } } -class Point { +class Point extends Drawing { constructor(parent, point, id, mutable, color) { - /* Set fields */ - this.pointCounter = 1; // The number of points that are currently set - this.id = id; - this.name = "drawing" + id; - this.parent = parent; - this.mutable = mutable; - this.color = color || globals.stdColor; - + super(parent, point, id, mutable, color); + this.parent.removeLayer(this.name); /* Define layer */ let l = { name: this.name, @@ -203,23 +197,6 @@ class Point { this.parent.locked = false; this.parent.updateAnnotationFields(point); } - /** Set the cursor to 'drag' or 'crosshair' in mouseover - * - * @param bool whether the dursor is in 'drag' style - */ - setDragCursor(bool) {} - setMutable(mutable) {} - getPointTuples() { - let l = this.parent.getLayer(this.name); - return [[l.x, l.y]]; - } - getPoints() { - let l = this.parent.getLayer(this.name); - return {x1: l.x, y1: l.y}; - } - remove() { - this.parent.removeLayer(this.name); - } } class Line extends Drawing {