@jbrowse/plugin-alignments 2.10.2 → 2.11.0
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/dist/AlignmentsFeatureDetail/LaunchBreakpointSplitViewPanel.d.ts +1 -1
- package/dist/AlignmentsFeatureDetail/LaunchBreakpointSplitViewPanel.js +3 -3
- package/dist/AlignmentsFeatureDetail/stateModelFactory.d.ts +1 -1
- package/dist/BamAdapter/BamAdapter.js +7 -8
- package/dist/CramAdapter/CramAdapter.js +14 -18
- package/dist/LinearPileupDisplay/SharedLinearPileupDisplayMixin.d.ts +18 -4
- package/dist/LinearPileupDisplay/SharedLinearPileupDisplayMixin.js +1 -1
- package/dist/LinearPileupDisplay/components/ColorByModifications.d.ts +1 -1
- package/dist/LinearPileupDisplay/components/SetFeatureHeight.d.ts +1 -1
- package/dist/LinearPileupDisplay/model.d.ts +26 -12
- package/dist/LinearPileupDisplay/model.js +11 -2
- package/dist/LinearSNPCoverageDisplay/models/model.d.ts +11 -2
- package/dist/PileupRenderer/components/PileupRendering.d.ts +6 -6
- package/dist/shared/BaseDisplayComponent.js +3 -1
- package/esm/AlignmentsFeatureDetail/LaunchBreakpointSplitViewPanel.d.ts +1 -1
- package/esm/AlignmentsFeatureDetail/LaunchBreakpointSplitViewPanel.js +3 -3
- package/esm/AlignmentsFeatureDetail/stateModelFactory.d.ts +1 -1
- package/esm/BamAdapter/BamAdapter.js +7 -8
- package/esm/CramAdapter/CramAdapter.js +15 -19
- package/esm/LinearPileupDisplay/SharedLinearPileupDisplayMixin.d.ts +18 -4
- package/esm/LinearPileupDisplay/SharedLinearPileupDisplayMixin.js +1 -1
- package/esm/LinearPileupDisplay/components/ColorByModifications.d.ts +1 -1
- package/esm/LinearPileupDisplay/components/SetFeatureHeight.d.ts +1 -1
- package/esm/LinearPileupDisplay/model.d.ts +26 -12
- package/esm/LinearPileupDisplay/model.js +11 -2
- package/esm/LinearSNPCoverageDisplay/models/model.d.ts +11 -2
- package/esm/PileupRenderer/components/PileupRendering.d.ts +6 -6
- package/esm/shared/BaseDisplayComponent.js +3 -1
- package/package.json +2 -2
|
@@ -65,7 +65,7 @@ function LaunchBreakpointSplitViewPanel({ model, feature, viewType, }) {
|
|
|
65
65
|
ret.push([res[i], res[i + 1]]);
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
|
-
return
|
|
68
|
+
return ret.length ? (react_1.default.createElement("div", null,
|
|
69
69
|
react_1.default.createElement(material_1.Typography, null, "Launch split views with breakend source and target"),
|
|
70
70
|
error ? react_1.default.createElement(ui_1.ErrorMessage, { error: error }) : null,
|
|
71
71
|
react_1.default.createElement("ul", null, ret.map((arg, index) => {
|
|
@@ -82,11 +82,11 @@ function LaunchBreakpointSplitViewPanel({ model, feature, viewType, }) {
|
|
|
82
82
|
f1.refName,
|
|
83
83
|
":",
|
|
84
84
|
(0, util_1.toLocale)(f1.strand === 1 ? f1.end : f1.start),
|
|
85
|
-
" ->",
|
|
86
85
|
' ',
|
|
86
|
+
"-> ",
|
|
87
87
|
f2.refName,
|
|
88
88
|
":",
|
|
89
89
|
(0, util_1.toLocale)(f2.strand === 1 ? f2.start : f2.end)))));
|
|
90
|
-
})))) : null
|
|
90
|
+
})))) : null;
|
|
91
91
|
}
|
|
92
92
|
exports.default = LaunchBreakpointSplitViewPanel;
|
|
@@ -37,8 +37,8 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
37
37
|
trackType: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<string>>;
|
|
38
38
|
maxDepth: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<number>>;
|
|
39
39
|
}>> & import("mobx-state-tree/dist/internal").NonEmptyObject & import("mobx-state-tree")._NotCustomized, {
|
|
40
|
-
type: "BaseFeatureWidget";
|
|
41
40
|
id: string;
|
|
41
|
+
type: "BaseFeatureWidget";
|
|
42
42
|
track: import("mobx-state-tree").ReferenceIdentifier | undefined;
|
|
43
43
|
view: import("mobx-state-tree").ReferenceIdentifier | undefined;
|
|
44
44
|
trackId: string | undefined;
|
|
@@ -65,14 +65,13 @@ class BamAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
|
|
|
65
65
|
const idToName = [];
|
|
66
66
|
const nameToId = {};
|
|
67
67
|
samHeader === null || samHeader === void 0 ? void 0 : samHeader.filter(l => l.tag === 'SQ').forEach((sqLine, refId) => {
|
|
68
|
-
sqLine.data.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
});
|
|
68
|
+
const SN = sqLine.data.find(item => item.tag === 'SN');
|
|
69
|
+
if (SN) {
|
|
70
|
+
// this is the ref name
|
|
71
|
+
const refName = SN.value;
|
|
72
|
+
nameToId[refName] = refId;
|
|
73
|
+
idToName[refId] = refName;
|
|
74
|
+
}
|
|
76
75
|
});
|
|
77
76
|
return { idToName, nameToId };
|
|
78
77
|
});
|
|
@@ -22,12 +22,6 @@ class CramAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
|
|
|
22
22
|
async configurePre() {
|
|
23
23
|
const cramLocation = this.getConf('cramLocation');
|
|
24
24
|
const craiLocation = this.getConf('craiLocation');
|
|
25
|
-
if (!cramLocation) {
|
|
26
|
-
throw new Error('missing cramLocation argument');
|
|
27
|
-
}
|
|
28
|
-
if (!craiLocation) {
|
|
29
|
-
throw new Error('missing craiLocation argument');
|
|
30
|
-
}
|
|
31
25
|
const pm = this.pluginManager;
|
|
32
26
|
const cram = new cram_1.IndexedCramFile({
|
|
33
27
|
cramFilehandle: (0, io_1.openLocation)(cramLocation, pm),
|
|
@@ -40,6 +34,9 @@ class CramAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
|
|
|
40
34
|
throw new Error('Error getting subadapter');
|
|
41
35
|
}
|
|
42
36
|
const seqConf = this.getConf('sequenceAdapter');
|
|
37
|
+
if (!seqConf) {
|
|
38
|
+
throw new Error('no sequenceAdapter supplied to CramAdapter config');
|
|
39
|
+
}
|
|
43
40
|
const subadapter = await this.getSubAdapter(seqConf);
|
|
44
41
|
return {
|
|
45
42
|
cram,
|
|
@@ -86,8 +83,9 @@ class CramAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
|
|
|
86
83
|
return chunkSeq.slice(trimStart, trimStart + trimLength);
|
|
87
84
|
})
|
|
88
85
|
.join('');
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
const qlen = end - start;
|
|
87
|
+
if (sequence.length !== qlen) {
|
|
88
|
+
throw new Error(`fetching ${refName}:${(0, util_1.toLocale)(start - 1)}-${(0, util_1.toLocale)(end)} returned ${(0, util_1.toLocale)(sequence.length)} bases, should have returned ${(0, util_1.toLocale)(qlen)}`);
|
|
91
89
|
}
|
|
92
90
|
return sequence;
|
|
93
91
|
}
|
|
@@ -104,14 +102,12 @@ class CramAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
|
|
|
104
102
|
samHeader
|
|
105
103
|
.filter(l => l.tag === 'SQ')
|
|
106
104
|
.forEach((sqLine, refId) => {
|
|
107
|
-
sqLine.data.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
});
|
|
105
|
+
const SN = sqLine.data.find(item => item.tag === 'SN');
|
|
106
|
+
if (SN) {
|
|
107
|
+
const refName = SN.value;
|
|
108
|
+
nameToId[refName] = refId;
|
|
109
|
+
idToName[refId] = refName;
|
|
110
|
+
}
|
|
115
111
|
});
|
|
116
112
|
const readGroups = samHeader
|
|
117
113
|
.filter(l => l.tag === 'RG')
|
|
@@ -161,7 +157,7 @@ class CramAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
|
|
|
161
157
|
const { signal, filterBy, statusCallback = () => { } } = opts || {};
|
|
162
158
|
const { refName, start, end, originalRefName } = region;
|
|
163
159
|
return (0, rxjs_1.ObservableCreate)(async (observer) => {
|
|
164
|
-
const { cram } = await this.setup(opts);
|
|
160
|
+
const { cram, samHeader } = await this.setup(opts);
|
|
165
161
|
const refId = this.refNameToId(refName);
|
|
166
162
|
if (refId === undefined) {
|
|
167
163
|
console.warn('Unknown refName', refName);
|
|
@@ -183,7 +179,7 @@ class CramAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
|
|
|
183
179
|
}
|
|
184
180
|
if (tagFilter) {
|
|
185
181
|
const v = tagFilter.tag === 'RG'
|
|
186
|
-
? (_a =
|
|
182
|
+
? (_a = samHeader.readGroups) === null || _a === void 0 ? void 0 : _a[record.readGroupId]
|
|
187
183
|
: record.tags[tagFilter.tag];
|
|
188
184
|
if (!(tagFilter.value === '*'
|
|
189
185
|
? v !== undefined
|
|
@@ -46,8 +46,15 @@ export declare function SharedLinearPileupDisplayMixin(configSchema: AnyConfigur
|
|
|
46
46
|
message: string | undefined;
|
|
47
47
|
maxHeightReached: boolean;
|
|
48
48
|
ReactComponent: ({ model, }: {
|
|
49
|
-
model:
|
|
50
|
-
|
|
49
|
+
model: {
|
|
50
|
+
error?: unknown;
|
|
51
|
+
reload: () => void;
|
|
52
|
+
message: import("react").ReactNode;
|
|
53
|
+
filled?: boolean | undefined;
|
|
54
|
+
status?: string | undefined;
|
|
55
|
+
reactElement?: import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | undefined;
|
|
56
|
+
};
|
|
57
|
+
}) => import("react").JSX.Element | undefined;
|
|
51
58
|
renderProps: any;
|
|
52
59
|
} & {
|
|
53
60
|
doReload(): void;
|
|
@@ -453,8 +460,15 @@ export declare function SharedLinearPileupDisplayMixin(configSchema: AnyConfigur
|
|
|
453
460
|
message: string | undefined;
|
|
454
461
|
maxHeightReached: boolean;
|
|
455
462
|
ReactComponent: ({ model, }: {
|
|
456
|
-
model:
|
|
457
|
-
|
|
463
|
+
model: {
|
|
464
|
+
error?: unknown;
|
|
465
|
+
reload: () => void;
|
|
466
|
+
message: import("react").ReactNode;
|
|
467
|
+
filled?: boolean | undefined;
|
|
468
|
+
status?: string | undefined;
|
|
469
|
+
reactElement?: import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | undefined;
|
|
470
|
+
};
|
|
471
|
+
}) => import("react").JSX.Element | undefined;
|
|
458
472
|
renderProps: any;
|
|
459
473
|
} & {
|
|
460
474
|
doReload(): void;
|
|
@@ -4,7 +4,7 @@ declare const SetFeatureHeightDialog: (props: {
|
|
|
4
4
|
setFeatureHeight: (arg?: number) => void;
|
|
5
5
|
setNoSpacing: (arg?: boolean) => void;
|
|
6
6
|
featureHeightSetting: number;
|
|
7
|
-
noSpacing?: boolean
|
|
7
|
+
noSpacing?: boolean;
|
|
8
8
|
};
|
|
9
9
|
handleClose: () => void;
|
|
10
10
|
}) => React.JSX.Element;
|
|
@@ -5,7 +5,7 @@ import { AnyConfigurationSchemaType } from '@jbrowse/core/configuration';
|
|
|
5
5
|
* #stateModel LinearPileupDisplay
|
|
6
6
|
* #category display
|
|
7
7
|
* extends
|
|
8
|
-
|
|
8
|
+
* - [SharedLinearPileupDisplayMixin](../sharedlinearpileupdisplaymixin)
|
|
9
9
|
*/
|
|
10
10
|
declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): import("mobx-state-tree").IModelType<{
|
|
11
11
|
id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
|
|
@@ -43,8 +43,15 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
43
43
|
message: string | undefined;
|
|
44
44
|
maxHeightReached: boolean;
|
|
45
45
|
ReactComponent: ({ model, }: {
|
|
46
|
-
model:
|
|
47
|
-
|
|
46
|
+
model: {
|
|
47
|
+
error?: unknown;
|
|
48
|
+
reload: () => void;
|
|
49
|
+
message: import("react").ReactNode;
|
|
50
|
+
filled?: boolean | undefined;
|
|
51
|
+
status?: string | undefined;
|
|
52
|
+
reactElement?: import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | undefined;
|
|
53
|
+
};
|
|
54
|
+
}) => import("react").JSX.Element | undefined;
|
|
48
55
|
renderProps: any;
|
|
49
56
|
} & {
|
|
50
57
|
doReload(): void;
|
|
@@ -89,16 +96,15 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
89
96
|
} & {
|
|
90
97
|
configuration: AnyConfigurationSchemaType;
|
|
91
98
|
featureHeight: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<number>>;
|
|
92
|
-
/**
|
|
93
|
-
* #action
|
|
94
|
-
*/
|
|
95
99
|
noSpacing: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<boolean>>;
|
|
96
100
|
fadeLikelihood: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<boolean>>;
|
|
97
101
|
trackMaxHeight: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<number>>;
|
|
98
102
|
colorBy: import("mobx-state-tree").IMaybe<import("mobx-state-tree").IModelType<{
|
|
99
103
|
type: import("mobx-state-tree").ISimpleType<string>;
|
|
100
104
|
tag: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<string>>;
|
|
101
|
-
extra: import("mobx-state-tree").IType<any, any, any>;
|
|
105
|
+
extra: import("mobx-state-tree").IType<any, any, any>; /**
|
|
106
|
+
* #action
|
|
107
|
+
*/
|
|
102
108
|
}, {}, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
|
|
103
109
|
filterBy: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").IModelType<{
|
|
104
110
|
flagInclude: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<number>, [undefined]>;
|
|
@@ -178,9 +184,7 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
178
184
|
readonly viewMenuActions: import("@jbrowse/core/ui").MenuItem[];
|
|
179
185
|
regionCannotBeRendered(): null;
|
|
180
186
|
} & {
|
|
181
|
-
setMessage(arg?: string | undefined): void;
|
|
182
|
-
* #getter
|
|
183
|
-
*/
|
|
187
|
+
setMessage(arg?: string | undefined): void;
|
|
184
188
|
setError(error?: unknown): void;
|
|
185
189
|
setRpcDriverName(rpcDriverName: string): void;
|
|
186
190
|
reload(): void;
|
|
@@ -230,7 +234,9 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
230
234
|
readonly DisplayMessageComponent: import("react").FC<any> | undefined;
|
|
231
235
|
} & {
|
|
232
236
|
readonly features: import("@jbrowse/core/util/compositeMap").default<string, import("@jbrowse/core/util").Feature>;
|
|
233
|
-
readonly featureUnderMouse: import("@jbrowse/core/util").Feature | undefined;
|
|
237
|
+
readonly featureUnderMouse: import("@jbrowse/core/util").Feature | undefined; /**
|
|
238
|
+
* #getter
|
|
239
|
+
*/
|
|
234
240
|
getFeatureOverlapping(blockKey: string, x: number, y: number): string | undefined;
|
|
235
241
|
getFeatureByID(blockKey: string, id: string): [number, number, number, number] | undefined;
|
|
236
242
|
searchFeatureByID(id: string): [number, number, number, number] | undefined;
|
|
@@ -239,7 +245,9 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
239
245
|
deleteBlock(key: string): void;
|
|
240
246
|
selectFeature(feature: import("@jbrowse/core/util").Feature): void;
|
|
241
247
|
clearFeatureSelection(): void;
|
|
242
|
-
setFeatureIdUnderMouse(feature?: string | undefined): void;
|
|
248
|
+
setFeatureIdUnderMouse(feature?: string | undefined): void; /**
|
|
249
|
+
* #method
|
|
250
|
+
*/
|
|
243
251
|
setContextMenuFeature(feature?: import("@jbrowse/core/util").Feature | undefined): void;
|
|
244
252
|
} & {
|
|
245
253
|
reload(): Promise<void>;
|
|
@@ -368,6 +376,12 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
368
376
|
* #action
|
|
369
377
|
*/
|
|
370
378
|
setSortedBy(type: string, tag?: string): void;
|
|
379
|
+
/**
|
|
380
|
+
* #action
|
|
381
|
+
* overrides base from SharedLinearPileupDisplay to make sortReady false
|
|
382
|
+
* since changing feature height destroys the sort-induced layout
|
|
383
|
+
*/
|
|
384
|
+
setFeatureHeight(n?: number): void;
|
|
371
385
|
} & {
|
|
372
386
|
/**
|
|
373
387
|
* #action
|
|
@@ -39,14 +39,14 @@ const Sort_1 = __importDefault(require("@mui/icons-material/Sort"));
|
|
|
39
39
|
// locals
|
|
40
40
|
const SharedLinearPileupDisplayMixin_1 = require("./SharedLinearPileupDisplayMixin");
|
|
41
41
|
const mobx_1 = require("mobx");
|
|
42
|
-
//
|
|
42
|
+
// lzies
|
|
43
43
|
const SortByTagDialog = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./components/SortByTag'))));
|
|
44
44
|
const ModificationsDialog = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./components/ColorByModifications'))));
|
|
45
45
|
/**
|
|
46
46
|
* #stateModel LinearPileupDisplay
|
|
47
47
|
* #category display
|
|
48
48
|
* extends
|
|
49
|
-
|
|
49
|
+
* - [SharedLinearPileupDisplayMixin](../sharedlinearpileupdisplaymixin)
|
|
50
50
|
*/
|
|
51
51
|
function stateModelFactory(configSchema) {
|
|
52
52
|
return mobx_state_tree_1.types
|
|
@@ -153,6 +153,15 @@ function stateModelFactory(configSchema) {
|
|
|
153
153
|
tag,
|
|
154
154
|
};
|
|
155
155
|
},
|
|
156
|
+
/**
|
|
157
|
+
* #action
|
|
158
|
+
* overrides base from SharedLinearPileupDisplay to make sortReady false
|
|
159
|
+
* since changing feature height destroys the sort-induced layout
|
|
160
|
+
*/
|
|
161
|
+
setFeatureHeight(n) {
|
|
162
|
+
self.sortReady = false;
|
|
163
|
+
self.featureHeight = n;
|
|
164
|
+
},
|
|
156
165
|
}))
|
|
157
166
|
.actions(self => {
|
|
158
167
|
// resets the sort object and refresh whole display on reload
|
|
@@ -42,8 +42,17 @@ declare function stateModelFactory(pluginManager: PluginManager, configSchema: A
|
|
|
42
42
|
message: string | undefined;
|
|
43
43
|
maxHeightReached: boolean;
|
|
44
44
|
ReactComponent: ({ model, }: {
|
|
45
|
-
model:
|
|
46
|
-
|
|
45
|
+
model: {
|
|
46
|
+
error?: unknown;
|
|
47
|
+
reload: () => void;
|
|
48
|
+
message: import("react").ReactNode; /**
|
|
49
|
+
* #property
|
|
50
|
+
*/
|
|
51
|
+
filled?: boolean | undefined;
|
|
52
|
+
status?: string | undefined;
|
|
53
|
+
reactElement?: import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | undefined;
|
|
54
|
+
};
|
|
55
|
+
}) => import("react").JSX.Element | undefined;
|
|
47
56
|
renderProps: any;
|
|
48
57
|
} & {
|
|
49
58
|
doReload(): void;
|
|
@@ -12,16 +12,16 @@ declare const PileupRendering: (props: {
|
|
|
12
12
|
type: string;
|
|
13
13
|
pos: number;
|
|
14
14
|
refName: string;
|
|
15
|
-
}
|
|
15
|
+
};
|
|
16
16
|
colorBy?: {
|
|
17
17
|
type: string;
|
|
18
|
-
tag?: string
|
|
19
|
-
}
|
|
18
|
+
tag?: string;
|
|
19
|
+
};
|
|
20
20
|
filterBy?: {
|
|
21
21
|
tagFilter?: {
|
|
22
22
|
tag: string;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
onMouseMove?: (
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
onMouseMove?: (event: React.MouseEvent, featureId?: string) => void;
|
|
26
26
|
}) => React.JSX.Element;
|
|
27
27
|
export default PileupRendering;
|
|
@@ -9,6 +9,7 @@ const plugin_linear_genome_view_1 = require("@jbrowse/plugin-linear-genome-view"
|
|
|
9
9
|
const mui_1 = require("tss-react/mui");
|
|
10
10
|
const mobx_react_1 = require("mobx-react");
|
|
11
11
|
const util_1 = require("@jbrowse/core/util");
|
|
12
|
+
const material_1 = require("@mui/material");
|
|
12
13
|
const useStyles = (0, mui_1.makeStyles)()(theme => ({
|
|
13
14
|
loading: {
|
|
14
15
|
backgroundColor: theme.palette.background.default,
|
|
@@ -24,7 +25,8 @@ const useStyles = (0, mui_1.makeStyles)()(theme => ({
|
|
|
24
25
|
}));
|
|
25
26
|
const BaseDisplayComponent = (0, mobx_react_1.observer)(function ({ model, children, }) {
|
|
26
27
|
const { error, regionTooLarge } = model;
|
|
27
|
-
return error ? (react_1.default.createElement(plugin_linear_genome_view_1.BlockMsg, { message: `${error}`, severity: "error",
|
|
28
|
+
return error ? (react_1.default.createElement(plugin_linear_genome_view_1.BlockMsg, { message: `${error}`, severity: "error", action: react_1.default.createElement(material_1.Tooltip, { title: "Reload" },
|
|
29
|
+
react_1.default.createElement(material_1.Button, { "data-testid": "reload_button", onClick: () => model.reload() }, "Reload")) })) : regionTooLarge ? (model.regionCannotBeRendered()) : (react_1.default.createElement(DataDisplay, { model: model }, children));
|
|
28
30
|
});
|
|
29
31
|
const DataDisplay = (0, mobx_react_1.observer)(function ({ model, children, }) {
|
|
30
32
|
const { drawn, loading } = model;
|
|
@@ -40,7 +40,7 @@ export default function LaunchBreakpointSplitViewPanel({ model, feature, viewTyp
|
|
|
40
40
|
ret.push([res[i], res[i + 1]]);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
return
|
|
43
|
+
return ret.length ? (React.createElement("div", null,
|
|
44
44
|
React.createElement(Typography, null, "Launch split views with breakend source and target"),
|
|
45
45
|
error ? React.createElement(ErrorMessage, { error: error }) : null,
|
|
46
46
|
React.createElement("ul", null, ret.map((arg, index) => {
|
|
@@ -57,10 +57,10 @@ export default function LaunchBreakpointSplitViewPanel({ model, feature, viewTyp
|
|
|
57
57
|
f1.refName,
|
|
58
58
|
":",
|
|
59
59
|
toLocale(f1.strand === 1 ? f1.end : f1.start),
|
|
60
|
-
" ->",
|
|
61
60
|
' ',
|
|
61
|
+
"-> ",
|
|
62
62
|
f2.refName,
|
|
63
63
|
":",
|
|
64
64
|
toLocale(f2.strand === 1 ? f2.start : f2.end)))));
|
|
65
|
-
})))) : null
|
|
65
|
+
})))) : null;
|
|
66
66
|
}
|
|
@@ -37,8 +37,8 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
37
37
|
trackType: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<string>>;
|
|
38
38
|
maxDepth: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<number>>;
|
|
39
39
|
}>> & import("mobx-state-tree/dist/internal").NonEmptyObject & import("mobx-state-tree")._NotCustomized, {
|
|
40
|
-
type: "BaseFeatureWidget";
|
|
41
40
|
id: string;
|
|
41
|
+
type: "BaseFeatureWidget";
|
|
42
42
|
track: import("mobx-state-tree").ReferenceIdentifier | undefined;
|
|
43
43
|
view: import("mobx-state-tree").ReferenceIdentifier | undefined;
|
|
44
44
|
trackId: string | undefined;
|
|
@@ -60,14 +60,13 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
|
|
|
60
60
|
const idToName = [];
|
|
61
61
|
const nameToId = {};
|
|
62
62
|
samHeader === null || samHeader === void 0 ? void 0 : samHeader.filter(l => l.tag === 'SQ').forEach((sqLine, refId) => {
|
|
63
|
-
sqLine.data.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
});
|
|
63
|
+
const SN = sqLine.data.find(item => item.tag === 'SN');
|
|
64
|
+
if (SN) {
|
|
65
|
+
// this is the ref name
|
|
66
|
+
const refName = SN.value;
|
|
67
|
+
nameToId[refName] = refId;
|
|
68
|
+
idToName[refId] = refName;
|
|
69
|
+
}
|
|
71
70
|
});
|
|
72
71
|
return { idToName, nameToId };
|
|
73
72
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CraiIndex, IndexedCramFile } from '@gmod/cram';
|
|
2
2
|
import { BaseFeatureDataAdapter, } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
3
|
-
import { checkAbortSignal, updateStatus, } from '@jbrowse/core/util';
|
|
3
|
+
import { checkAbortSignal, updateStatus, toLocale, } from '@jbrowse/core/util';
|
|
4
4
|
import { openLocation } from '@jbrowse/core/util/io';
|
|
5
5
|
import { ObservableCreate } from '@jbrowse/core/util/rxjs';
|
|
6
6
|
import { toArray } from 'rxjs/operators';
|
|
@@ -17,12 +17,6 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
|
|
|
17
17
|
async configurePre() {
|
|
18
18
|
const cramLocation = this.getConf('cramLocation');
|
|
19
19
|
const craiLocation = this.getConf('craiLocation');
|
|
20
|
-
if (!cramLocation) {
|
|
21
|
-
throw new Error('missing cramLocation argument');
|
|
22
|
-
}
|
|
23
|
-
if (!craiLocation) {
|
|
24
|
-
throw new Error('missing craiLocation argument');
|
|
25
|
-
}
|
|
26
20
|
const pm = this.pluginManager;
|
|
27
21
|
const cram = new IndexedCramFile({
|
|
28
22
|
cramFilehandle: openLocation(cramLocation, pm),
|
|
@@ -35,6 +29,9 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
|
|
|
35
29
|
throw new Error('Error getting subadapter');
|
|
36
30
|
}
|
|
37
31
|
const seqConf = this.getConf('sequenceAdapter');
|
|
32
|
+
if (!seqConf) {
|
|
33
|
+
throw new Error('no sequenceAdapter supplied to CramAdapter config');
|
|
34
|
+
}
|
|
38
35
|
const subadapter = await this.getSubAdapter(seqConf);
|
|
39
36
|
return {
|
|
40
37
|
cram,
|
|
@@ -81,8 +78,9 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
|
|
|
81
78
|
return chunkSeq.slice(trimStart, trimStart + trimLength);
|
|
82
79
|
})
|
|
83
80
|
.join('');
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
const qlen = end - start;
|
|
82
|
+
if (sequence.length !== qlen) {
|
|
83
|
+
throw new Error(`fetching ${refName}:${toLocale(start - 1)}-${toLocale(end)} returned ${toLocale(sequence.length)} bases, should have returned ${toLocale(qlen)}`);
|
|
86
84
|
}
|
|
87
85
|
return sequence;
|
|
88
86
|
}
|
|
@@ -99,14 +97,12 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
|
|
|
99
97
|
samHeader
|
|
100
98
|
.filter(l => l.tag === 'SQ')
|
|
101
99
|
.forEach((sqLine, refId) => {
|
|
102
|
-
sqLine.data.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
});
|
|
100
|
+
const SN = sqLine.data.find(item => item.tag === 'SN');
|
|
101
|
+
if (SN) {
|
|
102
|
+
const refName = SN.value;
|
|
103
|
+
nameToId[refName] = refId;
|
|
104
|
+
idToName[refId] = refName;
|
|
105
|
+
}
|
|
110
106
|
});
|
|
111
107
|
const readGroups = samHeader
|
|
112
108
|
.filter(l => l.tag === 'RG')
|
|
@@ -156,7 +152,7 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
|
|
|
156
152
|
const { signal, filterBy, statusCallback = () => { } } = opts || {};
|
|
157
153
|
const { refName, start, end, originalRefName } = region;
|
|
158
154
|
return ObservableCreate(async (observer) => {
|
|
159
|
-
const { cram } = await this.setup(opts);
|
|
155
|
+
const { cram, samHeader } = await this.setup(opts);
|
|
160
156
|
const refId = this.refNameToId(refName);
|
|
161
157
|
if (refId === undefined) {
|
|
162
158
|
console.warn('Unknown refName', refName);
|
|
@@ -178,7 +174,7 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
|
|
|
178
174
|
}
|
|
179
175
|
if (tagFilter) {
|
|
180
176
|
const v = tagFilter.tag === 'RG'
|
|
181
|
-
? (_a =
|
|
177
|
+
? (_a = samHeader.readGroups) === null || _a === void 0 ? void 0 : _a[record.readGroupId]
|
|
182
178
|
: record.tags[tagFilter.tag];
|
|
183
179
|
if (!(tagFilter.value === '*'
|
|
184
180
|
? v !== undefined
|
|
@@ -46,8 +46,15 @@ export declare function SharedLinearPileupDisplayMixin(configSchema: AnyConfigur
|
|
|
46
46
|
message: string | undefined;
|
|
47
47
|
maxHeightReached: boolean;
|
|
48
48
|
ReactComponent: ({ model, }: {
|
|
49
|
-
model:
|
|
50
|
-
|
|
49
|
+
model: {
|
|
50
|
+
error?: unknown;
|
|
51
|
+
reload: () => void;
|
|
52
|
+
message: import("react").ReactNode;
|
|
53
|
+
filled?: boolean | undefined;
|
|
54
|
+
status?: string | undefined;
|
|
55
|
+
reactElement?: import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | undefined;
|
|
56
|
+
};
|
|
57
|
+
}) => import("react").JSX.Element | undefined;
|
|
51
58
|
renderProps: any;
|
|
52
59
|
} & {
|
|
53
60
|
doReload(): void;
|
|
@@ -453,8 +460,15 @@ export declare function SharedLinearPileupDisplayMixin(configSchema: AnyConfigur
|
|
|
453
460
|
message: string | undefined;
|
|
454
461
|
maxHeightReached: boolean;
|
|
455
462
|
ReactComponent: ({ model, }: {
|
|
456
|
-
model:
|
|
457
|
-
|
|
463
|
+
model: {
|
|
464
|
+
error?: unknown;
|
|
465
|
+
reload: () => void;
|
|
466
|
+
message: import("react").ReactNode;
|
|
467
|
+
filled?: boolean | undefined;
|
|
468
|
+
status?: string | undefined;
|
|
469
|
+
reactElement?: import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | undefined;
|
|
470
|
+
};
|
|
471
|
+
}) => import("react").JSX.Element | undefined;
|
|
458
472
|
renderProps: any;
|
|
459
473
|
} & {
|
|
460
474
|
doReload(): void;
|
|
@@ -4,7 +4,7 @@ declare const SetFeatureHeightDialog: (props: {
|
|
|
4
4
|
setFeatureHeight: (arg?: number) => void;
|
|
5
5
|
setNoSpacing: (arg?: boolean) => void;
|
|
6
6
|
featureHeightSetting: number;
|
|
7
|
-
noSpacing?: boolean
|
|
7
|
+
noSpacing?: boolean;
|
|
8
8
|
};
|
|
9
9
|
handleClose: () => void;
|
|
10
10
|
}) => React.JSX.Element;
|
|
@@ -5,7 +5,7 @@ import { AnyConfigurationSchemaType } from '@jbrowse/core/configuration';
|
|
|
5
5
|
* #stateModel LinearPileupDisplay
|
|
6
6
|
* #category display
|
|
7
7
|
* extends
|
|
8
|
-
|
|
8
|
+
* - [SharedLinearPileupDisplayMixin](../sharedlinearpileupdisplaymixin)
|
|
9
9
|
*/
|
|
10
10
|
declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): import("mobx-state-tree").IModelType<{
|
|
11
11
|
id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
|
|
@@ -43,8 +43,15 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
43
43
|
message: string | undefined;
|
|
44
44
|
maxHeightReached: boolean;
|
|
45
45
|
ReactComponent: ({ model, }: {
|
|
46
|
-
model:
|
|
47
|
-
|
|
46
|
+
model: {
|
|
47
|
+
error?: unknown;
|
|
48
|
+
reload: () => void;
|
|
49
|
+
message: import("react").ReactNode;
|
|
50
|
+
filled?: boolean | undefined;
|
|
51
|
+
status?: string | undefined;
|
|
52
|
+
reactElement?: import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | undefined;
|
|
53
|
+
};
|
|
54
|
+
}) => import("react").JSX.Element | undefined;
|
|
48
55
|
renderProps: any;
|
|
49
56
|
} & {
|
|
50
57
|
doReload(): void;
|
|
@@ -89,16 +96,15 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
89
96
|
} & {
|
|
90
97
|
configuration: AnyConfigurationSchemaType;
|
|
91
98
|
featureHeight: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<number>>;
|
|
92
|
-
/**
|
|
93
|
-
* #action
|
|
94
|
-
*/
|
|
95
99
|
noSpacing: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<boolean>>;
|
|
96
100
|
fadeLikelihood: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<boolean>>;
|
|
97
101
|
trackMaxHeight: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<number>>;
|
|
98
102
|
colorBy: import("mobx-state-tree").IMaybe<import("mobx-state-tree").IModelType<{
|
|
99
103
|
type: import("mobx-state-tree").ISimpleType<string>;
|
|
100
104
|
tag: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<string>>;
|
|
101
|
-
extra: import("mobx-state-tree").IType<any, any, any>;
|
|
105
|
+
extra: import("mobx-state-tree").IType<any, any, any>; /**
|
|
106
|
+
* #action
|
|
107
|
+
*/
|
|
102
108
|
}, {}, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
|
|
103
109
|
filterBy: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").IModelType<{
|
|
104
110
|
flagInclude: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<number>, [undefined]>;
|
|
@@ -178,9 +184,7 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
178
184
|
readonly viewMenuActions: import("@jbrowse/core/ui").MenuItem[];
|
|
179
185
|
regionCannotBeRendered(): null;
|
|
180
186
|
} & {
|
|
181
|
-
setMessage(arg?: string | undefined): void;
|
|
182
|
-
* #getter
|
|
183
|
-
*/
|
|
187
|
+
setMessage(arg?: string | undefined): void;
|
|
184
188
|
setError(error?: unknown): void;
|
|
185
189
|
setRpcDriverName(rpcDriverName: string): void;
|
|
186
190
|
reload(): void;
|
|
@@ -230,7 +234,9 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
230
234
|
readonly DisplayMessageComponent: import("react").FC<any> | undefined;
|
|
231
235
|
} & {
|
|
232
236
|
readonly features: import("@jbrowse/core/util/compositeMap").default<string, import("@jbrowse/core/util").Feature>;
|
|
233
|
-
readonly featureUnderMouse: import("@jbrowse/core/util").Feature | undefined;
|
|
237
|
+
readonly featureUnderMouse: import("@jbrowse/core/util").Feature | undefined; /**
|
|
238
|
+
* #getter
|
|
239
|
+
*/
|
|
234
240
|
getFeatureOverlapping(blockKey: string, x: number, y: number): string | undefined;
|
|
235
241
|
getFeatureByID(blockKey: string, id: string): [number, number, number, number] | undefined;
|
|
236
242
|
searchFeatureByID(id: string): [number, number, number, number] | undefined;
|
|
@@ -239,7 +245,9 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
239
245
|
deleteBlock(key: string): void;
|
|
240
246
|
selectFeature(feature: import("@jbrowse/core/util").Feature): void;
|
|
241
247
|
clearFeatureSelection(): void;
|
|
242
|
-
setFeatureIdUnderMouse(feature?: string | undefined): void;
|
|
248
|
+
setFeatureIdUnderMouse(feature?: string | undefined): void; /**
|
|
249
|
+
* #method
|
|
250
|
+
*/
|
|
243
251
|
setContextMenuFeature(feature?: import("@jbrowse/core/util").Feature | undefined): void;
|
|
244
252
|
} & {
|
|
245
253
|
reload(): Promise<void>;
|
|
@@ -368,6 +376,12 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
368
376
|
* #action
|
|
369
377
|
*/
|
|
370
378
|
setSortedBy(type: string, tag?: string): void;
|
|
379
|
+
/**
|
|
380
|
+
* #action
|
|
381
|
+
* overrides base from SharedLinearPileupDisplay to make sortReady false
|
|
382
|
+
* since changing feature height destroys the sort-induced layout
|
|
383
|
+
*/
|
|
384
|
+
setFeatureHeight(n?: number): void;
|
|
371
385
|
} & {
|
|
372
386
|
/**
|
|
373
387
|
* #action
|
|
@@ -11,14 +11,14 @@ import SortIcon from '@mui/icons-material/Sort';
|
|
|
11
11
|
// locals
|
|
12
12
|
import { SharedLinearPileupDisplayMixin } from './SharedLinearPileupDisplayMixin';
|
|
13
13
|
import { observable } from 'mobx';
|
|
14
|
-
//
|
|
14
|
+
// lzies
|
|
15
15
|
const SortByTagDialog = lazy(() => import('./components/SortByTag'));
|
|
16
16
|
const ModificationsDialog = lazy(() => import('./components/ColorByModifications'));
|
|
17
17
|
/**
|
|
18
18
|
* #stateModel LinearPileupDisplay
|
|
19
19
|
* #category display
|
|
20
20
|
* extends
|
|
21
|
-
|
|
21
|
+
* - [SharedLinearPileupDisplayMixin](../sharedlinearpileupdisplaymixin)
|
|
22
22
|
*/
|
|
23
23
|
function stateModelFactory(configSchema) {
|
|
24
24
|
return types
|
|
@@ -125,6 +125,15 @@ function stateModelFactory(configSchema) {
|
|
|
125
125
|
tag,
|
|
126
126
|
};
|
|
127
127
|
},
|
|
128
|
+
/**
|
|
129
|
+
* #action
|
|
130
|
+
* overrides base from SharedLinearPileupDisplay to make sortReady false
|
|
131
|
+
* since changing feature height destroys the sort-induced layout
|
|
132
|
+
*/
|
|
133
|
+
setFeatureHeight(n) {
|
|
134
|
+
self.sortReady = false;
|
|
135
|
+
self.featureHeight = n;
|
|
136
|
+
},
|
|
128
137
|
}))
|
|
129
138
|
.actions(self => {
|
|
130
139
|
// resets the sort object and refresh whole display on reload
|
|
@@ -42,8 +42,17 @@ declare function stateModelFactory(pluginManager: PluginManager, configSchema: A
|
|
|
42
42
|
message: string | undefined;
|
|
43
43
|
maxHeightReached: boolean;
|
|
44
44
|
ReactComponent: ({ model, }: {
|
|
45
|
-
model:
|
|
46
|
-
|
|
45
|
+
model: {
|
|
46
|
+
error?: unknown;
|
|
47
|
+
reload: () => void;
|
|
48
|
+
message: import("react").ReactNode; /**
|
|
49
|
+
* #property
|
|
50
|
+
*/
|
|
51
|
+
filled?: boolean | undefined;
|
|
52
|
+
status?: string | undefined;
|
|
53
|
+
reactElement?: import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | undefined;
|
|
54
|
+
};
|
|
55
|
+
}) => import("react").JSX.Element | undefined;
|
|
47
56
|
renderProps: any;
|
|
48
57
|
} & {
|
|
49
58
|
doReload(): void;
|
|
@@ -12,16 +12,16 @@ declare const PileupRendering: (props: {
|
|
|
12
12
|
type: string;
|
|
13
13
|
pos: number;
|
|
14
14
|
refName: string;
|
|
15
|
-
}
|
|
15
|
+
};
|
|
16
16
|
colorBy?: {
|
|
17
17
|
type: string;
|
|
18
|
-
tag?: string
|
|
19
|
-
}
|
|
18
|
+
tag?: string;
|
|
19
|
+
};
|
|
20
20
|
filterBy?: {
|
|
21
21
|
tagFilter?: {
|
|
22
22
|
tag: string;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
onMouseMove?: (
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
onMouseMove?: (event: React.MouseEvent, featureId?: string) => void;
|
|
26
26
|
}) => React.JSX.Element;
|
|
27
27
|
export default PileupRendering;
|
|
@@ -4,6 +4,7 @@ import { BlockMsg, } from '@jbrowse/plugin-linear-genome-view';
|
|
|
4
4
|
import { makeStyles } from 'tss-react/mui';
|
|
5
5
|
import { observer } from 'mobx-react';
|
|
6
6
|
import { getContainingView } from '@jbrowse/core/util';
|
|
7
|
+
import { Button, Tooltip } from '@mui/material';
|
|
7
8
|
const useStyles = makeStyles()(theme => ({
|
|
8
9
|
loading: {
|
|
9
10
|
backgroundColor: theme.palette.background.default,
|
|
@@ -19,7 +20,8 @@ const useStyles = makeStyles()(theme => ({
|
|
|
19
20
|
}));
|
|
20
21
|
const BaseDisplayComponent = observer(function ({ model, children, }) {
|
|
21
22
|
const { error, regionTooLarge } = model;
|
|
22
|
-
return error ? (React.createElement(BlockMsg, { message: `${error}`, severity: "error",
|
|
23
|
+
return error ? (React.createElement(BlockMsg, { message: `${error}`, severity: "error", action: React.createElement(Tooltip, { title: "Reload" },
|
|
24
|
+
React.createElement(Button, { "data-testid": "reload_button", onClick: () => model.reload() }, "Reload")) })) : regionTooLarge ? (model.regionCannotBeRendered()) : (React.createElement(DataDisplay, { model: model }, children));
|
|
23
25
|
});
|
|
24
26
|
const DataDisplay = observer(function ({ model, children, }) {
|
|
25
27
|
const { drawn, loading } = model;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/plugin-alignments",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.0",
|
|
4
4
|
"description": "JBrowse 2 alignments adapters, tracks, etc.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jbrowse",
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"distModule": "esm/index.js",
|
|
64
64
|
"srcModule": "src/index.ts",
|
|
65
65
|
"module": "esm/index.js",
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "3d43a820b9274a6160aa4dc15616147f390d9094"
|
|
67
67
|
}
|