labo-components
Version:
275 lines (244 loc) • 10.7 kB
JavaScript
import ComponentUtil from "../../../util/ComponentUtil";
import MediaEvents from "../_MediaEvents";
import { ANNOTATION_TARGET } from "../../../util/AnnotationConstants";
import Strings from "../_Strings";
/*
This component handles the keyboard interactions for
- navigating the current player position
- creating/modifying segments
destroy() should be called manually to remove the listeners
*/
export default class KeyboardInteraction {
constructor({ context }) {
this.context = context;
this.currentPosition = 0;
this.listen();
}
listen = () => {
this.context.mediaEvents.bind(
MediaEvents.PLAYER_POS,
this.updateCurrentPosition
);
this.context.mediaEvents.bind(
MediaEvents.SET_PLAYER_POS,
this.updateCurrentPosition
);
document.addEventListener("keydown", this.onKeyPressed);
};
destroy = () => {
this.context.mediaEvents.unbind(
MediaEvents.PLAYER_POS,
this.updateCurrentPosition
);
this.context.mediaEvents.unbind(
MediaEvents.SET_PLAYER_POS,
this.updateCurrentPosition
);
document.removeEventListener("keydown", this.onKeyPressed);
};
updateCurrentPosition = (pos) => {
this.currentPosition = pos;
};
updatePlayerPos = (pos) => {
this.context.mediaEvents.trigger(MediaEvents.SET_PLAYER_POS, pos);
};
/**
* Update Segment data
*/
// Update segment with given selection
async saveActiveSelection() {
// When the selection is null: exit
if (!this.context.annotationClient.activeSelection) {
return;
}
// Update the segment
await this.context.annotationClient.saveSelection(
this.context.annotationClient.activeSelection
);
}
// Update segment with given selection
async saveNewActiveSelection(layerId = undefined) {
// When the selection is null: exit
if (!this.context.annotationClient.activeSelection) {
return;
}
// Update the segment
await this.context.annotationClient.saveSelection(
this.context.annotationClient.activeSelection,
true,
true,
layerId
);
}
onKeyPressed = (e) => {
switch (e.keyCode) {
case 68: // d
e.shiftKey &&
// shift+d
// Duplicate active annotation
ComponentUtil.checkFocusAndExec(async () => {
const annotationClient = this.context.annotationClient;
const annotation = annotationClient.activeAnnotation;
// require valid segment annotation
if (
!annotation ||
!annotation.target ||
annotationClient.activeAnnotation.target.type !==
ANNOTATION_TARGET.SEGMENT ||
!annotation.target.selector ||
!annotation.target.selector.refinedBy
) {
return;
}
// create new selection
const temporal = annotation.target.selector.refinedBy;
const selection = annotationClient.newTemporalSegment(
temporal.end,
temporal.end + (temporal.end - temporal.start)
);
// Create and activate a new segment, provide layerId
annotationClient.activeAnnotation = null;
await annotationClient.saveSelection(
selection,
true,
!annotation.body, // only notify if there is no annotation body
// else the notify will be called while saving the new annotation (body)
annotation.target.layerId
);
// duplicate / copy body
if (annotation.body) {
// Build annotationClient.activeAnnotation body based on the duplicated annotation body
annotationClient.activeAnnotation.body = annotation.body.map(
(origAnnotation) => {
const newAnnotation = Object.assign(
{},
origAnnotation
);
// delete the annotation Id
delete newAnnotation.annotationId;
return newAnnotation;
}
);
await annotationClient.save(true, true);
}
});
break;
case 88: // x
case 46: // delete
e.shiftKey &&
// shift + (x || delete)
// shift + ctrl/meta + (x || delete) => no confirmation
// Delete active annotation
ComponentUtil.checkFocusAndExec(() => {
if (
e.ctrlKey ||
e.metaKey ||
confirm(Strings.SEGMENT_DELETE_CONFIRM)
) {
this.context.annotationClient.activeAnnotation &&
this.context.annotationClient.delete(
this.context.annotationClient
.activeAnnotation
);
}
});
break;
case 73: // i
e.shiftKey
? // shift+i
// Set player position to active annotation start
ComponentUtil.checkFocusAndExec(this.playStart)
: // i
// Set active annotation start to current position
ComponentUtil.checkFocusAndExec(() => {
this.context.annotationClient.setStart(
this.currentPosition
);
this.saveActiveSelection();
});
break;
case 79: //o
e.shiftKey
? // shift+o
// Set player position to active annotation end
ComponentUtil.checkFocusAndExec(this.playEnd)
: // o
// Set active annotation end to current position
ComponentUtil.checkFocusAndExec(() => {
this.context.annotationClient.setEnd(
this.currentPosition
);
this.saveActiveSelection();
});
break;
case 67:
// shift+c
// Concat new temporal segment from the end of the previous segment (or 0)
// to the current playback position
// only include segments from same layer
e.shiftKey &&
ComponentUtil.checkFocusAndExec(() => {
const annotationClient = this.context.annotationClient;
const currentLayerId =
annotationClient.activeAnnotation &&
annotationClient.activeAnnotation.target &&
annotationClient.activeAnnotation.target.layerId
? annotationClient.activeAnnotation.target
.layerId
: 0;
const prevSectionEnd = annotationClient.getSegmentEndBefore(
this.currentPosition,
currentLayerId
);
annotationClient.newTemporalSegment(
prevSectionEnd,
this.currentPosition
);
this.saveNewActiveSelection(currentLayerId);
});
break;
case 78: // n
e.shiftKey &&
// shift+n
// Create new temporal segment of 1 second length
ComponentUtil.checkFocusAndExec(() => {
const annotationClient = this.context.annotationClient;
let layerId =
annotationClient.activeAnnotation &&
annotationClient.activeAnnotation.target
? annotationClient.activeAnnotation.target
.layerId
: undefined;
// if no current layer id is found, get first layer available (if any)
if (!layerId) {
layerId = this.context.annotationClient.segmentLayers.getFirstLayerId();
}
// if still no current layer id is found, fallback to 0
if (!layerId) {
layerId = 0;
}
annotationClient.newTemporalSegment(
this.currentPosition,
this.currentPosition + 1
);
this.saveNewActiveSelection(layerId);
});
break;
}
};
/* ------------------------------ SEGMENTATION FUNCTIONS (POSSIBLY MOVE TO STAN) ------------------------------ */
playStart = () => {
if (this.context.annotationClient.activeSelection) {
this.updatePlayerPos(
this.context.annotationClient.activeSelection.start
);
}
};
playEnd = () => {
if (this.context.annotationClient.activeSelection) {
this.updatePlayerPos(
this.context.annotationClient.activeSelection.end
);
}
};
}