@jbrowse/core 5.0.0-beta.10 → 5.0.0-beta.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +210 -80
- package/esm/BaseFeatureWidget/BaseFeatureDetail/ArrayValue.js +0 -2
- package/esm/BaseFeatureWidget/BaseFeatureDetail/Attributes.d.ts +3 -0
- package/esm/BaseFeatureWidget/BaseFeatureDetail/Attributes.js +14 -1
- package/esm/BaseFeatureWidget/BaseFeatureDetail/BasicValue.js +22 -6
- package/esm/BaseFeatureWidget/BaseFeatureDetail/jexlFilterActions.js +3 -4
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeatureDetails.d.ts +2 -0
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeatureDetails.js +5 -5
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeaturePanel.js +1 -1
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/dialogs/FeatureSequenceDialog.d.ts +3 -1
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/dialogs/FeatureSequenceDialog.js +2 -2
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/featureTypeUtil.d.ts +2 -1
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/featureTypeUtil.js +7 -14
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/CDNASequence.js +3 -3
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/useSequenceData.js +8 -5
- package/esm/BaseFeatureWidget/stateModelFactory.d.ts +7 -0
- package/esm/BaseFeatureWidget/stateModelFactory.js +11 -0
- package/esm/BaseFeatureWidget/types.d.ts +2 -0
- package/esm/CorePlugin.js +32 -1
- package/esm/PluginManager.d.ts +6 -3
- package/esm/PluginManager.js +6 -3
- package/esm/ReExports/coreModules.generated.js +291 -283
- package/esm/ReExports/coreWorkerModules.generated.js +288 -281
- package/esm/ReExports/list.js +10 -2
- package/esm/TextSearch/TextSearchManager.d.ts +3 -3
- package/esm/TextSearch/TextSearchManager.js +6 -13
- package/esm/configuration/configurationSchema.d.ts +33 -95
- package/esm/configuration/configurationSchema.js +50 -218
- package/esm/configuration/configurationSlot.d.ts +18 -64
- package/esm/configuration/configurationSlot.js +31 -97
- package/esm/configuration/formatDetailsConfigSchema.js +2 -0
- package/esm/configuration/getConf.d.ts +7 -36
- package/esm/configuration/getConf.js +7 -37
- package/esm/configuration/index.d.ts +5 -2
- package/esm/configuration/index.js +3 -1
- package/esm/configuration/partitionAdvanced.d.ts +12 -0
- package/esm/configuration/partitionAdvanced.js +42 -0
- package/esm/configuration/plot.d.ts +67 -0
- package/esm/configuration/plot.js +175 -0
- package/esm/configuration/readConfObject.d.ts +15 -17
- package/esm/configuration/readConfObject.js +19 -50
- package/esm/configuration/schemaTypes.js +5 -1
- package/esm/configuration/snapshotPreprocess.js +1 -3
- package/esm/configuration/types.d.ts +60 -159
- package/esm/pluggableElementTypes/DisplayType.d.ts +14 -0
- package/esm/pluggableElementTypes/DisplayType.js +13 -0
- package/esm/pluggableElementTypes/RpcMethodType.d.ts +7 -0
- package/esm/pluggableElementTypes/RpcMethodType.js +9 -0
- package/esm/pluggableElementTypes/WidgetType.d.ts +6 -0
- package/esm/pluggableElementTypes/WidgetType.js +6 -0
- package/esm/pluggableElementTypes/addMenuItems.d.ts +2 -1
- package/esm/pluggableElementTypes/addMenuItems.js +2 -1
- package/esm/pluggableElementTypes/index.js +1 -8
- package/esm/pluggableElementTypes/models/BaseDisplayModel.d.ts +58 -1
- package/esm/pluggableElementTypes/models/BaseDisplayModel.js +84 -2
- package/esm/pluggableElementTypes/models/BaseTrackModel.d.ts +2 -4
- package/esm/pluggableElementTypes/models/BaseTrackModel.js +2 -4
- package/esm/pluggableElementTypes/models/BaseViewModel.d.ts +2 -2
- package/esm/pluggableElementTypes/models/BaseViewModel.js +1 -1
- package/esm/pluggableElementTypes/models/baseTrackConfig.js +3 -0
- package/esm/rpc/RpcRegistry.d.ts +2 -1
- package/esm/rpc/byteBudget.d.ts +7 -0
- package/esm/rpc/byteBudget.js +10 -0
- package/esm/rpc/coreRpcMethods.d.ts +1 -1
- package/esm/rpc/coreRpcMethods.js +1 -1
- package/esm/rpc/explainTransferError.js +3 -3
- package/esm/rpc/methods/CoreLoadTrackCode.d.ts +17 -0
- package/esm/rpc/methods/CoreLoadTrackCode.js +24 -0
- package/esm/ui/AppLogo.js +43 -3
- package/esm/ui/JexlFilterDialog.d.ts +1 -1
- package/esm/ui/JexlFilterDialog.js +2 -3
- package/esm/ui/buildExtraTrackMenuItems.d.ts +2 -2
- package/esm/ui/colors.d.ts +41 -30
- package/esm/ui/colors.js +81 -44
- package/esm/ui/multiTrackMenuItems.d.ts +2 -2
- package/esm/util/TimeTraveller.d.ts +3 -0
- package/esm/util/TimeTraveller.js +34 -2
- package/esm/util/alignedBytes.d.ts +19 -0
- package/esm/util/alignedBytes.js +37 -0
- package/esm/util/analytics.d.ts +5 -6
- package/esm/util/analytics.js +5 -3
- package/esm/util/baseDisplayConfig.d.ts +8 -0
- package/esm/util/baseDisplayConfig.js +27 -0
- package/esm/util/bezierConnector.js +5 -1
- package/esm/util/cachedSetup.d.ts +16 -6
- package/esm/util/cachedSetup.js +108 -38
- package/esm/util/categoricalField.d.ts +23 -3
- package/esm/util/categoricalField.js +36 -3
- package/esm/util/cellMatches.d.ts +23 -0
- package/esm/util/cellMatches.js +264 -0
- package/esm/util/cellsStep.d.ts +27 -0
- package/esm/util/cellsStep.js +439 -0
- package/esm/util/color-bits/parse.js +22 -11
- package/esm/util/colorRamp.js +6 -0
- package/esm/util/colorScale.d.ts +9 -2
- package/esm/util/colorScale.js +33 -2
- package/esm/util/colorSchemes.d.ts +4 -2
- package/esm/util/colorSchemes.js +4 -1
- package/esm/util/copyToClipboard.js +8 -1
- package/esm/util/createAbortRotation.js +3 -3
- package/esm/util/featureTransforms.d.ts +7 -6
- package/esm/util/featureTransforms.js +131 -479
- package/esm/util/fetchPhases.d.ts +1 -1
- package/esm/util/formatBytes.js +1 -1
- package/esm/util/formatGuessers.d.ts +25 -14
- package/esm/util/formatGuessers.js +42 -27
- package/esm/util/formatRelativeTime.js +1 -1
- package/esm/util/getLocationUri.d.ts +2 -1
- package/esm/util/getLocationUri.js +8 -2
- package/esm/util/groupKeys.d.ts +4 -5
- package/esm/util/groupKeys.js +7 -6
- package/esm/util/intervals.js +9 -4
- package/esm/util/io/index.js +3 -1
- package/esm/util/isoformRank.d.ts +39 -0
- package/esm/util/isoformRank.js +118 -0
- package/esm/util/jexlFilters.d.ts +31 -49
- package/esm/util/jexlFilters.js +29 -39
- package/esm/util/locString.js +5 -2
- package/esm/util/markEncoding.js +48 -8
- package/esm/util/markEncodingTypes.d.ts +11 -2
- package/esm/util/quantileExtent.d.ts +22 -0
- package/esm/util/quantileExtent.js +24 -1
- package/esm/util/springAnimate.js +1 -1
- package/esm/util/stepTables.d.ts +14 -0
- package/esm/util/stepTables.js +38 -4
- package/esm/util/systemColorScheme.d.ts +4 -0
- package/esm/util/systemColorScheme.js +21 -0
- package/esm/util/tabix.d.ts +7 -3
- package/esm/util/tabix.js +3 -3
- package/esm/util/temporaryAssembly.d.ts +2 -2
- package/esm/util/temporaryAssembly.js +2 -3
- package/esm/util/trackConfigDelta.d.ts +1 -1
- package/esm/util/tracks.d.ts +28 -45
- package/esm/util/tracks.js +71 -82
- package/esm/util/types/index.d.ts +15 -7
- package/esm/util/types/index.js +21 -2
- package/esm/util/unknownSnapshotKeys.d.ts +3 -1
- package/esm/util/unknownSnapshotKeys.js +5 -11
- package/esm/util/usePanZoom.js +10 -3
- package/esm/util/useRafCommit.js +1 -0
- package/esm/util/useRowVirtualScroll.d.ts +1 -0
- package/esm/util/withFeatureDetails.js +4 -0
- package/package.json +13 -9
- package/esm/rpc/methods/CoreLoadAdapterCode.d.ts +0 -13
- package/esm/rpc/methods/CoreLoadAdapterCode.js +0 -16
package/esm/util/tracks.js
CHANGED
|
@@ -8,12 +8,16 @@ export { getFileName } from './getFileName.js';
|
|
|
8
8
|
// it. `fetchContext.ts` takes it from mstUtils instead, which is what keeps the
|
|
9
9
|
// configuration schemas this file pulls in out of the fetch harness's graph.
|
|
10
10
|
export { getRpcSessionId } from './parentWalk.js';
|
|
11
|
+
export { makeIndex } from './formatGuessers.js';
|
|
12
|
+
export { isLooseTrackConfig, } from '@jbrowse/add-track-core';
|
|
13
|
+
import { isLooseTrackConfig, } from '@jbrowse/add-track-core';
|
|
11
14
|
import { asModelType, getParent, getSnapshot, getType, hasParent, isAlive, isStateTreeNode, } from '@jbrowse/mobx-state-tree';
|
|
12
15
|
import { observable, runInAction, untracked } from 'mobx';
|
|
13
|
-
import { readConfObject } from '../configuration/index.js';
|
|
16
|
+
import { isConfigurationModel, readConfObject } from '../configuration/index.js';
|
|
14
17
|
import { adapterConfigCacheKey } from '../data_adapters/dataAdapterCache.js';
|
|
15
18
|
import { displayCandidates, displayTypesFedBy, } from '../pluggableElementTypes/models/baseTrackConfig.js';
|
|
16
19
|
import { getFileHandle, storeFileHandle, verifyPermission, } from './fileHandleStore.js';
|
|
20
|
+
import { makeIndex } from './formatGuessers.js';
|
|
17
21
|
import { getFileName } from './getFileName.js';
|
|
18
22
|
import { getContainingView, getEnv, getSession, objectHash, } from './mstUtils.js';
|
|
19
23
|
import { isViewModel } from './types/index.js';
|
|
@@ -86,8 +90,14 @@ export function isSameAssemblyName(a, b, assemblyManager) {
|
|
|
86
90
|
* copy a non-admin's edits would go to, ADR-032). `hasParent` is the guard:
|
|
87
91
|
* without it the walk raises MST's "Failed to find the parent" from a helper
|
|
88
92
|
* whose other two failure modes are a plain `undefined`.
|
|
93
|
+
*
|
|
94
|
+
* A frozen {@link TrackConfigEntry} omits a slot at its default, so one
|
|
95
|
+
* without `assemblyNames` names the slot's default, `[]`.
|
|
89
96
|
*/
|
|
90
97
|
function confAssemblyNames(conf) {
|
|
98
|
+
if (!isConfigurationModel(conf)) {
|
|
99
|
+
return conf.assemblyNames ?? [];
|
|
100
|
+
}
|
|
91
101
|
const trackAssemblyNames = readConfObject(conf, 'assemblyNames');
|
|
92
102
|
if (trackAssemblyNames) {
|
|
93
103
|
return trackAssemblyNames;
|
|
@@ -121,6 +131,13 @@ export function getConfAssemblyNames(conf) {
|
|
|
121
131
|
export function getConfAssemblyNamesOrNone(conf) {
|
|
122
132
|
return confAssemblyNames(conf) ?? [];
|
|
123
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* A track config as a frozen entry: `conf` itself, or a live node's snapshot.
|
|
136
|
+
* Either way a slot at its default is absent, so a read supplies the default.
|
|
137
|
+
*/
|
|
138
|
+
export function toTrackConfigEntry(conf) {
|
|
139
|
+
return isStateTreeNode(conf) ? getSnapshot(conf) : conf;
|
|
140
|
+
}
|
|
124
141
|
/**
|
|
125
142
|
* The RPC session id a track's own requests go out under, from its config:
|
|
126
143
|
* what `BaseTrackModel.rpcSessionId` computes, for a caller holding no model.
|
|
@@ -279,34 +296,6 @@ export function findFileHandleIds(obj) {
|
|
|
279
296
|
export async function restoreFileHandlesFromSnapshot(sessionSnapshot, requestPermission = false) {
|
|
280
297
|
return restoreFileHandles([...findFileHandleIds(sessionSnapshot)], requestPermission);
|
|
281
298
|
}
|
|
282
|
-
/**
|
|
283
|
-
* creates a new location from the provided location including the appropriate
|
|
284
|
-
* suffix and location type
|
|
285
|
-
*
|
|
286
|
-
* @param location - the FileLocation
|
|
287
|
-
* @param suffix - the file suffix (e.g. .bam)
|
|
288
|
-
* @returns the constructed location object from the provided parameters
|
|
289
|
-
*/
|
|
290
|
-
export function makeIndex(location, suffix) {
|
|
291
|
-
if ('uri' in location) {
|
|
292
|
-
return {
|
|
293
|
-
uri: location.uri + suffix,
|
|
294
|
-
locationType: 'UriLocation',
|
|
295
|
-
// carry the parent's baseUri so a derived sibling index resolves against
|
|
296
|
-
// the same config location as the file it indexes
|
|
297
|
-
...(location.baseUri ? { baseUri: location.baseUri } : {}),
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
else if ('localPath' in location) {
|
|
301
|
-
return {
|
|
302
|
-
localPath: location.localPath + suffix,
|
|
303
|
-
locationType: 'LocalPathLocation',
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
else {
|
|
307
|
-
return location;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
299
|
/**
|
|
311
300
|
* constructs a potential index file (with suffix) from the provided file name
|
|
312
301
|
*
|
|
@@ -450,17 +439,6 @@ export function guessTrackConfForLocation(file, index, pluginManager, assemblyNa
|
|
|
450
439
|
adapter,
|
|
451
440
|
};
|
|
452
441
|
}
|
|
453
|
-
/**
|
|
454
|
-
* Whether a track snapshot is the loose `{ uri, ... }` form — a data file with
|
|
455
|
-
* no `adapter` — that {@link expandLooseTrackConfig} expands.
|
|
456
|
-
*/
|
|
457
|
-
export function isLooseTrackConfig(snap) {
|
|
458
|
-
return (typeof snap === 'object' &&
|
|
459
|
-
snap !== null &&
|
|
460
|
-
!Array.isArray(snap) &&
|
|
461
|
-
typeof snap.uri === 'string' &&
|
|
462
|
-
!('adapter' in snap));
|
|
463
|
-
}
|
|
464
442
|
/**
|
|
465
443
|
* Expand a loose track config into a full one and leave any other snapshot
|
|
466
444
|
* untouched, so it can sit on every path a track snapshot enters the tree by:
|
|
@@ -498,21 +476,16 @@ export function generateUnknownTrackConf(trackName, trackUrl, categories) {
|
|
|
498
476
|
return generateProblemTrackConf(trackName, categories, 'Unknown', `Could not determine track type for "${trackUrl}"`);
|
|
499
477
|
}
|
|
500
478
|
export function getTrackName(conf, session) {
|
|
501
|
-
const
|
|
502
|
-
const trackName = isMst
|
|
479
|
+
const trackName = isConfigurationModel(conf)
|
|
503
480
|
? readConfObject(conf, 'name')
|
|
504
481
|
: (conf.name ?? '');
|
|
505
|
-
|
|
506
|
-
if (!trackName && trackType === 'ReferenceSequenceTrack') {
|
|
482
|
+
if (!trackName && conf.type === 'ReferenceSequenceTrack') {
|
|
507
483
|
const asm = session.assemblies.find(a => a.sequence === conf);
|
|
508
484
|
return asm
|
|
509
485
|
? `Reference sequence (${readConfObject(asm, 'displayName') || readConfObject(asm, 'name')})`
|
|
510
486
|
: 'Reference sequence';
|
|
511
487
|
}
|
|
512
|
-
|
|
513
|
-
? readConfObject(conf, 'trackId')
|
|
514
|
-
: (conf.trackId ?? '');
|
|
515
|
-
return trackName || trackId;
|
|
488
|
+
return trackName || (conf.trackId ?? '');
|
|
516
489
|
}
|
|
517
490
|
// The first-show path spreads the same settings into the display's `create()`
|
|
518
491
|
// snapshot, so a DECLARED PROP (`resolution` on the wiggle displays) has
|
|
@@ -649,12 +622,11 @@ export function filterTracks(tracks, self) {
|
|
|
649
622
|
const viewAssemblyNames = canonical(self.assemblyNames);
|
|
650
623
|
const viewDisplays = viewDisplayNames(pluginManager, view.type);
|
|
651
624
|
return tracks.filter(c => {
|
|
652
|
-
const trackAssemblyNames = readConfObject(c, 'assemblyNames');
|
|
653
625
|
return (
|
|
654
626
|
// a view that declares no assemblies (one still initializing) constrains
|
|
655
627
|
// nothing; otherwise the track must cover every one of them
|
|
656
628
|
(viewAssemblyNames.length === 0 ||
|
|
657
|
-
containsAll(
|
|
629
|
+
containsAll(canonical(getConfAssemblyNamesOrNone(c)), viewAssemblyNames)) &&
|
|
658
630
|
viewCanDisplayTrack(pluginManager, viewDisplays, {
|
|
659
631
|
type: c.type,
|
|
660
632
|
adapter: c.adapter,
|
|
@@ -740,6 +712,43 @@ export function normalizeTrackInit(t) {
|
|
|
740
712
|
};
|
|
741
713
|
}
|
|
742
714
|
}
|
|
715
|
+
/**
|
|
716
|
+
* The display a view of `viewType` opens `conf` as, chosen from the displays the
|
|
717
|
+
* track's adapter feeds and the view draws. `requested` is the display type a
|
|
718
|
+
* caller asks for; a name no display answers to leaves `picked` undefined.
|
|
719
|
+
* Aliases resolve first, so a caller or a config naming a pre-consolidation
|
|
720
|
+
* display type (`LinearPileupDisplay`) reaches the type that replaced it.
|
|
721
|
+
*/
|
|
722
|
+
function pickTrackDisplay(pluginManager, conf, viewTypeName, requested) {
|
|
723
|
+
const viewType = pluginManager.getViewType(viewTypeName);
|
|
724
|
+
const candidates = displayCandidates(pluginManager, conf);
|
|
725
|
+
const viewDisplayTypes = viewType.displayTypes.map(d => d.name);
|
|
726
|
+
const canonical = (name) => pluginManager.resolveDisplayTypeRecord(name)?.name ?? name;
|
|
727
|
+
const declaredDisplays = conf.displays ?? [];
|
|
728
|
+
const picked = pickDisplayForView({
|
|
729
|
+
candidates,
|
|
730
|
+
declaredDisplays: declaredDisplays.map(d => ({
|
|
731
|
+
...d,
|
|
732
|
+
type: canonical(d.type),
|
|
733
|
+
})),
|
|
734
|
+
requestedType: requested === undefined ? undefined : canonical(requested),
|
|
735
|
+
viewDisplayTypes,
|
|
736
|
+
preferredDisplayTypes: new Set(viewType.displayTypes
|
|
737
|
+
.filter(d => d.viewType === viewTypeName)
|
|
738
|
+
.map(d => d.name)),
|
|
739
|
+
});
|
|
740
|
+
return { picked, candidates, viewDisplayTypes };
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* The display type a view of `viewType` opens the track `rawConf` as, the one
|
|
744
|
+
* `showTrack` picks, or undefined when the view draws none of the track's
|
|
745
|
+
* displays. For a caller that needs to know the display before the view exists,
|
|
746
|
+
* such as a CLI choosing which settings to write.
|
|
747
|
+
*/
|
|
748
|
+
export function trackDisplayType(pluginManager, rawConf, viewType, requested) {
|
|
749
|
+
const conf = pluginManager.evaluateExtensionPoint('Core-preProcessTrackConfig', structuredClone(rawConf));
|
|
750
|
+
return pickTrackDisplay(pluginManager, conf, viewType, requested).picked?.type;
|
|
751
|
+
}
|
|
743
752
|
function resolveTrackDisplayChoice(self, trackId, displayInitialSnapshot, inlineConf) {
|
|
744
753
|
const { pluginManager } = getEnv(self);
|
|
745
754
|
const session = getSession(self);
|
|
@@ -782,27 +791,8 @@ function resolveTrackDisplayChoice(self, trackId, displayInitialSnapshot, inline
|
|
|
782
791
|
// display choice from the view it sits in, which is the same node
|
|
783
792
|
// getContainingView already resolves to for everything else beneath it.
|
|
784
793
|
const view = isViewModel(self) ? self : getContainingView(self);
|
|
785
|
-
const viewType = pluginManager.getViewType(view.type);
|
|
786
|
-
const candidates = displayCandidates(pluginManager, conf);
|
|
787
|
-
const viewDisplayTypes = viewType.displayTypes.map(d => d.name);
|
|
788
|
-
// Aliases resolve first, so a caller or a config naming a pre-consolidation
|
|
789
|
-
// display type (`LinearPileupDisplay`) reaches the type that replaced it; a
|
|
790
|
-
// name no display answers to stays as written, for the refusal to quote.
|
|
791
|
-
const canonical = (name) => pluginManager.resolveDisplayTypeRecord(name)?.name ?? name;
|
|
792
794
|
const requested = displayInitialSnapshot.type;
|
|
793
|
-
const
|
|
794
|
-
const picked = pickDisplayForView({
|
|
795
|
-
candidates,
|
|
796
|
-
declaredDisplays: declaredDisplays.map(d => ({
|
|
797
|
-
...d,
|
|
798
|
-
type: canonical(d.type),
|
|
799
|
-
})),
|
|
800
|
-
requestedType: requested === undefined ? undefined : canonical(requested),
|
|
801
|
-
viewDisplayTypes,
|
|
802
|
-
preferredDisplayTypes: new Set(viewType.displayTypes
|
|
803
|
-
.filter(d => d.viewType === view.type)
|
|
804
|
-
.map(d => d.name)),
|
|
805
|
-
});
|
|
795
|
+
const { picked, candidates, viewDisplayTypes } = pickTrackDisplay(pluginManager, conf, view.type, requested);
|
|
806
796
|
if (!picked) {
|
|
807
797
|
const drawable = candidates.filter(name => viewDisplayTypes.includes(name));
|
|
808
798
|
throw new Error(requested === undefined
|
|
@@ -933,11 +923,9 @@ export function warmTrackDisplayGeneric(self, trackId, displayInitialSnapshot =
|
|
|
933
923
|
const { pluginManager } = getEnv(self);
|
|
934
924
|
try {
|
|
935
925
|
const { picked } = resolveTrackDisplayChoice(self, trackId, displayInitialSnapshot);
|
|
936
|
-
pluginManager
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
.catch(() => { });
|
|
940
|
-
warmTrackAdapter(self, trackId, assemblyName);
|
|
926
|
+
const display = pluginManager.resolveDisplayTypeRecord(picked.type);
|
|
927
|
+
display?.loadStateModel().catch(() => { });
|
|
928
|
+
warmTrackAdapter(self, trackId, display?.rpcMethods ?? [], assemblyName);
|
|
941
929
|
}
|
|
942
930
|
catch {
|
|
943
931
|
// the launch reports an unresolvable track
|
|
@@ -956,8 +944,8 @@ function adapterTypesIn(value, pluginManager, found = new Set()) {
|
|
|
956
944
|
return found;
|
|
957
945
|
}
|
|
958
946
|
/**
|
|
959
|
-
* Load the track's adapter code and
|
|
960
|
-
* assembly load the track's first request waits for.
|
|
947
|
+
* Load the track's adapter code and its display's RPC code, and read its index,
|
|
948
|
+
* in the worker, beside the assembly load the track's first request waits for.
|
|
961
949
|
*
|
|
962
950
|
* Sent under the id the track's own requests will use, so it reaches, and
|
|
963
951
|
* boots, the worker that serves them. That id hashes the adapter config as the
|
|
@@ -977,7 +965,7 @@ function declaresLanes(adapterConfig, pluginManager) {
|
|
|
977
965
|
.getAdapterType(type)
|
|
978
966
|
.adapterCapabilities.includes('headerLanes'));
|
|
979
967
|
}
|
|
980
|
-
function warmTrackAdapter(self, trackId, assemblyName) {
|
|
968
|
+
function warmTrackAdapter(self, trackId, rpcMethods, assemblyName) {
|
|
981
969
|
const { pluginManager } = getEnv(self);
|
|
982
970
|
const session = getSession(self);
|
|
983
971
|
const raw = session.getTrackById(trackId);
|
|
@@ -993,8 +981,9 @@ function warmTrackAdapter(self, trackId, assemblyName) {
|
|
|
993
981
|
const sessionId = getConfRpcSessionId(conf);
|
|
994
982
|
session.rpcManager
|
|
995
983
|
// eslint-disable-next-line no-restricted-syntax -- a code download the track's first request would make anyway: nothing to show, and nothing a user can move on from
|
|
996
|
-
.call(sessionId, '
|
|
984
|
+
.call(sessionId, 'CoreLoadTrackCode', {
|
|
997
985
|
adapterTypes: [...adapterTypesIn(adapterConfig, pluginManager)],
|
|
986
|
+
rpcMethods: [...rpcMethods],
|
|
998
987
|
})
|
|
999
988
|
.catch(() => { });
|
|
1000
989
|
// a source that declares its lanes reads an index per lane asked about,
|
|
@@ -1081,9 +1070,9 @@ export function allSessionTracks(session) {
|
|
|
1081
1070
|
* range query.
|
|
1082
1071
|
*/
|
|
1083
1072
|
export function annotationTrackIds(session, assemblyName) {
|
|
1084
|
-
return allSessionTracks(session).flatMap(track =>
|
|
1085
|
-
|
|
1086
|
-
? [
|
|
1073
|
+
return allSessionTracks(session).flatMap(track => track.type === 'FeatureTrack' &&
|
|
1074
|
+
getConfAssemblyNamesOrNone(track).some(name => isSameAssemblyName(name, assemblyName, session.assemblyManager))
|
|
1075
|
+
? [track.trackId]
|
|
1087
1076
|
: []);
|
|
1088
1077
|
}
|
|
1089
1078
|
function isNavigableView(view) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ViewSnapshotInput } from '../../PluginManager.ts';
|
|
2
2
|
import type TextSearchManager from '../../TextSearch/TextSearchManager.ts';
|
|
3
|
-
import type { AnyConfigurationModel } from '../../configuration/index.ts';
|
|
3
|
+
import type { AnyConfigurationModel, AnyTrackConfig, TrackConfigEntry } from '../../configuration/index.ts';
|
|
4
4
|
import type { BaseInternetAccountModel, BaseTrackConfig } from '../../pluggableElementTypes/models/index.ts';
|
|
5
5
|
import type { PluginDefinition } from '../../pluginDefinitions.ts';
|
|
6
6
|
import type RpcManager from '../../rpc/RpcManager.ts';
|
|
@@ -53,17 +53,17 @@ export type AnimationMode = 'system' | 'enabled' | 'disabled';
|
|
|
53
53
|
* in `./services.ts`, which is the file that stays cheap to name.
|
|
54
54
|
*/
|
|
55
55
|
export interface TrackCatalog {
|
|
56
|
-
tracks:
|
|
56
|
+
tracks: TrackConfigEntry[];
|
|
57
57
|
assemblies: AnyConfigurationModel[];
|
|
58
58
|
connectionInstances?: {
|
|
59
59
|
tracks: AnyConfigurationModel[];
|
|
60
60
|
}[];
|
|
61
|
-
getTrackById: (id: string) =>
|
|
61
|
+
getTrackById: (id: string) => AnyTrackConfig | undefined;
|
|
62
62
|
}
|
|
63
63
|
/** minimum interface that all session state models must implement */
|
|
64
64
|
export interface AbstractSessionModel extends AbstractViewContainer, RenderingServices, NotificationSink, DialogHost, TrackCatalog {
|
|
65
65
|
/** @deprecated prefer the per-id reactive `getTrackById(id)` */
|
|
66
|
-
getTracksById: () => Record<string,
|
|
66
|
+
getTracksById: () => Record<string, AnyTrackConfig>;
|
|
67
67
|
jbrowse: IAnyStateTreeNode;
|
|
68
68
|
drawerPosition?: DrawerPosition;
|
|
69
69
|
stickyViewHeaders?: boolean;
|
|
@@ -112,8 +112,8 @@ export interface AbstractSessionModel extends AbstractViewContainer, RenderingSe
|
|
|
112
112
|
config: BaseTrackConfig;
|
|
113
113
|
view?: TrackActionView;
|
|
114
114
|
}) => MenuItem[];
|
|
115
|
-
getTrackActions?: (arg:
|
|
116
|
-
getTrackListMenuItems?: (arg:
|
|
115
|
+
getTrackActions?: (arg: AnyTrackConfig, view?: TrackActionView) => MenuItem[];
|
|
116
|
+
getTrackListMenuItems?: (arg: AnyTrackConfig, view?: TrackActionView) => MenuItem[];
|
|
117
117
|
addAssembly?: (conf: Record<string, unknown>) => void;
|
|
118
118
|
addSessionAssembly?: (conf: Record<string, unknown>) => void;
|
|
119
119
|
sessionAssemblies?: AnyConfigurationModel[];
|
|
@@ -184,6 +184,14 @@ export interface SessionWithEditableTrackConfig extends AbstractSessionModel {
|
|
|
184
184
|
getEditableTrackConfig(trackId: string, schemaType: IAnyType): AnyConfigurationModel | undefined;
|
|
185
185
|
}
|
|
186
186
|
export declare function isSessionWithEditableTrackConfig(t: unknown): t is SessionWithEditableTrackConfig;
|
|
187
|
+
/**
|
|
188
|
+
* The working copy `trackId` resolves to (ADR-032), through its track type's
|
|
189
|
+
* own schema, which is the one a shown track's `configuration` reference
|
|
190
|
+
* holds, so both reach one node. Re-resolves on every read: an undo or a
|
|
191
|
+
* session restore replaces the working copy. Undefined when the session keeps
|
|
192
|
+
* no working copies or nothing answers the id.
|
|
193
|
+
*/
|
|
194
|
+
export declare function getEditableTrackConfigById(session: unknown, trackId: string): AnyConfigurationModel | undefined;
|
|
187
195
|
/**
|
|
188
196
|
* abstract interface for a session that keeps a list of views, so that `addView`
|
|
189
197
|
* appends and the view a launch came out of survives it.
|
|
@@ -304,7 +312,7 @@ export interface SessionWithAddAssembly extends AbstractSessionModel {
|
|
|
304
312
|
export declare function isSessionWithAddAssembly(t: unknown): t is SessionWithAddAssembly;
|
|
305
313
|
/** abstract interface for a session that allows deleting track configs */
|
|
306
314
|
export interface SessionWithDeleteTrackConf extends AbstractSessionModel {
|
|
307
|
-
deleteTrackConf(configuration:
|
|
315
|
+
deleteTrackConf(configuration: AnyTrackConfig): void;
|
|
308
316
|
}
|
|
309
317
|
export declare function isSessionWithDeleteTrackConf(t: unknown): t is SessionWithDeleteTrackConf;
|
|
310
318
|
/** abstract interface for a session allows adding tracks */
|
package/esm/util/types/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isStateTreeNode } from '@jbrowse/mobx-state-tree';
|
|
1
|
+
import { getEnv, isStateTreeNode } from '@jbrowse/mobx-state-tree';
|
|
2
2
|
import { isSessionServices } from './services.js';
|
|
3
3
|
export { isSessionServices } from './services.js';
|
|
4
4
|
// The plain-data half of this file's former self, kept re-exported here so
|
|
@@ -24,6 +24,23 @@ export function isSessionWithBaseTrackConfig(t) {
|
|
|
24
24
|
export function isSessionWithEditableTrackConfig(t) {
|
|
25
25
|
return isSessionModel(t) && 'getEditableTrackConfig' in t;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* The working copy `trackId` resolves to (ADR-032), through its track type's
|
|
29
|
+
* own schema, which is the one a shown track's `configuration` reference
|
|
30
|
+
* holds, so both reach one node. Re-resolves on every read: an undo or a
|
|
31
|
+
* session restore replaces the working copy. Undefined when the session keeps
|
|
32
|
+
* no working copies or nothing answers the id.
|
|
33
|
+
*/
|
|
34
|
+
export function getEditableTrackConfigById(session, trackId) {
|
|
35
|
+
if (!isSessionWithEditableTrackConfig(session)) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const type = session.getTrackById(trackId)?.type;
|
|
39
|
+
const schema = typeof type === 'string'
|
|
40
|
+
? getEnv(session).pluginManager.getTrackType(type).configSchema
|
|
41
|
+
: undefined;
|
|
42
|
+
return schema ? session.getEditableTrackConfig(trackId, schema) : undefined;
|
|
43
|
+
}
|
|
27
44
|
export function isSessionWithMultipleViews(t) {
|
|
28
45
|
return isSessionModel(t) && isStateTreeNode(t.views);
|
|
29
46
|
}
|
|
@@ -106,7 +123,9 @@ export function isSessionModelWithConnections(thing) {
|
|
|
106
123
|
return isSessionModel(thing) && 'makeConnection' in thing;
|
|
107
124
|
}
|
|
108
125
|
export function isSessionModelWithConnectionEditing(thing) {
|
|
109
|
-
return isSessionModel(thing) &&
|
|
126
|
+
return (isSessionModel(thing) &&
|
|
127
|
+
'addConnectionConf' in thing &&
|
|
128
|
+
!('disableAddTracks' in thing && thing.disableAddTracks));
|
|
110
129
|
}
|
|
111
130
|
export function isSessionWithSessionPlugins(thing) {
|
|
112
131
|
return isSessionModel(thing) && 'sessionPlugins' in thing;
|
|
@@ -25,7 +25,9 @@ export declare function displaySetterName(key: string): string;
|
|
|
25
25
|
export declare function setterOnlyMessage(label: string, keys: string[]): string;
|
|
26
26
|
/**
|
|
27
27
|
* Name the keys a view was handed and could not place, from the partition
|
|
28
|
-
* `withLaunchInput` runs on the snapshot.
|
|
28
|
+
* `withLaunchInput` runs on the snapshot. The console alone, like
|
|
29
|
+
* `reportLegacyInit`: the view opened without them, and the keys are usually a
|
|
30
|
+
* v4 session's retired state that a visitor holding the link cannot act on.
|
|
29
31
|
*/
|
|
30
32
|
export declare function reportUnknownKeys(self: IStateTreeNode, keys: string[]): void;
|
|
31
33
|
/**
|
|
@@ -28,19 +28,13 @@ export function setterOnlyMessage(label, keys) {
|
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Name the keys a view was handed and could not place, from the partition
|
|
31
|
-
* `withLaunchInput` runs on the snapshot.
|
|
31
|
+
* `withLaunchInput` runs on the snapshot. The console alone, like
|
|
32
|
+
* `reportLegacyInit`: the view opened without them, and the keys are usually a
|
|
33
|
+
* v4 session's retired state that a visitor holding the link cannot act on.
|
|
32
34
|
*/
|
|
33
35
|
export function reportUnknownKeys(self, keys) {
|
|
34
|
-
if (
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
const message = unknownKeysMessage(viewLabel(self), keys);
|
|
38
|
-
console.warn(message);
|
|
39
|
-
try {
|
|
40
|
-
getNotificationSink(self).notify(message, 'warning');
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
// a view built outside a session has nowhere to put it
|
|
36
|
+
if (keys.length) {
|
|
37
|
+
console.warn(unknownKeysMessage(viewLabel(self), keys));
|
|
44
38
|
}
|
|
45
39
|
}
|
|
46
40
|
/**
|
package/esm/util/usePanZoom.js
CHANGED
|
@@ -113,6 +113,9 @@ export function usePanZoom(ref, view, { touchAction = 'none', } = {}) {
|
|
|
113
113
|
};
|
|
114
114
|
}, [ref, touchAction]);
|
|
115
115
|
function endDrag(event) {
|
|
116
|
+
if (dragRef.current?.pointerId !== event.pointerId) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
116
119
|
dragRef.current = undefined;
|
|
117
120
|
// release only what the move handler took — a press that stayed under the
|
|
118
121
|
// threshold never captured anything
|
|
@@ -131,7 +134,7 @@ export function usePanZoom(ref, view, { touchAction = 'none', } = {}) {
|
|
|
131
134
|
// lands on an icon inside the control. Shift is left alone too: it is
|
|
132
135
|
// what a range-select of the host's own would want, and it is what
|
|
133
136
|
// JBrowse's own view uses it for.
|
|
134
|
-
if (event.button !== 0 || event.shiftKey) {
|
|
137
|
+
if (!event.isPrimary || event.button !== 0 || event.shiftKey) {
|
|
135
138
|
return;
|
|
136
139
|
}
|
|
137
140
|
if (event.target instanceof Element &&
|
|
@@ -139,11 +142,15 @@ export function usePanZoom(ref, view, { touchAction = 'none', } = {}) {
|
|
|
139
142
|
return;
|
|
140
143
|
}
|
|
141
144
|
// Note what this does *not* do: capture the pointer. See onPointerMove.
|
|
142
|
-
dragRef.current = {
|
|
145
|
+
dragRef.current = {
|
|
146
|
+
x: event.clientX,
|
|
147
|
+
panning: false,
|
|
148
|
+
pointerId: event.pointerId,
|
|
149
|
+
};
|
|
143
150
|
},
|
|
144
151
|
onPointerMove(event) {
|
|
145
152
|
const drag = dragRef.current;
|
|
146
|
-
if (!drag) {
|
|
153
|
+
if (!drag || drag.pointerId !== event.pointerId) {
|
|
147
154
|
return;
|
|
148
155
|
}
|
|
149
156
|
if (!drag.panning) {
|
package/esm/util/useRafCommit.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { RowResizeTarget } from './applyRowResizeWheel.ts';
|
|
2
|
+
export type { RowResizeTarget } from './applyRowResizeWheel.ts';
|
|
2
3
|
/**
|
|
3
4
|
* Wheel gestures over a **row-stack** panel — the multi-sample variant displays
|
|
4
5
|
* and MAF: `shift`+wheel resizes the rows, keeping the row under the cursor
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isAlive } from '@jbrowse/mobx-state-tree';
|
|
2
|
+
import { isAbortException } from './aborting.js';
|
|
2
3
|
import { notifyFeatureDetailsMiss } from './openFeatureWidget.js';
|
|
3
4
|
import { getNotificationSink } from './sessionServices.js';
|
|
4
5
|
/**
|
|
@@ -48,6 +49,9 @@ export async function withFeatureDetails(self, fetch, onFeat, onMiss = () => {
|
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
catch (e) {
|
|
52
|
+
if (isAbortException(e)) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
51
55
|
// Logged whatever happened, and only toasted while the display is still
|
|
52
56
|
// there: `getSession` throws on a detached node, and a message naming a
|
|
53
57
|
// track the user has already closed is noise they cannot act on.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/core",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "JBrowse 2 core libraries used by plugins",
|
|
6
6
|
"keywords": [
|
|
@@ -130,6 +130,7 @@
|
|
|
130
130
|
"./ui/Snackbar": "./esm/ui/Snackbar.js",
|
|
131
131
|
"./ui/SnackbarModel": "./esm/ui/SnackbarModel.js",
|
|
132
132
|
"./ui/StackTraceButton": "./esm/ui/StackTraceButton.js",
|
|
133
|
+
"./ui/SubmitDialog": "./esm/ui/SubmitDialog.js",
|
|
133
134
|
"./ui/SvgColorLegend": "./esm/ui/SvgColorLegend.js",
|
|
134
135
|
"./ui/VerticalScrollbar": "./esm/ui/VerticalScrollbar.js",
|
|
135
136
|
"./ui/buildExtraTrackMenuItems": "./esm/ui/buildExtraTrackMenuItems.js",
|
|
@@ -161,11 +162,13 @@
|
|
|
161
162
|
"./util/adapterMetadata": "./esm/util/adapterMetadata.js",
|
|
162
163
|
"./util/addRelativeUris": "./esm/util/addRelativeUris.js",
|
|
163
164
|
"./util/aggregateFieldName": "./esm/util/aggregateFieldName.js",
|
|
165
|
+
"./util/alignedBytes": "./esm/util/alignedBytes.js",
|
|
164
166
|
"./util/analytics": "./esm/util/analytics.js",
|
|
165
167
|
"./util/animationAllowed": "./esm/util/animationAllowed.js",
|
|
166
168
|
"./util/assemblyConfigUtils": "./esm/util/assemblyConfigUtils.js",
|
|
167
169
|
"./util/bandHeight": "./esm/util/bandHeight.js",
|
|
168
170
|
"./util/bandLayout": "./esm/util/bandLayout.js",
|
|
171
|
+
"./util/baseDisplayConfig": "./esm/util/baseDisplayConfig.js",
|
|
169
172
|
"./util/bgzfWorkerPool": "./esm/util/bgzfWorkerPool.js",
|
|
170
173
|
"./util/blockTypes": "./esm/util/blockTypes.js",
|
|
171
174
|
"./util/cacheBudgets": "./esm/util/cacheBudgets.js",
|
|
@@ -216,6 +219,7 @@
|
|
|
216
219
|
"./util/io/RemoteFileWithRangeCache": "./esm/util/io/RemoteFileWithRangeCache.js",
|
|
217
220
|
"./util/isDataCurrent": "./esm/util/isDataCurrent.js",
|
|
218
221
|
"./util/isTextEntryFocused": "./esm/util/isTextEntryFocused.js",
|
|
222
|
+
"./util/isoformRank": "./esm/util/isoformRank.js",
|
|
219
223
|
"./util/jexl": "./esm/util/jexl.js",
|
|
220
224
|
"./util/jexlFilters": "./esm/util/jexlFilters.js",
|
|
221
225
|
"./util/jexlStrings": "./esm/util/jexlStrings.js",
|
|
@@ -297,12 +301,12 @@
|
|
|
297
301
|
"@emotion/utils": "^1.4.2",
|
|
298
302
|
"@floating-ui/react": "^0.27.20",
|
|
299
303
|
"@gmod/bgzf-filehandle": "^6.7.0",
|
|
300
|
-
"@gmod/range-cache-filehandle": "^1.
|
|
304
|
+
"@gmod/range-cache-filehandle": "^1.5.0",
|
|
301
305
|
"@gmod/shared-read-cache": "^1.7.2",
|
|
302
306
|
"@gmod/vcf": "^7.2.0",
|
|
303
307
|
"@jbrowse/jexl": "^6.0.0",
|
|
304
|
-
"@jbrowse/mobx-state-tree": "^6.
|
|
305
|
-
"@jbrowse/quick-lru": "^
|
|
308
|
+
"@jbrowse/mobx-state-tree": "^6.7.0",
|
|
309
|
+
"@jbrowse/quick-lru": "^8.0.0",
|
|
306
310
|
"@leeoniya/ufuzzy": "^1.0.19",
|
|
307
311
|
"@mui/icons-material": "^9.4.0",
|
|
308
312
|
"@mui/material": "^9.4.0",
|
|
@@ -311,14 +315,14 @@
|
|
|
311
315
|
"canvas-sequencer-ts": "^3.1.3",
|
|
312
316
|
"compare-versions": "^6.1.1",
|
|
313
317
|
"dompurify": "^3.4.16",
|
|
314
|
-
"generic-filehandle2": "^2.
|
|
318
|
+
"generic-filehandle2": "^2.5.0",
|
|
315
319
|
"idb": "^8.0.3",
|
|
316
|
-
"mobx": "^7.0.
|
|
320
|
+
"mobx": "^7.0.6",
|
|
317
321
|
"mobx-react": "^10.0.2",
|
|
318
322
|
"rxjs": "^7.8.2",
|
|
319
|
-
"source-map-js": "^1.2.
|
|
320
|
-
"@jbrowse/add-track-core": "^5.0.0-beta.
|
|
321
|
-
"@jbrowse/render-core": "^5.0.0-beta.
|
|
323
|
+
"source-map-js": "^1.2.2",
|
|
324
|
+
"@jbrowse/add-track-core": "^5.0.0-beta.13",
|
|
325
|
+
"@jbrowse/render-core": "^5.0.0-beta.13"
|
|
322
326
|
},
|
|
323
327
|
"peerDependencies": {
|
|
324
328
|
"react": ">=19.0.0",
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import RpcMethodType from '../../pluggableElementTypes/RpcMethodType.ts';
|
|
2
|
-
import type { RpcExecuteArgs } from '../RpcRegistry.ts';
|
|
3
|
-
/**
|
|
4
|
-
* Load these adapter types' code in the worker a track is about to use. An
|
|
5
|
-
* adapter's class arrives by dynamic import on the track's first request
|
|
6
|
-
* otherwise, and that download waits for the view to lay out and the assembly
|
|
7
|
-
* to load.
|
|
8
|
-
*/
|
|
9
|
-
export default class CoreLoadAdapterCode extends RpcMethodType<'CoreLoadAdapterCode'> {
|
|
10
|
-
name: 'CoreLoadAdapterCode';
|
|
11
|
-
execute({ adapterTypes }: RpcExecuteArgs<'CoreLoadAdapterCode'>): Promise<void>;
|
|
12
|
-
serializeArguments(args: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
13
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import RpcMethodType from '../../pluggableElementTypes/RpcMethodType.js';
|
|
2
|
-
/**
|
|
3
|
-
* Load these adapter types' code in the worker a track is about to use. An
|
|
4
|
-
* adapter's class arrives by dynamic import on the track's first request
|
|
5
|
-
* otherwise, and that download waits for the view to lay out and the assembly
|
|
6
|
-
* to load.
|
|
7
|
-
*/
|
|
8
|
-
export default class CoreLoadAdapterCode extends RpcMethodType {
|
|
9
|
-
name = 'CoreLoadAdapterCode';
|
|
10
|
-
async execute({ adapterTypes }) {
|
|
11
|
-
await Promise.all(adapterTypes.map(type => this.pluginManager.getAdapterType(type).getAdapterClass()));
|
|
12
|
-
}
|
|
13
|
-
async serializeArguments(args) {
|
|
14
|
-
return args;
|
|
15
|
-
}
|
|
16
|
-
}
|