diff --git a/cvat-core/README.md b/cvat-core/README.md index f2d22d21..5f00bc03 100644 --- a/cvat-core/README.md +++ b/cvat-core/README.md @@ -13,7 +13,7 @@ npm install - Building the module from sources in the ```dist``` directory: ```bash -npm run-script build +npm run build npm run build -- --mode=development # without a minification ``` diff --git a/cvat-core/package.json b/cvat-core/package.json index 4573565b..48ee0d0d 100644 --- a/cvat-core/package.json +++ b/cvat-core/package.json @@ -27,11 +27,10 @@ }, "dependencies": { "axios": "^0.18.0", - "browser-env": "^3.2.6", "error-stack-parser": "^2.0.2", + "form-data": "^2.5.0", "jest-config": "^24.8.0", "js-cookie": "^2.2.0", - "platform": "^1.3.5", - "stacktrace-gps": "^3.0.2" + "platform": "^1.3.5" } } diff --git a/cvat-core/src/annotations-collection.js b/cvat-core/src/annotations-collection.js index fc03e725..7a5db6f0 100644 --- a/cvat-core/src/annotations-collection.js +++ b/cvat-core/src/annotations-collection.js @@ -24,6 +24,18 @@ } = require('./annotations-objects'); const { checkObjectType } = require('./common'); const Statistics = require('./statistics'); + const { Label } = require('./labels'); + const { + DataError, + ArgumentError, + ScriptingError, + } = require('./exceptions'); + + const { + ObjectShape, + ObjectType, + } = require('./enums'); + const ObjectState = require('./object-state'); const colors = [ '#0066FF', '#AF593E', '#01A368', '#FF861F', '#ED0A3F', '#FF3F34', '#76D7EA', @@ -62,7 +74,7 @@ shapeModel = new PointsShape(shapeData, clientID, color, injection); break; default: - throw new window.cvat.exceptions.DataError( + throw new DataError( `An unexpected type of shape "${type}"`, ); } @@ -91,7 +103,7 @@ trackModel = new PointsTrack(trackData, clientID, color, injection); break; default: - throw new window.cvat.exceptions.DataError( + throw new DataError( `An unexpected type of track "${type}"`, ); } @@ -209,10 +221,10 @@ checkObjectType('shapes for merge', objectStates, null, Array); if (!objectStates.length) return; const objectsForMerge = objectStates.map((state) => { - checkObjectType('object state', state, null, window.cvat.classes.ObjectState); + checkObjectType('object state', state, null, ObjectState); const object = this.objects[state.clientID]; if (typeof (object) === 'undefined') { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'The object has not been saved yet. Call ObjectState.put([state]) before you can merge it', ); } @@ -222,13 +234,13 @@ const keyframes = {}; // frame: position const { label, shapeType } = objectStates[0]; if (!(label.id in this.labels)) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Unknown label for the task: ${label.id}`, ); } - if (!Object.values(window.cvat.enums.ObjectShape).includes(shapeType)) { - throw new window.cvat.exceptions.ArgumentError( + if (!Object.values(ObjectShape).includes(shapeType)) { + throw new ArgumentError( `Got unknown shapeType "${shapeType}"`, ); } @@ -243,13 +255,13 @@ const object = objectsForMerge[i]; const state = objectStates[i]; if (state.label.id !== label.id) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `All shape labels are expected to be ${label.name}, but got ${state.label.name}`, ); } if (state.shapeType !== shapeType) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `All shapes are expected to be ${shapeType}, but got ${state.shapeType}`, ); } @@ -258,7 +270,7 @@ if (object instanceof Shape) { // Frame already saved and it is not outside if (object.frame in keyframes && !keyframes[object.frame].outside) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'Expected only one visible shape per frame', ); } @@ -303,7 +315,7 @@ continue; } - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'Expected only one visible shape per frame', ); } @@ -338,7 +350,7 @@ }; } } else { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Trying to merge unknown object type: ${object.constructor.name}. ` + 'Only shapes and tracks are expected.', ); @@ -389,17 +401,17 @@ } split(objectState, frame) { - checkObjectType('object state', objectState, null, window.cvat.classes.ObjectState); + checkObjectType('object state', objectState, null, ObjectState); checkObjectType('frame', frame, 'integer', null); const object = this.objects[objectState.clientID]; if (typeof (object) === 'undefined') { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'The object has not been saved yet. Call annotations.put([state]) before', ); } - if (objectState.objectType !== window.cvat.enums.ObjectType.TRACK) { + if (objectState.objectType !== ObjectType.TRACK) { return; } @@ -478,10 +490,10 @@ checkObjectType('shapes for group', objectStates, null, Array); const objectsForGroup = objectStates.map((state) => { - checkObjectType('object state', state, null, window.cvat.classes.ObjectState); + checkObjectType('object state', state, null, ObjectState); const object = this.objects[state.clientID]; if (typeof (object) === 'undefined') { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'The object has not been saved yet. Call annotations.put([state]) before', ); } @@ -549,7 +561,7 @@ } else if (object instanceof Tag) { objectType = 'tag'; } else { - throw new window.cvat.exceptions.ScriptingError( + throw new ScriptingError( `Unexpected object type: "${objectType}"`, ); } @@ -637,11 +649,11 @@ } for (const state of objectStates) { - checkObjectType('object state', state, null, window.cvat.classes.ObjectState); + checkObjectType('object state', state, null, ObjectState); checkObjectType('state client ID', state.clientID, 'undefined', null); checkObjectType('state frame', state.frame, 'integer', null); checkObjectType('state attributes', state.attributes, null, Object); - checkObjectType('state label', state.label, null, window.cvat.classes.Label); + checkObjectType('state label', state.label, null, Label); const attributes = Object.keys(state.attributes) .reduce(convertAttributes.bind(state), []); @@ -666,10 +678,10 @@ checkObjectType('point coordinate', coord, 'number', null); } - if (!Object.values(window.cvat.enums.ObjectShape).includes(state.shapeType)) { - throw new window.cvat.exceptions.ArgumentError( + if (!Object.values(ObjectShape).includes(state.shapeType)) { + throw new ArgumentError( 'Object shape must be one of: ' - + `${JSON.stringify(Object.values(window.cvat.enums.ObjectShape))}`, + + `${JSON.stringify(Object.values(ObjectShape))}`, ); } @@ -703,9 +715,9 @@ }], }); } else { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'Object type must be one of: ' - + `${JSON.stringify(Object.values(window.cvat.enums.ObjectType))}`, + + `${JSON.stringify(Object.values(ObjectType))}`, ); } } @@ -723,12 +735,12 @@ let minimumDistance = null; let minimumState = null; for (const state of objectStates) { - checkObjectType('object state', state, null, window.cvat.classes.ObjectState); + checkObjectType('object state', state, null, ObjectState); if (state.outside) continue; const object = this.objects[state.clientID]; if (typeof (object) === 'undefined') { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'The object has not been saved yet. Call annotations.put([state]) before', ); } diff --git a/cvat-core/src/annotations-objects.js b/cvat-core/src/annotations-objects.js index 71fa42cf..a70553a7 100644 --- a/cvat-core/src/annotations-objects.js +++ b/cvat-core/src/annotations-objects.js @@ -10,6 +10,19 @@ (() => { const ObjectState = require('./object-state'); const { checkObjectType } = require('./common'); + const { + ObjectShape, + ObjectType, + AttributeType, + } = require('./enums'); + + const { + DataError, + ArgumentError, + ScriptingError, + } = require('./exceptions'); + + const { Label } = require('./labels'); // Called with the Annotation context function objectStateFactory(frame, data) { @@ -26,32 +39,32 @@ } function checkNumberOfPoints(shapeType, points) { - if (shapeType === window.cvat.enums.ObjectShape.RECTANGLE) { + if (shapeType === ObjectShape.RECTANGLE) { if (points.length / 2 !== 2) { - throw new window.cvat.exceptions.DataError( + throw new DataError( `Rectangle must have 2 points, but got ${points.length / 2}`, ); } - } else if (shapeType === window.cvat.enums.ObjectShape.POLYGON) { + } else if (shapeType === ObjectShape.POLYGON) { if (points.length / 2 < 3) { - throw new window.cvat.exceptions.DataError( + throw new DataError( `Polygon must have at least 3 points, but got ${points.length / 2}`, ); } - } else if (shapeType === window.cvat.enums.ObjectShape.POLYLINE) { + } else if (shapeType === ObjectShape.POLYLINE) { if (points.length / 2 < 2) { - throw new window.cvat.exceptions.DataError( + throw new DataError( `Polyline must have at least 2 points, but got ${points.length / 2}`, ); } - } else if (shapeType === window.cvat.enums.ObjectShape.POINTS) { + } else if (shapeType === ObjectShape.POINTS) { if (points.length / 2 < 1) { - throw new window.cvat.exceptions.DataError( + throw new DataError( `Points must have at least 1 points, but got ${points.length / 2}`, ); } } else { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Unknown value of shapeType has been recieved ${shapeType}`, ); } @@ -61,7 +74,7 @@ const MIN_SHAPE_LENGTH = 3; const MIN_SHAPE_AREA = 9; - if (shapeType === window.cvat.enums.ObjectShape.POINTS) { + if (shapeType === ObjectShape.POINTS) { return true; } @@ -77,7 +90,7 @@ ymax = Math.max(ymax, points[i + 1]); } - if (shapeType === window.cvat.enums.ObjectShape.POLYLINE) { + if (shapeType === ObjectShape.POLYLINE) { const length = Math.max( xmax - xmin, ymax - ymin, @@ -95,18 +108,18 @@ const type = attr.inputType; if (typeof (value) !== 'string') { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Attribute value is expected to be string, but got ${typeof (value)}`, ); } - if (type === window.cvat.enums.AttributeType.NUMBER) { + if (type === AttributeType.NUMBER) { return +value >= +values[0] && +value <= +values[1] && !((+value - +values[0]) % +values[2]); } - if (type === window.cvat.enums.AttributeType.CHECKBOX) { + if (type === AttributeType.CHECKBOX) { return ['true', 'false'].includes(value.toLowerCase()); } @@ -171,19 +184,19 @@ } save() { - throw new window.cvat.exceptions.ScriptingError( + throw new ScriptingError( 'Is not implemented', ); } get() { - throw new window.cvat.exceptions.ScriptingError( + throw new ScriptingError( 'Is not implemented', ); } toJSON() { - throw new window.cvat.exceptions.ScriptingError( + throw new ScriptingError( 'Is not implemented', ); } @@ -241,13 +254,13 @@ // Method is used to construct ObjectState objects get(frame) { if (frame !== this.frame) { - throw new window.cvat.exceptions.ScriptingError( + throw new ScriptingError( 'Got frame is not equal to the frame of the shape', ); } return { - objectType: window.cvat.enums.ObjectType.SHAPE, + objectType: ObjectType.SHAPE, shapeType: this.shapeType, clientID: this.clientID, serverID: this.serverID, @@ -264,7 +277,7 @@ save(frame, data) { if (frame !== this.frame) { - throw new window.cvat.exceptions.ScriptingError( + throw new ScriptingError( 'Got frame is not equal to the frame of the shape', ); } @@ -278,7 +291,7 @@ const updated = data.updateFlags; if (updated.label) { - checkObjectType('label', data.label, null, window.cvat.classes.Label); + checkObjectType('label', data.label, null, Label); copy.label = data.label; copy.attributes = {}; this.appendDefaultAttributes.call(copy, copy.label); @@ -297,7 +310,7 @@ && validateAttributeValue(value, labelAttributes[attrID])) { copy.attributes[attrID] = value; } else { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Trying to save unknown attribute with id ${attrID} and value ${value}`, ); } @@ -352,7 +365,7 @@ if (updated.color) { checkObjectType('color', data.color, 'string', null); if (/^#[0-9A-F]{6}$/i.test(data.color)) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Got invalid color value: "${data.color}"`, ); } @@ -456,7 +469,7 @@ { attributes: this.getAttributes(frame), group: this.group, - objectType: window.cvat.enums.ObjectType.TRACK, + objectType: ObjectType.TRACK, shapeType: this.shapeType, clientID: this.clientID, serverID: this.serverID, @@ -537,7 +550,7 @@ let positionUpdated = false; if (updated.label) { - checkObjectType('label', data.label, null, window.cvat.classes.Label); + checkObjectType('label', data.label, null, Label); copy.label = data.label; copy.attributes = {}; @@ -558,7 +571,7 @@ && validateAttributeValue(value, labelAttributes[attrID])) { copy.attributes[attrID] = value; } else { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Trying to save unknown attribute with id ${attrID} and value ${value}`, ); } @@ -622,7 +635,7 @@ if (updated.color) { checkObjectType('color', data.color, 'string', null); if (/^#[0-9A-F]{6}$/i.test(data.color)) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Got invalid color value: "${data.color}"`, ); } @@ -762,7 +775,7 @@ }; } - throw new window.cvat.exceptions.ScriptingError( + throw new ScriptingError( `No one neightbour frame found for the track with client ID: "${this.id}"`, ); } @@ -808,13 +821,13 @@ // Method is used to construct ObjectState objects get(frame) { if (frame !== this.frame) { - throw new window.cvat.exceptions.ScriptingError( + throw new ScriptingError( 'Got frame is not equal to the frame of the shape', ); } return { - objectType: window.cvat.enums.ObjectType.TAG, + objectType: ObjectType.TAG, clientID: this.clientID, serverID: this.serverID, lock: this.lock, @@ -826,7 +839,7 @@ save(frame, data) { if (frame !== this.frame) { - throw new window.cvat.exceptions.ScriptingError( + throw new ScriptingError( 'Got frame is not equal to the frame of the shape', ); } @@ -840,7 +853,7 @@ const updated = data.updateFlags; if (updated.label) { - checkObjectType('label', data.label, null, window.cvat.classes.Label); + checkObjectType('label', data.label, null, Label); copy.label = data.label; copy.attributes = {}; this.appendDefaultAttributes.call(copy, copy.label); @@ -882,7 +895,7 @@ class RectangleShape extends Shape { constructor(data, clientID, color, injection) { super(data, clientID, color, injection); - this.shapeType = window.cvat.enums.ObjectShape.RECTANGLE; + this.shapeType = ObjectShape.RECTANGLE; checkNumberOfPoints(this.shapeType, this.points); } @@ -908,7 +921,7 @@ class PolygonShape extends PolyShape { constructor(data, clientID, color, injection) { super(data, clientID, color, injection); - this.shapeType = window.cvat.enums.ObjectShape.POLYGON; + this.shapeType = ObjectShape.POLYGON; checkNumberOfPoints(this.shapeType, this.points); } @@ -983,7 +996,7 @@ class PolylineShape extends PolyShape { constructor(data, clientID, color, injection) { super(data, clientID, color, injection); - this.shapeType = window.cvat.enums.ObjectShape.POLYLINE; + this.shapeType = ObjectShape.POLYLINE; checkNumberOfPoints(this.shapeType, this.points); } @@ -1027,7 +1040,7 @@ class PointsShape extends PolyShape { constructor(data, clientID, color, injection) { super(data, clientID, color, injection); - this.shapeType = window.cvat.enums.ObjectShape.POINTS; + this.shapeType = ObjectShape.POINTS; checkNumberOfPoints(this.shapeType, this.points); } @@ -1049,7 +1062,7 @@ class RectangleTrack extends Track { constructor(data, clientID, color, injection) { super(data, clientID, color, injection); - this.shapeType = window.cvat.enums.ObjectShape.RECTANGLE; + this.shapeType = ObjectShape.RECTANGLE; for (const shape of Object.values(this.shapes)) { checkNumberOfPoints(this.shapeType, shape.points); } @@ -1374,7 +1387,7 @@ if (!targetMatched.length) { // Prevent infinity loop - throw new window.cvat.exceptions.ScriptingError('Interpolation mapping is empty'); + throw new ScriptingError('Interpolation mapping is empty'); } while (!targetMatched.includes(prev)) { @@ -1463,7 +1476,7 @@ class PolygonTrack extends PolyTrack { constructor(data, clientID, color, injection) { super(data, clientID, color, injection); - this.shapeType = window.cvat.enums.ObjectShape.POLYGON; + this.shapeType = ObjectShape.POLYGON; for (const shape of Object.values(this.shapes)) { checkNumberOfPoints(this.shapeType, shape.points); } @@ -1473,7 +1486,7 @@ class PolylineTrack extends PolyTrack { constructor(data, clientID, color, injection) { super(data, clientID, color, injection); - this.shapeType = window.cvat.enums.ObjectShape.POLYLINE; + this.shapeType = ObjectShape.POLYLINE; for (const shape of Object.values(this.shapes)) { checkNumberOfPoints(this.shapeType, shape.points); } @@ -1483,7 +1496,7 @@ class PointsTrack extends PolyTrack { constructor(data, clientID, color, injection) { super(data, clientID, color, injection); - this.shapeType = window.cvat.enums.ObjectShape.POINTS; + this.shapeType = ObjectShape.POINTS; for (const shape of Object.values(this.shapes)) { checkNumberOfPoints(this.shapeType, shape.points); } diff --git a/cvat-core/src/annotations-saver.js b/cvat-core/src/annotations-saver.js index 49233646..4d76f1fb 100644 --- a/cvat-core/src/annotations-saver.js +++ b/cvat-core/src/annotations-saver.js @@ -9,10 +9,12 @@ (() => { const serverProxy = require('./server-proxy'); + const { Task } = require('./session'); + const { ScriptingError } = ('./exceptions'); class AnnotationsSaver { constructor(version, collection, session) { - this.sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job'; + this.sessionType = session instanceof Task ? 'task' : 'job'; this.id = session.id; this.version = version; this.collection = collection; @@ -102,7 +104,7 @@ } else if (typeof (object.id) === 'undefined') { splitted.created[type].push(object); } else { - throw new window.cvat.exceptions.ScriptingError( + throw new ScriptingError( `Id of object is defined "${object.id}"` + 'but it absents in initial state', ); @@ -140,7 +142,7 @@ + indexes.shapes.length + indexes.tags.length; if (indexesLength !== savedLength) { - throw new window.cvat.exception.ScriptingError( + throw new ScriptingError( 'Number of indexes is differed by number of saved objects' + `${indexesLength} vs ${savedLength}`, ); diff --git a/cvat-core/src/annotations.js b/cvat-core/src/annotations.js index dd8edb6f..059ff006 100644 --- a/cvat-core/src/annotations.js +++ b/cvat-core/src/annotations.js @@ -12,6 +12,11 @@ const Collection = require('./annotations-collection'); const AnnotationsSaver = require('./annotations-saver'); const { checkObjectType } = require('./common'); + const { Task } = require('./session'); + const { + ScriptingError, + DataError, + } = require('./exceptions'); const jobCache = new WeakMap(); const taskCache = new WeakMap(); @@ -25,13 +30,13 @@ return jobCache; } - throw new window.cvat.exceptions.ScriptingError( + throw new ScriptingError( `Unknown session type was received ${sessionType}`, ); } async function getAnnotationsFromServer(session) { - const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job'; + const sessionType = session instanceof Task ? 'task' : 'job'; const cache = getCache(sessionType); if (!cache.has(session)) { @@ -65,13 +70,13 @@ async function getAnnotations(session, frame, filter) { await getAnnotationsFromServer(session); - const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job'; + const sessionType = session instanceof Task ? 'task' : 'job'; const cache = getCache(sessionType); return cache.get(session).collection.get(frame, filter); } async function saveAnnotations(session, onUpdate) { - const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job'; + const sessionType = session instanceof Task ? 'task' : 'job'; const cache = getCache(sessionType); if (cache.has(session)) { @@ -82,46 +87,46 @@ } function mergeAnnotations(session, objectStates) { - const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job'; + const sessionType = session instanceof Task ? 'task' : 'job'; const cache = getCache(sessionType); if (cache.has(session)) { return cache.get(session).collection.merge(objectStates); } - throw new window.cvat.exceptions.DataError( + throw new DataError( 'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before', ); } function splitAnnotations(session, objectState, frame) { - const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job'; + const sessionType = session instanceof Task ? 'task' : 'job'; const cache = getCache(sessionType); if (cache.has(session)) { return cache.get(session).collection.split(objectState, frame); } - throw new window.cvat.exceptions.DataError( + throw new DataError( 'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before', ); } function groupAnnotations(session, objectStates, reset) { - const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job'; + const sessionType = session instanceof Task ? 'task' : 'job'; const cache = getCache(sessionType); if (cache.has(session)) { return cache.get(session).collection.group(objectStates, reset); } - throw new window.cvat.exceptions.DataError( + throw new DataError( 'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before', ); } function hasUnsavedChanges(session) { - const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job'; + const sessionType = session instanceof Task ? 'task' : 'job'; const cache = getCache(sessionType); if (cache.has(session)) { @@ -133,7 +138,7 @@ async function clearAnnotations(session, reload) { checkObjectType('reload', reload, 'boolean', null); - const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job'; + const sessionType = session instanceof Task ? 'task' : 'job'; const cache = getCache(sessionType); if (cache.has(session)) { @@ -147,51 +152,51 @@ } function annotationsStatistics(session) { - const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job'; + const sessionType = session instanceof Task ? 'task' : 'job'; const cache = getCache(sessionType); if (cache.has(session)) { return cache.get(session).collection.statistics(); } - throw new window.cvat.exceptions.DataError( + throw new DataError( 'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before', ); } function putAnnotations(session, objectStates) { - const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job'; + const sessionType = session instanceof Task ? 'task' : 'job'; const cache = getCache(sessionType); if (cache.has(session)) { return cache.get(session).collection.put(objectStates); } - throw new window.cvat.exceptions.DataError( + throw new DataError( 'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before', ); } function selectObject(session, objectStates, x, y) { - const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job'; + const sessionType = session instanceof Task ? 'task' : 'job'; const cache = getCache(sessionType); if (cache.has(session)) { return cache.get(session).collection.select(objectStates, x, y); } - throw new window.cvat.exceptions.DataError( + throw new DataError( 'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before', ); } async function uploadAnnotations(session, file, format) { - const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job'; + const sessionType = session instanceof Task ? 'task' : 'job'; await serverProxy.annotations.uploadAnnotations(sessionType, session.id, file, format); } async function dumpAnnotations(session, name, format) { - const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job'; + const sessionType = session instanceof Task ? 'task' : 'job'; const result = await serverProxy.annotations .dumpAnnotations(sessionType, session.id, name, format); return result; diff --git a/cvat-core/src/api-implementation.js b/cvat-core/src/api-implementation.js index 0a7d8bb7..fde9c25b 100644 --- a/cvat-core/src/api-implementation.js +++ b/cvat-core/src/api-implementation.js @@ -21,9 +21,18 @@ checkFilter, } = require('./common'); + const { + TaskStatus, + TaskMode, + } = require('./enums'); + + const User = require('./user'); + const { ArgumentError } = require('./exceptions'); + const { Task } = require('./session'); + function implementAPI(cvat) { cvat.plugins.list.implementation = PluginRegistry.list; - cvat.plugins.register.implementation = PluginRegistry.register; + cvat.plugins.register.implementation = PluginRegistry.register.bind(cvat); cvat.server.about.implementation = async () => { const result = await serverProxy.server.about(); @@ -56,7 +65,7 @@ users = await serverProxy.users.getUsers(); } - users = users.map(user => new window.cvat.classes.User(user)); + users = users.map(user => new User(user)); return users; }; @@ -67,13 +76,13 @@ }); if (('taskID' in filter) && ('jobID' in filter)) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'Only one of fields "taskID" and "jobID" allowed simultaneously', ); } if (!Object.keys(filter).length) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'Job filter must not be empty', ); } @@ -90,7 +99,7 @@ // If task was found by its id, then create task instance and get Job instance from it if (tasks !== null && tasks.length) { - const task = new window.cvat.classes.Task(tasks[0]); + const task = new Task(tasks[0]); return filter.jobID ? task.jobs.filter(job => job.id === filter.jobID) : task.jobs; } @@ -105,13 +114,13 @@ owner: isString, assignee: isString, search: isString, - status: isEnum.bind(window.cvat.enums.TaskStatus), - mode: isEnum.bind(window.cvat.enums.TaskMode), + status: isEnum.bind(TaskStatus), + mode: isEnum.bind(TaskMode), }); if ('search' in filter && Object.keys(filter).length > 1) { if (!('page' in filter && Object.keys(filter).length === 2)) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'Do not use the filter field "search" with others', ); } @@ -119,7 +128,7 @@ if ('id' in filter && Object.keys(filter).length > 1) { if (!('page' in filter && Object.keys(filter).length === 2)) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'Do not use the filter field "id" with others', ); } @@ -133,7 +142,7 @@ } const tasksData = await serverProxy.tasks.getTasks(searchParams.toString()); - const tasks = tasksData.map(task => new window.cvat.classes.Task(task)); + const tasks = tasksData.map(task => new Task(task)); tasks.count = tasksData.count; return tasks; diff --git a/cvat-core/src/api.js b/cvat-core/src/api.js index af53cf6f..bf61f409 100644 --- a/cvat-core/src/api.js +++ b/cvat-core/src/api.js @@ -12,7 +12,7 @@ * @module API */ -(() => { +function build() { const PluginRegistry = require('./plugins'); const User = require('./user'); const ObjectState = require('./object-state'); @@ -41,7 +41,7 @@ } = require('./exceptions'); const pjson = require('../package.json'); - const clientID = +Date.now().toString().substr(-6); + const config = require('./config'); /** * API entrypoint @@ -274,7 +274,7 @@ * put: { * // The first argument "self" is a plugin, like in a case above * // The second argument is an argument of the - * // cvat.Job.annotations.put() + * // Job.annotations.put() * // It contains an array of objects to put * // In this sample we round objects coordinates and save them * enter(self, objects) { @@ -301,7 +301,7 @@ * internal: { * async getPlugins() { * // Collect information about installed plugins - * const plugins = await window.cvat.plugins.list(); + * const plugins = await cvat.plugins.list(); * return plugins.map((el) => { * return { * name: el.name, @@ -362,12 +362,32 @@ * value which is displayed in a logs * @memberof module:API.cvat.config */ - backendAPI: 'http://localhost:7000/api/v1', - proxy: false, - taskID: undefined, - jobID: undefined, - clientID: { - get: () => clientID, + get backendAPI() { + return config.backendAPI; + }, + set backendAPI(value) { + config.backendAPI = value; + }, + get proxy() { + return config.proxy; + }, + set proxy(value) { + config.proxy = value; + }, + get taskID() { + return config.taskID; + }, + set taskID(value) { + config.taskID = value; + }, + get jobID() { + return config.jobID; + }, + set jobID(value) { + config.jobID = value; + }, + get clientID() { + return config.clientID; }, }, /** @@ -439,18 +459,15 @@ cvat.plugins = Object.freeze(cvat.plugins); cvat.client = Object.freeze(cvat.client); cvat.enums = Object.freeze(cvat.enums); - cvat.Job = Object.freeze(cvat.Job); - cvat.Task = Object.freeze(cvat.Task); const implementAPI = require('./api-implementation'); - if (typeof (window) === 'undefined') { - // Dummy browser environment - require('browser-env')(); - } Math.clamp = function (value, min, max) { return Math.min(Math.max(value, min), max); }; - window.cvat = Object.freeze(implementAPI(cvat)); -})(); + const implemented = Object.freeze(implementAPI(cvat)); + return implemented; +} + +module.exports = build(); diff --git a/cvat-core/src/common.js b/cvat-core/src/common.js index dadbbf52..b0e6f014 100644 --- a/cvat-core/src/common.js +++ b/cvat-core/src/common.js @@ -3,7 +3,13 @@ * SPDX-License-Identifier: MIT */ +/* global + require:false +*/ + (() => { + const { ArgumentError } = require('./exceptions'); + function isBoolean(value) { return typeof (value) === 'boolean'; } @@ -33,11 +39,11 @@ for (const prop in filter) { if (Object.prototype.hasOwnProperty.call(filter, prop)) { if (!(prop in fields)) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Unsupported filter property has been recieved: "${prop}"`, ); } else if (!fields[prop](filter[prop])) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Received filter property "${prop}" is not satisfied for checker`, ); } @@ -53,20 +59,20 @@ return; } - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `"${name}" is expected to be "${type}", but "${typeof (value)}" has been got.`, ); } } else if (instance) { if (!(value instanceof instance)) { if (value !== undefined) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `"${name}" is expected to be ${instance.name}, but ` + `"${value.constructor.name}" has been got`, ); } - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `"${name}" is expected to be ${instance.name}, but "undefined" has been got.`, ); } diff --git a/cvat-core/src/config.js b/cvat-core/src/config.js new file mode 100644 index 00000000..30a86de3 --- /dev/null +++ b/cvat-core/src/config.js @@ -0,0 +1,12 @@ +/* +* Copyright (C) 2018 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +module.exports = { + backendAPI: 'http://localhost:7000/api/v1', + proxy: false, + taskID: undefined, + jobID: undefined, + clientID: +Date.now().toString().substr(-6), +}; diff --git a/cvat-core/src/exceptions.js b/cvat-core/src/exceptions.js index e10c6bae..e80c4ce0 100644 --- a/cvat-core/src/exceptions.js +++ b/cvat-core/src/exceptions.js @@ -10,6 +10,7 @@ (() => { const Platform = require('platform'); const ErrorStackParser = require('error-stack-parser'); + const config = require('./config'); /** * Base exception class @@ -35,7 +36,7 @@ jobID, taskID, clientID, - } = window.cvat.config; + } = config; const projID = undefined; // wasn't implemented diff --git a/cvat-core/src/frames.js b/cvat-core/src/frames.js index 5381fa8a..8a672387 100644 --- a/cvat-core/src/frames.js +++ b/cvat-core/src/frames.js @@ -11,6 +11,7 @@ (() => { const PluginRegistry = require('./plugins'); const serverProxy = require('./server-proxy'); + const { ArgumentError } = require('./exceptions'); // This is the frames storage const frameDataCache = {}; @@ -78,11 +79,11 @@ if (!(this.number in frameCache[this.tid])) { const frame = await serverProxy.frames.getData(this.tid, this.number); - if (window.URL.createObjectURL) { // browser env - const url = window.URL.createObjectURL(new Blob([frame])); - frameCache[this.tid][this.number] = url; - } else { + if (typeof (module) !== 'undefined' && module.exports) { frameCache[this.tid][this.number] = global.Buffer.from(frame, 'binary').toString('base64'); + } else { + const url = URL.createObjectURL(new Blob([frame])); + frameCache[this.tid][this.number] = url; } } @@ -103,14 +104,14 @@ [size] = frameDataCache[taskID].meta; } else if (mode === 'annotation') { if (frame >= frameDataCache[taskID].meta.length) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Meta information about frame ${frame} can't be received from the server`, ); } else { size = frameDataCache[taskID].meta[frame]; } } else { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Invalid mode is specified ${mode}`, ); } diff --git a/cvat-core/src/labels.js b/cvat-core/src/labels.js index 35c31278..de6ba99c 100644 --- a/cvat-core/src/labels.js +++ b/cvat-core/src/labels.js @@ -3,7 +3,14 @@ * SPDX-License-Identifier: MIT */ +/* global + require:false +*/ + (() => { + const { AttributeType } = require('./enums'); + const { ArgumentError } = require('./exceptions'); + /** * Class representing an attribute * @memberof module:API.cvat.classes @@ -32,8 +39,8 @@ } } - if (!Object.values(window.cvat.enums.AttributeType).includes(data.input_type)) { - throw new window.cvat.exceptions.ArgumentError( + if (!Object.values(AttributeType).includes(data.input_type)) { + throw new ArgumentError( `Got invalid attribute type ${data.input_type}`, ); } @@ -144,7 +151,7 @@ if (Object.prototype.hasOwnProperty.call(initialData, 'attributes') && Array.isArray(initialData.attributes)) { for (const attrData of initialData.attributes) { - data.attributes.push(new window.cvat.classes.Attribute(attrData)); + data.attributes.push(new Attribute(attrData)); } } diff --git a/cvat-core/src/object-state.js b/cvat-core/src/object-state.js index 23c406c0..0f6ddb64 100644 --- a/cvat-core/src/object-state.js +++ b/cvat-core/src/object-state.js @@ -9,6 +9,7 @@ (() => { const PluginRegistry = require('./plugins'); + const { ArgumentError } = require('./exceptions'); /** * Class representing a state of an object on a specific frame @@ -162,7 +163,7 @@ data.updateFlags.points = true; data.points = [...points]; } else { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'Points are expected to be an array ' + `but got ${typeof (points) === 'object' ? points.constructor.name : typeof (points)}`, @@ -261,7 +262,7 @@ get: () => data.attributes, set: (attributes) => { if (typeof (attributes) !== 'object') { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'Attributes are expected to be an object ' + `but got ${typeof (attributes) === 'object' ? attributes.constructor.name : typeof (attributes)}`, diff --git a/cvat-core/src/plugins.js b/cvat-core/src/plugins.js index e1054853..7dc08384 100644 --- a/cvat-core/src/plugins.js +++ b/cvat-core/src/plugins.js @@ -14,7 +14,7 @@ class PluginRegistry { static async apiWrapper(wrappedFunc, ...args) { // I have to optimize the wrapper - const pluginList = await window.cvat.plugins.list.implementation(); + const pluginList = await PluginRegistry.list(); for (const plugin of pluginList) { const pluginDecorators = plugin.functions .filter(obj => obj.callback === wrappedFunc)[0]; @@ -52,6 +52,7 @@ return result; } + // Called with cvat context static async register(plug) { const functions = []; @@ -92,7 +93,7 @@ functions.push(decorator); } }(plug, { - cvat: window.cvat, + cvat: this, })); Object.defineProperty(plug, 'functions', { diff --git a/cvat-core/src/server-proxy.js b/cvat-core/src/server-proxy.js index 32dec01d..a7b18b3e 100644 --- a/cvat-core/src/server-proxy.js +++ b/cvat-core/src/server-proxy.js @@ -9,6 +9,14 @@ */ (() => { + const FormData = require('form-data'); + const { + ServerError, + ScriptingError, + } = require('./exceptions'); + + const config = require('./config'); + class ServerProxy { constructor() { const Cookie = require('js-cookie'); @@ -25,16 +33,16 @@ } async function about() { - const { backendAPI } = window.cvat.config; + const { backendAPI } = config; let response = null; try { response = await Axios.get(`${backendAPI}/server/about`, { - proxy: window.cvat.config.proxy, + proxy: config.proxy, }); } catch (errorData) { const code = errorData.response ? errorData.response.status : errorData.code; - throw new window.cvat.exceptions.ServerError( + throw new ServerError( 'Could not get "about" information from the server', code, ); @@ -44,17 +52,17 @@ } async function share(directory) { - const { backendAPI } = window.cvat.config; + const { backendAPI } = config; directory = encodeURIComponent(directory); let response = null; try { response = await Axios.get(`${backendAPI}/server/share?directory=${directory}`, { - proxy: window.cvat.config.proxy, + proxy: config.proxy, }); } catch (errorData) { const code = errorData.response ? errorData.response.status : errorData.code; - throw new window.cvat.exceptions.ServerError( + throw new ServerError( 'Could not get "share" information from the server', code, ); @@ -64,18 +72,18 @@ } async function exception(exceptionObject) { - const { backendAPI } = window.cvat.config; + const { backendAPI } = config; try { await Axios.post(`${backendAPI}/server/exception`, JSON.stringify(exceptionObject), { - proxy: window.cvat.config.proxy, + proxy: config.proxy, headers: { 'Content-Type': 'application/json', }, }); } catch (errorData) { const code = errorData.response ? errorData.response.status : errorData.code; - throw new window.cvat.exceptions.ServerError( + throw new ServerError( 'Could not send an exception to the server', code, ); @@ -111,7 +119,7 @@ if (csrftoken) { setCSRFHeader(csrftoken); } else { - throw new window.cvat.exceptions.ScriptingError( + throw new ScriptingError( 'An environment has been detected as a browser' + ', but CSRF token has not been found in cookies', ); @@ -120,15 +128,15 @@ } } - const host = window.cvat.config.backendAPI.slice(0, -7); + const host = config.backendAPI.slice(0, -7); let csrf = null; try { csrf = await Axios.get(`${host}/auth/csrf`, { - proxy: window.cvat.config.proxy, + proxy: config.proxy, }); } catch (errorData) { const code = errorData.response ? errorData.response.status : errorData.code; - throw new window.cvat.exceptions.ServerError( + throw new ServerError( 'Could not get CSRF token from a server', code, ); @@ -148,7 +156,7 @@ authentificationData, { 'Content-Type': 'application/x-www-form-urlencoded', - proxy: window.cvat.config.proxy, + proxy: config.proxy, // do not redirect to a dashboard, // otherwise we don't get a session id in a response maxRedirects: 0, @@ -161,7 +169,7 @@ } else { const code = errorData.response ? errorData.response.status : errorData.code; - throw new window.cvat.exceptions.ServerError( + throw new ServerError( 'Could not login on a server', code, ); @@ -170,7 +178,7 @@ // TODO: Perhaps we should redesign the authorization method on the server. if (authentificationResponse.data.includes('didn\'t match')) { - throw new window.cvat.exceptions.ServerError( + throw new ServerError( 'The pair login/password is invalid', 403, ); @@ -180,15 +188,15 @@ } async function logout() { - const host = window.cvat.config.backendAPI.slice(0, -7); + const host = config.backendAPI.slice(0, -7); try { await Axios.get(`${host}/auth/logout`, { - proxy: window.cvat.config.proxy, + proxy: config.proxy, }); } catch (errorData) { const code = errorData.response ? errorData.response.status : errorData.code; - throw new window.cvat.exceptions.ServerError( + throw new ServerError( 'Could not logout from the server', code, ); @@ -196,16 +204,16 @@ } async function getTasks(filter = '') { - const { backendAPI } = window.cvat.config; + const { backendAPI } = config; let response = null; try { response = await Axios.get(`${backendAPI}/tasks?${filter}`, { - proxy: window.cvat.config.proxy, + proxy: config.proxy, }); } catch (errorData) { const code = errorData.response ? errorData.response.status : errorData.code; - throw new window.cvat.exceptions.ServerError( + throw new ServerError( 'Could not get tasks from a server', code, ); @@ -216,18 +224,18 @@ } async function saveTask(id, taskData) { - const { backendAPI } = window.cvat.config; + const { backendAPI } = config; try { await Axios.patch(`${backendAPI}/tasks/${id}`, JSON.stringify(taskData), { - proxy: window.cvat.config.proxy, + proxy: config.proxy, headers: { 'Content-Type': 'application/json', }, }); } catch (errorData) { const code = errorData.response ? errorData.response.status : errorData.code; - throw new window.cvat.exceptions.ServerError( + throw new ServerError( 'Could not save the task on the server', code, ); @@ -235,13 +243,13 @@ } async function deleteTask(id) { - const { backendAPI } = window.cvat.config; + const { backendAPI } = config; try { await Axios.delete(`${backendAPI}/tasks/${id}`); } catch (errorData) { const code = errorData.response ? errorData.response.status : errorData.code; - throw new window.cvat.exceptions.ServerError( + throw new ServerError( 'Could not delete the task from the server', code, ); @@ -249,7 +257,7 @@ } async function createTask(taskData, files, onUpdate) { - const { backendAPI } = window.cvat.config; + const { backendAPI } = config; async function wait(id) { return new Promise((resolve, reject) => { @@ -266,14 +274,14 @@ } else if (response.data.state === 'Failed') { // If request has been successful, but task hasn't been created // Then passed data is wrong and we can pass code 400 - reject(new window.cvat.exceptions.ServerError( + reject(new ServerError( 'Could not create the task on the server', 400, )); } else { // If server has another status, it is unexpected // Therefore it is server error and we can pass code 500 - reject(new window.cvat.exceptions.ServerError( + reject(new ServerError( `Unknown task state has been recieved: ${response.data.state}`, 500, )); @@ -282,7 +290,7 @@ const code = errorData.response ? errorData.response.status : errorData.code; - reject(new window.cvat.exceptions.ServerError( + reject(new ServerError( 'Data uploading error occured', code, )); @@ -293,7 +301,7 @@ }); } - const batchOfFiles = new window.FormData(); + const batchOfFiles = new FormData(); for (const key in files) { if (Object.prototype.hasOwnProperty.call(files, key)) { for (let i = 0; i < files[key].length; i++) { @@ -307,14 +315,14 @@ onUpdate('The task is being created on the server..'); try { response = await Axios.post(`${backendAPI}/tasks`, JSON.stringify(taskData), { - proxy: window.cvat.config.proxy, + proxy: config.proxy, headers: { 'Content-Type': 'application/json', }, }); } catch (errorData) { const code = errorData.response ? errorData.response.status : errorData.code; - throw new window.cvat.exceptions.ServerError( + throw new ServerError( 'Could not put task to the server', code, ); @@ -323,12 +331,12 @@ onUpdate('The data is being uploaded to the server..'); try { await Axios.post(`${backendAPI}/tasks/${response.data.id}/data`, batchOfFiles, { - proxy: window.cvat.config.proxy, + proxy: config.proxy, }); } catch (errorData) { await deleteTask(response.data.id); const code = errorData.response ? errorData.response.status : errorData.code; - throw new window.cvat.exceptions.ServerError( + throw new ServerError( 'Could not put data to the server', code, ); @@ -347,16 +355,16 @@ } async function getJob(jobID) { - const { backendAPI } = window.cvat.config; + const { backendAPI } = config; let response = null; try { response = await Axios.get(`${backendAPI}/jobs/${jobID}`, { - proxy: window.cvat.config.proxy, + proxy: config.proxy, }); } catch (errorData) { const code = errorData.response ? errorData.response.status : errorData.code; - throw new window.cvat.exceptions.ServerError( + throw new ServerError( 'Could not get jobs from a server', code, ); @@ -366,18 +374,18 @@ } async function saveJob(id, jobData) { - const { backendAPI } = window.cvat.config; + const { backendAPI } = config; try { await Axios.patch(`${backendAPI}/jobs/${id}`, JSON.stringify(jobData), { - proxy: window.cvat.config.proxy, + proxy: config.proxy, headers: { 'Content-Type': 'application/json', }, }); } catch (errorData) { const code = errorData.response ? errorData.response.status : errorData.code; - throw new window.cvat.exceptions.ServerError( + throw new ServerError( 'Could not save the job on the server', code, ); @@ -385,16 +393,16 @@ } async function getUsers() { - const { backendAPI } = window.cvat.config; + const { backendAPI } = config; let response = null; try { response = await Axios.get(`${backendAPI}/users`, { - proxy: window.cvat.config.proxy, + proxy: config.proxy, }); } catch (errorData) { const code = errorData.response ? errorData.response.status : errorData.code; - throw new window.cvat.exceptions.ServerError( + throw new ServerError( 'Could not get users from the server', code, ); @@ -404,16 +412,16 @@ } async function getSelf() { - const { backendAPI } = window.cvat.config; + const { backendAPI } = config; let response = null; try { response = await Axios.get(`${backendAPI}/users/self`, { - proxy: window.cvat.config.proxy, + proxy: config.proxy, }); } catch (errorData) { const code = errorData.response ? errorData.response.status : errorData.code; - throw new window.cvat.exceptions.ServerError( + throw new ServerError( 'Could not get users from the server', code, ); @@ -423,17 +431,17 @@ } async function getData(tid, frame) { - const { backendAPI } = window.cvat.config; + const { backendAPI } = config; let response = null; try { response = await Axios.get(`${backendAPI}/tasks/${tid}/frames/${frame}`, { - proxy: window.cvat.config.proxy, + proxy: config.proxy, responseType: 'blob', }); } catch (errorData) { const code = errorData.response ? errorData.response.status : errorData.code; - throw new window.cvat.exceptions.ServerError( + throw new ServerError( `Could not get frame ${frame} for the task ${tid} from the server`, code, ); @@ -443,16 +451,16 @@ } async function getMeta(tid) { - const { backendAPI } = window.cvat.config; + const { backendAPI } = config; let response = null; try { response = await Axios.get(`${backendAPI}/tasks/${tid}/frames/meta`, { - proxy: window.cvat.config.proxy, + proxy: config.proxy, }); } catch (errorData) { const code = errorData.response ? errorData.response.status : errorData.code; - throw new window.cvat.exceptions.ServerError( + throw new ServerError( `Could not get frame meta info for the task ${tid} from the server`, code, ); @@ -463,16 +471,16 @@ // Session is 'task' or 'job' async function getAnnotations(session, id) { - const { backendAPI } = window.cvat.config; + const { backendAPI } = config; let response = null; try { response = await Axios.get(`${backendAPI}/${session}s/${id}/annotations`, { - proxy: window.cvat.config.proxy, + proxy: config.proxy, }); } catch (errorData) { const code = errorData.response ? errorData.response.status : errorData.code; - throw new window.cvat.exceptions.ServerError( + throw new ServerError( `Could not get annotations for the ${session} ${id} from the server`, code, ); @@ -483,7 +491,7 @@ // Session is 'task' or 'job' async function updateAnnotations(session, id, data, action) { - const { backendAPI } = window.cvat.config; + const { backendAPI } = config; let requestFunc = null; let url = null; if (action.toUpperCase() === 'PUT') { @@ -497,14 +505,14 @@ let response = null; try { response = await requestFunc(url, JSON.stringify(data), { - proxy: window.cvat.config.proxy, + proxy: config.proxy, headers: { 'Content-Type': 'application/json', }, }); } catch (errorData) { const code = errorData.response ? errorData.response.status : errorData.code; - throw new window.cvat.exceptions.ServerError( + throw new ServerError( `Could not updated annotations for the ${session} ${id} on the server`, code, ); @@ -515,7 +523,7 @@ // Session is 'task' or 'job' async function uploadAnnotations(session, id, file, format) { - const { backendAPI } = window.cvat.config; + const { backendAPI } = config; let annotationData = new FormData(); annotationData.append('annotation_file', file); @@ -525,7 +533,7 @@ try { const response = await Axios .post(`${backendAPI}/${session}s/${id}/annotations?upload_format=${format}`, annotationData, { - proxy: window.cvat.config.proxy, + proxy: config.proxy, }); if (response.status === 202) { annotationData = new FormData(); @@ -536,7 +544,7 @@ } catch (errorData) { const code = errorData.response ? errorData.response.status : errorData.code; - const error = new window.cvat.exceptions.ServerError( + const error = new ServerError( `Could not upload annotations for the ${session} ${id}`, code, ); @@ -550,7 +558,7 @@ // Session is 'task' or 'job' async function dumpAnnotations(id, name, format) { - const { backendAPI } = window.cvat.config; + const { backendAPI } = config; const filename = name.replace(/\//g, '_'); let url = `${backendAPI}/tasks/${id}/annotations/${filename}?dump_format=${format}`; @@ -559,7 +567,7 @@ try { const response = await Axios .get(`${url}`, { - proxy: window.cvat.config.proxy, + proxy: config.proxy, }); if (response.status === 202) { setTimeout(request, 3000); @@ -570,7 +578,7 @@ } catch (errorData) { const code = errorData.response ? errorData.response.status : errorData.code; - const error = new window.cvat.exceptions.ServerError( + const error = new ServerError( `Could not dump annotations for the task ${id} from the server`, code, ); diff --git a/cvat-core/src/session.js b/cvat-core/src/session.js index 5c764eb4..0bb3a12d 100644 --- a/cvat-core/src/session.js +++ b/cvat-core/src/session.js @@ -11,20 +11,9 @@ const PluginRegistry = require('./plugins'); const serverProxy = require('./server-proxy'); const { getFrame } = require('./frames'); - const { - getAnnotations, - putAnnotations, - saveAnnotations, - hasUnsavedChanges, - mergeAnnotations, - splitAnnotations, - groupAnnotations, - clearAnnotations, - selectObject, - annotationsStatistics, - uploadAnnotations, - dumpAnnotations, - } = require('./annotations'); + const { ArgumentError } = require('./exceptions'); + const { TaskStatus } = require('./enums'); + const { Label } = require('./labels'); function buildDublicatedAPI(prototype) { Object.defineProperties(prototype, { @@ -509,7 +498,7 @@ } if (data[property] === undefined) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Job field "${property}" was not initialized`, ); } @@ -539,7 +528,7 @@ get: () => data.assignee, set: () => (assignee) => { if (!Number.isInteger(assignee) || assignee < 0) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'Value must be a non negative integer', ); } @@ -556,7 +545,7 @@ status: { get: () => data.status, set: (status) => { - const type = window.cvat.enums.TaskStatus; + const type = TaskStatus; let valueInEnum = false; for (const value in type) { if (type[value] === status) { @@ -566,7 +555,7 @@ } if (!valueInEnum) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'Value must be a value from the enumeration cvat.enums.TaskStatus', ); } @@ -647,128 +636,24 @@ } } - // Fill up the prototype by properties. Class syntax doesn't allow do it - // So, we do it seperately - buildDublicatedAPI(Job.prototype); - - Job.prototype.save.implementation = async function () { - // TODO: Add ability to change an assignee - if (this.id) { - const jobData = { - status: this.status, - }; - - await serverProxy.jobs.saveJob(this.id, jobData); - return this; - } - - throw new window.cvat.exceptions.ArgumentError( - 'Can not save job without and id', - ); - }; - - Job.prototype.frames.get.implementation = async function (frame) { - if (!Number.isInteger(frame) || frame < 0) { - throw new window.cvat.exceptions.ArgumentError( - `Frame must be a positive integer. Got: "${frame}"`, - ); - } - - if (frame < this.startFrame || frame > this.stopFrame) { - throw new window.cvat.exceptions.ArgumentError( - `The frame with number ${frame} is out of the job`, - ); - } - - const frameData = await getFrame(this.task.id, this.task.mode, frame); - return frameData; - }; - - // TODO: Check filter for annotations - Job.prototype.annotations.get.implementation = async function (frame, filter) { - if (frame < this.startFrame || frame > this.stopFrame) { - throw new window.cvat.exceptions.ArgumentError( - `Frame ${frame} does not exist in the job`, - ); - } - - const annotationsData = await getAnnotations(this, frame, filter); - return annotationsData; - }; - - Job.prototype.annotations.save.implementation = async function (onUpdate) { - const result = await saveAnnotations(this, onUpdate); - return result; - }; - - Job.prototype.annotations.merge.implementation = async function (objectStates) { - const result = await mergeAnnotations(this, objectStates); - return result; - }; - - Job.prototype.annotations.split.implementation = async function (objectState, frame) { - const result = await splitAnnotations(this, objectState, frame); - return result; - }; - - Job.prototype.annotations.group.implementation = async function (objectStates, reset) { - const result = await groupAnnotations(this, objectStates, reset); - return result; - }; - - Job.prototype.annotations.hasUnsavedChanges.implementation = function () { - const result = hasUnsavedChanges(this); - return result; - }; - - Job.prototype.annotations.clear.implementation = async function (reload) { - const result = await clearAnnotations(this, reload); - return result; - }; - - Job.prototype.annotations.select.implementation = function (frame, x, y) { - const result = selectObject(this, frame, x, y); - return result; - }; - - Job.prototype.annotations.statistics.implementation = function () { - const result = annotationsStatistics(this); - return result; - }; - - Job.prototype.annotations.put.implementation = function (objectStates) { - const result = putAnnotations(this, objectStates); - return result; - }; - - Job.prototype.annotations.upload.implementation = async function (file, format) { - const result = await uploadAnnotations(this, file, format); - return result; - }; - - Job.prototype.annotations.dump.implementation = async function (name, format) { - const result = await dumpAnnotations(this, name, format); - return result; - }; - /** * Class representing a task * @memberof module:API.cvat.classes * @extends Session */ class Task extends Session { - /** - * In a fact you need use the constructor only if you want to create a task - * @param {object} initialData - Object which is used for initalization - *
It can contain keys: - *
  • name - *
  • assignee - *
  • bug_tracker - *
  • z_order - *
  • labels - *
  • segment_size - *
  • overlap - */ + /** + * In a fact you need use the constructor only if you want to create a task + * @param {object} initialData - Object which is used for initalization + *
    It can contain keys: + *
  • name + *
  • assignee + *
  • bug_tracker + *
  • z_order + *
  • labels + *
  • segment_size + *
  • overlap + */ constructor(initialData) { super(); const data = { @@ -786,6 +671,9 @@ segment_size: undefined, z_order: undefined, image_quality: undefined, + start_frame: undefined, + stop_frame: undefined, + frame_filter: undefined, }; for (const property in data) { @@ -807,7 +695,7 @@ for (const segment of initialData.segments) { if (Array.isArray(segment.jobs)) { for (const job of segment.jobs) { - const jobInstance = new window.cvat.classes.Job({ + const jobInstance = new Job({ url: job.url, id: job.id, assignee: job.assignee, @@ -824,7 +712,7 @@ if (Array.isArray(initialData.labels)) { for (const label of initialData.labels) { - const classInstance = new window.cvat.classes.Label(label); + const classInstance = new Label(label); data.labels.push(classInstance); } } @@ -851,7 +739,7 @@ get: () => data.name, set: (value) => { if (!value.trim().length) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'Value must not be empty', ); } @@ -911,7 +799,7 @@ get: () => data.assignee, set: () => (assignee) => { if (!Number.isInteger(assignee) || assignee < 0) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'Value must be a non negative integer', ); } @@ -962,7 +850,7 @@ get: () => data.overlap, set: (overlap) => { if (!Number.isInteger(overlap) || overlap < 0) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'Value must be a non negative integer', ); } @@ -980,7 +868,7 @@ get: () => data.segment_size, set: (segment) => { if (!Number.isInteger(segment) || segment < 0) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'Value must be a positive integer', ); } @@ -998,7 +886,7 @@ get: () => data.z_order, set: (zOrder) => { if (typeof (zOrder) !== 'boolean') { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'Value must be a boolean', ); } @@ -1016,7 +904,7 @@ get: () => data.image_quality, set: (quality) => { if (!Number.isInteger(quality) || quality < 0) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'Value must be a positive integer', ); } @@ -1035,14 +923,14 @@ get: () => [...data.labels], set: (labels) => { if (!Array.isArray(labels)) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( 'Value must be an array of Labels', ); } for (const label of labels) { - if (!(label instanceof window.cvat.classes.Label)) { - throw new window.cvat.exceptions.ArgumentError( + if (!(label instanceof Label)) { + throw new ArgumentError( 'Each array value must be an instance of Label. ' + `${typeof (label)} was found`, ); @@ -1078,14 +966,14 @@ get: () => [...data.files.server_files], set: (serverFiles) => { if (!Array.isArray(serverFiles)) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Value must be an array. But ${typeof (serverFiles)} has been got.`, ); } for (const value of serverFiles) { if (typeof (value) !== 'string') { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Array values must be a string. But ${typeof (value)} has been got.`, ); } @@ -1106,14 +994,14 @@ get: () => [...data.files.client_files], set: (clientFiles) => { if (!Array.isArray(clientFiles)) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Value must be an array. But ${typeof (clientFiles)} has been got.`, ); } for (const value of clientFiles) { - if (!(value instanceof window.File)) { - throw new window.cvat.exceptions.ArgumentError( + if (!(value instanceof File)) { + throw new ArgumentError( `Array values must be a File. But ${value.constructor.name} has been got.`, ); } @@ -1134,14 +1022,14 @@ get: () => [...data.files.remote_files], set: (remoteFiles) => { if (!Array.isArray(remoteFiles)) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Value must be an array. But ${typeof (remoteFiles)} has been got.`, ); } for (const value of remoteFiles) { if (typeof (value) !== 'string') { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Array values must be a string. But ${typeof (value)} has been got.`, ); } @@ -1150,6 +1038,64 @@ Array.prototype.push.apply(data.files.remote_files, remoteFiles); }, }, + /** + * The first frame of a video to annotation + * @name startFrame + * @type {integer} + * @memberof module:API.cvat.classes.Task + * @instance + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + startFrame: { + get: () => data.start_frame, + set: (frame) => { + if (!Number.isInteger(frame) || frame < 0) { + throw new ArgumentError( + 'Value must be a not negative integer', + ); + } + data.start_frame = frame; + }, + }, + /** + * The last frame of a video to annotation + * @name stopFrame + * @type {integer} + * @memberof module:API.cvat.classes.Task + * @instance + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + stopFrame: { + get: () => data.stop_frame, + set: (frame) => { + if (!Number.isInteger(frame) || frame < 0) { + throw new ArgumentError( + 'Value must be a not negative integer', + ); + } + data.stop_frame = frame; + }, + }, + /** + * Filter to ignore some frames during task creation + * @name frameFilter + * @type {string} + * @memberof module:API.cvat.classes.Task + * @instance + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + frameFilter: { + get: () => data.frame_filter, + set: (filter) => { + if (typeof (filter) !== 'string') { + throw new ArgumentError( + `Filter value must be a string. But ${typeof (filter)} has been got.`, + ); + } + + data.frame_filter = filter; + }, + }, })); // When we call a function, for example: task.annotations.get() @@ -1213,10 +1159,129 @@ } } - // Fill up the prototype by properties. Class syntax doesn't allow do it - // So, we do it seperately + module.exports = { + Job, + Task, + }; + + const { + getAnnotations, + putAnnotations, + saveAnnotations, + hasUnsavedChanges, + mergeAnnotations, + splitAnnotations, + groupAnnotations, + clearAnnotations, + selectObject, + annotationsStatistics, + uploadAnnotations, + dumpAnnotations, + } = require('./annotations'); + + buildDublicatedAPI(Job.prototype); buildDublicatedAPI(Task.prototype); + Job.prototype.save.implementation = async function () { + // TODO: Add ability to change an assignee + if (this.id) { + const jobData = { + status: this.status, + }; + + await serverProxy.jobs.saveJob(this.id, jobData); + return this; + } + + throw new ArgumentError( + 'Can not save job without and id', + ); + }; + + Job.prototype.frames.get.implementation = async function (frame) { + if (!Number.isInteger(frame) || frame < 0) { + throw new ArgumentError( + `Frame must be a positive integer. Got: "${frame}"`, + ); + } + + if (frame < this.startFrame || frame > this.stopFrame) { + throw new ArgumentError( + `The frame with number ${frame} is out of the job`, + ); + } + + const frameData = await getFrame(this.task.id, this.task.mode, frame); + return frameData; + }; + + // TODO: Check filter for annotations + Job.prototype.annotations.get.implementation = async function (frame, filter) { + if (frame < this.startFrame || frame > this.stopFrame) { + throw new ArgumentError( + `Frame ${frame} does not exist in the job`, + ); + } + + const annotationsData = await getAnnotations(this, frame, filter); + return annotationsData; + }; + + Job.prototype.annotations.save.implementation = async function (onUpdate) { + const result = await saveAnnotations(this, onUpdate); + return result; + }; + + Job.prototype.annotations.merge.implementation = async function (objectStates) { + const result = await mergeAnnotations(this, objectStates); + return result; + }; + + Job.prototype.annotations.split.implementation = async function (objectState, frame) { + const result = await splitAnnotations(this, objectState, frame); + return result; + }; + + Job.prototype.annotations.group.implementation = async function (objectStates, reset) { + const result = await groupAnnotations(this, objectStates, reset); + return result; + }; + + Job.prototype.annotations.hasUnsavedChanges.implementation = function () { + const result = hasUnsavedChanges(this); + return result; + }; + + Job.prototype.annotations.clear.implementation = async function (reload) { + const result = await clearAnnotations(this, reload); + return result; + }; + + Job.prototype.annotations.select.implementation = function (frame, x, y) { + const result = selectObject(this, frame, x, y); + return result; + }; + + Job.prototype.annotations.statistics.implementation = function () { + const result = annotationsStatistics(this); + return result; + }; + + Job.prototype.annotations.put.implementation = function (objectStates) { + const result = putAnnotations(this, objectStates); + return result; + }; + + Job.prototype.annotations.upload.implementation = async function (file, format) { + const result = await uploadAnnotations(this, file, format); + return result; + }; + + Job.prototype.annotations.dump.implementation = async function (name, format) { + const result = await dumpAnnotations(this, name, format); + return result; + }; + Task.prototype.save.implementation = async function saveTaskImplementation(onUpdate) { // TODO: Add ability to change an owner and an assignee if (typeof (this.id) !== 'undefined') { @@ -1248,6 +1313,15 @@ if (this.overlap) { taskData.overlap = this.overlap; } + if (this.startFrame) { + taskData.start_frame = this.startFrame; + } + if (this.stopFrame) { + taskData.stop_frame = this.stopFrame; + } + if (this.frameFilter) { + taskData.frame_filter = this.frameFilter; + } const taskFiles = { client_files: this.clientFiles, @@ -1266,13 +1340,13 @@ Task.prototype.frames.get.implementation = async function (frame) { if (!Number.isInteger(frame) || frame < 0) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Frame must be a positive integer. Got: "${frame}"`, ); } if (frame >= this.size) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `The frame with number ${frame} is out of the task`, ); } @@ -1284,13 +1358,13 @@ // TODO: Check filter for annotations Task.prototype.annotations.get.implementation = async function (frame, filter) { if (!Number.isInteger(frame) || frame < 0) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Frame must be a positive integer. Got: "${frame}"`, ); } if (frame >= this.size) { - throw new window.cvat.exceptions.ArgumentError( + throw new ArgumentError( `Frame ${frame} does not exist in the task`, ); } @@ -1353,9 +1427,4 @@ const result = await dumpAnnotations(this, name, format); return result; }; - - module.exports = { - Job, - Task, - }; })(); diff --git a/cvat-core/tests/api/annotations.js b/cvat-core/tests/api/annotations.js index 6d6f3174..7da68301 100644 --- a/cvat-core/tests/api/annotations.js +++ b/cvat-core/tests/api/annotations.js @@ -16,7 +16,7 @@ jest.mock('../../src/server-proxy', () => { }); // Initialize api -require('../../src/api'); +window.cvat = require('../../src/api'); // Test cases describe('Feature: get annotations', () => { diff --git a/cvat-core/tests/api/frames.js b/cvat-core/tests/api/frames.js index 1fb76d18..2ecb51c9 100644 --- a/cvat-core/tests/api/frames.js +++ b/cvat-core/tests/api/frames.js @@ -16,7 +16,7 @@ jest.mock('../../src/server-proxy', () => { }); // Initialize api -require('../../src/api'); +window.cvat = require('../../src/api'); const { FrameData } = require('../../src/frames'); diff --git a/cvat-core/tests/api/jobs.js b/cvat-core/tests/api/jobs.js index 9fc48132..2a68c415 100644 --- a/cvat-core/tests/api/jobs.js +++ b/cvat-core/tests/api/jobs.js @@ -16,7 +16,7 @@ jest.mock('../../src/server-proxy', () => { }); // Initialize api -require('../../src/api'); +window.cvat = require('../../src/api'); const { Job } = require('../../src/session'); diff --git a/cvat-core/tests/api/object-state.js b/cvat-core/tests/api/object-state.js index d1dddd83..96988b3e 100644 --- a/cvat-core/tests/api/object-state.js +++ b/cvat-core/tests/api/object-state.js @@ -16,7 +16,7 @@ jest.mock('../../src/server-proxy', () => { }); // Initialize api -require('../../src/api'); +window.cvat = require('../../src/api'); describe('Feature: set attributes for an object state', () => { test('set a valid value', () => { diff --git a/cvat-core/tests/api/plugins.js b/cvat-core/tests/api/plugins.js index 92ba06c1..2db3cdd5 100644 --- a/cvat-core/tests/api/plugins.js +++ b/cvat-core/tests/api/plugins.js @@ -16,7 +16,7 @@ jest.mock('../../src/server-proxy', () => { }); // Initialize api -require('../../src/api'); +window.cvat = require('../../src/api'); describe('Feature: dummy feature', () => { test('dummy test', async () => { diff --git a/cvat-core/tests/api/server.js b/cvat-core/tests/api/server.js index b6f38c8f..d48e664d 100644 --- a/cvat-core/tests/api/server.js +++ b/cvat-core/tests/api/server.js @@ -16,7 +16,7 @@ jest.mock('../../src/server-proxy', () => { }); // Initialize api -require('../../src/api'); +window.cvat = require('../../src/api'); // Test cases describe('Feature: get info about cvat', () => { diff --git a/cvat-core/tests/api/tasks.js b/cvat-core/tests/api/tasks.js index 030ae6e0..ac6560b1 100644 --- a/cvat-core/tests/api/tasks.js +++ b/cvat-core/tests/api/tasks.js @@ -16,7 +16,7 @@ jest.mock('../../src/server-proxy', () => { }); // Initialize api -require('../../src/api'); +window.cvat = require('../../src/api'); const { Task } = require('../../src/session'); diff --git a/cvat-core/tests/api/user.js b/cvat-core/tests/api/user.js index d763ebd2..9d6765dc 100644 --- a/cvat-core/tests/api/user.js +++ b/cvat-core/tests/api/user.js @@ -16,7 +16,7 @@ jest.mock('../../src/server-proxy', () => { }); // Initialize api -require('../../src/api'); +window.cvat = require('../../src/api'); const User = require('../../src/user'); diff --git a/cvat-core/tests/mocks/server-proxy.mock.js b/cvat-core/tests/mocks/server-proxy.mock.js index e18a3db1..56d83924 100644 --- a/cvat-core/tests/mocks/server-proxy.mock.js +++ b/cvat-core/tests/mocks/server-proxy.mock.js @@ -19,7 +19,6 @@ const { frameMetaDummyData, } = require('./dummy-data.mock'); - class ServerProxy { constructor() { async function about() { diff --git a/cvat-core/webpack.config.js b/cvat-core/webpack.config.js index c691b7f6..22a9baf6 100644 --- a/cvat-core/webpack.config.js +++ b/cvat-core/webpack.config.js @@ -4,29 +4,42 @@ */ const path = require('path'); -const webpack = require('webpack'); -const configuration = 'production'; -const target = 'web'; -const plugins = []; - -if (configuration === 'production' && target === 'web') { - plugins.push( - /** - * IgnorePlugin will skip any require - * that matches the following regex. - */ - new webpack.IgnorePlugin(/browser-env/), - ); -} +const nodeConfig = { + target: 'node', + mode: 'production', + devtool: 'source-map', + entry: './src/api.js', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'cvat-core.node.js', + library: 'cvat', + libraryTarget: 'commonjs', + }, + module: { + rules: [{ + test: /.js?$/, + exclude: /node_modules/, + }], + }, + externals: { + canvas: 'commonjs canvas', + }, + stats: { + warnings: false, + }, +}; -module.exports = { - mode: configuration, +const webConfig = { + target: 'web', + mode: 'production', devtool: 'source-map', entry: './src/api.js', output: { path: path.resolve(__dirname, 'dist'), - filename: 'cvat.js', + filename: 'cvat-core.js', + library: 'cvat', + libraryTarget: 'window', }, module: { rules: [{ @@ -54,6 +67,6 @@ module.exports = { }, }], }, - plugins, - target, }; + +module.exports = [nodeConfig, webConfig]; diff --git a/cvat/apps/dashboard/static/dashboard/js/dashboard.js b/cvat/apps/dashboard/static/dashboard/js/dashboard.js index 08df9902..c622f498 100644 --- a/cvat/apps/dashboard/static/dashboard/js/dashboard.js +++ b/cvat/apps/dashboard/static/dashboard/js/dashboard.js @@ -408,81 +408,6 @@ class DashboardView { } _setupCreateDialog() { - function updateSelectedFiles() { - switch (files.length) { - case 0: - filesLabel.text('No Files'); - break; - case 1: - filesLabel.text(typeof(files[0]) === 'string' ? files[0] : files[0].name); - break; - default: - filesLabel.text(files.length + ' files'); - } - } - - - function validateName(name) { - const math = name.match('[a-zA-Z0-9_]+'); - return math !== null; - } - - function validateLabels(labels) { - try { - LabelsInfo.deserialize(labels) - return true; - } catch (error) { - return false; - } - } - - function validateBugTracker(bugTracker) { - return !bugTracker || !!bugTracker.match(/^http[s]?/); - } - - function validateSegmentSize(segmentSize) { - return (segmentSize >= 100 && segmentSize <= 50000); - } - - function validateOverlapSize(overlapSize, segmentSize) { - return (overlapSize >= 0 && overlapSize <= segmentSize - 1); - } - - function validateStopFrame(stopFrame, startFrame) { - return !customStopFrame.prop('checked') || stopFrame >= startFrame; - } - - function requestCreatingStatus(tid, onUpdateStatus, onSuccess, onError) { - function checkCallback() { - $.get(`/api/v1/tasks/${tid}/status`).done((data) => { - if (['Queued', 'Started'].includes(data.state)) { - if (data.message !== '') { - onUpdateStatus(data.message); - } - setTimeout(checkCallback, 1000); - } else { - if (data.state === 'Finished') { - onSuccess(); - } - else if (data.state === 'Failed') { - const message = `Can not create task. ${data.message}`; - onError(message); - } else { - const message = `Unknown state has been received: ${data.state}`; - onError(message); - } - - } - }).fail((errorData) => { - const message = `Can not check task status. Code: ${errorData.status}. ` + - `Message: ${errorData.responseText || errorData.statusText}`; - onError(message); - }); - } - - setTimeout(checkCallback, 1000); - } - const dashboardCreateTaskButton = $('#dashboardCreateTaskButton'); const createModal = $('#dashboardCreateModal'); const nameInput = $('#dashboardNameInput'); @@ -570,6 +495,10 @@ class DashboardView { return (overlapSize >= 0 && overlapSize <= segmentSize - 1); } + function validateStopFrame(stop, start) { + return !customStopFrame.prop('checked') || stop >= start; + } + dashboardCreateTaskButton.on('click', () => { $('#dashboardCreateModal').removeClass('hidden'); }); @@ -589,7 +518,8 @@ class DashboardView { localSourceRadio.on('click', () => { if (source === 'local') { return; - } else if (source === 'remote') { + } + if (source === 'remote') { selectFiles.parent().removeClass('hidden'); remoteFileInput.parent().addClass('hidden'); } @@ -612,7 +542,8 @@ class DashboardView { shareSourceRadio.on('click', () => { if (source === 'share') { return; - } else if (source === 'remote') { + } + if (source === 'remote') { selectFiles.parent().removeClass('hidden'); remoteFileInput.parent().addClass('hidden'); } @@ -658,8 +589,8 @@ class DashboardView { updateSelectedFiles(); }); - remoteFileInput.on('change', function(e) { - let text = remoteFileInput.prop('value'); + remoteFileInput.on('change', () => { + const text = remoteFileInput.prop('value'); files = text.split('\n').map(f => f.trim()).filter(f => f.length > 0); }); @@ -676,12 +607,12 @@ class DashboardView { zOrder = e.target.checked; }); - customSegmentSize.on('change', (e) => segmentSizeInput.prop('disabled', !e.target.checked)); - customOverlapSize.on('change', (e) => overlapSizeInput.prop('disabled', !e.target.checked)); - customCompressQuality.on('change', (e) => imageQualityInput.prop('disabled', !e.target.checked)); - customStartFrame.on('change', (e) => startFrameInput.prop('disabled', !e.target.checked)); - customStopFrame.on('change', (e) => stopFrameInput.prop('disabled', !e.target.checked)); - customFrameFilter.on('change', (e) => frameFilterInput.prop('disabled', !e.target.checked)); + customSegmentSize.on('change', e => segmentSizeInput.prop('disabled', !e.target.checked)); + customOverlapSize.on('change', e => overlapSizeInput.prop('disabled', !e.target.checked)); + customCompressQuality.on('change', e => imageQualityInput.prop('disabled', !e.target.checked)); + customStartFrame.on('change', e => startFrameInput.prop('disabled', !e.target.checked)); + customStopFrame.on('change', e => stopFrameInput.prop('disabled', !e.target.checked)); + customFrameFilter.on('change', e => frameFilterInput.prop('disabled', !e.target.checked)); segmentSizeInput.on('change', () => { const value = Math.clamp( @@ -716,8 +647,8 @@ class DashboardView { compressQuality = value; }); - startFrameInput.on('change', function() { - let value = Math.max( + startFrameInput.on('change', () => { + const value = Math.max( +startFrameInput.prop('value'), +startFrameInput.prop('min') ); @@ -725,8 +656,9 @@ class DashboardView { startFrameInput.prop('value', value); startFrame = value; }); - stopFrameInput.on('change', function() { - let value = Math.max( + + stopFrameInput.on('change', () => { + const value = Math.max( +stopFrameInput.prop('value'), +stopFrameInput.prop('min') ); @@ -734,7 +666,8 @@ class DashboardView { stopFrameInput.prop('value', value); stopFrame = value; }); - frameFilterInput.on('change', function() { + + frameFilterInput.on('change', () => { frameFilter = frameFilterInput.prop('value'); }); @@ -825,6 +758,15 @@ class DashboardView { if (customFrameFilter.prop('checked')) { description.frame_filter = frameFilter; } + if (customStartFrame.prop('checked')) { + description.start_frame = startFrame; + } + if (customStopFrame.prop('checked')) { + description.stop_frame = stopFrame; + } + if (customFrameFilter.prop('checked')) { + description.frame_filter = frameFilter; + } try { let task = new window.cvat.classes.Task(description); diff --git a/cvat/apps/engine/static/engine/js/cvat.js b/cvat/apps/engine/static/engine/js/cvat.js index 911e86d6..768307bb 100644 --- a/cvat/apps/engine/static/engine/js/cvat.js +++ b/cvat/apps/engine/static/engine/js/cvat.js @@ -1,22 +1,22 @@ -!function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=82)}([function(t,e){t.exports="object"==typeof window&&window&&window.Math==Math?window:"object"==typeof self&&self&&self.Math==Math?self:Function("return this")()},function(t,e,n){var r=n(27)("wks"),o=n(48),i=n(0).Symbol,s=n(89);t.exports=function(t){return r[t]||(r[t]=s&&i[t]||(s?i:o)("Symbol."+t))}},function(t,e,n){var r=n(9);t.exports=function(t){if(!r(t))throw TypeError(String(t)+" is not an object");return t}},function(t,e,n){"use strict";var r=n(26),o=n(100),i=n(21),s=n(16),a=n(64),c=s.set,u=s.getterFor("Array Iterator");t.exports=a(Array,"Array",function(t,e){c(this,{type:"Array Iterator",target:r(t),index:0,kind:e})},function(){var t=u(this),e=t.target,n=t.kind,r=t.index++;return!e||r>=e.length?(t.target=void 0,{value:void 0,done:!0}):"keys"==n?{value:r,done:!1}:"values"==n?{value:e[r],done:!1}:{value:[r,e[r]],done:!1}},"values"),i.Arguments=i.Array,o("keys"),o("values"),o("entries")},function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e,n){"use strict";var r=n(71),o=n(117),i=Object.prototype.toString;function s(t){return"[object Array]"===i.call(t)}function a(t){return null!==t&&"object"==typeof t}function c(t){return"[object Function]"===i.call(t)}function u(t,e){if(null!=t)if("object"!=typeof t&&(t=[t]),s(t))for(var n=0,r=t.length;ns;)a(r[s++]);e.reactions=[],e.notified=!1,n&&!e.rejection&&J(t,e)})}},G=function(t,e,n){var r,o;B?((r=F.createEvent("Event")).promise=e,r.reason=n,r.initEvent(t,!1,!0),c.dispatchEvent(r)):r={promise:e,reason:n},(o=c["on"+t])?o(r):"unhandledrejection"===t&&y("Unhandled promise rejection",n)},J=function(t,e){m.call(c,function(){var n,r=e.value;if(X(e)&&(n=O(function(){L?_.emit("unhandledRejection",r,t):G("unhandledrejection",t,r)}),e.rejection=L||X(e)?2:1,n.error))throw n.value})},X=function(t){return 1!==t.rejection&&!t.parent},V=function(t,e){m.call(c,function(){L?_.emit("rejectionHandled",t):G("rejectionhandled",t,e.value)})},H=function(t,e,n,r){return function(o){t(e,n,o,r)}},K=function(t,e,n,r){e.done||(e.done=!0,r&&(e=r),e.value=n,e.state=2,q(t,e,!0))},Z=function(t,e,n,r){if(!e.done){e.done=!0,r&&(e=r);try{if(t===n)throw C("Promise can't be resolved itself");var o=W(n);o?v(function(){var r={done:!1};try{o.call(n,H(Z,t,r,e),H(K,t,r,e))}catch(n){K(t,r,n,e)}}):(e.value=n,e.state=1,q(t,e,!1))}catch(n){K(t,{done:!1},n,e)}}};z&&(P=function(t){f(this,P,s),p(t),r.call(this);var e=A(this);try{t(H(Z,this,e),H(K,this,e))}catch(t){K(this,e,t)}},(r=function(t){T(this,{type:s,done:!1,notified:!1,parent:!1,reactions:[],rejection:!1,state:0,value:void 0})}).prototype=n(59)(P.prototype,{then:function(t,e){var n=I(this),r=$(g(this,P));return r.ok="function"!=typeof t||t,r.fail="function"==typeof e&&e,r.domain=L?_.domain:void 0,n.parent=!0,n.reactions.push(r),0!=n.state&&q(this,n,!1),r.promise},catch:function(t){return this.then(void 0,t)}}),o=function(){var t=new r,e=A(t);this.promise=t,this.resolve=H(Z,t,e),this.reject=H(K,t,e)},x.f=$=function(t){return t===P||t===i?new o(t):D(t)},a||"function"!=typeof N||u({global:!0,enumerable:!0,forced:!0},{fetch:function(t){return w(P,N.apply(c,arguments))}})),u({global:!0,wrap:!0,forced:z},{Promise:P}),n(22)(P,s,!1,!0),n(97)(s),i=n(60).Promise,u({target:s,stat:!0,forced:z},{reject:function(t){var e=$(this);return e.reject.call(void 0,t),e.promise}}),u({target:s,stat:!0,forced:a||z},{resolve:function(t){return w(a&&this===i?P:this,t)}}),u({target:s,stat:!0,forced:U},{all:function(t){var e=this,n=$(e),r=n.resolve,o=n.reject,i=O(function(){var n=[],i=0,s=1;d(t,function(t){var a=i++,c=!1;n.push(void 0),s++,e.resolve(t).then(function(t){c||(c=!0,n[a]=t,--s||r(n))},o)}),--s||r(n)});return i.error&&o(i.value),n.promise},race:function(t){var e=this,n=$(e),r=n.reject,o=O(function(){d(t,function(t){e.resolve(t).then(n.resolve,r)})});return o.error&&r(o.value),n.promise}})},function(t,e){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,e,n){var r=n(13),o=n(25);t.exports=n(12)?function(t,e,n){return r.f(t,e,o(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e,n){var r=n(0),o=n(33).f,i=n(10),s=n(15),a=n(36),c=n(49),u=n(52);t.exports=function(t,e){var n,l,p,f,h,d=t.target,b=t.global,g=t.stat;if(n=b?r:g?r[d]||a(d,{}):(r[d]||{}).prototype)for(l in e){if(f=e[l],p=t.noTargetGet?(h=o(n,l))&&h.value:n[l],!u(b?l:d+(g?".":"#")+l,t.forced)&&void 0!==p){if(typeof f==typeof p)continue;c(f,p)}(t.sham||p&&p.sham)&&i(f,"sham",!0),s(n,l,f,t)}}},function(t,e,n){t.exports=!n(4)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,e,n){var r=n(12),o=n(46),i=n(2),s=n(34),a=Object.defineProperty;e.f=r?a:function(t,e,n){if(i(t),e=s(e,!0),i(n),o)try{return a(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported");return"value"in n&&(t[e]=n.value),t}},function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},function(t,e,n){var r=n(0),o=n(10),i=n(6),s=n(36),a=n(47),c=n(16),u=c.get,l=c.enforce,p=String(a).split("toString");n(27)("inspectSource",function(t){return a.call(t)}),(t.exports=function(t,e,n,a){var c=!!a&&!!a.unsafe,u=!!a&&!!a.enumerable,f=!!a&&!!a.noTargetGet;"function"==typeof n&&("string"!=typeof e||i(n,"name")||o(n,"name",e),l(n).source=p.join("string"==typeof e?e:"")),t!==r?(c?!f&&t[e]&&(u=!0):delete t[e],u?t[e]=n:o(t,e,n)):u?t[e]=n:s(e,n)})(Function.prototype,"toString",function(){return"function"==typeof this&&u(this).source||a.call(this)})},function(t,e,n){var r,o,i,s=n(83),a=n(9),c=n(10),u=n(6),l=n(37),p=n(38),f=n(0).WeakMap;if(s){var h=new f,d=h.get,b=h.has,g=h.set;r=function(t,e){return g.call(h,t,e),e},o=function(t){return d.call(h,t)||{}},i=function(t){return b.call(h,t)}}else{var m=l("state");p[m]=!0,r=function(t,e){return c(t,m,e),e},o=function(t){return u(t,m)?t[m]:{}},i=function(t){return u(t,m)}}t.exports={set:r,get:o,has:i,enforce:function(t){return i(t)?o(t):r(t,{})},getterFor:function(t){return function(e){var n;if(!a(e)||(n=o(e)).type!==t)throw TypeError("Incompatible receiver, "+t+" required");return n}}}},function(t,e,n){n(3),n(8),n(109),n(7),(()=>{const e=new class{constructor(){const t=n(114),e=n(115);function r(t){e.defaults.headers.delete["X-CSRFToken"]=t,e.defaults.headers.patch["X-CSRFToken"]=t,e.defaults.headers.post["X-CSRFToken"]=t,e.defaults.headers.put["X-CSRFToken"]=t,e.defaults.withCredentials=!0}async function o(t=""){const{backendAPI:n}=window.cvat.config;let r=null;try{r=await e.get(`${n}/tasks?${t}`,{proxy:window.cvat.config.proxy})}catch(t){const e=t.response?t.response.status:t.code;throw new window.cvat.exceptions.ServerError("Could not get tasks from a server",e)}return r.data.results.count=r.data.count,r.data.results}async function i(t){const{backendAPI:n}=window.cvat.config;try{await e.delete(`${n}/tasks/${t}`)}catch(t){const e=t.response?t.response.status:t.code;throw new window.cvat.exceptions.ServerError("Could not delete the task from the server",e)}}const s=t.get("csrftoken");s&&r(s),Object.defineProperties(this,Object.freeze({server:{value:Object.freeze({about:async function(){const{backendAPI:t}=window.cvat.config;let n=null;try{n=await e.get(`${t}/server/about`,{proxy:window.cvat.config.proxy})}catch(t){const e=t.response?t.response.status:t.code;throw new window.cvat.exceptions.ServerError('Could not get "about" information from the server',e)}return n.data},share:async function(t){const{backendAPI:n}=window.cvat.config;t=encodeURIComponent(t);let r=null;try{r=await e.get(`${n}/server/share?directory=${t}`,{proxy:window.cvat.config.proxy})}catch(t){const e=t.response?t.response.status:t.code;throw new window.cvat.exceptions.ServerError('Could not get "share" information from the server',e)}return r.data},exception:async function(t){const{backendAPI:n}=window.cvat.config;try{await e.post(`${n}/server/exception`,JSON.stringify(t),{proxy:window.cvat.config.proxy,headers:{"Content-Type":"application/json"}})}catch(t){const e=t.response?t.response.status:t.code;throw new window.cvat.exceptions.ServerError("Could not send an exception to the server",e)}},login:async function(n,o){function i(n){if(n.headers["set-cookie"]){let o="";for(let e of n.headers["set-cookie"]){[e]=e.split(";");const n=e.split("=")[0],i=e.split("=")[1];"csrftoken"===n&&r(i),t.set(n,i),o+=`${e};`}e.defaults.headers.common.Cookie=o}else{let e=n.data.csrf;if(e)r(e),t.set("csrftoken",e);else{if(!(e=t.get("csrftoken")))throw new window.cvat.exceptions.ScriptingError("An environment has been detected as a browser, but CSRF token has not been found in cookies");r(e)}}}const s=window.cvat.config.backendAPI.slice(0,-7);let a=null;try{a=await e.get(`${s}/auth/csrf`,{proxy:window.cvat.config.proxy})}catch(t){const e=t.response?t.response.status:t.code;throw new window.cvat.exceptions.ServerError("Could not get CSRF token from a server",e)}i(a);const c=[`${encodeURIComponent("username")}=${encodeURIComponent(n)}`,`${encodeURIComponent("password")}=${encodeURIComponent(o)}`].join("&").replace(/%20/g,"+");let u=null;try{u=await e.post(`${s}/auth/login`,c,{"Content-Type":"application/x-www-form-urlencoded",proxy:window.cvat.config.proxy,maxRedirects:0})}catch(t){if(302!==t.response.status){const e=t.response?t.response.status:t.code;throw new window.cvat.exceptions.ServerError("Could not login on a server",e)}u=t.response}if(u.data.includes("didn't match"))throw new window.cvat.exceptions.ServerError("The pair login/password is invalid",403);i(u)},logout:async function(){const t=window.cvat.config.backendAPI.slice(0,-7);try{await e.get(`${t}/auth/logout`,{proxy:window.cvat.config.proxy})}catch(t){const e=t.response?t.response.status:t.code;throw new window.cvat.exceptions.ServerError("Could not logout from the server",e)}}}),writable:!1},tasks:{value:Object.freeze({getTasks:o,saveTask:async function(t,n){const{backendAPI:r}=window.cvat.config;try{await e.patch(`${r}/tasks/${t}`,JSON.stringify(n),{proxy:window.cvat.config.proxy,headers:{"Content-Type":"application/json"}})}catch(t){const e=t.response?t.response.status:t.code;throw new window.cvat.exceptions.ServerError("Could not save the task on the server",e)}},createTask:async function(t,n,r){const{backendAPI:s}=window.cvat.config,a=new window.FormData;for(const t in n)if(Object.prototype.hasOwnProperty.call(n,t))for(let e=0;e{setTimeout(async function i(){try{const a=await e.get(`${s}/tasks/${t}/status`);["Queued","Started"].includes(a.data.state)?(""!==a.data.message&&r(a.data.message),setTimeout(i,1e3)):"Finished"===a.data.state?n():"Failed"===a.data.state?o(new window.cvat.exceptions.ServerError("Could not create the task on the server",400)):o(new window.cvat.exceptions.ServerError(`Unknown task state has been recieved: ${a.data.state}`,500))}catch(t){const e=t.response?t.response.status:t.code;o(new window.cvat.exceptions.ServerError("Data uploading error occured",e))}},1e3)})}(c.data.id)}catch(t){throw await i(c.data.id),t}return(await o(`?id=${c.id}`))[0]},deleteTask:i}),writable:!1},jobs:{value:Object.freeze({getJob:async function(t){const{backendAPI:n}=window.cvat.config;let r=null;try{r=await e.get(`${n}/jobs/${t}`,{proxy:window.cvat.config.proxy})}catch(t){const e=t.response?t.response.status:t.code;throw new window.cvat.exceptions.ServerError("Could not get jobs from a server",e)}return r.data},saveJob:async function(t,n){const{backendAPI:r}=window.cvat.config;try{await e.patch(`${r}/jobs/${t}`,JSON.stringify(n),{proxy:window.cvat.config.proxy,headers:{"Content-Type":"application/json"}})}catch(t){const e=t.response?t.response.status:t.code;throw new window.cvat.exceptions.ServerError("Could not save the job on the server",e)}}}),writable:!1},users:{value:Object.freeze({getUsers:async function(){const{backendAPI:t}=window.cvat.config;let n=null;try{n=await e.get(`${t}/users`,{proxy:window.cvat.config.proxy})}catch(t){const e=t.response?t.response.status:t.code;throw new window.cvat.exceptions.ServerError("Could not get users from the server",e)}return n.data.results},getSelf:async function(){const{backendAPI:t}=window.cvat.config;let n=null;try{n=await e.get(`${t}/users/self`,{proxy:window.cvat.config.proxy})}catch(t){const e=t.response?t.response.status:t.code;throw new window.cvat.exceptions.ServerError("Could not get users from the server",e)}return n.data}}),writable:!1},frames:{value:Object.freeze({getData:async function(t,n){const{backendAPI:r}=window.cvat.config;let o=null;try{o=await e.get(`${r}/tasks/${t}/frames/${n}`,{proxy:window.cvat.config.proxy,responseType:"blob"})}catch(e){const r=e.response?e.response.status:e.code;throw new window.cvat.exceptions.ServerError(`Could not get frame ${n} for the task ${t} from the server`,r)}return o.data},getMeta:async function(t){const{backendAPI:n}=window.cvat.config;let r=null;try{r=await e.get(`${n}/tasks/${t}/frames/meta`,{proxy:window.cvat.config.proxy})}catch(e){const n=e.response?e.response.status:e.code;throw new window.cvat.exceptions.ServerError(`Could not get frame meta info for the task ${t} from the server`,n)}return r.data}}),writable:!1},annotations:{value:Object.freeze({updateAnnotations:async function(t,n,r,o){const{backendAPI:i}=window.cvat.config;let s=null,a=null;"PUT"===o.toUpperCase()?(s=e.put.bind(e),a=`${i}/${t}s/${n}/annotations`):(s=e.patch.bind(e),a=`${i}/${t}s/${n}/annotations?action=${o}`);let c=null;try{c=await s(a,JSON.stringify(r),{proxy:window.cvat.config.proxy,headers:{"Content-Type":"application/json"}})}catch(e){const r=e.response?e.response.status:e.code;throw new window.cvat.exceptions.ServerError(`Could not updated annotations for the ${t} ${n} on the server`,r)}return c.data},getAnnotations:async function(t,n){const{backendAPI:r}=window.cvat.config;let o=null;try{o=await e.get(`${r}/${t}s/${n}/annotations`,{proxy:window.cvat.config.proxy})}catch(e){const r=e.response?e.response.status:e.code;throw new window.cvat.exceptions.ServerError(`Could not get annotations for the ${t} ${n} from the server`,r)}return o.data},dumpAnnotations:async function(t,n,r){const{backendAPI:o}=window.cvat.config,i=n.replace(/\//g,"_");let s=`${o}/tasks/${t}/annotations/${i}?dump_format=${r}`;return new Promise((n,r)=>{setTimeout(async function o(){try{202===(await e.get(`${s}`,{proxy:window.cvat.config.proxy})).status?setTimeout(o,3e3):n(s=`${s}&action=download`)}catch(e){const n=e.response?e.response.status:e.code,o=new window.cvat.exceptions.ServerError(`Could not dump annotations for the task ${t} from the server`,n);r(o)}})})},uploadAnnotations:async function(t,n,r,o){const{backendAPI:i}=window.cvat.config;let s=new FormData;return s.append("annotation_file",r),new Promise((r,a)=>{setTimeout(async function c(){try{202===(await e.post(`${i}/${t}s/${n}/annotations?upload_format=${o}`,s,{proxy:window.cvat.config.proxy})).status?(s=new FormData,setTimeout(c,3e3)):r()}catch(e){const r=e.response?e.response.status:e.code,o=new window.cvat.exceptions.ServerError(`Could not upload annotations for the ${t} ${n}`,r);a(o)}})})}}),writable:!1}}))}};t.exports=e})()},function(t,e){t.exports=!1},function(t,e){t.exports=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t}},function(t,e){t.exports=function(t){if("function"!=typeof t)throw TypeError(String(t)+" is not a function");return t}},function(t,e){t.exports={}},function(t,e,n){var r=n(13).f,o=n(6),i=n(1)("toStringTag");t.exports=function(t,e,n){t&&!o(t=n?t:t.prototype,i)&&r(t,i,{configurable:!0,value:e})}},function(t,e,n){n(99),n(3),n(8),n(7),(()=>{const{PluginError:e}=n(68),r=[];t.exports=class{static async apiWrapper(t,...n){const r=await window.cvat.plugins.list.implementation();for(const o of r){const r=o.functions.filter(e=>e.callback===t)[0];if(r&&r.enter)try{await r.enter.call(this,o,...n)}catch(t){throw t instanceof e?t:new e(`Exception in plugin ${o.name}: ${t.toString()}`)}}let o=await t.implementation.call(this,...n);for(const i of r){const r=i.functions.filter(e=>e.callback===t)[0];if(r&&r.leave)try{o=await r.leave.call(this,i,o,...n)}catch(t){throw t instanceof e?t:new e(`Exception in plugin ${i.name}: ${t.toString()}`)}}return o}static async register(t){const n=[];if("object"!=typeof t)throw new e(`Plugin should be an object, but got "${typeof t}"`);if(!("name"in t)||"string"!=typeof t.name)throw new e('Plugin must contain a "name" field and it must be a string');if(!("description"in t)||"string"!=typeof t.description)throw new e('Plugin must contain a "description" field and it must be a string');if("functions"in t)throw new e('Plugin must not contain a "functions" field');!function t(e,r){const o={};for(const n in e)Object.prototype.hasOwnProperty.call(e,n)&&("object"==typeof e[n]?Object.prototype.hasOwnProperty.call(r,n)&&t(e[n],r[n]):["enter","leave"].includes(n)&&"function"==typeof r&&(e[n],1)&&(o.callback=r,o[n]=e[n]));Object.keys(o).length&&n.push(o)}(t,{cvat:window.cvat}),Object.defineProperty(t,"functions",{value:n,writable:!1}),r.push(t)}static async list(){return r}}})()},function(t,e,n){var r=n(19);t.exports=function(t){return Object(r(t))}},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e,n){var r=n(45),o=n(19);t.exports=function(t){return r(o(t))}},function(t,e,n){var r=n(0),o=n(36),i=r["__core-js_shared__"]||o("__core-js_shared__",{});(t.exports=function(t,e){return i[t]||(i[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.0.1",mode:n(18)?"pure":"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})},function(t,e,n){var r=n(29),o=Math.min;t.exports=function(t){return t>0?o(r(t),9007199254740991):0}},function(t,e){var n=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:n)(t)}},function(t,e,n){var r=n(20);t.exports=function(t,e,n){if(r(t),void 0===e)return t;switch(n){case 0:return function(){return t.call(e)};case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}}},function(t,e,n){var r=n(90),o=n(1)("iterator"),i=n(21);t.exports=function(t){if(null!=t)return t[o]||t["@@iterator"]||i[r(t)]}},function(t,e){(()=>{t.exports={isBoolean:function(t){return"boolean"==typeof t},isInteger:function(t){return"number"==typeof t&&Number.isInteger(t)},isEnum:function(t){for(const e in this)if(Object.prototype.hasOwnProperty.call(this,e)&&this[e]===t)return!0;return!1},isString:function(t){return"string"==typeof t},checkFilter:function(t,e){for(const n in t)if(Object.prototype.hasOwnProperty.call(t,n)){if(!(n in e))throw new window.cvat.exceptions.ArgumentError(`Unsupported filter property has been recieved: "${n}"`);if(!e[n](t[n]))throw new window.cvat.exceptions.ArgumentError(`Received filter property "${n}" is not satisfied for checker`)}},checkObjectType:function(t,e,n,r){if(n){if(typeof e!==n){if("integer"===n&&Number.isInteger(e))return;throw new window.cvat.exceptions.ArgumentError(`"${t}" is expected to be "${n}", but "${typeof e}" has been got.`)}}else if(r&&!(e instanceof r)){if(void 0!==e)throw new window.cvat.exceptions.ArgumentError(`"${t}" is expected to be ${r.name}, but `+`"${e.constructor.name}" has been got`);throw new window.cvat.exceptions.ArgumentError(`"${t}" is expected to be ${r.name}, but "undefined" has been got.`)}}}})()},function(t,e,n){var r=n(12),o=n(44),i=n(25),s=n(26),a=n(34),c=n(6),u=n(46),l=Object.getOwnPropertyDescriptor;e.f=r?l:function(t,e){if(t=s(t),e=a(e,!0),u)try{return l(t,e)}catch(t){}if(c(t,e))return i(!o.f.call(t,e),t[e])}},function(t,e,n){var r=n(9);t.exports=function(t,e){if(!r(t))return t;var n,o;if(e&&"function"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;if("function"==typeof(n=t.valueOf)&&!r(o=n.call(t)))return o;if(!e&&"function"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;throw TypeError("Can't convert object to primitive value")}},function(t,e,n){var r=n(9),o=n(0).document,i=r(o)&&r(o.createElement);t.exports=function(t){return i?o.createElement(t):{}}},function(t,e,n){var r=n(0),o=n(10);t.exports=function(t,e){try{o(r,t,e)}catch(n){r[t]=e}return e}},function(t,e,n){var r=n(27)("keys"),o=n(48);t.exports=function(t){return r[t]||(r[t]=o(t))}},function(t,e){t.exports={}},function(t,e){t.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},function(t,e){t.exports=function(t,e,n){if(!(t instanceof e))throw TypeError("Incorrect "+(n?n+" ":"")+"invocation");return t}},function(t,e,n){var r=n(29),o=n(19);t.exports=function(t,e,n){var i,s,a=String(o(t)),c=r(e),u=a.length;return c<0||c>=u?n?"":void 0:(i=a.charCodeAt(c))<55296||i>56319||c+1===u||(s=a.charCodeAt(c+1))<56320||s>57343?n?a.charAt(c):i:n?a.slice(c,c+2):s-56320+(i-55296<<10)+65536}},function(t,e,n){"use strict";(function(e){var r=n(5),o=n(120),i={"Content-Type":"application/x-www-form-urlencoded"};function s(t,e){!r.isUndefined(t)&&r.isUndefined(t["Content-Type"])&&(t["Content-Type"]=e)}var a,c={adapter:("undefined"!=typeof XMLHttpRequest?a=n(72):void 0!==e&&(a=n(72)),a),transformRequest:[function(t,e){return o(e,"Content-Type"),r.isFormData(t)||r.isArrayBuffer(t)||r.isBuffer(t)||r.isStream(t)||r.isFile(t)||r.isBlob(t)?t:r.isArrayBufferView(t)?t.buffer:r.isURLSearchParams(t)?(s(e,"application/x-www-form-urlencoded;charset=utf-8"),t.toString()):r.isObject(t)?(s(e,"application/json;charset=utf-8"),JSON.stringify(t)):t}],transformResponse:[function(t){if("string"==typeof t)try{t=JSON.parse(t)}catch(t){}return t}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,validateStatus:function(t){return t>=200&&t<300}};c.headers={common:{Accept:"application/json, text/plain, */*"}},r.forEach(["delete","get","head"],function(t){c.headers[t]={}}),r.forEach(["post","put","patch"],function(t){c.headers[t]=r.merge(i)}),t.exports=c}).call(this,n(119))},function(t,e,n){"use strict";n(11)({target:"URL",proto:!0,enumerable:!0},{toJSON:function(){return URL.prototype.toString.call(this)}})},function(t,e,n){"use strict";var r={}.propertyIsEnumerable,o=Object.getOwnPropertyDescriptor,i=o&&!r.call({1:2},1);e.f=i?function(t){var e=o(this,t);return!!e&&e.enumerable}:r},function(t,e,n){var r=n(4),o=n(14),i="".split;t.exports=r(function(){return!Object("z").propertyIsEnumerable(0)})?function(t){return"String"==o(t)?i.call(t,""):Object(t)}:Object},function(t,e,n){t.exports=!n(12)&&!n(4)(function(){return 7!=Object.defineProperty(n(35)("div"),"a",{get:function(){return 7}}).a})},function(t,e,n){t.exports=n(27)("native-function-to-string",Function.toString)},function(t,e){var n=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++n+r).toString(36))}},function(t,e,n){var r=n(6),o=n(84),i=n(33),s=n(13);t.exports=function(t,e){for(var n=o(e),a=s.f,c=i.f,u=0;uc;)r(a,n=e[c++])&&(~i(u,n)||u.push(n));return u}},function(t,e){e.f=Object.getOwnPropertySymbols},function(t,e,n){var r=n(4),o=/#|\.prototype\./,i=function(t,e){var n=a[s(t)];return n==u||n!=c&&("function"==typeof e?r(e):!!e)},s=i.normalize=function(t){return String(t).replace(o,".").toLowerCase()},a=i.data={},c=i.NATIVE="N",u=i.POLYFILL="P";t.exports=i},function(t,e,n){var r=n(21),o=n(1)("iterator"),i=Array.prototype;t.exports=function(t){return void 0!==t&&(r.Array===t||i[o]===t)}},function(t,e,n){var r=n(2);t.exports=function(t,e,n,o){try{return o?e(r(n)[0],n[1]):e(n)}catch(e){var i=t.return;throw void 0!==i&&r(i.call(t)),e}}},function(t,e,n){var r,o,i,s=n(0),a=n(14),c=n(30),u=n(56),l=n(35),p=s.setImmediate,f=s.clearImmediate,h=s.process,d=s.MessageChannel,b=s.Dispatch,g=0,m={},v=function(){var t=+this;if(m.hasOwnProperty(t)){var e=m[t];delete m[t],e()}},w=function(t){v.call(t.data)};p&&f||(p=function(t){for(var e=[],n=1;arguments.length>n;)e.push(arguments[n++]);return m[++g]=function(){("function"==typeof t?t:Function(t)).apply(void 0,e)},r(g),g},f=function(t){delete m[t]},"process"==a(h)?r=function(t){h.nextTick(c(v,t,1))}:b&&b.now?r=function(t){b.now(c(v,t,1))}:d?(i=(o=new d).port2,o.port1.onmessage=w,r=c(i.postMessage,i,1)):s.addEventListener&&"function"==typeof postMessage&&!s.importScripts?(r=function(t){s.postMessage(t+"","*")},s.addEventListener("message",w,!1)):r="onreadystatechange"in l("script")?function(t){u.appendChild(l("script")).onreadystatechange=function(){u.removeChild(this),v.call(t)}}:function(t){setTimeout(c(v,t,1),0)}),t.exports={set:p,clear:f}},function(t,e,n){var r=n(0).document;t.exports=r&&r.documentElement},function(t,e,n){var r=n(0).navigator;t.exports=r&&r.userAgent||""},function(t,e,n){"use strict";var r=n(20),o=function(t){var e,n;this.promise=new t(function(t,r){if(void 0!==e||void 0!==n)throw TypeError("Bad Promise constructor");e=t,n=r}),this.resolve=r(e),this.reject=r(n)};t.exports.f=function(t){return new o(t)}},function(t,e,n){var r=n(15);t.exports=function(t,e,n){for(var o in e)r(t,o,e[o],n);return t}},function(t,e,n){t.exports=n(0)},function(t,e,n){var r=n(2),o=n(62),i=n(39),s=n(56),a=n(35),c=n(37)("IE_PROTO"),u=function(){},l=function(){var t,e=a("iframe"),n=i.length;for(e.style.display="none",s.appendChild(e),e.src=String("javascript:"),(t=e.contentWindow.document).open(),t.write("