diff --git a/cvat/apps/engine/static/engine/js/annotationUI.js b/cvat/apps/engine/static/engine/js/annotationUI.js
index e2bfce72..eefd6ca0 100644
--- a/cvat/apps/engine/static/engine/js/annotationUI.js
+++ b/cvat/apps/engine/static/engine/js/annotationUI.js
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: MIT
*/
-/* exported callAnnotationUI translateSVGPos blurAllElements drawBoxSize */
+/* exported callAnnotationUI translateSVGPos blurAllElements drawBoxSize copyToClipboard */
"use strict";
function callAnnotationUI(jid) {
@@ -55,8 +55,46 @@ function buildAnnotationUI(job, shapeData, loadJobEvent) {
z_order: job.z_order,
id: job.jobid
},
+ search: {
+ value: window.location.search,
+
+ set: function(name, value) {
+ let searchParams = new URLSearchParams(this.value);
+
+ if (typeof value === 'undefined' || value === null) {
+ if (searchParams.has(name)) {
+ searchParams.delete(name);
+ }
+ }
+ else searchParams.set(name, value);
+ this.value = `${searchParams.toString()}`;
+ },
+
+ get: function(name) {
+ try {
+ let decodedURI = decodeURIComponent(this.value);
+ let urlSearchParams = new URLSearchParams(decodedURI);
+ if (urlSearchParams.has(name)) {
+ return urlSearchParams.get(name);
+ }
+ else return null;
+ }
+ catch (error) {
+ showMessage('Bad URL has been found');
+ this.value = window.location.href;
+ return null;
+ }
+ },
+
+ toString: function() {
+ return `${window.location.origin}/?${this.value}`;
+ }
+ }
};
+ // Remove external search parameters from url
+ window.history.replaceState(null, null, `${window.location.origin}/?id=${job.jobid}`);
+
window.cvat.config = new Config();
// Setup components
@@ -137,7 +175,7 @@ function buildAnnotationUI(job, shapeData, loadJobEvent) {
playerModel.subscribe(shapeBufferView);
playerModel.subscribe(shapeGrouperView);
playerModel.subscribe(polyshapeEditorView);
- playerModel.shift(getURISearchParameter('frame') || 0, true);
+ playerModel.shift(window.cvat.search.get('frame') || 0, true);
let shortkeys = window.cvat.config.shortkeys;
@@ -185,6 +223,16 @@ function buildAnnotationUI(job, shapeData, loadJobEvent) {
});
}
+
+function copyToClipboard(text) {
+ let tempInput = $("");
+ $("body").append(tempInput);
+ tempInput.prop('value', text).select();
+ document.execCommand("copy");
+ tempInput.remove();
+}
+
+
function setupFrameFilters() {
let brightnessRange = $('#playerBrightnessRange');
let contrastRange = $('#playerContrastRange');
diff --git a/cvat/apps/engine/static/engine/js/base.js b/cvat/apps/engine/static/engine/js/base.js
index a164e3cd..bfc937c4 100644
--- a/cvat/apps/engine/static/engine/js/base.js
+++ b/cvat/apps/engine/static/engine/js/base.js
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: MIT
*/
-/* exported confirm showMessage showOverlay dumpAnnotationRequest getURISearchParameter setURISearchParameter */
+/* exported confirm showMessage showOverlay dumpAnnotationRequest */
"use strict";
Math.clamp = function(x, min, max) {
@@ -160,45 +160,6 @@ function dumpAnnotationRequest(dumpButton, taskID) {
}
}
-
-function setURISearchParameter(name, value) {
- let searchParams = new URLSearchParams(window.location.search);
- if (typeof value === 'undefined' || value === null) {
- if (searchParams.has(name)) {
- searchParams.delete(name);
- }
- }
- else searchParams.set(name, value);
-
- window.history.replaceState(null, null, `?${searchParams.toString()}`);
-}
-
-
-function resetURISearchParameters() {
- let searchParams = new URLSearchParams();
- searchParams.set('id', window.cvat.job.id);
- window.history.replaceState(null, null, `?${searchParams.toString()}`);
-}
-
-
-function getURISearchParameter(name) {
- let decodedURI = '';
- try {
- decodedURI = decodeURIComponent(window.location.search);
- }
- catch (error) {
- showMessage('Bad URL has been found');
- resetURISearchParameters();
- }
-
- let urlSearchParams = new URLSearchParams(decodedURI);
- if (urlSearchParams.has(name)) {
- return urlSearchParams.get(name);
- }
- else return null;
-}
-
-
/* These HTTP methods do not require CSRF protection */
function csrfSafeMethod(method) {
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
diff --git a/cvat/apps/engine/static/engine/js/player.js b/cvat/apps/engine/static/engine/js/player.js
index 2dead4bd..9c3c5d78 100644
--- a/cvat/apps/engine/static/engine/js/player.js
+++ b/cvat/apps/engine/static/engine/js/player.js
@@ -647,6 +647,7 @@ class PlayerView {
this._frameNumber = $('#frameNumber');
this._playerGridPattern = $('#playerGridPattern');
this._playerGridPath = $('#playerGridPath');
+ this._contextMenuUI = $('#playerContextMenu');
$('*').on('mouseup', () => this._controller.frameMouseUp());
this._playerUI.on('wheel', (e) => this._controller.zoom(e));
@@ -763,6 +764,38 @@ class PlayerView {
this._multiplePrevButtonUI.find('polygon').append($(document.createElementNS('http://www.w3.org/2000/svg', 'title'))
.html(`${shortkeys['backward_frame'].view_value} - ${shortkeys['backward_frame'].description}`));
+
+ this._contextMenuUI.click((e) => {
+ $('.custom-menu').hide(100);
+ switch($(e.target).attr("action")) {
+ case "job_url": {
+ window.cvat.search.set('frame', null);
+ window.cvat.search.set('filter', null);
+ copyToClipboard(window.cvat.search.toString());
+ break;
+ }
+ case "frame_url":
+ window.cvat.search.set('frame', window.cvat.player.frames.current);
+ window.cvat.search.set('filter', null);
+ copyToClipboard(window.cvat.search.toString());
+ window.cvat.search.set('frame', null);
+ break;
+ }
+ });
+
+ this._playerContentUI.on('contextmenu.playerContextMenu', (e) => {
+ $('.custom-menu').hide(100);
+ this._contextMenuUI.finish().show(100).offset({
+ top: e.pageY - 10,
+ left: e.pageX - 10,
+ });
+ e.preventDefault();
+ });
+
+ this._playerContentUI.on('mousedown.playerContextMenu', () => {
+ $('.custom-menu').hide(100);
+ });
+
playerModel.subscribe(this);
}
@@ -780,7 +813,6 @@ class PlayerView {
this._loadingUI.addClass('hidden');
if (this._playerBackgroundUI.css('background-image').slice(5,-2) != image.src) {
this._playerBackgroundUI.css('background-image', 'url(' + '"' + image.src + '"' + ')');
- setURISearchParameter('frame', frames.current);
}
if (model.playing) {
diff --git a/cvat/apps/engine/static/engine/js/shapeCollection.js b/cvat/apps/engine/static/engine/js/shapeCollection.js
index 28091b6b..3cf28415 100644
--- a/cvat/apps/engine/static/engine/js/shapeCollection.js
+++ b/cvat/apps/engine/static/engine/js/shapeCollection.js
@@ -988,6 +988,10 @@ class ShapeCollectionController {
get filterController() {
return this._filterController;
}
+
+ get activeShape() {
+ return this._model.activeShape;
+ }
}
class ShapeCollectionView {
@@ -1103,7 +1107,10 @@ class ShapeCollectionView {
});
this._frameContent.on('mousemove', function(e) {
- if (e.ctrlKey || e.which === 2 || e.target.classList.contains('svg_select_points')) {
+ if (e.ctrlKey || e.shiftKey || e.which === 2 || e.target.classList.contains('svg_select_points')) {
+ if (e.shiftKey) {
+ this._controller.resetActive();
+ }
return;
}
@@ -1117,9 +1124,20 @@ class ShapeCollectionView {
$('#shapeContextMenu li').click((e) => {
let menu = $('#shapeContextMenu');
- menu.hide(100);
+ $('.custom-menu').hide(100);
switch($(e.target).attr("action")) {
+ case "object_url": {
+ let active = this._controller.activeShape;
+ if (active) {
+ window.cvat.search.set('frame', window.cvat.player.frames.current);
+ window.cvat.search.set('filter', `*[id="${active.id}"]`);
+ copyToClipboard(window.cvat.search.toString());
+ window.cvat.search.set('frame', null);
+ window.cvat.search.set('filter', null);
+ }
+ break;
+ }
case "change_color":
this._controller.switchActiveColor();
break;
@@ -1162,7 +1180,7 @@ class ShapeCollectionView {
$('#pointContextMenu li').click((e) => {
let menu = $('#pointContextMenu');
let idx = +menu.attr('point_idx');
- menu.hide(100);
+ $('.custom-menu').hide(100);
switch($(e.target).attr("action")) {
case "remove_point":
diff --git a/cvat/apps/engine/static/engine/js/shapeFilter.js b/cvat/apps/engine/static/engine/js/shapeFilter.js
index 36d86d2c..06310e8a 100644
--- a/cvat/apps/engine/static/engine/js/shapeFilter.js
+++ b/cvat/apps/engine/static/engine/js/shapeFilter.js
@@ -113,11 +113,10 @@ class FilterView {
let value = $.trim(e.target.value);
if (this._controller.updateFilter(value, false)) {
this._filterString.css('color', 'green');
- setURISearchParameter('filter', value || null);
}
else {
this._filterString.css('color', 'red');
- setURISearchParameter('filter', null);
+ this._controller.updateFilter('', false);
}
});
@@ -129,17 +128,15 @@ class FilterView {
this._resetFilterButton.on('click', () => {
this._filterString.prop('value', '');
this._controller.updateFilter('', false);
- setURISearchParameter('filter', null);
});
- if (getURISearchParameter('filter')) {
- let value = getURISearchParameter('filter');
- this._filterString.prop('value', value);
- if (this._controller.updateFilter(value, true)) {
+ let initialFilter = window.cvat.search.get('filter');
+ if (initialFilter) {
+ this._filterString.prop('value', initialFilter);
+ if (this._controller.updateFilter(initialFilter, true)) {
this._filterString.css('color', 'green');
}
else {
- setURISearchParameter('filter', null);
this._filterString.prop('value', '');
this._filterString.css('color', 'red');
}
diff --git a/cvat/apps/engine/static/engine/js/shapes.js b/cvat/apps/engine/static/engine/js/shapes.js
index 4207c5e5..c78cc729 100644
--- a/cvat/apps/engine/static/engine/js/shapes.js
+++ b/cvat/apps/engine/static/engine/js/shapes.js
@@ -1492,8 +1492,7 @@ class ShapeView extends Listener {
// Setup context menu
this._uis.shape.on('mousedown.contextMenu', (e) => {
if (e.which === 1) {
- this._shapeContextMenu.hide(100);
- this._pointContextMenu.hide(100);
+ $('.custom-menu').hide(100);
}
if (e.which === 3) {
e.stopPropagation();
@@ -1501,7 +1500,7 @@ class ShapeView extends Listener {
});
this._uis.shape.on('contextmenu.contextMenu', (e) => {
- this._pointContextMenu.hide(100);
+ $('.custom-menu').hide(100);
let type = this._controller.type.split('_');
if (type[0] === 'interpolation') {
this._shapeContextMenu.find('.interpolationItem').removeClass('hidden');
@@ -1553,8 +1552,7 @@ class ShapeView extends Listener {
this._flags.editable = false;
}
- this._pointContextMenu.hide(100);
- this._shapeContextMenu.hide(100);
+ $('.custom-menu').hide(100);
}
@@ -2829,7 +2827,7 @@ class PolyShapeView extends ShapeView {
point = $(point);
point.on('contextmenu.contextMenu', (e) => {
- this._shapeContextMenu.hide(100);
+ $('.custom-menu').hide(100);
this._pointContextMenu.attr('point_idx', point.index());
this._pointContextMenu.attr('dom_point_id', point.attr('id'));
diff --git a/cvat/apps/engine/templates/engine/annotation.html b/cvat/apps/engine/templates/engine/annotation.html
index 0042b798..67e6b137 100644
--- a/cvat/apps/engine/templates/engine/annotation.html
+++ b/cvat/apps/engine/templates/engine/annotation.html
@@ -77,7 +77,9 @@