ohif-viewer/Packages/ohif-viewerbase/client/lib/classes/plugins/ViewportPlugin.js

171 lines
5.7 KiB
JavaScript

import { Session } from 'meteor/session';
import { Tracker } from 'meteor/tracker';
import { OHIF } from 'meteor/ohif:core';
import { OHIFPlugin } from "./OHIFPlugin";
export class ViewportPlugin extends OHIFPlugin {
constructor(name) {
super();
this.name = name;
this._destroyed = false;
this._setupListeners();
}
/**
* Retrieve a Display Set for a specific viewport by viewport index,
* if one is already displayed in the viewport.
*
* @static
* @param {Number} viewportIndex
* @return {undefined|ImageSet}
*/
static getDisplaySet(viewportIndex) {
// TODO: Move layoutManager from viewerbase to viewer
const { layoutManager } = OHIF.viewerbase;
const viewportData = layoutManager.viewportData[viewportIndex];
const { studyInstanceUid, displaySetInstanceUid } = viewportData;
const studyMetadata = OHIF.viewer.StudyMetadataList.findBy({ studyInstanceUID: studyInstanceUid });
return studyMetadata.findDisplaySet(displaySet => {
return displaySet.displaySetInstanceUid === displaySetInstanceUid;
});
}
/**
* Set up the viewport using the plugin.
*
* This should be implemented by the child class.
*
* @abstract
*
* @param {HTMLElement} div
* @param {ImageSet} displaySet
* @param {Object} viewportDetails
*/
setupViewport(div, displaySet, viewportDetails) {
throw new Error('You must override this method!');
}
/**
* Switch a single viewport to use the current ViewportPlugin
*
* @param {Number} viewportIndex The viewport to switch to the current plugin type
*/
setViewportToPlugin(viewportIndex) {
if (!this.name) {
throw new Error('ViewportPlugin subclasses must have a name');
}
const { layoutManager } = OHIF.viewerbase;
const viewportData = layoutManager.viewportData[viewportIndex];
if (viewportData.plugin === this.name) {
OHIF.log.info(`setViewportToPlugin: Viewport ${viewportIndex} already set to plugin ${this.name}`);
}
viewportData.plugin = this.name;
layoutManager.rerenderViewportWithNewDisplaySet(viewportIndex, viewportData);
}
/**
* Runs 'setupViewport' for the ViewportPlugin on all viewports which should
* be rendered by this plugin, but have not yet been initialized. Viewports
* which already contain contents are skipped.
*
* @private
*/
_initEmptyPluginViewports() {
if (!this.name) {
throw new Error('ViewportPlugin subclasses must have a name');
}
// Find all Viewport HTMLElements currently using this plugin
const pluginDivs = Array.from(document.querySelectorAll(`.viewport-plugin-${this.name}`));
// If there are no Viewports using this plugin, stop here
if (!pluginDivs.length) {
return;
}
const emptyPluginDivs = pluginDivs.filter(div => {
// Keep only divs owned by the plugin which have no contents
return div.innerHTML.trim() === '';
});
OHIF.log.info(`${this.name}: Initializing ${emptyPluginDivs.length} viewports`);
// Retrieve the list of all viewports, so we can figure out the viewport details
const allViewports = Array.from(document.querySelectorAll('.viewportContainer'));
const { layoutManager } = OHIF.viewerbase;
emptyPluginDivs.forEach(div => {
// Identify the Viewport index, and any display set that is currently
// hung in the viewport
const viewportIndex = allViewports.indexOf(div.parentNode);
const viewportData = layoutManager.viewportData[viewportIndex];
const displaySet = ViewportPlugin.getDisplaySet(viewportIndex);
// Use the plugin's setupViewport function to render the contents
// of this viewport.
this.setupViewport(div, viewportData, displaySet);
});
}
/**
* Listen for changes to the viewport layout which would necessitate a
* rerendering of the viewports. When this happens, re-render all viewports
* which are using this plugin.
*
* @private
*/
_setupListeners() {
if (!this.name) {
throw new Error('ViewportPlugin subclasses must have a name');
}
console.warn(`_setupListeners: ${this.name}`);
// TODO: Stop using Meteor's reactivity here
Tracker.autorun((computation) => {
const random = Session.get('LayoutManagerUpdated');
console.warn(`LayoutManagerUpdated: ${this.name}: ${random}`);
// Bail out if this is the first time the autorun
// executes (i.e. when it is being defined).
//
// Note: This has to be checked after the dependency on the
// Session variable above, or the reactive dependency will not
// be established.
if (computation.firstRun === true) {
return;
}
// In case we need to disable the use
// of this plugin, we can also stop the
// reactive computation by setting
// this.destroyed to true.
if (this._destroyed === true) {
computation.stop();
}
// Identify all viewports which should be
// rendered by the ViewportPlugin, and render
// them.
this._initEmptyPluginViewports();
});
}
/**
* Stop listening for changes to the viewport layout in order to
* automatically rerender viewports setup for use by this plugin.
*/
stopListeners() {
this._destroyed = true;
}
}