fix(jump): Jump to measurement wasn't jumping when presentation already existed (#3318)

* fix: Jump to measurement after presentation store

* Fix initial load of DICOM SR

* Fix measurement highlight issues

* Clear measurements changed to array

* Fix merge issues

* PR comments

* Removed unused formatting.  Change to single event

* Fix the unit test

* PR review comments

* refactor(viewport): simplify imports, align destructuring

Simplify import statements by removing unused imports, organize existing imports, and align destructured imports consistently across the component.

---------
This commit is contained in:
Bill Wallace 2023-05-01 12:54:33 -04:00 committed by GitHub
parent 66f6e3eade
commit b5b936b53e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 640 additions and 298 deletions

View File

@ -3,10 +3,8 @@ import { DisplaySetService, classes } from '@ohif/core';
const ImageSet = classes.ImageSet;
const findInstance = (measurement, displaySetService: DisplaySetService) => {
const {
displaySetInstanceUID,
ReferencedSOPInstanceUID: sopUid,
} = measurement;
const { displaySetInstanceUID, ReferencedSOPInstanceUID: sopUid } =
measurement;
const referencedDisplaySet = displaySetService.getDisplaySetByUID(
displaySetInstanceUID
);
@ -52,7 +50,7 @@ const findReferencedInstances = (
const createReferencedImageDisplaySet = (displaySetService, displaySet) => {
const instances = findReferencedInstances(displaySetService, displaySet);
// This will be a member function of the created image set
const updateInstances = function() {
const updateInstances = function () {
this.images.splice(
0,
this.images.length,
@ -77,6 +75,8 @@ const createReferencedImageDisplaySet = (displaySetService, displaySet) => {
numImageFrames: instances.length,
SOPClassHandlerId: `@ohif/extension-default.sopClassHandlerModule.stack`,
isReconstructable: false,
// This object is made of multiple instances from other series
isCompositeStack: true,
madeInClient: true,
updateInstances,
});

View File

@ -2,18 +2,10 @@ import PropTypes from 'prop-types';
import React, { useCallback, useContext, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import OHIF, { utils, ServicesManager, ExtensionManager } from '@ohif/core';
import { CornerstoneServices } from '@ohif/extension-cornerstone/types';
import { setTrackingUniqueIdentifiersForElement } from '../tools/modules/dicomSRModule';
import {
Icon,
Notification,
Tooltip,
useViewportDialog,
useViewportGrid,
ViewportActionBar,
} from '@ohif/ui';
import { Icon, Tooltip, useViewportGrid, ViewportActionBar } from '@ohif/ui';
import hydrateStructuredReport from '../utils/hydrateStructuredReport';
import createReferencedImageDisplaySet from '../utils/createReferencedImageDisplaySet';
@ -40,7 +32,7 @@ function OHIFCornerstoneSRViewport(props) {
displaySetService,
cornerstoneViewportService,
measurementService,
} = servicesManager.services as CornerstoneServices;
} = servicesManager.services;
// SR viewport will always have a single display set
if (displaySets.length > 1) {
@ -50,7 +42,6 @@ function OHIFCornerstoneSRViewport(props) {
const srDisplaySet = displaySets[0];
const [viewportGrid, viewportGridService] = useViewportGrid();
const [viewportDialogState, viewportDialogApi] = useViewportDialog();
const [measurementSelected, setMeasurementSelected] = useState(0);
const [measurementCount, setMeasurementCount] = useState(1);
const [activeImageDisplaySetData, setActiveImageDisplaySetData] = useState(

View File

@ -9,6 +9,7 @@ import {
StackViewport,
utilities as csUtils,
} from '@cornerstonejs/core';
import { MeasurementService } from '@ohif/core';
import {
CinePlayer,
useCine,
@ -30,6 +31,12 @@ import CornerstoneServices from '../types/CornerstoneServices';
const STACK = 'stack';
/**
* Caches the jump to measurement operation, so that if display set is shown,
* it can jump to the measurement.
*/
let cacheJumpToMeasurementEvent;
function areEqual(prevProps, nextProps) {
if (nextProps.needsRerendering) {
return false;
@ -412,6 +419,13 @@ const OHIFCornerstoneViewport = React.memo(props => {
lutPresentation:
lutPresentationStore[presentationIds?.lutPresentationId],
};
let measurement;
if (cacheJumpToMeasurementEvent?.viewportIndex === viewportIndex) {
measurement = cacheJumpToMeasurementEvent.measurement;
// Delete the position presentation so that viewport navigates direct
presentations.positionPresentation = null;
cacheJumpToMeasurementEvent = null;
}
cornerstoneViewportService.setViewportData(
viewportIndex,
@ -420,6 +434,10 @@ const OHIFCornerstoneViewport = React.memo(props => {
displaySetOptions,
presentations
);
if (measurement) {
cs3DTools.annotation.selection.setAnnotationSelected(measurement.uid);
}
};
loadViewportData();
@ -536,22 +554,30 @@ function _subscribeToJumpToMeasurementEvents(
displaySet => displaySet.displaySetInstanceUID
);
const { unsubscribe } = measurementService.subscribe(
measurementService.EVENTS.JUMP_TO_MEASUREMENT,
({ measurement }) => {
if (!measurement) return;
// Jump the the measurement if the viewport contains the displaySetUID (fusion)
if (displaysUIDs.includes(measurement.displaySetInstanceUID)) {
_jumpToMeasurement(
measurement,
elementRef,
viewportIndex,
measurementService,
displaySetService,
viewportGridService,
cornerstoneViewportService
MeasurementService.EVENTS.JUMP_TO_MEASUREMENT_VIEWPORT,
props => {
cacheJumpToMeasurementEvent = props;
const { viewportIndex: jumpIndex, measurement, isConsumed } = props;
if (!measurement || isConsumed) return;
if (cacheJumpToMeasurementEvent.cornerstoneViewport === undefined) {
// Decide on which viewport should handle this
cacheJumpToMeasurementEvent.cornerstoneViewport = cornerstoneViewportService.getViewportIndexToJump(
jumpIndex,
measurement.displaySetInstanceUID,
{ referencedImageId: measurement.referencedImageId }
);
}
if (cacheJumpToMeasurementEvent.cornerstoneViewport !== viewportIndex)
return;
_jumpToMeasurement(
measurement,
elementRef,
viewportIndex,
measurementService,
displaySetService,
viewportGridService,
cornerstoneViewportService
);
}
);
@ -568,21 +594,19 @@ function _checkForCachedJumpToMeasurementEvents(
viewportGridService,
cornerstoneViewportService
) {
if (!cacheJumpToMeasurementEvent) return;
if (cacheJumpToMeasurementEvent.isConsumed) {
cacheJumpToMeasurementEvent = null;
return;
}
const displaysUIDs = displaySets.map(
displaySet => displaySet.displaySetInstanceUID
);
if (!displaysUIDs?.length) return;
const measurementIdToJumpTo = measurementService.getJumpToMeasurement(
viewportIndex
);
if (measurementIdToJumpTo && elementRef) {
// Jump to measurement if the measurement exists
const measurement = measurementService.getMeasurement(
measurementIdToJumpTo
);
// Jump to measurement if the measurement exists
const { measurement } = cacheJumpToMeasurementEvent;
if (measurement && elementRef) {
if (displaysUIDs.includes(measurement?.displaySetInstanceUID)) {
_jumpToMeasurement(
measurement,
@ -611,6 +635,7 @@ function _jumpToMeasurement(
if (!SOPInstanceUID) {
console.warn('cannot jump in a non-acquisition plane measurements yet');
return;
}
const referencedDisplaySet = displaySetService.getDisplaySetByUID(
@ -623,15 +648,17 @@ function _jumpToMeasurement(
viewportGridService.setActiveViewportIndex(viewportIndex);
const enableElement = getEnabledElement(targetElement);
const enabledElement = getEnabledElement(targetElement);
const viewportInfo = cornerstoneViewportService.getViewportInfoByIndex(
viewportIndex
);
if (enableElement) {
if (enabledElement) {
// See how the jumpToSlice() of Cornerstone3D deals with imageIdx param.
const viewport = enableElement.viewport as IStackViewport | IVolumeViewport;
const viewport = enabledElement.viewport as
| IStackViewport
| IVolumeViewport;
let imageIdIndex = 0;
let viewportCameraDirectionMatch = true;
@ -680,7 +707,8 @@ function _jumpToMeasurement(
cs3DTools.annotation.selection.setAnnotationSelected(measurement.uid);
// Jump to measurement consumed, remove.
measurementService.removeJumpToMeasurement(viewportIndex);
cacheJumpToMeasurementEvent?.consume?.();
cacheJumpToMeasurementEvent = null;
}
}

View File

@ -10,6 +10,7 @@ import {
utilities as cstUtils,
ReferenceLinesTool,
} from '@cornerstonejs/tools';
import { Types as OhifTypes } from '@ohif/core';
import CornerstoneViewportDownloadForm from './utils/CornerstoneViewportDownloadForm';
import callInputDialog from './utils/callInputDialog';
@ -19,7 +20,10 @@ import { getFirstAnnotationSelected } from './utils/measurementServiceMappings/u
import getActiveViewportEnabledElement from './utils/getActiveViewportEnabledElement';
import { CornerstoneServices } from './types';
function commandsModule({ servicesManager, commandsManager }) {
function commandsModule({
servicesManager,
commandsManager,
}: OhifTypes.Extensions.ExtensionParams): OhifTypes.Extensions.CommandsModule {
const {
viewportGridService,
toolGroupService,
@ -484,33 +488,22 @@ function commandsModule({ servicesManager, commandsManager }) {
}
}
},
firstImage: () => {
// Get current active viewport (return if none active)
const enabledElement = _getActiveViewportEnabledElement();
if (!enabledElement) {
return;
}
const { viewport } = enabledElement;
// Check viewport is supported
if (
viewport! instanceof StackViewport &&
viewport! instanceof VolumeViewport
) {
throw new Error('Unsupported viewport type');
}
// Set slice to first slice
const options = { imageIndex: 0 };
cstUtils.jumpToSlice(viewport.element, options);
},
lastImage: () => {
/** Jumps the active viewport or the specified one to the given slice index */
jumpToImage: ({ imageIndex, viewport: gridViewport }): void => {
// Get current active viewport (return if none active)
const enabledElement = _getActiveViewportEnabledElement();
if (!enabledElement) {
return;
let viewport;
if (!gridViewport) {
const enabledElement = _getActiveViewportEnabledElement();
if (!enabledElement) {
return;
}
viewport = enabledElement.viewport;
} else {
viewport = cornerstoneViewportService.getCornerstoneViewport(
gridViewport.id
);
}
const { viewport } = enabledElement;
// Get number of slices
// -> Copied from cornerstone3D jumpToSlice\_getImageSliceData()
@ -525,8 +518,14 @@ function commandsModule({ servicesManager, commandsManager }) {
throw new Error('Unsupported viewport type');
}
const jumpIndex =
imageIndex < 0 ? numberOfSlices + imageIndex : imageIndex;
if (jumpIndex >= numberOfSlices || jumpIndex < 0) {
throw new Error(`Can't jump to ${imageIndex}`);
}
// Set slice to last slice
const options = { imageIndex: numberOfSlices - 1 };
const options = { imageIndex: jumpIndex };
cstUtils.jumpToSlice(viewport.element, options);
},
scroll: ({ direction }) => {
@ -625,8 +624,6 @@ function commandsModule({ servicesManager, commandsManager }) {
getNearbyToolData: {
commandFn: actions.getNearbyToolData,
storeContexts: [],
options: {},
},
getNearbyAnnotation: {
commandFn: actions.getNearbyAnnotation,
@ -636,142 +633,100 @@ function commandsModule({ servicesManager, commandsManager }) {
deleteMeasurement: {
commandFn: actions.deleteMeasurement,
storeContexts: [],
options: {},
},
setMeasurementLabel: {
commandFn: actions.setMeasurementLabel,
storeContexts: [],
options: {},
},
updateMeasurement: {
commandFn: actions.updateMeasurement,
storeContexts: [],
options: {},
},
setWindowLevel: {
commandFn: actions.setWindowLevel,
storeContexts: [],
options: {},
},
toolbarServiceRecordInteraction: {
commandFn: actions.toolbarServiceRecordInteraction,
storeContexts: [],
options: {},
},
setToolActive: {
commandFn: actions.setToolActive,
storeContexts: [],
options: {},
},
rotateViewportCW: {
commandFn: actions.rotateViewport,
storeContexts: [],
options: { rotation: 90 },
},
rotateViewportCCW: {
commandFn: actions.rotateViewport,
storeContexts: [],
options: { rotation: -90 },
},
incrementActiveViewport: {
commandFn: actions.incrementActiveViewport,
storeContexts: [],
},
decrementActiveViewport: {
commandFn: actions.decrementActiveViewport,
storeContexts: [],
},
flipViewportHorizontal: {
commandFn: actions.flipViewportHorizontal,
storeContexts: [],
options: {},
},
flipViewportVertical: {
commandFn: actions.flipViewportVertical,
storeContexts: [],
options: {},
},
invertViewport: {
commandFn: actions.invertViewport,
storeContexts: [],
options: {},
},
resetViewport: {
commandFn: actions.resetViewport,
storeContexts: [],
options: {},
},
scaleUpViewport: {
commandFn: actions.scaleViewport,
storeContexts: [],
options: { direction: 1 },
},
scaleDownViewport: {
commandFn: actions.scaleViewport,
storeContexts: [],
options: { direction: -1 },
},
fitViewportToWindow: {
commandFn: actions.scaleViewport,
storeContexts: [],
options: { direction: 0 },
},
nextImage: {
commandFn: actions.scroll,
storeContexts: [],
options: { direction: 1 },
},
previousImage: {
commandFn: actions.scroll,
storeContexts: [],
options: { direction: -1 },
},
firstImage: {
commandFn: actions.firstImage,
storeContexts: [],
options: {},
commandFn: actions.jumpToImage,
options: { imageIndex: 0 },
},
lastImage: {
commandFn: actions.lastImage,
storeContexts: [],
options: {},
commandFn: actions.jumpToImage,
options: { imageIndex: -1 },
},
jumpToImage: {
commandFn: actions.jumpToImage,
},
showDownloadViewportModal: {
commandFn: actions.showDownloadViewportModal,
storeContexts: [],
options: {},
},
toggleCine: {
commandFn: actions.toggleCine,
storeContexts: [],
options: {},
},
arrowTextCallback: {
commandFn: actions.arrowTextCallback,
storeContexts: [],
options: {},
},
setViewportActive: {
commandFn: actions.setViewportActive,
storeContexts: [],
options: {},
},
setViewportColormap: {
commandFn: actions.setViewportColormap,
storeContexts: [],
options: {},
},
toggleStackImageSync: {
commandFn: actions.toggleStackImageSync,
storeContexts: [],
options: {},
},
toggleReferenceLines: {
commandFn: actions.toggleReferenceLines,
storeContexts: [],
options: {},
},
};

View File

@ -16,14 +16,14 @@ const VOLUME_LOADER_SCHEME = 'cornerstoneStreamingImageVolume';
class CornerstoneCacheService {
static REGISTRATION = {
name: 'cornerstoneCacheService',
altName: 'CornerstoneCacheService',
name: 'cornerstoneCacheService',
altName: 'CornerstoneCacheService',
create: ({
servicesManager,
}: Types.Extensions.ExtensionParams): CornerstoneCacheService => {
return new CornerstoneCacheService(servicesManager);
},
};
},
};
stackImageIds: Map<string, string[]> = new Map();
volumeImageIds: Map<string, string[]> = new Map();
@ -143,13 +143,18 @@ class CornerstoneCacheService {
this.stackImageIds.set(displaySet.displaySetInstanceUID, stackImageIds);
}
const { displaySetInstanceUID, StudyInstanceUID } = displaySet;
const {
displaySetInstanceUID,
StudyInstanceUID,
isCompositeStack,
} = displaySet;
const StackViewportData: StackViewportData = {
viewportType,
data: {
StudyInstanceUID,
displaySetInstanceUID,
isCompositeStack,
imageIds: stackImageIds,
},
};

View File

@ -906,6 +906,34 @@ class CornerstoneViewportService extends PubSubService
return images[0].FrameOfReferenceUID;
}
}
/**
* Looks through the viewports to see if the specified measurement can be
* displayed in one of the viewports.
*
* @param measurement
* The measurement that is desired to view.
* @param activeViewportIndex - the index that was active at the time the jump
* was initiated.
* @return the viewportIndex to display the given measurement
*/
public getViewportIndexToJump(
activeViewportIndex: number,
displaySetInstanceUID: string,
cameraProps: unknown
): number {
const viewportInfo = this.viewportsInfo.get(activeViewportIndex);
const { referencedImageId } = cameraProps;
if (viewportInfo?.contains(displaySetInstanceUID, referencedImageId)) {
return activeViewportIndex;
}
return (
[...this.viewportsById.values()].find(viewportInfo =>
viewportInfo.contains(displaySetInstanceUID, referencedImageId)
)?.viewportIndex ?? -1
);
}
}
export default CornerstoneViewportService;

View File

@ -88,6 +88,20 @@ export type DisplaySet = {
const STACK = 'stack';
const DEFAULT_TOOLGROUP_ID = 'default';
// Return true if the data contains the given display set UID OR the imageId
// if it is a composite object.
const dataContains = (
data,
displaySetUID: string,
imageId?: string
): boolean => {
if (data.displaySetInstanceUID === displaySetUID) return true;
if (imageId && data.isCompositeStack && data.imageIds) {
return !!data.imageIds.find(dataId => dataId === imageId);
}
return false;
};
class ViewportInfo {
private viewportId = '';
private viewportIndex: number;
@ -104,6 +118,21 @@ class ViewportInfo {
this.setPublicDisplaySetOptions([{}]);
}
/**
* Return true if the viewport contains the given display set UID,
* OR if it is a composite stack and contains the given imageId
*/
public contains(displaySetUID: string, imageId: string): boolean {
if (!this.viewportData?.data) return false;
if (this.viewportData.data.length) {
return !!this.viewportData.find(data =>
dataContains(data, displaySetUID, imageId)
);
}
return dataContains(this.viewportData.data, displaySetUID, imageId);
}
public destroy = (): void => {
this.element = null;
this.viewportData = null;

View File

@ -1,12 +1,12 @@
import {
Enums,
Types,
} from '@cornerstonejs/core';
import { Enums, Types } from '@cornerstonejs/core';
type StackData = {
StudyInstanceUID: string;
displaySetInstanceUID: string;
// A composite stack is one created from other display sets - kind of like
// madeInClient, but specific to indicating that the imageIds can come from
// different series or even studies.
isCompositeStack?: boolean;
imageIds: string[];
frameRate?: number;
isClip?: boolean;
@ -20,19 +20,14 @@ type VolumeData = {
imageIds?: string[];
};
type StackViewportData = {
type StackViewportData = {
viewportType: Enums.ViewportType;
data: StackData;
};
type VolumeViewportData = {
type VolumeViewportData = {
viewportType: Enums.ViewportType;
data: VolumeData[];
};
export type {
StackViewportData,
VolumeViewportData,
StackData,
VolumeData,
};
export type { StackViewportData, VolumeViewportData, StackData, VolumeData };

View File

@ -188,6 +188,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
if (length === null || length === undefined) return displayText;
const roundedLength = utils.roundNumber(length, 2);
displayText.push(
`${roundedLength} mm (S: ${SeriesNumber}${instanceText}${frameText})`

View File

@ -1,4 +1,4 @@
import { ServicesManager, utils } from '@ohif/core';
import { ServicesManager, utils, Types } from '@ohif/core';
import {
ContextMenuController,
@ -244,6 +244,12 @@ const commandsModule = ({
}
}
// Do this after successfully applying the update
// Note, don't store the active display set - it is only needed while
// changing display sets. This causes jump to measurement to fail on
// multi-study display.
delete displaySetSelectorMap[
`${activeStudyUID || hpInfo.activeStudyUID}:activeDisplaySet:0`
];
stateSyncService.store(stateSyncReduce);
// This is a default action applied
actions.toggleHpTools(hangingProtocolService.getActiveProtocol());

View File

@ -47,6 +47,15 @@ function TrackedMeasurementsContextProvider(
const uid = trackedMeasurements[0].uid;
console.log(
'jumping to measurement reset viewport',
viewportGrid.activeViewportIndex,
trackedMeasurements[0]
);
viewportGridService.setDisplaySetsForViewport({
viewportIndex: viewportGrid.activeViewportIndex,
displaySetInstanceUIDs: [trackedMeasurements[0].displaySetInstanceUID],
});
measurementService.jumpToMeasurement(
viewportGrid.activeViewportIndex,
uid

View File

@ -67,7 +67,10 @@ const EVENTS = {
RAW_MEASUREMENT_ADDED: 'event::raw_measurement_added',
MEASUREMENT_REMOVED: 'event::measurement_removed',
MEASUREMENTS_CLEARED: 'event::measurements_cleared',
JUMP_TO_MEASUREMENT: 'event:jump_to_measurement',
// Give the viewport a chance to jump to the measurement
JUMP_TO_MEASUREMENT_VIEWPORT: 'event:jump_to_measurement_viewport',
// Give the layout a chance to jump to the measurement
JUMP_TO_MEASUREMENT_LAYOUT: 'event:jump_to_measurement_layout',
};
const VALUE_TYPES = {
@ -104,15 +107,16 @@ class MeasurementService extends PubSubService {
},
};
public static readonly EVENTS = EVENTS;
public static VALUE_TYPES = VALUE_TYPES;
public readonly VALUE_TYPES = VALUE_TYPES;
private measurements = new Map();
constructor() {
super(EVENTS);
this.sources = {};
this.mappings = {};
this.measurements = {};
this._jumpToMeasurementCache = {};
}
/**
@ -120,7 +124,7 @@ class MeasurementService extends PubSubService {
* This method should be used to add custom tool schema to the measurement service.
* @param {Array} schema schema for validation
*/
addMeasurementSchemaKeys(schema) {
public addMeasurementSchemaKeys(schema): void {
if (!Array.isArray(schema)) {
schema = [schema];
}
@ -160,11 +164,7 @@ class MeasurementService extends PubSubService {
* @return {Measurement[]} Array of measurements
*/
getMeasurements() {
const measurements = this._arrayOfObjects(this.measurements);
return (
measurements &&
measurements.map(m => this.measurements[Object.keys(m)[0]])
);
return [...this.measurements.values()];
}
/**
@ -173,18 +173,14 @@ class MeasurementService extends PubSubService {
* @param {string} uid measurement uid
* @return {Measurement} Measurement instance
*/
getMeasurement(measurementUID) {
let measurement = null;
const measurements = this.measurements[measurementUID];
if (measurements && Object.keys(measurements).length > 0) {
measurement = this.measurements[measurementUID];
}
return measurement;
public getMeasurement(measurementUID: string) {
return this.measurements.get(measurementUID);
}
setMeasurementSelected(measurementUID, selected) {
public setMeasurementSelected(
measurementUID: string,
selected: boolean
): void {
const measurement = this.getMeasurement(measurementUID);
if (!measurement) {
return;
@ -373,8 +369,8 @@ class MeasurementService extends PubSubService {
}
}
update(measurementUID, measurement, notYetUpdatedAtSource = false) {
if (!this.measurements[measurementUID]) {
update(measurementUID: string, measurement, notYetUpdatedAtSource = false) {
if (!this.measurements.has(measurementUID)) {
return;
}
@ -388,7 +384,7 @@ class MeasurementService extends PubSubService {
updatedMeasurement
);
this.measurements[measurementUID] = updatedMeasurement;
this.measurements.set(measurementUID, updatedMeasurement);
this._broadcastEvent(this.EVENTS.MEASUREMENT_UPDATED, {
source: measurement.source,
@ -470,15 +466,15 @@ class MeasurementService extends PubSubService {
uid: internalUID,
};
if (this.measurements[internalUID]) {
this.measurements[internalUID] = newMeasurement;
if (this.measurements.get(internalUID)) {
this.measurements.set(internalUID, newMeasurement);
this._broadcastEvent(this.EVENTS.MEASUREMENT_UPDATED, {
source,
measurement: newMeasurement,
});
} else {
log.info('Measurement added', newMeasurement);
this.measurements[internalUID] = newMeasurement;
this.measurements.set(internalUID, newMeasurement);
this._broadcastEvent(this.EVENTS.RAW_MEASUREMENT_ADDED, {
source,
measurement: newMeasurement,
@ -557,7 +553,7 @@ class MeasurementService extends PubSubService {
);
}
const oldMeasurement = this.measurements[internalUID];
const oldMeasurement = this.measurements.get(internalUID);
const newMeasurement = {
...oldMeasurement,
@ -569,7 +565,7 @@ class MeasurementService extends PubSubService {
if (oldMeasurement) {
// TODO: Ultimately, each annotation should have a selected flag right from the soure.
// For now, it is just added in OHIF here and in setMeasurementSelected.
this.measurements[internalUID] = newMeasurement;
this.measurements.set(internalUID, newMeasurement);
if (isUpdate) {
this._broadcastEvent(this.EVENTS.MEASUREMENT_UPDATED, {
source,
@ -585,7 +581,7 @@ class MeasurementService extends PubSubService {
}
} else {
log.info('Measurement started.', newMeasurement);
this.measurements[internalUID] = newMeasurement;
this.measurements.set(internalUID, newMeasurement);
}
return newMeasurement.uid;
@ -598,12 +594,12 @@ class MeasurementService extends PubSubService {
* @param {MeasurementSource} source The measurement source instance
*/
remove(measurementUID, source, eventDetails) {
if (!measurementUID || !this.measurements[measurementUID]) {
if (!measurementUID || !this.measurements.has(measurementUID)) {
log.warn(`No uid provided, or unable to find measurement by uid.`);
return;
}
delete this.measurements[measurementUID];
this.measurements.delete(measurementUID);
this._broadcastEvent(this.EVENTS.MEASUREMENT_REMOVED, {
source,
measurement: measurementUID,
@ -613,9 +609,8 @@ class MeasurementService extends PubSubService {
clearMeasurements() {
// Make a copy of the measurements
const measurements = { ...this.measurements };
this.measurements = {};
this._jumpToMeasurementCache = {};
const measurements = [...this.measurements.values()];
this.measurements.clear();
this._broadcastEvent(this.EVENTS.MEASUREMENTS_CLEARED, { measurements });
}
@ -628,27 +623,39 @@ class MeasurementService extends PubSubService {
this.clearMeasurements();
}
jumpToMeasurement(viewportIndex, measurementUID) {
const measurement = this.measurements[measurementUID];
/**
* This method calls the subscriptions for JUMP_TO_MEASUREMENT_VIEWPORT
* and JUMP_TO_MEASUREMENT_LAYOUT. There are two events which are
* fired because there are two different items which might want to handle
* the event. First, there might already be a viewport which can handle
* the event. If so, then the layout doesn't need to necessarily change.
* This is communicated by the isConsumed value on the event itself.
* Otherwise, the layout itself may need to be navigated to in order
* to provide a viewport which can show the given measurement.
*
* When a viewport decides to apply the event, it should call the consume()
* method on the event, so that other listeners know they do not need to
* navigate. This does NOT affect whether the layout event is fired, and
* merely causes it to fire the event with the isConsumed set to true.
*/
public jumpToMeasurement(
viewportIndex: number,
measurementUID: string
): void {
const measurement = this.measurements.get(measurementUID);
if (!measurement) {
log.warn(`No measurement uid, or unable to find by uid.`);
return;
}
this._addJumpToMeasurement(viewportIndex, measurementUID);
this._broadcastEvent(this.EVENTS.JUMP_TO_MEASUREMENT, {
const consumableEvent = this.createConsumableEvent({
viewportIndex,
measurement,
});
}
getJumpToMeasurement(viewportIndex) {
return this._jumpToMeasurementCache[viewportIndex];
}
removeJumpToMeasurement(viewportIndex) {
delete this._jumpToMeasurementCache[viewportIndex];
this._broadcastEvent(EVENTS.JUMP_TO_MEASUREMENT_VIEWPORT, consumableEvent);
this._broadcastEvent(EVENTS.JUMP_TO_MEASUREMENT_LAYOUT, consumableEvent);
}
_getSourceUID(name, version) {
@ -663,10 +670,6 @@ class MeasurementService extends PubSubService {
return sourceUID;
}
_addJumpToMeasurement(viewportIndex, measurementUID) {
this._jumpToMeasurementCache[viewportIndex] = measurementUID;
}
_getMappingByMeasurementSource(measurement, annotationType) {
if (this._isValidSource(measurement.source)) {
return this.mappings[measurement.source.uid].find(

View File

@ -1,12 +1,13 @@
import { PubSubService } from '../_shared/pubSubServiceInterface';
const EVENTS = {
ACTIVE_VIEWPORT_INDEX_CHANGED: 'event::activeviewportindexchanged',
LAYOUT_CHANGED: 'event::layoutChanged',
GRID_STATE_CHANGED: 'event::gridStateChanged',
};
import { getPresentationIds, PresentationIds } from './getPresentationIds';
class ViewportGridService extends PubSubService {
public static readonly EVENTS = {
ACTIVE_VIEWPORT_INDEX_CHANGED: 'event::activeviewportindexchanged',
LAYOUT_CHANGED: 'event::layoutChanged',
GRID_STATE_CHANGED: 'event::gridStateChanged',
};
public static REGISTRATION = {
name: 'viewportGridService',
altName: 'ViewportGridService',
@ -14,12 +15,13 @@ class ViewportGridService extends PubSubService {
return new ViewportGridService();
},
};
public static EVENTS = EVENTS;
public static getPresentationIds = getPresentationIds;
serviceImplementation = {};
constructor() {
super(EVENTS);
super(ViewportGridService.EVENTS);
this.serviceImplementation = {};
}
@ -37,10 +39,12 @@ class ViewportGridService extends PubSubService {
this.serviceImplementation._getState = getStateImplementation;
}
if (setActiveViewportIndexImplementation) {
this.serviceImplementation._setActiveViewportIndex = setActiveViewportIndexImplementation;
this.serviceImplementation._setActiveViewportIndex =
setActiveViewportIndexImplementation;
}
if (setDisplaySetsForViewportsImplementation) {
this.serviceImplementation._setDisplaySetsForViewports = setDisplaySetsForViewportsImplementation;
this.serviceImplementation._setDisplaySetsForViewports =
setDisplaySetsForViewportsImplementation;
}
if (setLayoutImplementation) {
this.serviceImplementation._setLayout = setLayoutImplementation;
@ -55,7 +59,8 @@ class ViewportGridService extends PubSubService {
this.serviceImplementation._set = setImplementation;
}
if (getNumViewportPanesImplementation) {
this.serviceImplementation._getNumViewportPanes = getNumViewportPanesImplementation;
this.serviceImplementation._getNumViewportPanes =
getNumViewportPanesImplementation;
}
}
@ -75,11 +80,29 @@ class ViewportGridService extends PubSubService {
public setDisplaySetsForViewport(props) {
// Just update a single viewport, but use the multi-viewport update for it.
this.serviceImplementation._setDisplaySetsForViewports([props]);
this.setDisplaySetsForViewports([props]);
}
public setDisplaySetsForViewports(props) {
this.serviceImplementation._setDisplaySetsForViewports(props);
const state = this.getState();
const viewports = [];
for (const viewport of props) {
const updatedViewport = state.viewports[viewport.viewportIndex];
if (updatedViewport) {
viewports.push(updatedViewport);
} else {
console.warn(
"ViewportGridService::Didn't find updated viewport",
viewport
);
}
}
this._broadcastEvent(ViewportGridService.EVENTS.GRID_STATE_CHANGED, {
state,
viewports,
});
}
/**
@ -150,3 +173,5 @@ class ViewportGridService extends PubSubService {
}
export default ViewportGridService;
export type { PresentationIds };

View File

@ -118,3 +118,4 @@ const getPresentationIds = (viewport, viewports): PresentationIds => {
};
export default getPresentationIds;
export { getPresentationIds };

View File

@ -103,4 +103,20 @@ export class PubSubService {
this.unsubscriptions.forEach(unsub => unsub());
this.unsubscriptions = [];
}
/**
* Creates an event that records whether or not someone
* has consumed it. Call eventData.consume() to consume the event.
* Check eventData.isConsumed to see if it is consumed or not.
* @param props - to include in the event
*/
protected createConsumableEvent(props) {
return {
...props,
isConsumed: false,
consume: function Consume() {
this.isConsumed = true;
},
}
}
}

View File

@ -24,27 +24,22 @@ describe('absoluteUrl', () => {
});
test('should return the original path when there path in the window.origin after the domain and port', () => {
global.window = Object.create(window);
delete global.window.location;
const url = 'http://dummy.com';
Object.defineProperty(window, 'location', {
value: {
origin: url,
},
writable: true,
});
global.window.location = {
origin: url,
};
const absoluteUrlOutput = absoluteUrl('path_1/path_2/path_3');
expect(absoluteUrlOutput).toEqual('/path_1/path_2/path_3');
});
test('should be able to return the absolute path even when the path contains duplicates', () => {
global.window = Object.create(window);
global.window ||= Object.create(window);
const url = 'http://dummy.com';
Object.defineProperty(window, 'location', {
value: {
origin: url,
},
writable: true,
});
delete global.window.location;
global.window.location = {
origin: url,
};
const absoluteUrlOutput = absoluteUrl('path_1/path_1/path_1');
expect(absoluteUrlOutput).toEqual('/path_1/path_1/path_1');
});

View File

@ -29,7 +29,8 @@ There are seven events that get publish in `MeasurementService`:
| RAW_MEASUREMENT_ADDED | Fires when a raw measurement is added (e.g., dicom-sr) |
| MEASUREMENT_REMOVED | Fires when a measurement is removed |
| MEASUREMENTS_CLEARED | Fires when all measurements are deleted |
| JUMP_TO_MEASUREMENT | Fires when a measurement is requested to be jump to |
| JUMP_TO_MEASUREMENT_VIEWPORT | Fires when a measurement is requested to be jumped to, applying to individual viewports. |
| JUMP_TO_MEASUREMENT_LAYOUT | Fires when a measurement is requested to be jumped to, applying to the overall layout. |
## API

View File

@ -7,8 +7,8 @@ import React, {
} from 'react';
import PropTypes from 'prop-types';
import isEqual from 'lodash.isequal';
import { ViewportGridService } from '@ohif/core';
import viewportLabels from '../utils/viewportLabels';
import getPresentationIds from './getPresentationIds';
const DEFAULT_STATE = {
activeViewportIndex: 0,
@ -173,7 +173,7 @@ export function ViewportGridProvider({ children, service }) {
displaySetOptions,
viewportLabel: viewportLabels[viewportIndex],
};
viewportOptions.presentationIds = getPresentationIds(
viewportOptions.presentationIds = ViewportGridService.getPresentationIds(
newViewport,
viewports
);
@ -265,7 +265,7 @@ export function ViewportGridProvider({ children, service }) {
state.viewports
);
if (!viewport.viewportOptions.presentationIds) {
viewport.viewportOptions.presentationIds = getPresentationIds(
viewport.viewportOptions.presentationIds = ViewportGridService.getPresentationIds(
viewport,
viewports
);
@ -403,12 +403,16 @@ export function ViewportGridProvider({ children, service }) {
getNumViewportPanes,
]);
// run many of the calls through the service itself since we want to publish events
const api = {
getState,
setActiveViewportIndex: index => service.setActiveViewportIndex(index), // run it through the service itself since we want to publish events
setDisplaySetsForViewports,
setLayout: layout => service.setLayout(layout), // run it through the service itself since we want to publish events
reset,
setActiveViewportIndex: index => service.setActiveViewportIndex(index),
setDisplaySetsForViewport: props =>
service.setDisplaySetsForViewports([props]),
setDisplaySetsForViewports: props =>
service.setDisplaySetsForViewports(props),
setLayout: layout => service.setLayout(layout),
reset: () => service.reset(),
set: gridLayoutState => service.setState(gridLayoutState), // run it through the service itself since we want to publish events
getNumViewportPanes,
};
@ -422,9 +426,7 @@ export function ViewportGridProvider({ children, service }) {
ViewportGridProvider.propTypes = {
children: PropTypes.any,
service: PropTypes.shape({
setServiceImplementation: PropTypes.func,
}).isRequired,
service: PropTypes.instanceOf(ViewportGridService).isRequired,
};
export const useViewportGrid = () => useContext(ViewportGridContext);

View File

@ -18,4 +18,4 @@ const StringNumber = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
*/
const StringArray = PropTypes.oneOfType([PropTypes.string, PropTypes.array]);
export { StringNumber, StringArray, ThumbnailType, PresentationIds };
export { StringNumber, StringArray, ThumbnailType };

View File

@ -1,6 +1,6 @@
import React, { useEffect, useCallback } from 'react';
import PropTypes from 'prop-types';
import { ServicesManager, Types } from '@ohif/core';
import { ServicesManager, Types, MeasurementService } from '@ohif/core';
import { ViewportGrid, ViewportPane, useViewportGrid } from '@ohif/ui';
import { utils } from '@ohif/core';
import EmptyViewport from './EmptyViewport';
@ -23,13 +23,6 @@ const ORIENTATION_MAP = {
},
};
const compareViewportOptions = (opts1, opts2) => {
if ((opts1.viewportType || 'stack') != opts2.viewportType) {
return false;
}
return true;
};
function ViewerViewportGrid(props) {
const { servicesManager, viewportComponents, dataSource } = props;
const [viewportGrid, viewportGridService] = useViewportGrid();
@ -160,77 +153,48 @@ function ViewerViewportGrid(props) {
useEffect(() => {
const { unsubscribe } = measurementService.subscribe(
measurementService.EVENTS.JUMP_TO_MEASUREMENT,
({ viewportIndex, measurement }) => {
const {
displaySetInstanceUID: referencedDisplaySetInstanceUID,
metadata: { viewPlaneNormal },
} = measurement;
// if we already have the displaySet in one of the viewports
// Todo: handle fusion display sets?
for (const viewport of viewports) {
const isMatch = viewport.displaySetInstanceUIDs.includes(
referencedDisplaySetInstanceUID
);
if (isMatch) {
return;
}
}
const displaySet = displaySetService.getDisplaySetByUID(
referencedDisplaySetInstanceUID
);
let imageIndex;
// jump straight to the initial image index if we can
if (displaySet.images && measurement.SOPInstanceUID) {
imageIndex = displaySet.images.findIndex(
image => image.SOPInstanceUID === measurement.SOPInstanceUID
);
}
MeasurementService.EVENTS.JUMP_TO_MEASUREMENT_LAYOUT,
({ viewportIndex, measurement, isConsumed }) => {
if (isConsumed) return;
// This occurs when no viewport has elected to consume the event
// so we need to change layouts into a layout which can consume
// the event.
const { displaySetInstanceUID: referencedDisplaySetInstanceUID } =
measurement;
const updatedViewports = _getUpdatedViewports(
viewportIndex,
referencedDisplaySetInstanceUID
);
// Arbitrarily assign the viewport to element 0
const viewport = updatedViewports?.[0];
if (!updatedViewports || !updatedViewports.length) {
if (!viewport) {
console.warn(
'ViewportGrid::Unable to navigate to viewport containing',
referencedDisplaySetInstanceUID
);
return;
}
updatedViewports.forEach(vp => {
vp.viewportOptions ||= {};
const { orientation, viewportType } = vp.viewportOptions;
let initialImageOptions;
viewport.viewportOptions ||= {};
viewport.viewportOptions.orientation = 'acquisition';
// For initial imageIndex to hang be careful for the volume viewport
if (viewportType === 'stack' || !viewportType) {
initialImageOptions = {
index: imageIndex,
};
} else if (viewportType === 'volume') {
// For the volume viewports, be careful to not jump in the viewports
// that are not in the same orientation.
// Todo: this doesn't work for viewports that have custom orientation
// vectors specified
if (
orientation &&
viewPlaneNormal &&
isEqualWithin(
ORIENTATION_MAP[orientation]?.viewPlaneNormal,
viewPlaneNormal
)
) {
initialImageOptions = {
index: imageIndex,
const displaySet = displaySetService.getDisplaySetByUID(
referencedDisplaySetInstanceUID
);
// jump straight to the initial image index if we can
if (displaySet.images && measurement.SOPInstanceUID) {
for (let index = 0; index < displaySet.images.length; index++) {
const image = displaySet.images[index];
if (image.SOPInstanceUID === measurement.SOPInstanceUID) {
viewport.viewportOptions.initialImageOptions = {
index,
};
break;
}
}
vp.viewportOptions.initialImageOptions = initialImageOptions;
});
}
viewportGridService.setDisplaySetsForViewports(updatedViewports);
}
);

298
yarn.lock
View File

@ -1272,7 +1272,7 @@
core-js-pure "^3.25.1"
regenerator-runtime "^0.13.11"
"@babel/runtime@7.17.9", "@babel/runtime@7.7.6", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.6", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
"@babel/runtime@7.17.9", "@babel/runtime@7.7.6", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.6", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
version "7.21.0"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673"
integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==
@ -1387,16 +1387,31 @@
resolved "https://registry.npmjs.org/@cornerstonejs/calculate-suv/-/calculate-suv-1.0.3.tgz#6d99a72032c0f90cebf44dc6f0b12a5f1102e884"
integrity sha512-2SwVJKzC1DzyxdxJtCht9dhTND2GFjLwhhkDyyC7vJq5tIgbhxgPk1CSwovO1pxmoybAXzjOxnaubllxLgoT+w==
"@cornerstonejs/codec-charls@^0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-charls/-/codec-charls-0.1.1.tgz#e55d4aa908732d0cc902888b7f3856c5a996df7f"
integrity sha512-Y250DGVzmownJ7WgpHxNqWvfTnv4/malaKm/tWm0xE1FxhQE8iErMWFpKxpNDk3MdfXO4/98piVsUwmJMiWoDQ==
"@cornerstonejs/codec-charls@^1.2.3":
version "1.2.3"
resolved "https://registry.npmjs.org/@cornerstonejs/codec-charls/-/codec-charls-1.2.3.tgz#6952c420486822ac8404409ae0ed5a559aff6e25"
integrity sha512-qKUe6DN0dnGzhhfZLYhH9UZacMcudjxcaLXCrpxJImT/M/PQvZCT2rllu6VGJbWKJWG+dMVV2zmmleZcdJ7/cA==
"@cornerstonejs/codec-libjpeg-turbo-8bit@^0.0.7":
version "0.0.7"
resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-libjpeg-turbo-8bit/-/codec-libjpeg-turbo-8bit-0.0.7.tgz#2ea9b575eed19e6e7e3701b7a50a4ae0ffbef0c4"
integrity sha512-qgm6BuVAy5mNP8SJ+A6+VbmPnqgj8jPvJrw4HbUoAzndmf9/VHjTYwawn3kmZWya5ErFAsXQ6c0U0noB1LKAiA==
"@cornerstonejs/codec-libjpeg-turbo-8bit@^1.2.2":
version "1.2.2"
resolved "https://registry.npmjs.org/@cornerstonejs/codec-libjpeg-turbo-8bit/-/codec-libjpeg-turbo-8bit-1.2.2.tgz#ae384b149d6655e3dd6e18b9891fab479ab5e144"
integrity sha512-aAUMK2958YNpOb/7G6e2/aG7hExTiFTASlMt/v90XA0pRHdWiNg5ny4S5SAju0FbIw4zcMnR0qfY+yW3VG2ivg==
"@cornerstonejs/codec-openjpeg@^0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjpeg/-/codec-openjpeg-0.1.1.tgz#5bd1c52a33a425299299e970312731fa0cc2711b"
integrity sha512-HOMMOLV6xy8O/agNGGvrl0a8DwShpBvWxAzEzv2pqq12d3r5z/3MyIgNA3Oj/8bIBVvvVXxh9RX7rMDRHJdowg==
"@cornerstonejs/codec-openjpeg@^1.2.2":
version "1.2.2"
resolved "https://registry.npmjs.org/@cornerstonejs/codec-openjpeg/-/codec-openjpeg-1.2.2.tgz#f0b524235b5551426b46db197a37b06f8ac805d7"
@ -3408,6 +3423,35 @@
npmlog "^4.1.2"
write-file-atomic "^2.3.0"
"@mapbox/jsonlint-lines-primitives@~2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz#ce56e539f83552b58d10d672ea4d6fc9adc7b234"
integrity sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==
"@mapbox/mapbox-gl-style-spec@^13.23.1":
version "13.28.0"
resolved "https://registry.yarnpkg.com/@mapbox/mapbox-gl-style-spec/-/mapbox-gl-style-spec-13.28.0.tgz#2ec226320a0f77856046e000df9b419303a56458"
integrity sha512-B8xM7Fp1nh5kejfIl4SWeY0gtIeewbuRencqO3cJDrCHZpaPg7uY+V8abuR+esMeuOjRl5cLhVTP40v+1ywxbg==
dependencies:
"@mapbox/jsonlint-lines-primitives" "~2.0.2"
"@mapbox/point-geometry" "^0.1.0"
"@mapbox/unitbezier" "^0.0.0"
csscolorparser "~1.0.2"
json-stringify-pretty-compact "^2.0.0"
minimist "^1.2.6"
rw "^1.3.3"
sort-object "^0.3.2"
"@mapbox/point-geometry@^0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz#8a83f9335c7860effa2eeeca254332aa0aeed8f2"
integrity sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==
"@mapbox/unitbezier@^0.0.0":
version "0.0.0"
resolved "https://registry.yarnpkg.com/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz#15651bd553a67b8581fb398810c98ad86a34524e"
integrity sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==
"@mdx-js/mdx@^1.6.22":
version "1.6.22"
resolved "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba"
@ -3669,6 +3713,11 @@
resolved "https://registry.npmjs.org/@percy/sdk-utils/-/sdk-utils-1.24.0.tgz#b6e83333c437ac106386e10a776dc823c1a90f33"
integrity sha512-kfYxX0rHP5N2Da6HyfjRCVaeNahAO9XV5WD4SKWKKjdKVkV/Z5/XjVgSKlTBLSYxnWDzYJJ4UHZV43Mw+facMA==
"@petamoriken/float16@^3.4.7":
version "3.8.0"
resolved "https://registry.yarnpkg.com/@petamoriken/float16/-/float16-3.8.0.tgz#3a48b7938e1a62188a61ec02d5b12630f671401f"
integrity sha512-AhVAm6SQ+zgxIiOzwVdUcDmKlu/qU39FiYD2UD6kQQaVenrn0dGZewIghWAENGQsvC+1avLCuT+T2/3Gsp/W3w==
"@philpl/buble@^0.19.7":
version "0.19.7"
resolved "https://registry.npmjs.org/@philpl/buble/-/buble-0.19.7.tgz#27231e6391393793b64bc1c982fc7b593198b893"
@ -7614,6 +7663,13 @@ colorette@^2.0.10, colorette@^2.0.14, colorette@^2.0.16, colorette@^2.0.19:
resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a"
integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==
colormap@^2.3:
version "2.3.2"
resolved "https://registry.yarnpkg.com/colormap/-/colormap-2.3.2.tgz#4422c1178ce563806e265b96782737be85815abf"
integrity sha512-jDOjaoEEmA9AgA11B/jCSAvYE95r3wRoAyTf3LEHGiUVlNHJaL1mRkf5AyLSpQBVGfTEPwGEqCIzL+kgr2WgNA==
dependencies:
lerp "^1.0.3"
colors@~1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
@ -7697,6 +7753,11 @@ compare-func@^2.0.0:
array-ify "^1.0.0"
dot-prop "^5.1.0"
complex.js@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/complex.js/-/complex.js-2.1.1.tgz#0675dac8e464ec431fb2ab7d30f41d889fb25c31"
integrity sha512-8njCHOTtFFLtegk6zQo0kkVX1rngygb/KQI6z1qZxlFI3scluC+LVTCFbrkWjBv4vvLlbQ9t88IPMC6k95VTTg==
component-emitter@^1.2.1:
version "1.3.0"
resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
@ -8332,6 +8393,11 @@ css-what@^6.0.1, css-what@^6.1.0:
resolved "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
csscolorparser@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/csscolorparser/-/csscolorparser-1.0.3.tgz#b34f391eea4da8f3e98231e2ccd8df9c041f171b"
integrity sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==
cssdb@^7.1.0:
version "7.5.4"
resolved "https://registry.npmjs.org/cssdb/-/cssdb-7.5.4.tgz#e34dafee5184d67634604e345e389ca79ac179ea"
@ -8680,6 +8746,17 @@ dayjs@^1.10.4:
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2"
integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==
dcmjs@^0.27:
version "0.27.0"
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.27.0.tgz#2662818c8b20494e366583e6dd3577c20d04d6ff"
integrity sha512-26wtatOLh+0b0aFy9iOg7PdOLG9EHevn9nEOn7Aoo5l7P9aFAMZ3XAa9Q+NULzLE2Q7DcIf2TQvfyVtdzhQzeg==
dependencies:
"@babel/runtime-corejs2" "^7.17.8"
gl-matrix "^3.1.0"
lodash.clonedeep "^4.5.0"
loglevelnext "^3.0.1"
ndarray "^1.0.19"
dcmjs@^0.29.5:
version "0.29.6"
resolved "https://registry.npmjs.org/dcmjs/-/dcmjs-0.29.6.tgz#6ca1543e74bae29657d1f7f2273407e23266c011"
@ -8743,6 +8820,11 @@ decamelize@^1.1.0, decamelize@^1.1.2, decamelize@^1.2.0:
resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==
decimal.js@^10.4.3:
version "10.4.3"
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
decode-named-character-reference@^1.0.0:
version "1.0.2"
resolved "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e"
@ -9068,16 +9150,43 @@ dezalgo@^1.0.0:
asap "^2.0.0"
wrappy "1"
dicom-microscopy-viewer@^0.44.0:
version "0.44.0"
resolved "https://registry.yarnpkg.com/dicom-microscopy-viewer/-/dicom-microscopy-viewer-0.44.0.tgz#d4a9e985acb23c5b82a9aedbe379b39198b8fd55"
integrity sha512-7rcm8bXTcOLsXrhBPwWe5gf4Sj1Rz1lRh+ECcaznOEr/uvX6BTAYLqNJNmeAUMoKcadboEeDrmLihCwCteRSJQ==
dependencies:
"@cornerstonejs/codec-charls" "^0.1.1"
"@cornerstonejs/codec-libjpeg-turbo-8bit" "^0.0.7"
"@cornerstonejs/codec-openjpeg" "^0.1.1"
colormap "^2.3"
dcmjs "^0.27"
dicomicc "^0.1"
dicomweb-client "^0.8"
image-type "^4.1"
mathjs "^11.2"
ol "^7.1"
uuid "^9.0"
dicom-parser@^1.8.9:
version "1.8.21"
resolved "https://registry.npmjs.org/dicom-parser/-/dicom-parser-1.8.21.tgz#916fdc77776367976b8457cad462b5b7cf74eaea"
integrity sha512-lYCweHQDsC8UFpXErPlg86Px2A8bay0HiUY+wzoG3xv5GzgqVHU3lziwSc/Gzn7VV7y2KeP072SzCviuOoU02w==
dicomicc@^0.1:
version "0.1.0"
resolved "https://registry.yarnpkg.com/dicomicc/-/dicomicc-0.1.0.tgz#c73acc60a8e2d73a20f462c8c7d0e1e0d977c486"
integrity sha512-kZejPGjLQ9NsgovSyVsiAuCpq6LofNR9Erc8Tt/vQAYGYCoQnTyWDlg5D0TJJQATKul7cSr9k/q0TF8G9qdDkQ==
dicomweb-client@^0.10.2:
version "0.10.2"
resolved "https://registry.yarnpkg.com/dicomweb-client/-/dicomweb-client-0.10.2.tgz#9c2e466264a5d3b56c18edaafd360e89912cac13"
integrity sha512-sGjq3TxM7jgbe7cBpqddT1VlfOlwXmE7Q12qCSGDOkEL7fgn+Ak/5oW/LiRP2FzmfYzcbpO32r+hC8i4ITXQLw==
dicomweb-client@^0.8:
version "0.8.4"
resolved "https://registry.yarnpkg.com/dicomweb-client/-/dicomweb-client-0.8.4.tgz#3da814cedb9415facb50bc5f43af8d961a991c74"
integrity sha512-/6oY3/Fg9JyAlbTWuJOYbVqici3+nlZt43+Z/Y47RNiqLc028JcxNlY28u4VQqksxfB59f1hhNbsqsHyDT4vhw==
didyoumean@^1.2.2:
version "1.2.2"
resolved "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
@ -9391,6 +9500,11 @@ duplexify@^3.4.2, duplexify@^3.5.0, duplexify@^3.6.0:
readable-stream "^2.0.0"
stream-shift "^1.0.0"
earcut@^2.2.3:
version "2.2.4"
resolved "https://registry.yarnpkg.com/earcut/-/earcut-2.2.4.tgz#6d02fd4d68160c114825d06890a92ecaae60343a"
integrity sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==
eastasianwidth@^0.2.0:
version "0.2.0"
resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
@ -9703,6 +9817,11 @@ escape-html@^1.0.3, escape-html@~1.0.3:
resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
escape-latex@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/escape-latex/-/escape-latex-1.2.0.tgz#07c03818cf7dac250cce517f4fda1b001ef2bca1"
integrity sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==
escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@ -10666,6 +10785,11 @@ file-system-cache@^2.0.0:
fs-extra "^11.1.0"
ramda "^0.28.0"
file-type@^10.10.0:
version "10.11.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-10.11.0.tgz#2961d09e4675b9fb9a3ee6b69e9cd23f43fd1890"
integrity sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw==
file-uri-to-path@1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
@ -11133,6 +11257,19 @@ gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2:
resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
geotiff@^2.0.7:
version "2.0.7"
resolved "https://registry.yarnpkg.com/geotiff/-/geotiff-2.0.7.tgz#358e578233af70bfb0b4dee62d599ad78fc5cfca"
integrity sha512-FKvFTNowMU5K6lHYY2f83d4lS2rsCNdpUC28AX61x9ZzzqPNaWFElWv93xj0eJFaNyOYA63ic5OzJ88dHpoA5Q==
dependencies:
"@petamoriken/float16" "^3.4.7"
lerc "^3.0.0"
pako "^2.0.4"
parse-headers "^2.0.2"
quick-lru "^6.1.1"
web-worker "^1.2.0"
xml-utils "^1.0.2"
get-caller-file@^2.0.1, get-caller-file@^2.0.5:
version "2.0.5"
resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
@ -12151,7 +12288,7 @@ identity-obj-proxy@3.0.x:
dependencies:
harmony-reflect "^1.4.6"
ieee754@^1.1.13:
ieee754@^1.1.12, ieee754@^1.1.13:
version "1.2.1"
resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
@ -12185,6 +12322,13 @@ image-size@^1.0.1:
dependencies:
queue "6.0.2"
image-type@^4.1:
version "4.1.0"
resolved "https://registry.yarnpkg.com/image-type/-/image-type-4.1.0.tgz#72a88d64ff5021371ed67b9a466442100be57cd1"
integrity sha512-CFJMJ8QK8lJvRlTCEgarL4ro6hfDQKif2HjSvYCdQZESaIPV4v9imrf7BQHK+sQeTeNeMpWciR9hyC/g8ybXEg==
dependencies:
file-type "^10.10.0"
immer@^9.0.7:
version "9.0.21"
resolved "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176"
@ -13127,6 +13271,11 @@ jake@^10.8.5:
filelist "^1.0.1"
minimatch "^3.0.4"
javascript-natural-sort@^0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59"
integrity sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==
jest-canvas-mock@^2.1.0:
version "2.5.0"
resolved "https://registry.npmjs.org/jest-canvas-mock/-/jest-canvas-mock-2.5.0.tgz#3e60f87f77ddfa273cf8e7e4ea5f86fa827c7117"
@ -13752,6 +13901,11 @@ json-stable-stringify-without-jsonify@^1.0.1:
resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
json-stringify-pretty-compact@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/json-stringify-pretty-compact/-/json-stringify-pretty-compact-2.0.0.tgz#e77c419f52ff00c45a31f07f4c820c2433143885"
integrity sha512-WRitRfs6BGq4q8gTgOy4ek7iPFXjbra0H3PmDLKm2xnZ+Gh1HUhiKGgCZkSPNULlP7mvfu6FV/mOLhCarspADQ==
json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
@ -13928,6 +14082,11 @@ left-pad@^1.3.0:
resolved "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e"
integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==
lerc@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/lerc/-/lerc-3.0.0.tgz#36f36fbd4ba46f0abf4833799fff2e7d6865f5cb"
integrity sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==
lerna@^3.15.0:
version "3.22.1"
resolved "https://registry.npmjs.org/lerna/-/lerna-3.22.1.tgz#82027ac3da9c627fd8bf02ccfeff806a98e65b62"
@ -13952,6 +14111,11 @@ lerna@^3.15.0:
import-local "^2.0.0"
npmlog "^4.1.2"
lerp@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/lerp/-/lerp-1.0.3.tgz#a18c8968f917896de15ccfcc28d55a6b731e776e"
integrity sha512-70Rh4rCkJDvwWiTsyZ1HmJGvnyfFah4m6iTux29XmasRiZPDBpT9Cfa4ai73+uLZxnlKruUS62jj2lb11wURiA==
leven@^3.1.0:
version "3.1.0"
resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
@ -14476,6 +14640,11 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"
mapbox-to-css-font@^2.4.1:
version "2.4.2"
resolved "https://registry.yarnpkg.com/mapbox-to-css-font/-/mapbox-to-css-font-2.4.2.tgz#a9e31b363ad8ca881cd339ca99f2d2a6b02ea5dd"
integrity sha512-f+NBjJJY4T3dHtlEz1wCG7YFlkODEjFIYlxDdLIDMNpkSksqTt+l/d4rjuwItxuzkuMFvPyrjzV2lxRM4ePcIA==
markdown-escapes@^1.0.0:
version "1.0.4"
resolved "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535"
@ -14496,6 +14665,21 @@ material-colors@^1.2.1:
resolved "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46"
integrity sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==
mathjs@^11.2:
version "11.8.0"
resolved "https://registry.yarnpkg.com/mathjs/-/mathjs-11.8.0.tgz#b02e66461ec068fadf1e90c221121704dc14d8f5"
integrity sha512-I7r8HCoqUGyEiHQdeOCF2m2k9N+tcOHO3cZQ3tyJkMMBQMFqMR7dMQEboBMJAiFW2Um3PEItGPwcOc4P6KRqwg==
dependencies:
"@babel/runtime" "^7.21.0"
complex.js "^2.1.1"
decimal.js "^10.4.3"
escape-latex "^1.2.0"
fraction.js "^4.2.0"
javascript-natural-sort "^0.7.1"
seedrandom "^3.0.5"
tiny-emitter "^2.1.0"
typed-function "^4.1.0"
mdast-squeeze-paragraphs@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz#7c4c114679c3bee27ef10b58e2e015be79f1ef97"
@ -15956,6 +16140,25 @@ oidc-client@1.11.5:
crypto-js "^4.0.0"
serialize-javascript "^4.0.0"
ol-mapbox-style@^9.2.0:
version "9.7.0"
resolved "https://registry.yarnpkg.com/ol-mapbox-style/-/ol-mapbox-style-9.7.0.tgz#38a4f7abc8f0a94f378dcdb7cefdcc69ca3f6287"
integrity sha512-YX3u8FBJHsRHaoGxmd724Mp5WPTuV7wLQW6zZhcihMuInsSdCX1EiZfU+8IAL7jG0pbgl5YgC0aWE/MXJcUXxg==
dependencies:
"@mapbox/mapbox-gl-style-spec" "^13.23.1"
mapbox-to-css-font "^2.4.1"
ol@^7.1:
version "7.3.0"
resolved "https://registry.yarnpkg.com/ol/-/ol-7.3.0.tgz#7ffb5f258dafa4a3e218208aad9054d61f6fe786"
integrity sha512-08vJE4xITKPazQ9qJjeqYjRngnM9s+1eSv219Pdlrjj3LpLqjEH386ncq+76Dw1oGPGR8eLVEePk7FEd9XqqMw==
dependencies:
earcut "^2.2.3"
geotiff "^2.0.7"
ol-mapbox-style "^9.2.0"
pbf "3.2.1"
rbush "^3.0.1"
on-finished@2.4.1:
version "2.4.1"
resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
@ -16301,6 +16504,11 @@ parse-github-repo-url@^1.3.0:
resolved "https://registry.npmjs.org/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz#9e7d8bb252a6cb6ba42595060b7bf6df3dbc1f50"
integrity sha512-bSWyzBKqcSL4RrncTpGsEKoJ7H8a4L3++ifTAbTFeMHyq2wRV+42DGmQcHIrJIvdcacjIOxEuKH/w4tthF17gg==
parse-headers@^2.0.2:
version "2.0.5"
resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.5.tgz#069793f9356a54008571eb7f9761153e6c770da9"
integrity sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==
parse-json@^2.2.0:
version "2.2.0"
resolved "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
@ -16501,6 +16709,14 @@ pause-stream@0.0.11:
dependencies:
through "~2.3"
pbf@3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/pbf/-/pbf-3.2.1.tgz#b4c1b9e72af966cd82c6531691115cc0409ffe2a"
integrity sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==
dependencies:
ieee754 "^1.1.12"
resolve-protobuf-schema "^2.1.0"
peek-stream@^1.1.0:
version "1.1.3"
resolved "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz#3b35d84b7ccbbd262fff31dc10da56856ead6d67"
@ -17588,7 +17804,7 @@ prettier@^1.18.2:
prettier@^2.8.0:
version "2.8.8"
resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
pretty-bytes@^5.3.0, pretty-bytes@^5.4.1, pretty-bytes@^5.6.0:
@ -17728,6 +17944,11 @@ proto-list@~1.2.1:
resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==
protocol-buffers-schema@^3.3.1:
version "3.6.0"
resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz#77bc75a48b2ff142c1ad5b5b90c94cd0fa2efd03"
integrity sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==
protocols@^1.4.0:
version "1.4.8"
resolved "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8"
@ -17919,6 +18140,16 @@ quick-lru@^5.1.1:
resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
quick-lru@^6.1.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-6.1.1.tgz#f8e5bf9010376c126c80c1a62827a526c0e60adf"
integrity sha512-S27GBT+F0NTRiehtbrgaSE1idUAJ5bX8dPAQTdylEyNlrdcH5X4Lz7Edz3DYzecbsCluD5zO8ZNEe04z3D3u6Q==
quickselect@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-2.0.0.tgz#f19680a486a5eefb581303e023e98faaf25dd018"
integrity sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==
raf@^3.4.1:
version "3.4.1"
resolved "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
@ -17958,6 +18189,13 @@ raw-body@2.5.1:
iconv-lite "0.4.24"
unpipe "1.0.0"
rbush@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/rbush/-/rbush-3.0.1.tgz#5fafa8a79b3b9afdfe5008403a720cc1de882ecf"
integrity sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==
dependencies:
quickselect "^2.0.0"
rc@1.2.8, rc@^1.0.1, rc@^1.1.6, rc@^1.2.7, rc@^1.2.8:
version "1.2.8"
resolved "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
@ -19043,6 +19281,13 @@ resolve-pathname@^3.0.0:
resolved "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd"
integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
resolve-protobuf-schema@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz#9ca9a9e69cf192bbdaf1006ec1973948aa4a3758"
integrity sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==
dependencies:
protocol-buffers-schema "^3.3.1"
resolve-url@^0.2.1:
version "0.2.1"
resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
@ -19219,6 +19464,11 @@ run-queue@^1.0.0, run-queue@^1.0.3:
dependencies:
aproba "^1.1.1"
rw@^1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
integrity sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==
rxjs@^6.3.3, rxjs@^6.4.0:
version "6.6.7"
resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9"
@ -19368,7 +19618,7 @@ section-matter@^1.0.0:
extend-shallow "^2.0.1"
kind-of "^6.0.0"
seedrandom@3.0.5:
seedrandom@3.0.5, seedrandom@^3.0.5:
version "3.0.5"
resolved "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7"
integrity sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==
@ -19813,11 +20063,21 @@ socks@~2.3.2:
ip "1.1.5"
smart-buffer "^4.1.0"
sort-asc@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/sort-asc/-/sort-asc-0.1.0.tgz#ab799df61fc73ea0956c79c4b531ed1e9e7727e9"
integrity sha512-jBgdDd+rQ+HkZF2/OHCmace5dvpos/aWQpcxuyRs9QUbPRnkEJmYVo81PIGpjIdpOcsnJ4rGjStfDHsbn+UVyw==
sort-css-media-queries@2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.1.0.tgz#7c85e06f79826baabb232f5560e9745d7a78c4ce"
integrity sha512-IeWvo8NkNiY2vVYdPa27MCQiR0MN0M80johAYFVxWWXQ44KU84WNxjslwBHmc/7ZL2ccwkM7/e6S5aiKZXm7jA==
sort-desc@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/sort-desc/-/sort-desc-0.1.1.tgz#198b8c0cdeb095c463341861e3925d4ee359a9ee"
integrity sha512-jfZacW5SKOP97BF5rX5kQfJmRVZP5/adDUTY8fCSPvNcXDVpUEe2pr/iKGlcyZzchRJZrswnp68fgk3qBXgkJw==
sort-keys@^1.0.0:
version "1.1.2"
resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad"
@ -19832,6 +20092,14 @@ sort-keys@^2.0.0:
dependencies:
is-plain-obj "^1.0.0"
sort-object@^0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/sort-object/-/sort-object-0.3.2.tgz#98e0d199ede40e07c61a84403c61d6c3b290f9e2"
integrity sha512-aAQiEdqFTTdsvUFxXm3umdo04J7MRljoVGbBlkH7BgNsMvVNAJyGj7C/wV1A8wHWAJj/YikeZbfuCKqhggNWGA==
dependencies:
sort-asc "^0.1.0"
sort-desc "^0.1.1"
source-list-map@^2.0.0:
version "2.0.1"
resolved "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
@ -20823,6 +21091,11 @@ timsort@^0.3.0:
resolved "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
integrity sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==
tiny-emitter@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
tiny-invariant@^1.0.2:
version "1.3.1"
resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642"
@ -21084,6 +21357,11 @@ typed-array-length@^1.0.4:
for-each "^0.3.3"
is-typed-array "^1.1.9"
typed-function@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/typed-function/-/typed-function-4.1.0.tgz#da4bdd8a6d19a89e22732f75e4a410860aaf9712"
integrity sha512-DGwUl6cioBW5gw2L+6SMupGwH/kZOqivy17E4nsh1JI9fKF87orMmlQx3KISQPmg3sfnOUGlwVkroosvgddrlg==
typedarray-to-buffer@^3.1.5:
version "3.1.5"
resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
@ -21580,7 +21858,7 @@ uuid@^8.3.2:
resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
uuid@^9.0.0:
uuid@^9.0, uuid@^9.0.0:
version "9.0.0"
resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5"
integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==
@ -21779,6 +22057,11 @@ web-streams-polyfill@^3.0.3:
resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6"
integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==
web-worker@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/web-worker/-/web-worker-1.2.0.tgz#5d85a04a7fbc1e7db58f66595d7a3ac7c9c180da"
integrity sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==
webgl-constants@^1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz#f9633ee87fea56647a60b9ce735cbdfb891c6855"
@ -22499,6 +22782,11 @@ xml-name-validator@^3.0.0:
resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
xml-utils@^1.0.2:
version "1.7.0"
resolved "https://registry.yarnpkg.com/xml-utils/-/xml-utils-1.7.0.tgz#333ce391d8918f872aaf98d2cf90f9ef9b82bd0f"
integrity sha512-bWB489+RQQclC7A9OW8e5BzbT8Tu//jtAOvkYwewFr+Q9T9KDGvfzC1lp0pYPEQPEoPQLDkmxkepSC/2gIAZGw==
xml@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5"