@jbrowse/plugin-linear-genome-view
Version:
JBrowse 2 linear genome view
1,110 lines (1,109 loc) • 48.3 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.renderToSvg = exports.SearchBox = exports.RefNameAutocomplete = exports.ReactComponent = exports.LinearGenomeView = void 0;
exports.stateModelFactory = stateModelFactory;
const react_1 = require("react");
const configuration_1 = require("@jbrowse/core/configuration");
const models_1 = require("@jbrowse/core/pluggableElementTypes/models");
const ui_1 = require("@jbrowse/core/ui");
const Icons_1 = require("@jbrowse/core/ui/Icons");
const util_1 = require("@jbrowse/core/util");
const Base1DUtils_1 = require("@jbrowse/core/util/Base1DUtils");
const Base1DViewModel_1 = __importDefault(require("@jbrowse/core/util/Base1DViewModel"));
const calculateDynamicBlocks_1 = __importDefault(require("@jbrowse/core/util/calculateDynamicBlocks"));
const calculateStaticBlocks_1 = __importDefault(require("@jbrowse/core/util/calculateStaticBlocks"));
const tracks_1 = require("@jbrowse/core/util/tracks");
const mst_1 = require("@jbrowse/core/util/types/mst");
const product_core_1 = require("@jbrowse/product-core");
const FolderOpen_1 = __importDefault(require("@mui/icons-material/FolderOpen"));
const Label_1 = __importDefault(require("@mui/icons-material/Label"));
const MenuOpen_1 = __importDefault(require("@mui/icons-material/MenuOpen"));
const Palette_1 = __importDefault(require("@mui/icons-material/Palette"));
const PhotoCamera_1 = __importDefault(require("@mui/icons-material/PhotoCamera"));
const Search_1 = __importDefault(require("@mui/icons-material/Search"));
const SyncAlt_1 = __importDefault(require("@mui/icons-material/SyncAlt"));
const Visibility_1 = __importDefault(require("@mui/icons-material/Visibility"));
const ZoomIn_1 = __importDefault(require("@mui/icons-material/ZoomIn"));
const file_saver_1 = require("file-saver");
const mobx_1 = require("mobx");
const mobx_state_tree_1 = require("mobx-state-tree");
const Header_1 = __importDefault(require("./components/Header"));
const util_2 = require("./util");
const searchUtils_1 = require("../searchUtils");
const MiniControls_1 = __importDefault(require("./components/MiniControls"));
const consts_1 = require("./consts");
const ReturnToImportFormDialog = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('@jbrowse/core/ui/ReturnToImportFormDialog'))));
const SequenceSearchDialog = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./components/SequenceSearchDialog'))));
const ExportSvgDialog = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./components/ExportSvgDialog'))));
const GetSequenceDialog = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./components/GetSequenceDialog'))));
const SearchResultsDialog = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./components/SearchResultsDialog'))));
function stateModelFactory(pluginManager) {
return mobx_state_tree_1.types
.compose('LinearGenomeView', models_1.BaseViewModel, mobx_state_tree_1.types.model({
id: mst_1.ElementId,
type: mobx_state_tree_1.types.literal('LinearGenomeView'),
offsetPx: 0,
bpPerPx: 1,
displayedRegions: mobx_state_tree_1.types.optional(mobx_state_tree_1.types.frozen(), []),
tracks: mobx_state_tree_1.types.array(pluginManager.pluggableMstType('track', 'stateModel')),
hideHeader: false,
hideHeaderOverview: false,
hideNoTracksActive: false,
trackSelectorType: mobx_state_tree_1.types.optional(mobx_state_tree_1.types.enumeration(['hierarchical']), 'hierarchical'),
showCenterLine: mobx_state_tree_1.types.optional(mobx_state_tree_1.types.boolean, () => (0, util_1.localStorageGetBoolean)('lgv-showCenterLine', false)),
showCytobandsSetting: mobx_state_tree_1.types.optional(mobx_state_tree_1.types.boolean, () => (0, util_1.localStorageGetBoolean)('lgv-showCytobands', true)),
trackLabels: mobx_state_tree_1.types.optional(mobx_state_tree_1.types.string, () => (0, util_1.localStorageGetItem)('lgv-trackLabels') || ''),
showGridlines: true,
highlight: mobx_state_tree_1.types.optional(mobx_state_tree_1.types.array(mobx_state_tree_1.types.frozen()), []),
colorByCDS: mobx_state_tree_1.types.optional(mobx_state_tree_1.types.boolean, () => (0, util_1.localStorageGetBoolean)('lgv-colorByCDS', false)),
showTrackOutlines: mobx_state_tree_1.types.optional(mobx_state_tree_1.types.boolean, () => (0, util_1.localStorageGetBoolean)('lgv-showTrackOutlines', true)),
init: mobx_state_tree_1.types.frozen(),
}))
.volatile(() => ({
volatileWidth: undefined,
minimumBlockWidth: 3,
draggingTrackId: undefined,
volatileError: undefined,
afterDisplayedRegionsSetCallbacks: [],
scaleFactor: 1,
trackRefs: {},
coarseDynamicBlocks: [],
coarseTotalBp: 0,
leftOffset: undefined,
rightOffset: undefined,
}))
.views(self => ({
get pinnedTracks() {
return self.tracks.filter(t => t.pinned);
},
get unpinnedTracks() {
return self.tracks.filter(t => !t.pinned);
},
get trackLabelsSetting() {
const sessionSetting = (0, configuration_1.getConf)((0, util_1.getSession)(self), [
'LinearGenomeViewPlugin',
'trackLabels',
]);
return self.trackLabels || sessionSetting;
},
get width() {
if (self.volatileWidth === undefined) {
throw new Error('width undefined, make sure to check for model.initialized');
}
return self.volatileWidth;
},
get interRegionPaddingWidth() {
return consts_1.INTER_REGION_PADDING_WIDTH;
},
get assemblyNames() {
return [
...new Set(self.displayedRegions.map(region => region.assemblyName)),
];
},
get assemblyDisplayNames() {
const { assemblyManager } = (0, util_1.getSession)(self);
return this.assemblyNames.map(assemblyName => {
var _a;
const assembly = assemblyManager.get(assemblyName);
return (_a = assembly === null || assembly === void 0 ? void 0 : assembly.displayName) !== null && _a !== void 0 ? _a : assemblyName;
});
},
get isTopLevelView() {
const session = (0, util_1.getSession)(self);
return session.views.some(r => r.id === self.id);
},
get stickyViewHeaders() {
const session = (0, util_1.getSession)(self);
return (0, product_core_1.isSessionWithMultipleViews)(session)
? this.isTopLevelView && session.stickyViewHeaders
: false;
},
get rubberbandTop() {
let pinnedTracksTop = 0;
if (this.stickyViewHeaders) {
pinnedTracksTop = ui_1.VIEW_HEADER_HEIGHT;
if (!self.hideHeader) {
pinnedTracksTop += consts_1.HEADER_BAR_HEIGHT;
if (!self.hideHeaderOverview) {
pinnedTracksTop += consts_1.HEADER_OVERVIEW_HEIGHT;
}
}
}
return pinnedTracksTop;
},
get pinnedTracksTop() {
return this.rubberbandTop + consts_1.SCALE_BAR_HEIGHT;
},
}))
.views(self => ({
scaleBarDisplayPrefix() {
return (0, mobx_state_tree_1.getParent)(self, 2).type === 'LinearSyntenyView'
? self.assemblyDisplayNames[0]
: '';
},
MiniControlsComponent() {
return MiniControls_1.default;
},
HeaderComponent() {
return Header_1.default;
},
get assembliesNotFound() {
const { assemblyManager } = (0, util_1.getSession)(self);
const r0 = self.assemblyNames
.map(a => (!assemblyManager.get(a) ? a : undefined))
.filter(f => !!f)
.join(',');
return r0 ? `Assemblies ${r0} not found` : undefined;
},
get assemblyErrors() {
const { assemblyManager } = (0, util_1.getSession)(self);
return self.assemblyNames
.map(a => { var _a; return (_a = assemblyManager.get(a)) === null || _a === void 0 ? void 0 : _a.error; })
.filter(f => !!f)
.join(', ');
},
get assembliesInitialized() {
const { assemblyManager } = (0, util_1.getSession)(self);
return self.assemblyNames.every(a => { var _a; return (_a = assemblyManager.get(a)) === null || _a === void 0 ? void 0 : _a.initialized; });
},
get initialized() {
return self.volatileWidth !== undefined && this.assembliesInitialized;
},
get hasDisplayedRegions() {
return self.displayedRegions.length > 0;
},
get scaleBarHeight() {
return consts_1.SCALE_BAR_HEIGHT + consts_1.RESIZE_HANDLE_HEIGHT;
},
get headerHeight() {
if (self.hideHeader) {
return 0;
}
else if (self.hideHeaderOverview) {
return consts_1.HEADER_BAR_HEIGHT;
}
else {
return consts_1.HEADER_BAR_HEIGHT + consts_1.HEADER_OVERVIEW_HEIGHT;
}
},
get trackHeights() {
return (0, util_1.sum)(self.tracks.map(t => t.displays[0].height));
},
get trackHeightsWithResizeHandles() {
return this.trackHeights + self.tracks.length * consts_1.RESIZE_HANDLE_HEIGHT;
},
get height() {
return (this.trackHeightsWithResizeHandles +
this.headerHeight +
this.scaleBarHeight);
},
get totalBp() {
return (0, util_1.sum)(self.displayedRegions.map(r => r.end - r.start));
},
get maxBpPerPx() {
return this.totalBp / (self.width * 0.9);
},
get minBpPerPx() {
return 1 / 50;
},
get error() {
return (self.volatileError || this.assemblyErrors || this.assembliesNotFound);
},
get maxOffset() {
const leftPadding = 10;
return this.displayedRegionsTotalPx - leftPadding;
},
get minOffset() {
const rightPadding = 30;
return -self.width + rightPadding;
},
get displayedRegionsTotalPx() {
return this.totalBp / self.bpPerPx;
},
renderProps() {
return {
...(0, tracks_1.getParentRenderProps)(self),
bpPerPx: self.bpPerPx,
colorByCDS: self.colorByCDS,
};
},
searchScope(assemblyName) {
return {
assemblyName,
includeAggregateIndexes: true,
tracks: self.tracks,
};
},
getTrack(id) {
return self.tracks.find(t => t.configuration.trackId === id);
},
rankSearchResults(results) {
return results;
},
rewriteOnClicks(trackType, viewMenuActions) {
for (const action of viewMenuActions) {
if ('subMenu' in action) {
this.rewriteOnClicks(trackType, action.subMenu);
}
if ('onClick' in action) {
const holdOnClick = action.onClick;
action.onClick = (...args) => {
for (const track of self.tracks) {
if (track.type === trackType) {
holdOnClick.apply(track, [track, ...args]);
}
}
};
}
}
},
get trackTypeActions() {
const allActions = new Map();
for (const track of self.tracks) {
const trackInMap = allActions.get(track.type);
if (!trackInMap) {
const viewMenuActions = structuredClone(track.viewMenuActions);
this.rewriteOnClicks(track.type, viewMenuActions);
allActions.set(track.type, viewMenuActions);
}
}
return allActions;
},
}))
.actions(self => ({
setShowTrackOutlines(arg) {
self.showTrackOutlines = arg;
},
setColorByCDS(flag) {
self.colorByCDS = flag;
},
setShowCytobands(flag) {
self.showCytobandsSetting = flag;
},
setWidth(newWidth) {
self.volatileWidth = newWidth;
},
setError(error) {
self.volatileError = error;
},
setHideHeader(b) {
self.hideHeader = b;
},
setHideHeaderOverview(b) {
self.hideHeaderOverview = b;
},
setHideNoTracksActive(b) {
self.hideNoTracksActive = b;
},
setShowGridlines(b) {
self.showGridlines = b;
},
addToHighlights(highlight) {
self.highlight.push(highlight);
},
setHighlight(highlight) {
self.highlight = (0, mobx_state_tree_1.cast)(highlight);
},
removeHighlight(highlight) {
self.highlight.remove(highlight);
},
scrollTo(offsetPx) {
const newOffsetPx = (0, util_1.clamp)(offsetPx, self.minOffset, self.maxOffset);
self.offsetPx = newOffsetPx;
return newOffsetPx;
},
zoomTo(bpPerPx, offset = self.width / 2, centerAtOffset = false) {
const newBpPerPx = (0, util_1.clamp)(bpPerPx, self.minBpPerPx, self.maxBpPerPx);
if (newBpPerPx === self.bpPerPx) {
return newBpPerPx;
}
const oldBpPerPx = self.bpPerPx;
if (Math.abs(oldBpPerPx - newBpPerPx) < 0.000001) {
console.warn('zoomTo bpPerPx rounding error');
return oldBpPerPx;
}
self.bpPerPx = newBpPerPx;
this.scrollTo(Math.round(((self.offsetPx + offset) * oldBpPerPx) / newBpPerPx -
(centerAtOffset ? self.width / 2 : offset)));
return newBpPerPx;
},
setOffsets(left, right) {
self.leftOffset = left;
self.rightOffset = right;
},
setSearchResults(searchResults, searchQuery, assemblyName) {
(0, util_1.getSession)(self).queueDialog(handleClose => [
SearchResultsDialog,
{
model: self,
searchResults,
searchQuery,
handleClose,
assemblyName,
},
]);
},
setNewView(bpPerPx, offsetPx) {
this.zoomTo(bpPerPx);
this.scrollTo(offsetPx);
},
horizontallyFlip() {
self.displayedRegions = (0, mobx_state_tree_1.cast)([...self.displayedRegions]
.reverse()
.map(region => ({ ...region, reversed: !region.reversed })));
this.scrollTo(self.totalBp / self.bpPerPx - self.offsetPx - self.width);
},
showTrack(trackId, initialSnapshot = {}, displayInitialSnapshot = {}) {
const schema = pluginManager.pluggableConfigSchemaType('track');
const conf = (0, mobx_state_tree_1.resolveIdentifier)(schema, (0, mobx_state_tree_1.getRoot)(self), trackId);
if (!conf) {
throw new Error(`Could not resolve identifier "${trackId}"`);
}
const trackType = pluginManager.getTrackType(conf === null || conf === void 0 ? void 0 : conf.type);
if (!trackType) {
throw new Error(`Unknown track type ${conf.type}`);
}
const viewType = pluginManager.getViewType(self.type);
const supportedDisplays = new Set(viewType.displayTypes.map(d => d.name));
const displayConf = conf.displays.find((d) => supportedDisplays.has(d.type));
if (!displayConf) {
throw new Error(`Could not find a compatible display for view type ${self.type}`);
}
const t = self.tracks.filter(t => t.configuration === conf);
if (t.length === 0) {
const track = trackType.stateModel.create({
...initialSnapshot,
type: conf.type,
configuration: conf,
displays: [
{
type: displayConf.type,
configuration: displayConf,
...displayInitialSnapshot,
},
],
});
self.tracks.push(track);
return track;
}
return t[0];
},
hideTrack(trackId) {
const schema = pluginManager.pluggableConfigSchemaType('track');
const conf = (0, mobx_state_tree_1.resolveIdentifier)(schema, (0, mobx_state_tree_1.getRoot)(self), trackId);
const tracks = self.tracks.filter(t => t.configuration === conf);
(0, mobx_1.transaction)(() => {
for (const track of tracks) {
self.tracks.remove(track);
}
});
return tracks.length;
},
}))
.actions(self => ({
moveTrackDown(id) {
const idx = self.tracks.findIndex(v => v.id === id);
if (idx === -1) {
return;
}
if (idx !== -1 && idx < self.tracks.length - 1) {
self.tracks.splice(idx, 2, self.tracks[idx + 1], self.tracks[idx]);
}
},
moveTrackUp(id) {
const idx = self.tracks.findIndex(track => track.id === id);
if (idx > 0) {
self.tracks.splice(idx - 1, 2, self.tracks[idx], self.tracks[idx - 1]);
}
},
moveTrackToTop(id) {
const idx = self.tracks.findIndex(track => track.id === id);
self.tracks = (0, mobx_state_tree_1.cast)([
self.tracks[idx],
...self.tracks.filter(track => track.id !== id),
]);
},
moveTrackToBottom(id) {
const idx = self.tracks.findIndex(track => track.id === id);
self.tracks = (0, mobx_state_tree_1.cast)([
...self.tracks.filter(track => track.id !== id),
self.tracks[idx],
]);
},
moveTrack(movingId, targetId) {
const oldIndex = self.tracks.findIndex(track => track.id === movingId);
if (oldIndex === -1) {
throw new Error(`Track ID ${movingId} not found`);
}
const newIndex = self.tracks.findIndex(track => track.id === targetId);
if (newIndex === -1) {
throw new Error(`Track ID ${targetId} not found`);
}
const tracks = self.tracks.filter((_, idx) => idx !== oldIndex);
tracks.splice(newIndex, 0, self.tracks[oldIndex]);
self.tracks = (0, mobx_state_tree_1.cast)(tracks);
},
toggleTrack(trackId) {
const hiddenCount = self.hideTrack(trackId);
if (!hiddenCount) {
self.showTrack(trackId);
return true;
}
return false;
},
setTrackLabels(setting) {
localStorage.setItem('lgv-trackLabels', setting);
self.trackLabels = setting;
},
setShowCenterLine(b) {
self.showCenterLine = b;
},
setDisplayedRegions(regions) {
self.displayedRegions = (0, mobx_state_tree_1.cast)(regions);
self.zoomTo(self.bpPerPx);
},
activateTrackSelector() {
if (self.trackSelectorType === 'hierarchical') {
const session = (0, util_1.getSession)(self);
if ((0, util_1.isSessionModelWithWidgets)(session)) {
const selector = session.addWidget('HierarchicalTrackSelectorWidget', 'hierarchicalTrackSelector', { view: self });
session.showWidget(selector);
return selector;
}
}
throw new Error(`invalid track selector type ${self.trackSelectorType}`);
},
getSelectedRegions(leftOffset, rightOffset) {
const snap = (0, mobx_state_tree_1.getSnapshot)(self);
const simView = Base1DViewModel_1.default.create({
...snap,
interRegionPaddingWidth: self.interRegionPaddingWidth,
});
simView.setVolatileWidth(self.width);
simView.moveTo(leftOffset, rightOffset);
return simView.dynamicBlocks.contentBlocks.map(region => ({
...region,
start: Math.floor(region.start),
end: Math.ceil(region.end),
}));
},
afterDisplayedRegionsSet(cb) {
self.afterDisplayedRegionsSetCallbacks.push(cb);
},
horizontalScroll(distance) {
const oldOffsetPx = self.offsetPx;
const newOffsetPx = self.scrollTo(self.offsetPx + distance);
return newOffsetPx - oldOffsetPx;
},
center() {
const centerBp = self.totalBp / 2;
const centerPx = centerBp / self.bpPerPx;
self.scrollTo(Math.round(centerPx - self.width / 2));
},
showAllRegions() {
self.zoomTo(self.maxBpPerPx);
this.center();
},
showAllRegionsInAssembly(assemblyName) {
const session = (0, util_1.getSession)(self);
const { assemblyManager } = session;
if (!assemblyName) {
const names = new Set(self.displayedRegions.map(r => r.assemblyName));
if (names.size > 1) {
session.notify(`Can't perform operation with multiple assemblies currently`);
return;
}
;
[assemblyName] = [...names];
}
const assembly = assemblyManager.get(assemblyName);
if (assembly) {
const { regions } = assembly;
if (regions) {
this.setDisplayedRegions(regions);
self.zoomTo(self.maxBpPerPx);
this.center();
}
}
},
setDraggingTrackId(idx) {
self.draggingTrackId = idx;
},
setScaleFactor(factor) {
self.scaleFactor = factor;
},
clearView() {
this.setDisplayedRegions([]);
self.tracks.clear();
self.scrollTo(0);
self.zoomTo(10);
},
setInit(arg) {
self.init = arg;
},
async exportSvg(opts = {}) {
const { renderToSvg } = await Promise.resolve().then(() => __importStar(require('./svgcomponents/SVGLinearGenomeView')));
const html = await renderToSvg(self, opts);
const blob = new Blob([html], { type: 'image/svg+xml' });
(0, file_saver_1.saveAs)(blob, opts.filename || 'image.svg');
},
}))
.actions(self => {
let cancelLastAnimation = () => { };
function slide(viewWidths) {
const [animate, cancelAnimation] = (0, util_1.springAnimate)(self.offsetPx, self.offsetPx + self.width * viewWidths, self.scrollTo, undefined, undefined, 200);
cancelLastAnimation();
cancelLastAnimation = cancelAnimation;
animate();
}
return { slide };
})
.actions(self => {
let cancelLastAnimation = () => { };
function zoom(targetBpPerPx) {
self.zoomTo(self.bpPerPx);
if ((targetBpPerPx < self.bpPerPx && self.bpPerPx === self.minBpPerPx) ||
(targetBpPerPx > self.bpPerPx && self.bpPerPx === self.maxBpPerPx)) {
return;
}
const factor = self.bpPerPx / targetBpPerPx;
const [animate, cancelAnimation] = (0, util_1.springAnimate)(1, factor, self.setScaleFactor, () => {
self.zoomTo(targetBpPerPx);
self.setScaleFactor(1);
});
cancelLastAnimation();
cancelLastAnimation = cancelAnimation;
animate();
}
return { zoom };
})
.views(self => ({
get canShowCytobands() {
return self.displayedRegions.length === 1 && this.anyCytobandsExist;
},
get showCytobands() {
return this.canShowCytobands && self.showCytobandsSetting;
},
get anyCytobandsExist() {
const { assemblyManager } = (0, util_1.getSession)(self);
return self.assemblyNames.some(a => { var _a, _b; return (_b = (_a = assemblyManager.get(a)) === null || _a === void 0 ? void 0 : _a.cytobands) === null || _b === void 0 ? void 0 : _b.length; });
},
get cytobandOffset() {
var _a;
return this.showCytobands
? (0, util_1.measureText)(((_a = self.displayedRegions[0]) === null || _a === void 0 ? void 0 : _a.refName) || '', 12) + 15
: 0;
},
}))
.views(self => ({
menuItems() {
const { canShowCytobands, showCytobands } = self;
const session = (0, util_1.getSession)(self);
const menuItems = [
{
label: 'Return to import form',
onClick: () => {
(0, util_1.getSession)(self).queueDialog(handleClose => [
ReturnToImportFormDialog,
{ model: self, handleClose },
]);
},
icon: FolderOpen_1.default,
},
...((0, util_1.isSessionWithAddTracks)(session)
? [
{
label: 'Sequence search',
icon: Search_1.default,
onClick: () => {
(0, util_1.getSession)(self).queueDialog(handleClose => [
SequenceSearchDialog,
{
model: self,
handleClose,
},
]);
},
},
]
: []),
{
label: 'Export SVG',
icon: PhotoCamera_1.default,
onClick: () => {
(0, util_1.getSession)(self).queueDialog(handleClose => [
ExportSvgDialog,
{
model: self,
handleClose,
},
]);
},
},
{
label: 'Open track selector',
onClick: self.activateTrackSelector,
icon: Icons_1.TrackSelector,
},
{
label: 'Horizontally flip',
icon: SyncAlt_1.default,
onClick: self.horizontallyFlip,
},
{
label: 'Color by CDS and draw amino acids',
type: 'checkbox',
checked: self.colorByCDS,
icon: Palette_1.default,
onClick: () => {
self.setColorByCDS(!self.colorByCDS);
},
},
{
label: 'Show...',
icon: Visibility_1.default,
subMenu: [
{
label: 'Show all regions in assembly',
onClick: self.showAllRegionsInAssembly,
},
{
label: 'Show center line',
type: 'checkbox',
checked: self.showCenterLine,
onClick: () => {
self.setShowCenterLine(!self.showCenterLine);
},
},
{
label: 'Show header',
type: 'checkbox',
checked: !self.hideHeader,
onClick: () => {
self.setHideHeader(!self.hideHeader);
},
},
{
label: 'Show track outlines',
type: 'checkbox',
checked: self.showTrackOutlines,
onClick: () => {
self.setShowTrackOutlines(!self.showTrackOutlines);
},
},
{
label: 'Show header overview',
type: 'checkbox',
checked: !self.hideHeaderOverview,
onClick: () => {
self.setHideHeaderOverview(!self.hideHeaderOverview);
},
disabled: self.hideHeader,
},
{
label: 'Show no tracks active button',
type: 'checkbox',
checked: !self.hideNoTracksActive,
onClick: () => {
self.setHideNoTracksActive(!self.hideNoTracksActive);
},
},
{
label: 'Show guidelines',
type: 'checkbox',
checked: self.showGridlines,
onClick: () => {
self.setShowGridlines(!self.showGridlines);
},
},
...(canShowCytobands
? [
{
label: 'Show ideogram',
type: 'checkbox',
checked: self.showCytobands,
onClick: () => {
self.setShowCytobands(!showCytobands);
},
},
]
: []),
],
},
{
label: 'Track labels',
icon: Label_1.default,
subMenu: [
{
label: 'Overlapping',
icon: Visibility_1.default,
type: 'radio',
checked: self.trackLabelsSetting === 'overlapping',
onClick: () => {
self.setTrackLabels('overlapping');
},
},
{
label: 'Offset',
icon: Visibility_1.default,
type: 'radio',
checked: self.trackLabelsSetting === 'offset',
onClick: () => {
self.setTrackLabels('offset');
},
},
{
label: 'Hidden',
icon: Visibility_1.default,
type: 'radio',
checked: self.trackLabelsSetting === 'hidden',
onClick: () => {
self.setTrackLabels('hidden');
},
},
],
},
];
for (const [key, value] of self.trackTypeActions.entries()) {
if (value.length) {
menuItems.push({ type: 'divider' }, { type: 'subHeader', label: key });
for (const action of value) {
menuItems.push(action);
}
}
}
return menuItems;
},
}))
.views(self => {
let currentlyCalculatedStaticBlocks;
let stringifiedCurrentlyCalculatedStaticBlocks = '';
return {
get staticBlocks() {
const ret = (0, calculateStaticBlocks_1.default)(self);
const sret = JSON.stringify(ret);
if (stringifiedCurrentlyCalculatedStaticBlocks !== sret) {
currentlyCalculatedStaticBlocks = ret;
stringifiedCurrentlyCalculatedStaticBlocks = sret;
}
return currentlyCalculatedStaticBlocks;
},
get dynamicBlocks() {
return (0, calculateDynamicBlocks_1.default)(self);
},
get roundedDynamicBlocks() {
return this.dynamicBlocks.contentBlocks.map(block => ({
...block,
start: Math.floor(block.start),
end: Math.ceil(block.end),
}));
},
get visibleLocStrings() {
return (0, util_2.calculateVisibleLocStrings)(this.dynamicBlocks.contentBlocks);
},
get coarseVisibleLocStrings() {
return (0, util_2.calculateVisibleLocStrings)(self.coarseDynamicBlocks);
},
};
})
.actions(self => ({
setCoarseDynamicBlocks(blocks) {
self.coarseDynamicBlocks = blocks.contentBlocks;
self.coarseTotalBp = blocks.totalBp;
},
}))
.actions(self => ({
moveTo(start, end) {
(0, Base1DUtils_1.moveTo)(self, start, end);
},
async navToLocString(input, optAssemblyName, grow) {
const { assemblyNames } = self;
const { assemblyManager } = (0, util_1.getSession)(self);
const assemblyName = optAssemblyName || assemblyNames[0];
if (assemblyName) {
await assemblyManager.waitForAssembly(assemblyName);
}
return this.navToLocations((0, util_2.parseLocStrings)(input, assemblyName, (ref, asm) => assemblyManager.isValidRefName(ref, asm)), assemblyName, grow);
},
async navToSearchString({ input, assembly, }) {
await (0, searchUtils_1.handleSelectedRegion)({
input,
assembly,
model: self,
});
},
async navToLocation(parsedLocString, assemblyName, grow) {
return this.navToLocations([parsedLocString], assemblyName, grow);
},
async navToLocations(regions, assemblyName, grow) {
const { assemblyManager } = (0, util_1.getSession)(self);
await (0, mobx_1.when)(() => self.volatileWidth !== undefined);
const locations = await (0, util_2.generateLocations)({
regions,
assemblyManager,
assemblyName,
grow,
});
if (locations.length === 1) {
const location = locations[0];
const { reversed, parentRegion, start, end } = location;
self.setDisplayedRegions([
{
reversed,
...parentRegion,
},
]);
this.navTo({
...location,
start: (0, util_1.clamp)(start !== null && start !== void 0 ? start : 0, 0, parentRegion.end),
end: (0, util_1.clamp)(end !== null && end !== void 0 ? end : parentRegion.end, 0, parentRegion.end),
});
}
else {
self.setDisplayedRegions(locations.map(location => {
const { start, end } = location;
return start === undefined || end === undefined
? location.parentRegion
: {
...location,
start,
end,
};
}));
self.showAllRegions();
}
},
navTo(query) {
this.navToMultiple([query]);
},
navToMultiple(locations) {
if (locations.some(l => l.start !== undefined && l.end !== undefined && l.start > l.end)) {
throw new Error('found start greater than end');
}
const firstLocation = locations.at(0);
const lastLocation = locations.at(-1);
if (!firstLocation || !lastLocation) {
return;
}
const defaultAssemblyName = self.assemblyNames[0];
const { assemblyManager } = (0, util_1.getSession)(self);
const firstAssembly = assemblyManager.get(firstLocation.assemblyName || defaultAssemblyName);
const firstRefName = (firstAssembly === null || firstAssembly === void 0 ? void 0 : firstAssembly.getCanonicalRefName(firstLocation.refName)) ||
firstLocation.refName;
const firstRegion = self.displayedRegions.find(r => r.refName === firstRefName);
const lastAssembly = assemblyManager.get(lastLocation.assemblyName || defaultAssemblyName);
const lastRefName = (lastAssembly === null || lastAssembly === void 0 ? void 0 : lastAssembly.getCanonicalRefName(lastLocation.refName)) ||
lastLocation.refName;
const lastRegion = (0, util_1.findLast)(self.displayedRegions, r => r.refName === lastRefName);
if (!firstRegion) {
throw new Error(`could not find a region with refName "${firstRefName}"`);
}
if (!lastRegion) {
throw new Error(`could not find a region with refName "${lastRefName}"`);
}
const firstStart = firstLocation.start === undefined
? firstRegion.start
: firstLocation.start;
const firstEnd = firstLocation.end === undefined ? firstRegion.end : firstLocation.end;
const lastStart = lastLocation.start === undefined
? lastRegion.start
: lastLocation.start;
const lastEnd = lastLocation.end === undefined ? lastRegion.end : lastLocation.end;
const firstIndex = self.displayedRegions.findIndex(r => firstRefName === r.refName &&
firstStart >= r.start &&
firstStart <= r.end &&
firstEnd <= r.end &&
firstEnd >= r.start);
const lastIndex = self.displayedRegions.findIndex(r => lastRefName === r.refName &&
lastStart >= r.start &&
lastStart <= r.end &&
lastEnd <= r.end &&
lastEnd >= r.start);
if (firstIndex === -1 || lastIndex === -1) {
throw new Error(`could not find a region that contained "${locations.map(l => (0, util_1.assembleLocString)(l))}"`);
}
const startDisplayedRegion = self.displayedRegions[firstIndex];
const endDisplayedRegion = self.displayedRegions[lastIndex];
const startOffset = startDisplayedRegion.reversed
? startDisplayedRegion.end - firstEnd
: firstStart - startDisplayedRegion.start;
const endOffset = endDisplayedRegion.reversed
? endDisplayedRegion.end - lastStart
: lastEnd - endDisplayedRegion.start;
this.moveTo({
index: firstIndex,
offset: startOffset,
}, {
index: lastIndex,
offset: endOffset,
});
},
}))
.views(self => ({
rubberBandMenuItems() {
return [
{
label: 'Zoom to region',
icon: ZoomIn_1.default,
onClick: () => {
self.moveTo(self.leftOffset, self.rightOffset);
},
},
{
label: 'Get sequence',
icon: MenuOpen_1.default,
onClick: () => {
(0, util_1.getSession)(self).queueDialog(handleClose => [
GetSequenceDialog,
{ model: self, handleClose },
]);
},
},
];
},
bpToPx({ refName, coord, regionNumber, }) {
return (0, Base1DUtils_1.bpToPx)({ refName, coord, regionNumber, self });
},
centerAt(coord, refName, regionNumber) {
const centerPx = this.bpToPx({
refName,
coord,
regionNumber,
});
if (centerPx !== undefined) {
self.scrollTo(Math.round(centerPx.offsetPx - self.width / 2));
}
},
pxToBp(px) {
return (0, Base1DUtils_1.pxToBp)(self, px);
},
get centerLineInfo() {
return self.displayedRegions.length > 0
? this.pxToBp(self.width / 2)
: undefined;
},
}))
.actions(self => ({
afterCreate() {
function handler(e) {
const session = (0, util_1.getSession)(self);
if (session.focusedViewId === self.id && (e.ctrlKey || e.metaKey)) {
if (e.code === 'ArrowLeft') {
e.preventDefault();
self.slide(-0.9);
}
else if (e.code === 'ArrowRight') {
e.preventDefault();
self.slide(0.9);
}
else if (e.code === 'ArrowUp' && self.scaleFactor === 1) {
e.preventDefault();
self.zoom(self.bpPerPx / 2);
}
else if (e.code === 'ArrowDown' && self.scaleFactor === 1) {
e.preventDefault();
self.zoom(self.bpPerPx * 2);
}
}
}
document.addEventListener('keydown', handler);
(0, mobx_state_tree_1.addDisposer)(self, () => {
document.removeEventListener('keydown', handler);
});
},
afterAttach() {
(0, mobx_state_tree_1.addDisposer)(self, (0, mobx_1.autorun)(() => {
var _a;
const { init } = self;
if (init) {
self
.navToLocString(init.loc, init.assembly)
.catch((e) => {
console.error(init, e);
(0, util_1.getSession)(self).notifyError(`${e}`, e);
});
(_a = init.tracks) === null || _a === void 0 ? void 0 : _a.map(t => self.showTrack(t));
self.setInit(undefined);
}
}));
(0, mobx_state_tree_1.addDisposer)(self, (0, mobx_1.autorun)(() => {
if (self.initialized) {
self.setCoarseDynamicBlocks(self.dynamicBlocks);
}
}, { delay: 150 }));
(0, mobx_state_tree_1.addDisposer)(self, (0, mobx_1.autorun)(() => {
const s = (s) => JSON.stringify(s);
const { showCytobandsSetting, showCenterLine, colorByCDS } = self;
(0, util_1.localStorageSetItem)('lgv-showCytobands', s(showCytobandsSetting));
(0, util_1.localStorageSetItem)('lgv-showCenterLine', s(showCenterLine));
(0, util_1.localStorageSetItem)('lgv-colorByCDS', s(colorByCDS));
}));
},
}))
.preProcessSnapshot(snap => {
if (!snap) {
return snap;
}
const { highlight, ...rest } = snap;
return {
highlight: Array.isArray(highlight) || highlight === undefined
? highlight
: [highlight],
...rest,
};
})
.postProcessSnapshot(snap => {
if (!snap) {
return snap;
}
else {
const { init, ...rest } = snap;
return rest;
}
});
}
var LinearGenomeView_1 = require("./components/LinearGenomeView");
Object.defineProperty(exports, "LinearGenomeView", { enumerable: true, get: function () { return __importDefault(LinearGenomeView_1).default; } });
Object.defineProperty(exports, "ReactComponent", { enumerable: true, get: function () { return __importDefault(LinearGenomeView_1).default; } });
var RefNameAutocomplete_1 = require("./components/RefNameAutocomplete");
Object.defineProperty(exports, "RefNameAutocomplete", { enumerable: true, get: function () { return __importDefault(RefNameAutocomplete_1).default; } });
var SearchBox_1 = require("./components/SearchBox");
Object.defineProperty(exports, "SearchBox", { enumerable: true, get: function () { return __importDefault(SearchBox_1).default; } });
var SVGLinearGenomeView_1 = require("./svgcomponents/SVGLinearGenomeView");
Object.defineProperty(exports, "renderToSvg", { enumerable: true, get: function () { return SVGLinearGenomeView_1.renderToSvg; } });