Updated cvat-core (#591)

* Two webpack configuration instead one

* Two webpack configuration instead one

* Setup webpack build

* Removed references window.cvat

* Removed window.cvat

* Stop frame, start frame, frame filter

* Updated save method

* Updated dashboard to create tasks with custom stopFrame/startFramre

* Fixed clientID

* Removed browser environment from nodejs

* Updated webpack.config.js

* Updated filename
main
Boris Sekachev 7 years ago committed by Nikita Manovich
parent 5dd4f3981e
commit cad5ac8653

@ -13,7 +13,7 @@ npm install
- Building the module from sources in the ```dist``` directory: - Building the module from sources in the ```dist``` directory:
```bash ```bash
npm run-script build npm run build
npm run build -- --mode=development # without a minification npm run build -- --mode=development # without a minification
``` ```

@ -27,11 +27,10 @@
}, },
"dependencies": { "dependencies": {
"axios": "^0.18.0", "axios": "^0.18.0",
"browser-env": "^3.2.6",
"error-stack-parser": "^2.0.2", "error-stack-parser": "^2.0.2",
"form-data": "^2.5.0",
"jest-config": "^24.8.0", "jest-config": "^24.8.0",
"js-cookie": "^2.2.0", "js-cookie": "^2.2.0",
"platform": "^1.3.5", "platform": "^1.3.5"
"stacktrace-gps": "^3.0.2"
} }
} }

@ -24,6 +24,18 @@
} = require('./annotations-objects'); } = require('./annotations-objects');
const { checkObjectType } = require('./common'); const { checkObjectType } = require('./common');
const Statistics = require('./statistics'); 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 = [ const colors = [
'#0066FF', '#AF593E', '#01A368', '#FF861F', '#ED0A3F', '#FF3F34', '#76D7EA', '#0066FF', '#AF593E', '#01A368', '#FF861F', '#ED0A3F', '#FF3F34', '#76D7EA',
@ -62,7 +74,7 @@
shapeModel = new PointsShape(shapeData, clientID, color, injection); shapeModel = new PointsShape(shapeData, clientID, color, injection);
break; break;
default: default:
throw new window.cvat.exceptions.DataError( throw new DataError(
`An unexpected type of shape "${type}"`, `An unexpected type of shape "${type}"`,
); );
} }
@ -91,7 +103,7 @@
trackModel = new PointsTrack(trackData, clientID, color, injection); trackModel = new PointsTrack(trackData, clientID, color, injection);
break; break;
default: default:
throw new window.cvat.exceptions.DataError( throw new DataError(
`An unexpected type of track "${type}"`, `An unexpected type of track "${type}"`,
); );
} }
@ -209,10 +221,10 @@
checkObjectType('shapes for merge', objectStates, null, Array); checkObjectType('shapes for merge', objectStates, null, Array);
if (!objectStates.length) return; if (!objectStates.length) return;
const objectsForMerge = objectStates.map((state) => { 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]; const object = this.objects[state.clientID];
if (typeof (object) === 'undefined') { 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', '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 keyframes = {}; // frame: position
const { label, shapeType } = objectStates[0]; const { label, shapeType } = objectStates[0];
if (!(label.id in this.labels)) { if (!(label.id in this.labels)) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`Unknown label for the task: ${label.id}`, `Unknown label for the task: ${label.id}`,
); );
} }
if (!Object.values(window.cvat.enums.ObjectShape).includes(shapeType)) { if (!Object.values(ObjectShape).includes(shapeType)) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`Got unknown shapeType "${shapeType}"`, `Got unknown shapeType "${shapeType}"`,
); );
} }
@ -243,13 +255,13 @@
const object = objectsForMerge[i]; const object = objectsForMerge[i];
const state = objectStates[i]; const state = objectStates[i];
if (state.label.id !== label.id) { 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}`, `All shape labels are expected to be ${label.name}, but got ${state.label.name}`,
); );
} }
if (state.shapeType !== shapeType) { if (state.shapeType !== shapeType) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`All shapes are expected to be ${shapeType}, but got ${state.shapeType}`, `All shapes are expected to be ${shapeType}, but got ${state.shapeType}`,
); );
} }
@ -258,7 +270,7 @@
if (object instanceof Shape) { if (object instanceof Shape) {
// Frame already saved and it is not outside // Frame already saved and it is not outside
if (object.frame in keyframes && !keyframes[object.frame].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', 'Expected only one visible shape per frame',
); );
} }
@ -303,7 +315,7 @@
continue; continue;
} }
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
'Expected only one visible shape per frame', 'Expected only one visible shape per frame',
); );
} }
@ -338,7 +350,7 @@
}; };
} }
} else { } else {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`Trying to merge unknown object type: ${object.constructor.name}. ` `Trying to merge unknown object type: ${object.constructor.name}. `
+ 'Only shapes and tracks are expected.', + 'Only shapes and tracks are expected.',
); );
@ -389,17 +401,17 @@
} }
split(objectState, frame) { split(objectState, frame) {
checkObjectType('object state', objectState, null, window.cvat.classes.ObjectState); checkObjectType('object state', objectState, null, ObjectState);
checkObjectType('frame', frame, 'integer', null); checkObjectType('frame', frame, 'integer', null);
const object = this.objects[objectState.clientID]; const object = this.objects[objectState.clientID];
if (typeof (object) === 'undefined') { 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', '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; return;
} }
@ -478,10 +490,10 @@
checkObjectType('shapes for group', objectStates, null, Array); checkObjectType('shapes for group', objectStates, null, Array);
const objectsForGroup = objectStates.map((state) => { 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]; const object = this.objects[state.clientID];
if (typeof (object) === 'undefined') { 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', 'The object has not been saved yet. Call annotations.put([state]) before',
); );
} }
@ -549,7 +561,7 @@
} else if (object instanceof Tag) { } else if (object instanceof Tag) {
objectType = 'tag'; objectType = 'tag';
} else { } else {
throw new window.cvat.exceptions.ScriptingError( throw new ScriptingError(
`Unexpected object type: "${objectType}"`, `Unexpected object type: "${objectType}"`,
); );
} }
@ -637,11 +649,11 @@
} }
for (const state of objectStates) { 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 client ID', state.clientID, 'undefined', null);
checkObjectType('state frame', state.frame, 'integer', null); checkObjectType('state frame', state.frame, 'integer', null);
checkObjectType('state attributes', state.attributes, null, Object); 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) const attributes = Object.keys(state.attributes)
.reduce(convertAttributes.bind(state), []); .reduce(convertAttributes.bind(state), []);
@ -666,10 +678,10 @@
checkObjectType('point coordinate', coord, 'number', null); checkObjectType('point coordinate', coord, 'number', null);
} }
if (!Object.values(window.cvat.enums.ObjectShape).includes(state.shapeType)) { if (!Object.values(ObjectShape).includes(state.shapeType)) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
'Object shape must be one of: ' 'Object shape must be one of: '
+ `${JSON.stringify(Object.values(window.cvat.enums.ObjectShape))}`, + `${JSON.stringify(Object.values(ObjectShape))}`,
); );
} }
@ -703,9 +715,9 @@
}], }],
}); });
} else { } else {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
'Object type must be one of: ' '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 minimumDistance = null;
let minimumState = null; let minimumState = null;
for (const state of objectStates) { for (const state of objectStates) {
checkObjectType('object state', state, null, window.cvat.classes.ObjectState); checkObjectType('object state', state, null, ObjectState);
if (state.outside) continue; if (state.outside) continue;
const object = this.objects[state.clientID]; const object = this.objects[state.clientID];
if (typeof (object) === 'undefined') { 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', 'The object has not been saved yet. Call annotations.put([state]) before',
); );
} }

@ -10,6 +10,19 @@
(() => { (() => {
const ObjectState = require('./object-state'); const ObjectState = require('./object-state');
const { checkObjectType } = require('./common'); 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 // Called with the Annotation context
function objectStateFactory(frame, data) { function objectStateFactory(frame, data) {
@ -26,32 +39,32 @@
} }
function checkNumberOfPoints(shapeType, points) { function checkNumberOfPoints(shapeType, points) {
if (shapeType === window.cvat.enums.ObjectShape.RECTANGLE) { if (shapeType === ObjectShape.RECTANGLE) {
if (points.length / 2 !== 2) { if (points.length / 2 !== 2) {
throw new window.cvat.exceptions.DataError( throw new DataError(
`Rectangle must have 2 points, but got ${points.length / 2}`, `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) { 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}`, `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) { 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}`, `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) { 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}`, `Points must have at least 1 points, but got ${points.length / 2}`,
); );
} }
} else { } else {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`Unknown value of shapeType has been recieved ${shapeType}`, `Unknown value of shapeType has been recieved ${shapeType}`,
); );
} }
@ -61,7 +74,7 @@
const MIN_SHAPE_LENGTH = 3; const MIN_SHAPE_LENGTH = 3;
const MIN_SHAPE_AREA = 9; const MIN_SHAPE_AREA = 9;
if (shapeType === window.cvat.enums.ObjectShape.POINTS) { if (shapeType === ObjectShape.POINTS) {
return true; return true;
} }
@ -77,7 +90,7 @@
ymax = Math.max(ymax, points[i + 1]); ymax = Math.max(ymax, points[i + 1]);
} }
if (shapeType === window.cvat.enums.ObjectShape.POLYLINE) { if (shapeType === ObjectShape.POLYLINE) {
const length = Math.max( const length = Math.max(
xmax - xmin, xmax - xmin,
ymax - ymin, ymax - ymin,
@ -95,18 +108,18 @@
const type = attr.inputType; const type = attr.inputType;
if (typeof (value) !== 'string') { if (typeof (value) !== 'string') {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`Attribute value is expected to be string, but got ${typeof (value)}`, `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] return +value >= +values[0]
&& +value <= +values[1] && +value <= +values[1]
&& !((+value - +values[0]) % +values[2]); && !((+value - +values[0]) % +values[2]);
} }
if (type === window.cvat.enums.AttributeType.CHECKBOX) { if (type === AttributeType.CHECKBOX) {
return ['true', 'false'].includes(value.toLowerCase()); return ['true', 'false'].includes(value.toLowerCase());
} }
@ -171,19 +184,19 @@
} }
save() { save() {
throw new window.cvat.exceptions.ScriptingError( throw new ScriptingError(
'Is not implemented', 'Is not implemented',
); );
} }
get() { get() {
throw new window.cvat.exceptions.ScriptingError( throw new ScriptingError(
'Is not implemented', 'Is not implemented',
); );
} }
toJSON() { toJSON() {
throw new window.cvat.exceptions.ScriptingError( throw new ScriptingError(
'Is not implemented', 'Is not implemented',
); );
} }
@ -241,13 +254,13 @@
// Method is used to construct ObjectState objects // Method is used to construct ObjectState objects
get(frame) { get(frame) {
if (frame !== this.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', 'Got frame is not equal to the frame of the shape',
); );
} }
return { return {
objectType: window.cvat.enums.ObjectType.SHAPE, objectType: ObjectType.SHAPE,
shapeType: this.shapeType, shapeType: this.shapeType,
clientID: this.clientID, clientID: this.clientID,
serverID: this.serverID, serverID: this.serverID,
@ -264,7 +277,7 @@
save(frame, data) { save(frame, data) {
if (frame !== this.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', 'Got frame is not equal to the frame of the shape',
); );
} }
@ -278,7 +291,7 @@
const updated = data.updateFlags; const updated = data.updateFlags;
if (updated.label) { if (updated.label) {
checkObjectType('label', data.label, null, window.cvat.classes.Label); checkObjectType('label', data.label, null, Label);
copy.label = data.label; copy.label = data.label;
copy.attributes = {}; copy.attributes = {};
this.appendDefaultAttributes.call(copy, copy.label); this.appendDefaultAttributes.call(copy, copy.label);
@ -297,7 +310,7 @@
&& validateAttributeValue(value, labelAttributes[attrID])) { && validateAttributeValue(value, labelAttributes[attrID])) {
copy.attributes[attrID] = value; copy.attributes[attrID] = value;
} else { } else {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`Trying to save unknown attribute with id ${attrID} and value ${value}`, `Trying to save unknown attribute with id ${attrID} and value ${value}`,
); );
} }
@ -352,7 +365,7 @@
if (updated.color) { if (updated.color) {
checkObjectType('color', data.color, 'string', null); checkObjectType('color', data.color, 'string', null);
if (/^#[0-9A-F]{6}$/i.test(data.color)) { if (/^#[0-9A-F]{6}$/i.test(data.color)) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`Got invalid color value: "${data.color}"`, `Got invalid color value: "${data.color}"`,
); );
} }
@ -456,7 +469,7 @@
{ {
attributes: this.getAttributes(frame), attributes: this.getAttributes(frame),
group: this.group, group: this.group,
objectType: window.cvat.enums.ObjectType.TRACK, objectType: ObjectType.TRACK,
shapeType: this.shapeType, shapeType: this.shapeType,
clientID: this.clientID, clientID: this.clientID,
serverID: this.serverID, serverID: this.serverID,
@ -537,7 +550,7 @@
let positionUpdated = false; let positionUpdated = false;
if (updated.label) { if (updated.label) {
checkObjectType('label', data.label, null, window.cvat.classes.Label); checkObjectType('label', data.label, null, Label);
copy.label = data.label; copy.label = data.label;
copy.attributes = {}; copy.attributes = {};
@ -558,7 +571,7 @@
&& validateAttributeValue(value, labelAttributes[attrID])) { && validateAttributeValue(value, labelAttributes[attrID])) {
copy.attributes[attrID] = value; copy.attributes[attrID] = value;
} else { } else {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`Trying to save unknown attribute with id ${attrID} and value ${value}`, `Trying to save unknown attribute with id ${attrID} and value ${value}`,
); );
} }
@ -622,7 +635,7 @@
if (updated.color) { if (updated.color) {
checkObjectType('color', data.color, 'string', null); checkObjectType('color', data.color, 'string', null);
if (/^#[0-9A-F]{6}$/i.test(data.color)) { if (/^#[0-9A-F]{6}$/i.test(data.color)) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`Got invalid color value: "${data.color}"`, `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}"`, `No one neightbour frame found for the track with client ID: "${this.id}"`,
); );
} }
@ -808,13 +821,13 @@
// Method is used to construct ObjectState objects // Method is used to construct ObjectState objects
get(frame) { get(frame) {
if (frame !== this.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', 'Got frame is not equal to the frame of the shape',
); );
} }
return { return {
objectType: window.cvat.enums.ObjectType.TAG, objectType: ObjectType.TAG,
clientID: this.clientID, clientID: this.clientID,
serverID: this.serverID, serverID: this.serverID,
lock: this.lock, lock: this.lock,
@ -826,7 +839,7 @@
save(frame, data) { save(frame, data) {
if (frame !== this.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', 'Got frame is not equal to the frame of the shape',
); );
} }
@ -840,7 +853,7 @@
const updated = data.updateFlags; const updated = data.updateFlags;
if (updated.label) { if (updated.label) {
checkObjectType('label', data.label, null, window.cvat.classes.Label); checkObjectType('label', data.label, null, Label);
copy.label = data.label; copy.label = data.label;
copy.attributes = {}; copy.attributes = {};
this.appendDefaultAttributes.call(copy, copy.label); this.appendDefaultAttributes.call(copy, copy.label);
@ -882,7 +895,7 @@
class RectangleShape extends Shape { class RectangleShape extends Shape {
constructor(data, clientID, color, injection) { constructor(data, clientID, color, injection) {
super(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); checkNumberOfPoints(this.shapeType, this.points);
} }
@ -908,7 +921,7 @@
class PolygonShape extends PolyShape { class PolygonShape extends PolyShape {
constructor(data, clientID, color, injection) { constructor(data, clientID, color, injection) {
super(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); checkNumberOfPoints(this.shapeType, this.points);
} }
@ -983,7 +996,7 @@
class PolylineShape extends PolyShape { class PolylineShape extends PolyShape {
constructor(data, clientID, color, injection) { constructor(data, clientID, color, injection) {
super(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); checkNumberOfPoints(this.shapeType, this.points);
} }
@ -1027,7 +1040,7 @@
class PointsShape extends PolyShape { class PointsShape extends PolyShape {
constructor(data, clientID, color, injection) { constructor(data, clientID, color, injection) {
super(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); checkNumberOfPoints(this.shapeType, this.points);
} }
@ -1049,7 +1062,7 @@
class RectangleTrack extends Track { class RectangleTrack extends Track {
constructor(data, clientID, color, injection) { constructor(data, clientID, color, injection) {
super(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)) { for (const shape of Object.values(this.shapes)) {
checkNumberOfPoints(this.shapeType, shape.points); checkNumberOfPoints(this.shapeType, shape.points);
} }
@ -1374,7 +1387,7 @@
if (!targetMatched.length) { if (!targetMatched.length) {
// Prevent infinity loop // Prevent infinity loop
throw new window.cvat.exceptions.ScriptingError('Interpolation mapping is empty'); throw new ScriptingError('Interpolation mapping is empty');
} }
while (!targetMatched.includes(prev)) { while (!targetMatched.includes(prev)) {
@ -1463,7 +1476,7 @@
class PolygonTrack extends PolyTrack { class PolygonTrack extends PolyTrack {
constructor(data, clientID, color, injection) { constructor(data, clientID, color, injection) {
super(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)) { for (const shape of Object.values(this.shapes)) {
checkNumberOfPoints(this.shapeType, shape.points); checkNumberOfPoints(this.shapeType, shape.points);
} }
@ -1473,7 +1486,7 @@
class PolylineTrack extends PolyTrack { class PolylineTrack extends PolyTrack {
constructor(data, clientID, color, injection) { constructor(data, clientID, color, injection) {
super(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)) { for (const shape of Object.values(this.shapes)) {
checkNumberOfPoints(this.shapeType, shape.points); checkNumberOfPoints(this.shapeType, shape.points);
} }
@ -1483,7 +1496,7 @@
class PointsTrack extends PolyTrack { class PointsTrack extends PolyTrack {
constructor(data, clientID, color, injection) { constructor(data, clientID, color, injection) {
super(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)) { for (const shape of Object.values(this.shapes)) {
checkNumberOfPoints(this.shapeType, shape.points); checkNumberOfPoints(this.shapeType, shape.points);
} }

@ -9,10 +9,12 @@
(() => { (() => {
const serverProxy = require('./server-proxy'); const serverProxy = require('./server-proxy');
const { Task } = require('./session');
const { ScriptingError } = ('./exceptions');
class AnnotationsSaver { class AnnotationsSaver {
constructor(version, collection, session) { 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.id = session.id;
this.version = version; this.version = version;
this.collection = collection; this.collection = collection;
@ -102,7 +104,7 @@
} else if (typeof (object.id) === 'undefined') { } else if (typeof (object.id) === 'undefined') {
splitted.created[type].push(object); splitted.created[type].push(object);
} else { } else {
throw new window.cvat.exceptions.ScriptingError( throw new ScriptingError(
`Id of object is defined "${object.id}"` `Id of object is defined "${object.id}"`
+ 'but it absents in initial state', + 'but it absents in initial state',
); );
@ -140,7 +142,7 @@
+ indexes.shapes.length + indexes.tags.length; + indexes.shapes.length + indexes.tags.length;
if (indexesLength !== savedLength) { if (indexesLength !== savedLength) {
throw new window.cvat.exception.ScriptingError( throw new ScriptingError(
'Number of indexes is differed by number of saved objects' 'Number of indexes is differed by number of saved objects'
+ `${indexesLength} vs ${savedLength}`, + `${indexesLength} vs ${savedLength}`,
); );

@ -12,6 +12,11 @@
const Collection = require('./annotations-collection'); const Collection = require('./annotations-collection');
const AnnotationsSaver = require('./annotations-saver'); const AnnotationsSaver = require('./annotations-saver');
const { checkObjectType } = require('./common'); const { checkObjectType } = require('./common');
const { Task } = require('./session');
const {
ScriptingError,
DataError,
} = require('./exceptions');
const jobCache = new WeakMap(); const jobCache = new WeakMap();
const taskCache = new WeakMap(); const taskCache = new WeakMap();
@ -25,13 +30,13 @@
return jobCache; return jobCache;
} }
throw new window.cvat.exceptions.ScriptingError( throw new ScriptingError(
`Unknown session type was received ${sessionType}`, `Unknown session type was received ${sessionType}`,
); );
} }
async function getAnnotationsFromServer(session) { 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); const cache = getCache(sessionType);
if (!cache.has(session)) { if (!cache.has(session)) {
@ -65,13 +70,13 @@
async function getAnnotations(session, frame, filter) { async function getAnnotations(session, frame, filter) {
await getAnnotationsFromServer(session); await getAnnotationsFromServer(session);
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job'; const sessionType = session instanceof Task ? 'task' : 'job';
const cache = getCache(sessionType); const cache = getCache(sessionType);
return cache.get(session).collection.get(frame, filter); return cache.get(session).collection.get(frame, filter);
} }
async function saveAnnotations(session, onUpdate) { 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); const cache = getCache(sessionType);
if (cache.has(session)) { if (cache.has(session)) {
@ -82,46 +87,46 @@
} }
function mergeAnnotations(session, objectStates) { 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); const cache = getCache(sessionType);
if (cache.has(session)) { if (cache.has(session)) {
return cache.get(session).collection.merge(objectStates); 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', 'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before',
); );
} }
function splitAnnotations(session, objectState, frame) { 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); const cache = getCache(sessionType);
if (cache.has(session)) { if (cache.has(session)) {
return cache.get(session).collection.split(objectState, frame); 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', 'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before',
); );
} }
function groupAnnotations(session, objectStates, reset) { 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); const cache = getCache(sessionType);
if (cache.has(session)) { if (cache.has(session)) {
return cache.get(session).collection.group(objectStates, reset); 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', 'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before',
); );
} }
function hasUnsavedChanges(session) { function hasUnsavedChanges(session) {
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job'; const sessionType = session instanceof Task ? 'task' : 'job';
const cache = getCache(sessionType); const cache = getCache(sessionType);
if (cache.has(session)) { if (cache.has(session)) {
@ -133,7 +138,7 @@
async function clearAnnotations(session, reload) { async function clearAnnotations(session, reload) {
checkObjectType('reload', reload, 'boolean', null); 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); const cache = getCache(sessionType);
if (cache.has(session)) { if (cache.has(session)) {
@ -147,51 +152,51 @@
} }
function annotationsStatistics(session) { function annotationsStatistics(session) {
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job'; const sessionType = session instanceof Task ? 'task' : 'job';
const cache = getCache(sessionType); const cache = getCache(sessionType);
if (cache.has(session)) { if (cache.has(session)) {
return cache.get(session).collection.statistics(); 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', 'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before',
); );
} }
function putAnnotations(session, objectStates) { 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); const cache = getCache(sessionType);
if (cache.has(session)) { if (cache.has(session)) {
return cache.get(session).collection.put(objectStates); 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', 'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before',
); );
} }
function selectObject(session, objectStates, x, y) { 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); const cache = getCache(sessionType);
if (cache.has(session)) { if (cache.has(session)) {
return cache.get(session).collection.select(objectStates, x, y); 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', 'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before',
); );
} }
async function uploadAnnotations(session, file, format) { 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); await serverProxy.annotations.uploadAnnotations(sessionType, session.id, file, format);
} }
async function dumpAnnotations(session, name, 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 const result = await serverProxy.annotations
.dumpAnnotations(sessionType, session.id, name, format); .dumpAnnotations(sessionType, session.id, name, format);
return result; return result;

@ -21,9 +21,18 @@
checkFilter, checkFilter,
} = require('./common'); } = require('./common');
const {
TaskStatus,
TaskMode,
} = require('./enums');
const User = require('./user');
const { ArgumentError } = require('./exceptions');
const { Task } = require('./session');
function implementAPI(cvat) { function implementAPI(cvat) {
cvat.plugins.list.implementation = PluginRegistry.list; 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 () => { cvat.server.about.implementation = async () => {
const result = await serverProxy.server.about(); const result = await serverProxy.server.about();
@ -56,7 +65,7 @@
users = await serverProxy.users.getUsers(); users = await serverProxy.users.getUsers();
} }
users = users.map(user => new window.cvat.classes.User(user)); users = users.map(user => new User(user));
return users; return users;
}; };
@ -67,13 +76,13 @@
}); });
if (('taskID' in filter) && ('jobID' in filter)) { 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', 'Only one of fields "taskID" and "jobID" allowed simultaneously',
); );
} }
if (!Object.keys(filter).length) { if (!Object.keys(filter).length) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
'Job filter must not be empty', '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 task was found by its id, then create task instance and get Job instance from it
if (tasks !== null && tasks.length) { 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; return filter.jobID ? task.jobs.filter(job => job.id === filter.jobID) : task.jobs;
} }
@ -105,13 +114,13 @@
owner: isString, owner: isString,
assignee: isString, assignee: isString,
search: isString, search: isString,
status: isEnum.bind(window.cvat.enums.TaskStatus), status: isEnum.bind(TaskStatus),
mode: isEnum.bind(window.cvat.enums.TaskMode), mode: isEnum.bind(TaskMode),
}); });
if ('search' in filter && Object.keys(filter).length > 1) { if ('search' in filter && Object.keys(filter).length > 1) {
if (!('page' in filter && Object.keys(filter).length === 2)) { 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', 'Do not use the filter field "search" with others',
); );
} }
@ -119,7 +128,7 @@
if ('id' in filter && Object.keys(filter).length > 1) { if ('id' in filter && Object.keys(filter).length > 1) {
if (!('page' in filter && Object.keys(filter).length === 2)) { 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', 'Do not use the filter field "id" with others',
); );
} }
@ -133,7 +142,7 @@
} }
const tasksData = await serverProxy.tasks.getTasks(searchParams.toString()); 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; tasks.count = tasksData.count;
return tasks; return tasks;

@ -12,7 +12,7 @@
* @module API * @module API
*/ */
(() => { function build() {
const PluginRegistry = require('./plugins'); const PluginRegistry = require('./plugins');
const User = require('./user'); const User = require('./user');
const ObjectState = require('./object-state'); const ObjectState = require('./object-state');
@ -41,7 +41,7 @@
} = require('./exceptions'); } = require('./exceptions');
const pjson = require('../package.json'); const pjson = require('../package.json');
const clientID = +Date.now().toString().substr(-6); const config = require('./config');
/** /**
* API entrypoint * API entrypoint
@ -274,7 +274,7 @@
* put: { * put: {
* // The first argument "self" is a plugin, like in a case above * // The first argument "self" is a plugin, like in a case above
* // The second argument is an argument of the * // The second argument is an argument of the
* // cvat.Job.annotations.put() * // Job.annotations.put()
* // It contains an array of objects to put * // It contains an array of objects to put
* // In this sample we round objects coordinates and save them * // In this sample we round objects coordinates and save them
* enter(self, objects) { * enter(self, objects) {
@ -301,7 +301,7 @@
* internal: { * internal: {
* async getPlugins() { * async getPlugins() {
* // Collect information about installed plugins * // Collect information about installed plugins
* const plugins = await window.cvat.plugins.list(); * const plugins = await cvat.plugins.list();
* return plugins.map((el) => { * return plugins.map((el) => {
* return { * return {
* name: el.name, * name: el.name,
@ -362,12 +362,32 @@
* value which is displayed in a logs * value which is displayed in a logs
* @memberof module:API.cvat.config * @memberof module:API.cvat.config
*/ */
backendAPI: 'http://localhost:7000/api/v1', get backendAPI() {
proxy: false, return config.backendAPI;
taskID: undefined, },
jobID: undefined, set backendAPI(value) {
clientID: { config.backendAPI = value;
get: () => clientID, },
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.plugins = Object.freeze(cvat.plugins);
cvat.client = Object.freeze(cvat.client); cvat.client = Object.freeze(cvat.client);
cvat.enums = Object.freeze(cvat.enums); cvat.enums = Object.freeze(cvat.enums);
cvat.Job = Object.freeze(cvat.Job);
cvat.Task = Object.freeze(cvat.Task);
const implementAPI = require('./api-implementation'); const implementAPI = require('./api-implementation');
if (typeof (window) === 'undefined') {
// Dummy browser environment
require('browser-env')();
}
Math.clamp = function (value, min, max) { Math.clamp = function (value, min, max) {
return Math.min(Math.max(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();

@ -3,7 +3,13 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
/* global
require:false
*/
(() => { (() => {
const { ArgumentError } = require('./exceptions');
function isBoolean(value) { function isBoolean(value) {
return typeof (value) === 'boolean'; return typeof (value) === 'boolean';
} }
@ -33,11 +39,11 @@
for (const prop in filter) { for (const prop in filter) {
if (Object.prototype.hasOwnProperty.call(filter, prop)) { if (Object.prototype.hasOwnProperty.call(filter, prop)) {
if (!(prop in fields)) { if (!(prop in fields)) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`Unsupported filter property has been recieved: "${prop}"`, `Unsupported filter property has been recieved: "${prop}"`,
); );
} else if (!fields[prop](filter[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`, `Received filter property "${prop}" is not satisfied for checker`,
); );
} }
@ -53,20 +59,20 @@
return; return;
} }
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`"${name}" is expected to be "${type}", but "${typeof (value)}" has been got.`, `"${name}" is expected to be "${type}", but "${typeof (value)}" has been got.`,
); );
} }
} else if (instance) { } else if (instance) {
if (!(value instanceof instance)) { if (!(value instanceof instance)) {
if (value !== undefined) { if (value !== undefined) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`"${name}" is expected to be ${instance.name}, but ` `"${name}" is expected to be ${instance.name}, but `
+ `"${value.constructor.name}" has been got`, + `"${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.`, `"${name}" is expected to be ${instance.name}, but "undefined" has been got.`,
); );
} }

@ -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),
};

@ -10,6 +10,7 @@
(() => { (() => {
const Platform = require('platform'); const Platform = require('platform');
const ErrorStackParser = require('error-stack-parser'); const ErrorStackParser = require('error-stack-parser');
const config = require('./config');
/** /**
* Base exception class * Base exception class
@ -35,7 +36,7 @@
jobID, jobID,
taskID, taskID,
clientID, clientID,
} = window.cvat.config; } = config;
const projID = undefined; // wasn't implemented const projID = undefined; // wasn't implemented

@ -11,6 +11,7 @@
(() => { (() => {
const PluginRegistry = require('./plugins'); const PluginRegistry = require('./plugins');
const serverProxy = require('./server-proxy'); const serverProxy = require('./server-proxy');
const { ArgumentError } = require('./exceptions');
// This is the frames storage // This is the frames storage
const frameDataCache = {}; const frameDataCache = {};
@ -78,11 +79,11 @@
if (!(this.number in frameCache[this.tid])) { if (!(this.number in frameCache[this.tid])) {
const frame = await serverProxy.frames.getData(this.tid, this.number); const frame = await serverProxy.frames.getData(this.tid, this.number);
if (window.URL.createObjectURL) { // browser env if (typeof (module) !== 'undefined' && module.exports) {
const url = window.URL.createObjectURL(new Blob([frame]));
frameCache[this.tid][this.number] = url;
} else {
frameCache[this.tid][this.number] = global.Buffer.from(frame, 'binary').toString('base64'); 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; [size] = frameDataCache[taskID].meta;
} else if (mode === 'annotation') { } else if (mode === 'annotation') {
if (frame >= frameDataCache[taskID].meta.length) { 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`, `Meta information about frame ${frame} can't be received from the server`,
); );
} else { } else {
size = frameDataCache[taskID].meta[frame]; size = frameDataCache[taskID].meta[frame];
} }
} else { } else {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`Invalid mode is specified ${mode}`, `Invalid mode is specified ${mode}`,
); );
} }

@ -3,7 +3,14 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
/* global
require:false
*/
(() => { (() => {
const { AttributeType } = require('./enums');
const { ArgumentError } = require('./exceptions');
/** /**
* Class representing an attribute * Class representing an attribute
* @memberof module:API.cvat.classes * @memberof module:API.cvat.classes
@ -32,8 +39,8 @@
} }
} }
if (!Object.values(window.cvat.enums.AttributeType).includes(data.input_type)) { if (!Object.values(AttributeType).includes(data.input_type)) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`Got invalid attribute type ${data.input_type}`, `Got invalid attribute type ${data.input_type}`,
); );
} }
@ -144,7 +151,7 @@
if (Object.prototype.hasOwnProperty.call(initialData, 'attributes') if (Object.prototype.hasOwnProperty.call(initialData, 'attributes')
&& Array.isArray(initialData.attributes)) { && Array.isArray(initialData.attributes)) {
for (const attrData of initialData.attributes) { for (const attrData of initialData.attributes) {
data.attributes.push(new window.cvat.classes.Attribute(attrData)); data.attributes.push(new Attribute(attrData));
} }
} }

@ -9,6 +9,7 @@
(() => { (() => {
const PluginRegistry = require('./plugins'); const PluginRegistry = require('./plugins');
const { ArgumentError } = require('./exceptions');
/** /**
* Class representing a state of an object on a specific frame * Class representing a state of an object on a specific frame
@ -162,7 +163,7 @@
data.updateFlags.points = true; data.updateFlags.points = true;
data.points = [...points]; data.points = [...points];
} else { } else {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
'Points are expected to be an array ' 'Points are expected to be an array '
+ `but got ${typeof (points) === 'object' + `but got ${typeof (points) === 'object'
? points.constructor.name : typeof (points)}`, ? points.constructor.name : typeof (points)}`,
@ -261,7 +262,7 @@
get: () => data.attributes, get: () => data.attributes,
set: (attributes) => { set: (attributes) => {
if (typeof (attributes) !== 'object') { if (typeof (attributes) !== 'object') {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
'Attributes are expected to be an object ' 'Attributes are expected to be an object '
+ `but got ${typeof (attributes) === 'object' + `but got ${typeof (attributes) === 'object'
? attributes.constructor.name : typeof (attributes)}`, ? attributes.constructor.name : typeof (attributes)}`,

@ -14,7 +14,7 @@
class PluginRegistry { class PluginRegistry {
static async apiWrapper(wrappedFunc, ...args) { static async apiWrapper(wrappedFunc, ...args) {
// I have to optimize the wrapper // I have to optimize the wrapper
const pluginList = await window.cvat.plugins.list.implementation(); const pluginList = await PluginRegistry.list();
for (const plugin of pluginList) { for (const plugin of pluginList) {
const pluginDecorators = plugin.functions const pluginDecorators = plugin.functions
.filter(obj => obj.callback === wrappedFunc)[0]; .filter(obj => obj.callback === wrappedFunc)[0];
@ -52,6 +52,7 @@
return result; return result;
} }
// Called with cvat context
static async register(plug) { static async register(plug) {
const functions = []; const functions = [];
@ -92,7 +93,7 @@
functions.push(decorator); functions.push(decorator);
} }
}(plug, { }(plug, {
cvat: window.cvat, cvat: this,
})); }));
Object.defineProperty(plug, 'functions', { Object.defineProperty(plug, 'functions', {

@ -9,6 +9,14 @@
*/ */
(() => { (() => {
const FormData = require('form-data');
const {
ServerError,
ScriptingError,
} = require('./exceptions');
const config = require('./config');
class ServerProxy { class ServerProxy {
constructor() { constructor() {
const Cookie = require('js-cookie'); const Cookie = require('js-cookie');
@ -25,16 +33,16 @@
} }
async function about() { async function about() {
const { backendAPI } = window.cvat.config; const { backendAPI } = config;
let response = null; let response = null;
try { try {
response = await Axios.get(`${backendAPI}/server/about`, { response = await Axios.get(`${backendAPI}/server/about`, {
proxy: window.cvat.config.proxy, proxy: config.proxy,
}); });
} catch (errorData) { } catch (errorData) {
const code = errorData.response ? errorData.response.status : errorData.code; 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', 'Could not get "about" information from the server',
code, code,
); );
@ -44,17 +52,17 @@
} }
async function share(directory) { async function share(directory) {
const { backendAPI } = window.cvat.config; const { backendAPI } = config;
directory = encodeURIComponent(directory); directory = encodeURIComponent(directory);
let response = null; let response = null;
try { try {
response = await Axios.get(`${backendAPI}/server/share?directory=${directory}`, { response = await Axios.get(`${backendAPI}/server/share?directory=${directory}`, {
proxy: window.cvat.config.proxy, proxy: config.proxy,
}); });
} catch (errorData) { } catch (errorData) {
const code = errorData.response ? errorData.response.status : errorData.code; 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', 'Could not get "share" information from the server',
code, code,
); );
@ -64,18 +72,18 @@
} }
async function exception(exceptionObject) { async function exception(exceptionObject) {
const { backendAPI } = window.cvat.config; const { backendAPI } = config;
try { try {
await Axios.post(`${backendAPI}/server/exception`, JSON.stringify(exceptionObject), { await Axios.post(`${backendAPI}/server/exception`, JSON.stringify(exceptionObject), {
proxy: window.cvat.config.proxy, proxy: config.proxy,
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
}); });
} catch (errorData) { } catch (errorData) {
const code = errorData.response ? errorData.response.status : errorData.code; 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', 'Could not send an exception to the server',
code, code,
); );
@ -111,7 +119,7 @@
if (csrftoken) { if (csrftoken) {
setCSRFHeader(csrftoken); setCSRFHeader(csrftoken);
} else { } else {
throw new window.cvat.exceptions.ScriptingError( throw new ScriptingError(
'An environment has been detected as a browser' 'An environment has been detected as a browser'
+ ', but CSRF token has not been found in cookies', + ', 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; let csrf = null;
try { try {
csrf = await Axios.get(`${host}/auth/csrf`, { csrf = await Axios.get(`${host}/auth/csrf`, {
proxy: window.cvat.config.proxy, proxy: config.proxy,
}); });
} catch (errorData) { } catch (errorData) {
const code = errorData.response ? errorData.response.status : errorData.code; 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', 'Could not get CSRF token from a server',
code, code,
); );
@ -148,7 +156,7 @@
authentificationData, authentificationData,
{ {
'Content-Type': 'application/x-www-form-urlencoded', 'Content-Type': 'application/x-www-form-urlencoded',
proxy: window.cvat.config.proxy, proxy: config.proxy,
// do not redirect to a dashboard, // do not redirect to a dashboard,
// otherwise we don't get a session id in a response // otherwise we don't get a session id in a response
maxRedirects: 0, maxRedirects: 0,
@ -161,7 +169,7 @@
} else { } else {
const code = errorData.response const code = errorData.response
? errorData.response.status : errorData.code; ? errorData.response.status : errorData.code;
throw new window.cvat.exceptions.ServerError( throw new ServerError(
'Could not login on a server', 'Could not login on a server',
code, code,
); );
@ -170,7 +178,7 @@
// TODO: Perhaps we should redesign the authorization method on the server. // TODO: Perhaps we should redesign the authorization method on the server.
if (authentificationResponse.data.includes('didn\'t match')) { if (authentificationResponse.data.includes('didn\'t match')) {
throw new window.cvat.exceptions.ServerError( throw new ServerError(
'The pair login/password is invalid', 'The pair login/password is invalid',
403, 403,
); );
@ -180,15 +188,15 @@
} }
async function logout() { async function logout() {
const host = window.cvat.config.backendAPI.slice(0, -7); const host = config.backendAPI.slice(0, -7);
try { try {
await Axios.get(`${host}/auth/logout`, { await Axios.get(`${host}/auth/logout`, {
proxy: window.cvat.config.proxy, proxy: config.proxy,
}); });
} catch (errorData) { } catch (errorData) {
const code = errorData.response ? errorData.response.status : errorData.code; const code = errorData.response ? errorData.response.status : errorData.code;
throw new window.cvat.exceptions.ServerError( throw new ServerError(
'Could not logout from the server', 'Could not logout from the server',
code, code,
); );
@ -196,16 +204,16 @@
} }
async function getTasks(filter = '') { async function getTasks(filter = '') {
const { backendAPI } = window.cvat.config; const { backendAPI } = config;
let response = null; let response = null;
try { try {
response = await Axios.get(`${backendAPI}/tasks?${filter}`, { response = await Axios.get(`${backendAPI}/tasks?${filter}`, {
proxy: window.cvat.config.proxy, proxy: config.proxy,
}); });
} catch (errorData) { } catch (errorData) {
const code = errorData.response ? errorData.response.status : errorData.code; 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', 'Could not get tasks from a server',
code, code,
); );
@ -216,18 +224,18 @@
} }
async function saveTask(id, taskData) { async function saveTask(id, taskData) {
const { backendAPI } = window.cvat.config; const { backendAPI } = config;
try { try {
await Axios.patch(`${backendAPI}/tasks/${id}`, JSON.stringify(taskData), { await Axios.patch(`${backendAPI}/tasks/${id}`, JSON.stringify(taskData), {
proxy: window.cvat.config.proxy, proxy: config.proxy,
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
}); });
} catch (errorData) { } catch (errorData) {
const code = errorData.response ? errorData.response.status : errorData.code; 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', 'Could not save the task on the server',
code, code,
); );
@ -235,13 +243,13 @@
} }
async function deleteTask(id) { async function deleteTask(id) {
const { backendAPI } = window.cvat.config; const { backendAPI } = config;
try { try {
await Axios.delete(`${backendAPI}/tasks/${id}`); await Axios.delete(`${backendAPI}/tasks/${id}`);
} catch (errorData) { } catch (errorData) {
const code = errorData.response ? errorData.response.status : errorData.code; 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', 'Could not delete the task from the server',
code, code,
); );
@ -249,7 +257,7 @@
} }
async function createTask(taskData, files, onUpdate) { async function createTask(taskData, files, onUpdate) {
const { backendAPI } = window.cvat.config; const { backendAPI } = config;
async function wait(id) { async function wait(id) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -266,14 +274,14 @@
} else if (response.data.state === 'Failed') { } else if (response.data.state === 'Failed') {
// If request has been successful, but task hasn't been created // If request has been successful, but task hasn't been created
// Then passed data is wrong and we can pass code 400 // 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', 'Could not create the task on the server',
400, 400,
)); ));
} else { } else {
// If server has another status, it is unexpected // If server has another status, it is unexpected
// Therefore it is server error and we can pass code 500 // 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}`, `Unknown task state has been recieved: ${response.data.state}`,
500, 500,
)); ));
@ -282,7 +290,7 @@
const code = errorData.response const code = errorData.response
? errorData.response.status : errorData.code; ? errorData.response.status : errorData.code;
reject(new window.cvat.exceptions.ServerError( reject(new ServerError(
'Data uploading error occured', 'Data uploading error occured',
code, code,
)); ));
@ -293,7 +301,7 @@
}); });
} }
const batchOfFiles = new window.FormData(); const batchOfFiles = new FormData();
for (const key in files) { for (const key in files) {
if (Object.prototype.hasOwnProperty.call(files, key)) { if (Object.prototype.hasOwnProperty.call(files, key)) {
for (let i = 0; i < files[key].length; i++) { for (let i = 0; i < files[key].length; i++) {
@ -307,14 +315,14 @@
onUpdate('The task is being created on the server..'); onUpdate('The task is being created on the server..');
try { try {
response = await Axios.post(`${backendAPI}/tasks`, JSON.stringify(taskData), { response = await Axios.post(`${backendAPI}/tasks`, JSON.stringify(taskData), {
proxy: window.cvat.config.proxy, proxy: config.proxy,
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
}); });
} catch (errorData) { } catch (errorData) {
const code = errorData.response ? errorData.response.status : errorData.code; 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', 'Could not put task to the server',
code, code,
); );
@ -323,12 +331,12 @@
onUpdate('The data is being uploaded to the server..'); onUpdate('The data is being uploaded to the server..');
try { try {
await Axios.post(`${backendAPI}/tasks/${response.data.id}/data`, batchOfFiles, { await Axios.post(`${backendAPI}/tasks/${response.data.id}/data`, batchOfFiles, {
proxy: window.cvat.config.proxy, proxy: config.proxy,
}); });
} catch (errorData) { } catch (errorData) {
await deleteTask(response.data.id); await deleteTask(response.data.id);
const code = errorData.response ? errorData.response.status : errorData.code; 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', 'Could not put data to the server',
code, code,
); );
@ -347,16 +355,16 @@
} }
async function getJob(jobID) { async function getJob(jobID) {
const { backendAPI } = window.cvat.config; const { backendAPI } = config;
let response = null; let response = null;
try { try {
response = await Axios.get(`${backendAPI}/jobs/${jobID}`, { response = await Axios.get(`${backendAPI}/jobs/${jobID}`, {
proxy: window.cvat.config.proxy, proxy: config.proxy,
}); });
} catch (errorData) { } catch (errorData) {
const code = errorData.response ? errorData.response.status : errorData.code; 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', 'Could not get jobs from a server',
code, code,
); );
@ -366,18 +374,18 @@
} }
async function saveJob(id, jobData) { async function saveJob(id, jobData) {
const { backendAPI } = window.cvat.config; const { backendAPI } = config;
try { try {
await Axios.patch(`${backendAPI}/jobs/${id}`, JSON.stringify(jobData), { await Axios.patch(`${backendAPI}/jobs/${id}`, JSON.stringify(jobData), {
proxy: window.cvat.config.proxy, proxy: config.proxy,
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
}); });
} catch (errorData) { } catch (errorData) {
const code = errorData.response ? errorData.response.status : errorData.code; 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', 'Could not save the job on the server',
code, code,
); );
@ -385,16 +393,16 @@
} }
async function getUsers() { async function getUsers() {
const { backendAPI } = window.cvat.config; const { backendAPI } = config;
let response = null; let response = null;
try { try {
response = await Axios.get(`${backendAPI}/users`, { response = await Axios.get(`${backendAPI}/users`, {
proxy: window.cvat.config.proxy, proxy: config.proxy,
}); });
} catch (errorData) { } catch (errorData) {
const code = errorData.response ? errorData.response.status : errorData.code; 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', 'Could not get users from the server',
code, code,
); );
@ -404,16 +412,16 @@
} }
async function getSelf() { async function getSelf() {
const { backendAPI } = window.cvat.config; const { backendAPI } = config;
let response = null; let response = null;
try { try {
response = await Axios.get(`${backendAPI}/users/self`, { response = await Axios.get(`${backendAPI}/users/self`, {
proxy: window.cvat.config.proxy, proxy: config.proxy,
}); });
} catch (errorData) { } catch (errorData) {
const code = errorData.response ? errorData.response.status : errorData.code; 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', 'Could not get users from the server',
code, code,
); );
@ -423,17 +431,17 @@
} }
async function getData(tid, frame) { async function getData(tid, frame) {
const { backendAPI } = window.cvat.config; const { backendAPI } = config;
let response = null; let response = null;
try { try {
response = await Axios.get(`${backendAPI}/tasks/${tid}/frames/${frame}`, { response = await Axios.get(`${backendAPI}/tasks/${tid}/frames/${frame}`, {
proxy: window.cvat.config.proxy, proxy: config.proxy,
responseType: 'blob', responseType: 'blob',
}); });
} catch (errorData) { } catch (errorData) {
const code = errorData.response ? errorData.response.status : errorData.code; 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`, `Could not get frame ${frame} for the task ${tid} from the server`,
code, code,
); );
@ -443,16 +451,16 @@
} }
async function getMeta(tid) { async function getMeta(tid) {
const { backendAPI } = window.cvat.config; const { backendAPI } = config;
let response = null; let response = null;
try { try {
response = await Axios.get(`${backendAPI}/tasks/${tid}/frames/meta`, { response = await Axios.get(`${backendAPI}/tasks/${tid}/frames/meta`, {
proxy: window.cvat.config.proxy, proxy: config.proxy,
}); });
} catch (errorData) { } catch (errorData) {
const code = errorData.response ? errorData.response.status : errorData.code; 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`, `Could not get frame meta info for the task ${tid} from the server`,
code, code,
); );
@ -463,16 +471,16 @@
// Session is 'task' or 'job' // Session is 'task' or 'job'
async function getAnnotations(session, id) { async function getAnnotations(session, id) {
const { backendAPI } = window.cvat.config; const { backendAPI } = config;
let response = null; let response = null;
try { try {
response = await Axios.get(`${backendAPI}/${session}s/${id}/annotations`, { response = await Axios.get(`${backendAPI}/${session}s/${id}/annotations`, {
proxy: window.cvat.config.proxy, proxy: config.proxy,
}); });
} catch (errorData) { } catch (errorData) {
const code = errorData.response ? errorData.response.status : errorData.code; 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`, `Could not get annotations for the ${session} ${id} from the server`,
code, code,
); );
@ -483,7 +491,7 @@
// Session is 'task' or 'job' // Session is 'task' or 'job'
async function updateAnnotations(session, id, data, action) { async function updateAnnotations(session, id, data, action) {
const { backendAPI } = window.cvat.config; const { backendAPI } = config;
let requestFunc = null; let requestFunc = null;
let url = null; let url = null;
if (action.toUpperCase() === 'PUT') { if (action.toUpperCase() === 'PUT') {
@ -497,14 +505,14 @@
let response = null; let response = null;
try { try {
response = await requestFunc(url, JSON.stringify(data), { response = await requestFunc(url, JSON.stringify(data), {
proxy: window.cvat.config.proxy, proxy: config.proxy,
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
}); });
} catch (errorData) { } catch (errorData) {
const code = errorData.response ? errorData.response.status : errorData.code; 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`, `Could not updated annotations for the ${session} ${id} on the server`,
code, code,
); );
@ -515,7 +523,7 @@
// Session is 'task' or 'job' // Session is 'task' or 'job'
async function uploadAnnotations(session, id, file, format) { async function uploadAnnotations(session, id, file, format) {
const { backendAPI } = window.cvat.config; const { backendAPI } = config;
let annotationData = new FormData(); let annotationData = new FormData();
annotationData.append('annotation_file', file); annotationData.append('annotation_file', file);
@ -525,7 +533,7 @@
try { try {
const response = await Axios const response = await Axios
.post(`${backendAPI}/${session}s/${id}/annotations?upload_format=${format}`, annotationData, { .post(`${backendAPI}/${session}s/${id}/annotations?upload_format=${format}`, annotationData, {
proxy: window.cvat.config.proxy, proxy: config.proxy,
}); });
if (response.status === 202) { if (response.status === 202) {
annotationData = new FormData(); annotationData = new FormData();
@ -536,7 +544,7 @@
} catch (errorData) { } catch (errorData) {
const code = errorData.response const code = errorData.response
? errorData.response.status : errorData.code; ? errorData.response.status : errorData.code;
const error = new window.cvat.exceptions.ServerError( const error = new ServerError(
`Could not upload annotations for the ${session} ${id}`, `Could not upload annotations for the ${session} ${id}`,
code, code,
); );
@ -550,7 +558,7 @@
// Session is 'task' or 'job' // Session is 'task' or 'job'
async function dumpAnnotations(id, name, format) { async function dumpAnnotations(id, name, format) {
const { backendAPI } = window.cvat.config; const { backendAPI } = config;
const filename = name.replace(/\//g, '_'); const filename = name.replace(/\//g, '_');
let url = `${backendAPI}/tasks/${id}/annotations/${filename}?dump_format=${format}`; let url = `${backendAPI}/tasks/${id}/annotations/${filename}?dump_format=${format}`;
@ -559,7 +567,7 @@
try { try {
const response = await Axios const response = await Axios
.get(`${url}`, { .get(`${url}`, {
proxy: window.cvat.config.proxy, proxy: config.proxy,
}); });
if (response.status === 202) { if (response.status === 202) {
setTimeout(request, 3000); setTimeout(request, 3000);
@ -570,7 +578,7 @@
} catch (errorData) { } catch (errorData) {
const code = errorData.response const code = errorData.response
? errorData.response.status : errorData.code; ? 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`, `Could not dump annotations for the task ${id} from the server`,
code, code,
); );

@ -11,20 +11,9 @@
const PluginRegistry = require('./plugins'); const PluginRegistry = require('./plugins');
const serverProxy = require('./server-proxy'); const serverProxy = require('./server-proxy');
const { getFrame } = require('./frames'); const { getFrame } = require('./frames');
const { const { ArgumentError } = require('./exceptions');
getAnnotations, const { TaskStatus } = require('./enums');
putAnnotations, const { Label } = require('./labels');
saveAnnotations,
hasUnsavedChanges,
mergeAnnotations,
splitAnnotations,
groupAnnotations,
clearAnnotations,
selectObject,
annotationsStatistics,
uploadAnnotations,
dumpAnnotations,
} = require('./annotations');
function buildDublicatedAPI(prototype) { function buildDublicatedAPI(prototype) {
Object.defineProperties(prototype, { Object.defineProperties(prototype, {
@ -509,7 +498,7 @@
} }
if (data[property] === undefined) { if (data[property] === undefined) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`Job field "${property}" was not initialized`, `Job field "${property}" was not initialized`,
); );
} }
@ -539,7 +528,7 @@
get: () => data.assignee, get: () => data.assignee,
set: () => (assignee) => { set: () => (assignee) => {
if (!Number.isInteger(assignee) || assignee < 0) { if (!Number.isInteger(assignee) || assignee < 0) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
'Value must be a non negative integer', 'Value must be a non negative integer',
); );
} }
@ -556,7 +545,7 @@
status: { status: {
get: () => data.status, get: () => data.status,
set: (status) => { set: (status) => {
const type = window.cvat.enums.TaskStatus; const type = TaskStatus;
let valueInEnum = false; let valueInEnum = false;
for (const value in type) { for (const value in type) {
if (type[value] === status) { if (type[value] === status) {
@ -566,7 +555,7 @@
} }
if (!valueInEnum) { if (!valueInEnum) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
'Value must be a value from the enumeration cvat.enums.TaskStatus', '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 * Class representing a task
* @memberof module:API.cvat.classes * @memberof module:API.cvat.classes
* @extends Session * @extends Session
*/ */
class Task extends Session { class Task extends Session {
/** /**
* In a fact you need use the constructor only if you want to create a task * 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 * @param {object} initialData - Object which is used for initalization
* <br> It can contain keys: * <br> It can contain keys:
* <br> <li style="margin-left: 10px;"> name * <br> <li style="margin-left: 10px;"> name
* <br> <li style="margin-left: 10px;"> assignee * <br> <li style="margin-left: 10px;"> assignee
* <br> <li style="margin-left: 10px;"> bug_tracker * <br> <li style="margin-left: 10px;"> bug_tracker
* <br> <li style="margin-left: 10px;"> z_order * <br> <li style="margin-left: 10px;"> z_order
* <br> <li style="margin-left: 10px;"> labels * <br> <li style="margin-left: 10px;"> labels
* <br> <li style="margin-left: 10px;"> segment_size * <br> <li style="margin-left: 10px;"> segment_size
* <br> <li style="margin-left: 10px;"> overlap * <br> <li style="margin-left: 10px;"> overlap
*/ */
constructor(initialData) { constructor(initialData) {
super(); super();
const data = { const data = {
@ -786,6 +671,9 @@
segment_size: undefined, segment_size: undefined,
z_order: undefined, z_order: undefined,
image_quality: undefined, image_quality: undefined,
start_frame: undefined,
stop_frame: undefined,
frame_filter: undefined,
}; };
for (const property in data) { for (const property in data) {
@ -807,7 +695,7 @@
for (const segment of initialData.segments) { for (const segment of initialData.segments) {
if (Array.isArray(segment.jobs)) { if (Array.isArray(segment.jobs)) {
for (const job of segment.jobs) { for (const job of segment.jobs) {
const jobInstance = new window.cvat.classes.Job({ const jobInstance = new Job({
url: job.url, url: job.url,
id: job.id, id: job.id,
assignee: job.assignee, assignee: job.assignee,
@ -824,7 +712,7 @@
if (Array.isArray(initialData.labels)) { if (Array.isArray(initialData.labels)) {
for (const label of 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); data.labels.push(classInstance);
} }
} }
@ -851,7 +739,7 @@
get: () => data.name, get: () => data.name,
set: (value) => { set: (value) => {
if (!value.trim().length) { if (!value.trim().length) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
'Value must not be empty', 'Value must not be empty',
); );
} }
@ -911,7 +799,7 @@
get: () => data.assignee, get: () => data.assignee,
set: () => (assignee) => { set: () => (assignee) => {
if (!Number.isInteger(assignee) || assignee < 0) { if (!Number.isInteger(assignee) || assignee < 0) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
'Value must be a non negative integer', 'Value must be a non negative integer',
); );
} }
@ -962,7 +850,7 @@
get: () => data.overlap, get: () => data.overlap,
set: (overlap) => { set: (overlap) => {
if (!Number.isInteger(overlap) || overlap < 0) { if (!Number.isInteger(overlap) || overlap < 0) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
'Value must be a non negative integer', 'Value must be a non negative integer',
); );
} }
@ -980,7 +868,7 @@
get: () => data.segment_size, get: () => data.segment_size,
set: (segment) => { set: (segment) => {
if (!Number.isInteger(segment) || segment < 0) { if (!Number.isInteger(segment) || segment < 0) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
'Value must be a positive integer', 'Value must be a positive integer',
); );
} }
@ -998,7 +886,7 @@
get: () => data.z_order, get: () => data.z_order,
set: (zOrder) => { set: (zOrder) => {
if (typeof (zOrder) !== 'boolean') { if (typeof (zOrder) !== 'boolean') {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
'Value must be a boolean', 'Value must be a boolean',
); );
} }
@ -1016,7 +904,7 @@
get: () => data.image_quality, get: () => data.image_quality,
set: (quality) => { set: (quality) => {
if (!Number.isInteger(quality) || quality < 0) { if (!Number.isInteger(quality) || quality < 0) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
'Value must be a positive integer', 'Value must be a positive integer',
); );
} }
@ -1035,14 +923,14 @@
get: () => [...data.labels], get: () => [...data.labels],
set: (labels) => { set: (labels) => {
if (!Array.isArray(labels)) { if (!Array.isArray(labels)) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
'Value must be an array of Labels', 'Value must be an array of Labels',
); );
} }
for (const label of labels) { for (const label of labels) {
if (!(label instanceof window.cvat.classes.Label)) { if (!(label instanceof Label)) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
'Each array value must be an instance of Label. ' 'Each array value must be an instance of Label. '
+ `${typeof (label)} was found`, + `${typeof (label)} was found`,
); );
@ -1078,14 +966,14 @@
get: () => [...data.files.server_files], get: () => [...data.files.server_files],
set: (serverFiles) => { set: (serverFiles) => {
if (!Array.isArray(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.`, `Value must be an array. But ${typeof (serverFiles)} has been got.`,
); );
} }
for (const value of serverFiles) { for (const value of serverFiles) {
if (typeof (value) !== 'string') { 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.`, `Array values must be a string. But ${typeof (value)} has been got.`,
); );
} }
@ -1106,14 +994,14 @@
get: () => [...data.files.client_files], get: () => [...data.files.client_files],
set: (clientFiles) => { set: (clientFiles) => {
if (!Array.isArray(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.`, `Value must be an array. But ${typeof (clientFiles)} has been got.`,
); );
} }
for (const value of clientFiles) { for (const value of clientFiles) {
if (!(value instanceof window.File)) { if (!(value instanceof File)) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`Array values must be a File. But ${value.constructor.name} has been got.`, `Array values must be a File. But ${value.constructor.name} has been got.`,
); );
} }
@ -1134,14 +1022,14 @@
get: () => [...data.files.remote_files], get: () => [...data.files.remote_files],
set: (remoteFiles) => { set: (remoteFiles) => {
if (!Array.isArray(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.`, `Value must be an array. But ${typeof (remoteFiles)} has been got.`,
); );
} }
for (const value of remoteFiles) { for (const value of remoteFiles) {
if (typeof (value) !== 'string') { 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.`, `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); 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() // 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 module.exports = {
// So, we do it seperately Job,
Task,
};
const {
getAnnotations,
putAnnotations,
saveAnnotations,
hasUnsavedChanges,
mergeAnnotations,
splitAnnotations,
groupAnnotations,
clearAnnotations,
selectObject,
annotationsStatistics,
uploadAnnotations,
dumpAnnotations,
} = require('./annotations');
buildDublicatedAPI(Job.prototype);
buildDublicatedAPI(Task.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) { Task.prototype.save.implementation = async function saveTaskImplementation(onUpdate) {
// TODO: Add ability to change an owner and an assignee // TODO: Add ability to change an owner and an assignee
if (typeof (this.id) !== 'undefined') { if (typeof (this.id) !== 'undefined') {
@ -1248,6 +1313,15 @@
if (this.overlap) { if (this.overlap) {
taskData.overlap = 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 = { const taskFiles = {
client_files: this.clientFiles, client_files: this.clientFiles,
@ -1266,13 +1340,13 @@
Task.prototype.frames.get.implementation = async function (frame) { Task.prototype.frames.get.implementation = async function (frame) {
if (!Number.isInteger(frame) || frame < 0) { if (!Number.isInteger(frame) || frame < 0) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`Frame must be a positive integer. Got: "${frame}"`, `Frame must be a positive integer. Got: "${frame}"`,
); );
} }
if (frame >= this.size) { if (frame >= this.size) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`The frame with number ${frame} is out of the task`, `The frame with number ${frame} is out of the task`,
); );
} }
@ -1284,13 +1358,13 @@
// TODO: Check filter for annotations // TODO: Check filter for annotations
Task.prototype.annotations.get.implementation = async function (frame, filter) { Task.prototype.annotations.get.implementation = async function (frame, filter) {
if (!Number.isInteger(frame) || frame < 0) { if (!Number.isInteger(frame) || frame < 0) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`Frame must be a positive integer. Got: "${frame}"`, `Frame must be a positive integer. Got: "${frame}"`,
); );
} }
if (frame >= this.size) { if (frame >= this.size) {
throw new window.cvat.exceptions.ArgumentError( throw new ArgumentError(
`Frame ${frame} does not exist in the task`, `Frame ${frame} does not exist in the task`,
); );
} }
@ -1353,9 +1427,4 @@
const result = await dumpAnnotations(this, name, format); const result = await dumpAnnotations(this, name, format);
return result; return result;
}; };
module.exports = {
Job,
Task,
};
})(); })();

@ -16,7 +16,7 @@ jest.mock('../../src/server-proxy', () => {
}); });
// Initialize api // Initialize api
require('../../src/api'); window.cvat = require('../../src/api');
// Test cases // Test cases
describe('Feature: get annotations', () => { describe('Feature: get annotations', () => {

@ -16,7 +16,7 @@ jest.mock('../../src/server-proxy', () => {
}); });
// Initialize api // Initialize api
require('../../src/api'); window.cvat = require('../../src/api');
const { FrameData } = require('../../src/frames'); const { FrameData } = require('../../src/frames');

@ -16,7 +16,7 @@ jest.mock('../../src/server-proxy', () => {
}); });
// Initialize api // Initialize api
require('../../src/api'); window.cvat = require('../../src/api');
const { Job } = require('../../src/session'); const { Job } = require('../../src/session');

@ -16,7 +16,7 @@ jest.mock('../../src/server-proxy', () => {
}); });
// Initialize api // Initialize api
require('../../src/api'); window.cvat = require('../../src/api');
describe('Feature: set attributes for an object state', () => { describe('Feature: set attributes for an object state', () => {
test('set a valid value', () => { test('set a valid value', () => {

@ -16,7 +16,7 @@ jest.mock('../../src/server-proxy', () => {
}); });
// Initialize api // Initialize api
require('../../src/api'); window.cvat = require('../../src/api');
describe('Feature: dummy feature', () => { describe('Feature: dummy feature', () => {
test('dummy test', async () => { test('dummy test', async () => {

@ -16,7 +16,7 @@ jest.mock('../../src/server-proxy', () => {
}); });
// Initialize api // Initialize api
require('../../src/api'); window.cvat = require('../../src/api');
// Test cases // Test cases
describe('Feature: get info about cvat', () => { describe('Feature: get info about cvat', () => {

@ -16,7 +16,7 @@ jest.mock('../../src/server-proxy', () => {
}); });
// Initialize api // Initialize api
require('../../src/api'); window.cvat = require('../../src/api');
const { Task } = require('../../src/session'); const { Task } = require('../../src/session');

@ -16,7 +16,7 @@ jest.mock('../../src/server-proxy', () => {
}); });
// Initialize api // Initialize api
require('../../src/api'); window.cvat = require('../../src/api');
const User = require('../../src/user'); const User = require('../../src/user');

@ -19,7 +19,6 @@ const {
frameMetaDummyData, frameMetaDummyData,
} = require('./dummy-data.mock'); } = require('./dummy-data.mock');
class ServerProxy { class ServerProxy {
constructor() { constructor() {
async function about() { async function about() {

@ -4,29 +4,42 @@
*/ */
const path = require('path'); const path = require('path');
const webpack = require('webpack');
const configuration = 'production'; const nodeConfig = {
const target = 'web'; target: 'node',
const plugins = []; mode: 'production',
devtool: 'source-map',
if (configuration === 'production' && target === 'web') { entry: './src/api.js',
plugins.push( output: {
/** path: path.resolve(__dirname, 'dist'),
* IgnorePlugin will skip any require filename: 'cvat-core.node.js',
* that matches the following regex. library: 'cvat',
*/ libraryTarget: 'commonjs',
new webpack.IgnorePlugin(/browser-env/), },
); module: {
} rules: [{
test: /.js?$/,
exclude: /node_modules/,
}],
},
externals: {
canvas: 'commonjs canvas',
},
stats: {
warnings: false,
},
};
module.exports = { const webConfig = {
mode: configuration, target: 'web',
mode: 'production',
devtool: 'source-map', devtool: 'source-map',
entry: './src/api.js', entry: './src/api.js',
output: { output: {
path: path.resolve(__dirname, 'dist'), path: path.resolve(__dirname, 'dist'),
filename: 'cvat.js', filename: 'cvat-core.js',
library: 'cvat',
libraryTarget: 'window',
}, },
module: { module: {
rules: [{ rules: [{
@ -54,6 +67,6 @@ module.exports = {
}, },
}], }],
}, },
plugins,
target,
}; };
module.exports = [nodeConfig, webConfig];

@ -408,81 +408,6 @@ class DashboardView {
} }
_setupCreateDialog() { _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 dashboardCreateTaskButton = $('#dashboardCreateTaskButton');
const createModal = $('#dashboardCreateModal'); const createModal = $('#dashboardCreateModal');
const nameInput = $('#dashboardNameInput'); const nameInput = $('#dashboardNameInput');
@ -570,6 +495,10 @@ class DashboardView {
return (overlapSize >= 0 && overlapSize <= segmentSize - 1); return (overlapSize >= 0 && overlapSize <= segmentSize - 1);
} }
function validateStopFrame(stop, start) {
return !customStopFrame.prop('checked') || stop >= start;
}
dashboardCreateTaskButton.on('click', () => { dashboardCreateTaskButton.on('click', () => {
$('#dashboardCreateModal').removeClass('hidden'); $('#dashboardCreateModal').removeClass('hidden');
}); });
@ -589,7 +518,8 @@ class DashboardView {
localSourceRadio.on('click', () => { localSourceRadio.on('click', () => {
if (source === 'local') { if (source === 'local') {
return; return;
} else if (source === 'remote') { }
if (source === 'remote') {
selectFiles.parent().removeClass('hidden'); selectFiles.parent().removeClass('hidden');
remoteFileInput.parent().addClass('hidden'); remoteFileInput.parent().addClass('hidden');
} }
@ -612,7 +542,8 @@ class DashboardView {
shareSourceRadio.on('click', () => { shareSourceRadio.on('click', () => {
if (source === 'share') { if (source === 'share') {
return; return;
} else if (source === 'remote') { }
if (source === 'remote') {
selectFiles.parent().removeClass('hidden'); selectFiles.parent().removeClass('hidden');
remoteFileInput.parent().addClass('hidden'); remoteFileInput.parent().addClass('hidden');
} }
@ -658,8 +589,8 @@ class DashboardView {
updateSelectedFiles(); updateSelectedFiles();
}); });
remoteFileInput.on('change', function(e) { remoteFileInput.on('change', () => {
let text = remoteFileInput.prop('value'); const text = remoteFileInput.prop('value');
files = text.split('\n').map(f => f.trim()).filter(f => f.length > 0); files = text.split('\n').map(f => f.trim()).filter(f => f.length > 0);
}); });
@ -676,12 +607,12 @@ class DashboardView {
zOrder = e.target.checked; zOrder = e.target.checked;
}); });
customSegmentSize.on('change', (e) => segmentSizeInput.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)); customOverlapSize.on('change', e => overlapSizeInput.prop('disabled', !e.target.checked));
customCompressQuality.on('change', (e) => imageQualityInput.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)); customStartFrame.on('change', e => startFrameInput.prop('disabled', !e.target.checked));
customStopFrame.on('change', (e) => stopFrameInput.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)); customFrameFilter.on('change', e => frameFilterInput.prop('disabled', !e.target.checked));
segmentSizeInput.on('change', () => { segmentSizeInput.on('change', () => {
const value = Math.clamp( const value = Math.clamp(
@ -716,8 +647,8 @@ class DashboardView {
compressQuality = value; compressQuality = value;
}); });
startFrameInput.on('change', function() { startFrameInput.on('change', () => {
let value = Math.max( const value = Math.max(
+startFrameInput.prop('value'), +startFrameInput.prop('value'),
+startFrameInput.prop('min') +startFrameInput.prop('min')
); );
@ -725,8 +656,9 @@ class DashboardView {
startFrameInput.prop('value', value); startFrameInput.prop('value', value);
startFrame = value; startFrame = value;
}); });
stopFrameInput.on('change', function() {
let value = Math.max( stopFrameInput.on('change', () => {
const value = Math.max(
+stopFrameInput.prop('value'), +stopFrameInput.prop('value'),
+stopFrameInput.prop('min') +stopFrameInput.prop('min')
); );
@ -734,7 +666,8 @@ class DashboardView {
stopFrameInput.prop('value', value); stopFrameInput.prop('value', value);
stopFrame = value; stopFrame = value;
}); });
frameFilterInput.on('change', function() {
frameFilterInput.on('change', () => {
frameFilter = frameFilterInput.prop('value'); frameFilter = frameFilterInput.prop('value');
}); });
@ -825,6 +758,15 @@ class DashboardView {
if (customFrameFilter.prop('checked')) { if (customFrameFilter.prop('checked')) {
description.frame_filter = frameFilter; 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 { try {
let task = new window.cvat.classes.Task(description); let task = new window.cvat.classes.Task(description);

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save