genoverse 3.2.0 → 4.0.0-beta1
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/.eslintrc.js +93 -162
- package/.github/workflows/test.yml +9 -10
- package/.github/workflows/update-gh-pages.yml +33 -0
- package/LICENSE.TXT +2 -2
- package/README.md +176 -3
- package/{i → assets}/sort_handle.png +0 -0
- package/babel.config.js +19 -0
- package/dist/129.css +334 -0
- package/dist/129.css.map +1 -0
- package/dist/129.genoverse.js +2 -0
- package/dist/129.genoverse.js.map +1 -0
- package/dist/15d98c18221c8bcb2334.ttf +0 -0
- package/dist/166.css +2 -0
- package/dist/166.genoverse.js +1 -0
- package/dist/216.css +20 -0
- package/dist/216.css.map +1 -0
- package/dist/232.css +114 -0
- package/dist/232.css.map +1 -0
- package/dist/232.genoverse.js +2 -0
- package/dist/232.genoverse.js.map +1 -0
- package/dist/2e659e443f3e98569e9f.png +0 -0
- package/dist/394.css +114 -0
- package/dist/394.css.map +1 -0
- package/dist/394.genoverse.js +2 -0
- package/dist/394.genoverse.js.map +1 -0
- package/dist/469.css +24 -0
- package/dist/469.css.map +1 -0
- package/dist/469.genoverse.js +2 -0
- package/dist/469.genoverse.js.map +1 -0
- package/dist/4896d4b04430cc3dfb06.woff2 +0 -0
- package/dist/530.css +39 -0
- package/dist/530.css.map +1 -0
- package/dist/530.genoverse.js +2 -0
- package/dist/530.genoverse.js.map +1 -0
- package/dist/547.css +469 -0
- package/dist/547.css.map +1 -0
- package/dist/547.genoverse.js +1 -0
- package/dist/729.css +315 -0
- package/dist/729.css.map +1 -0
- package/dist/79da213423ac0def2058.ttf +0 -0
- package/dist/804.genoverse.js +2 -0
- package/dist/804.genoverse.js.map +1 -0
- package/dist/842.genoverse.js +2 -0
- package/dist/842.genoverse.js.map +1 -0
- package/dist/893.genoverse.js +2 -0
- package/dist/893.genoverse.js.map +1 -0
- package/dist/949.css +315 -0
- package/dist/949.css.map +1 -0
- package/dist/949.genoverse.js +2 -0
- package/dist/949.genoverse.js.map +1 -0
- package/dist/952.css +315 -0
- package/dist/952.css.map +1 -0
- package/dist/952.genoverse.js +2 -0
- package/dist/952.genoverse.js.map +1 -0
- package/dist/d79c2ec96ab9ff1161a2.woff2 +0 -0
- package/dist/genoverse.js +2 -0
- package/dist/genoverse.js.map +1 -0
- package/index.html +13 -14
- package/jest.config.js +5 -0
- package/jest.setup.js +13 -0
- package/package.json +29 -12
- package/{css → src/css}/controlPanel.css +0 -0
- package/{css → src/css}/fileDrop.css +0 -0
- package/src/css/fontawesome.css +3 -0
- package/{css → src/css}/fullscreen.css +0 -0
- package/{css → src/css}/genoverse.css +1 -1
- package/{css → src/css}/karyotype.css +2 -0
- package/{css → src/css}/resizer.css +0 -0
- package/{css → src/css}/tooltips.css +0 -0
- package/{css → src/css}/trackControls.css +0 -0
- package/src/js/Genoverse.js +1747 -0
- package/{js → src/js}/Track/Controller/Sequence.js +6 -4
- package/src/js/Track/Controller/Stranded.js +83 -0
- package/{js → src/js}/Track/Controller.js +201 -160
- package/src/js/Track/Model/File/BAM.js +47 -0
- package/src/js/Track/Model/File/BED.js +122 -0
- package/src/js/Track/Model/File/GFF.js +42 -0
- package/src/js/Track/Model/File/VCF.js +109 -0
- package/src/js/Track/Model/File/WIG.js +82 -0
- package/src/js/Track/Model/File.js +36 -0
- package/src/js/Track/Model/Gene/Ensembl.js +24 -0
- package/{js → src/js}/Track/Model/Gene.js +3 -1
- package/src/js/Track/Model/Sequence/Ensembl.js +6 -0
- package/{js → src/js}/Track/Model/Sequence/Fasta.js +24 -17
- package/{js → src/js}/Track/Model/Sequence.js +10 -7
- package/{js → src/js}/Track/Model/SequenceVariation.js +17 -11
- package/{js → src/js}/Track/Model/Stranded.js +11 -8
- package/src/js/Track/Model/Transcript/Ensembl.js +73 -0
- package/{js → src/js}/Track/Model/Transcript.js +3 -1
- package/{js → src/js}/Track/Model.js +128 -97
- package/{js → src/js}/Track/View/Gene/Ensembl.js +6 -4
- package/src/js/Track/View/Gene.js +8 -0
- package/{js → src/js}/Track/View/Sequence.js +18 -22
- package/src/js/Track/View/SequenceVariation.js +117 -0
- package/src/js/Track/View/Transcript/Ensembl.js +17 -0
- package/src/js/Track/View/Transcript.js +32 -0
- package/{js → src/js}/Track/View.js +200 -159
- package/{js → src/js}/Track/library/Chromosome.js +18 -13
- package/src/js/Track/library/File/BAM.js +34 -0
- package/src/js/Track/library/File/BED.js +27 -0
- package/src/js/Track/library/File/BIGBED.js +51 -0
- package/src/js/Track/library/File/BIGWIG.js +54 -0
- package/src/js/Track/library/File/GFF.js +10 -0
- package/{js → src/js}/Track/library/File/VCF.js +29 -22
- package/src/js/Track/library/File/WIG.js +8 -0
- package/{js → src/js}/Track/library/File.js +4 -2
- package/src/js/Track/library/Gene.js +44 -0
- package/src/js/Track/library/Graph/Bar.js +263 -0
- package/src/js/Track/library/Graph/Line.js +335 -0
- package/{js → src/js}/Track/library/Graph.js +137 -114
- package/{js → src/js}/Track/library/HighlightRegion.js +118 -93
- package/src/js/Track/library/Legend.js +258 -0
- package/{js → src/js}/Track/library/Scalebar.js +69 -49
- package/{js → src/js}/Track/library/Scaleline.js +29 -27
- package/src/js/Track/library/Static.js +82 -0
- package/{js → src/js}/Track/library/dbSNP.js +47 -50
- package/src/js/Track.js +649 -0
- package/{js → src/js}/genomes/grch37.js +52 -52
- package/{js → src/js}/genomes/grch38.js +52 -52
- package/src/js/lib/BWReader.js +562 -0
- package/src/js/lib/VCFReader.js +296 -0
- package/src/js/lib/dalliance/bam.js +517 -0
- package/src/js/lib/dalliance/bin.js +317 -0
- package/src/js/lib/dalliance/jszlib-inflate.js +2159 -0
- package/src/js/lib/dalliance/lh3utils.js +105 -0
- package/src/js/lib/dalliance/sha1.js +334 -0
- package/src/js/lib/import-tracks.js +42 -0
- package/{js/lib → src/js/lib/jquery-plugins}/jquery.mousehold.js +0 -0
- package/{js/lib → src/js/lib/jquery-plugins}/jquery.mousewheel.js +0 -0
- package/{js/lib → src/js/lib/jquery-plugins}/jquery.tipsy.js +0 -0
- package/src/js/lib/jquery.js +26 -0
- package/src/js/lib/polyfills.js +11 -0
- package/src/js/lib/wrap-functions.js +88 -0
- package/src/js/plugins/controlPanel.js +388 -0
- package/src/js/plugins/fileDrop.js +81 -0
- package/src/js/plugins/focusRegion.js +13 -0
- package/{js → src/js}/plugins/fullscreen.js +18 -14
- package/{js → src/js}/plugins/karyotype.js +51 -45
- package/src/js/plugins/resizer.js +52 -0
- package/{js → src/js}/plugins/tooltips.js +31 -29
- package/src/js/plugins/trackControls.js +159 -0
- package/test/View/render-legends.test.js +1 -1
- package/test/create-and-destroy.test.js +2 -2
- package/test/track-ordering.test.js +3 -2
- package/test/track_config/config-settings.test.js +1 -1
- package/test/utils.js +4 -2
- package/webpack.config.js +103 -34
- package/css/font-awesome.css +0 -3
- package/expanded.html +0 -120
- package/fontawesome/css/fontawesome.min.css +0 -5
- package/fontawesome/css/regular.min.css +0 -5
- package/fontawesome/css/solid.min.css +0 -5
- package/fontawesome/webfonts/fa-brands-400.ttf +0 -0
- package/fontawesome/webfonts/fa-brands-400.woff +0 -0
- package/fontawesome/webfonts/fa-brands-400.woff2 +0 -0
- package/fontawesome/webfonts/fa-regular-400.ttf +0 -0
- package/fontawesome/webfonts/fa-regular-400.woff +0 -0
- package/fontawesome/webfonts/fa-regular-400.woff2 +0 -0
- package/fontawesome/webfonts/fa-solid-900.ttf +0 -0
- package/fontawesome/webfonts/fa-solid-900.woff +0 -0
- package/fontawesome/webfonts/fa-solid-900.woff2 +0 -0
- package/help.pdf +0 -0
- package/index.js +0 -83
- package/js/Genoverse.js +0 -1681
- package/js/Track/Controller/Stranded.js +0 -73
- package/js/Track/Model/File/BAM.js +0 -44
- package/js/Track/Model/File/BED.js +0 -116
- package/js/Track/Model/File/GFF.js +0 -40
- package/js/Track/Model/File/VCF.js +0 -101
- package/js/Track/Model/File/WIG.js +0 -67
- package/js/Track/Model/File.js +0 -36
- package/js/Track/Model/Gene/Ensembl.js +0 -22
- package/js/Track/Model/Sequence/Ensembl.js +0 -4
- package/js/Track/Model/Transcript/Ensembl.js +0 -67
- package/js/Track/View/Gene.js +0 -6
- package/js/Track/View/Sequence/Variation.js +0 -115
- package/js/Track/View/Transcript/Ensembl.js +0 -12
- package/js/Track/View/Transcript.js +0 -28
- package/js/Track/library/File/BAM.js +0 -30
- package/js/Track/library/File/BED.js +0 -24
- package/js/Track/library/File/BIGBED.js +0 -47
- package/js/Track/library/File/BIGWIG.js +0 -52
- package/js/Track/library/File/GFF.js +0 -9
- package/js/Track/library/File/WIG.js +0 -5
- package/js/Track/library/Gene.js +0 -37
- package/js/Track/library/Graph/Bar.js +0 -235
- package/js/Track/library/Graph/Line.js +0 -296
- package/js/Track/library/Legend.js +0 -224
- package/js/Track/library/Static.js +0 -78
- package/js/Track.js +0 -632
- package/js/genoverse.min.js +0 -2
- package/js/genoverse.min.js.map +0 -1
- package/js/lib/BWReader.js +0 -578
- package/js/lib/Base.js +0 -145
- package/js/lib/VCFReader.js +0 -286
- package/js/lib/dalliance/js/bam.js +0 -494
- package/js/lib/dalliance/js/bin.js +0 -185
- package/js/lib/dalliance/js/das.js +0 -749
- package/js/lib/dalliance/js/utils.js +0 -370
- package/js/lib/dalliance-lib.js +0 -3594
- package/js/lib/dalliance-lib.min.js +0 -68
- package/js/lib/jDataView.js +0 -2
- package/js/lib/jParser.js +0 -192
- package/js/lib/jquery-ui.js +0 -8
- package/js/lib/jquery.js +0 -2
- package/js/lib/rtree.js +0 -1
- package/js/plugins/controlPanel.js +0 -395
- package/js/plugins/fileDrop.js +0 -62
- package/js/plugins/focusRegion.js +0 -12
- package/js/plugins/resizer.js +0 -45
- package/js/plugins/trackControls.js +0 -143
- package/utils/expandedTemplate.html +0 -46
- package/utils/git-hooks/post-commit +0 -9
- package/utils/git-hooks/pre-commit +0 -7
- package/utils/git-hooks/setup +0 -6
- package/utils/makeExpanded.js +0 -19
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import Track, { Model, View } from '../../Track';
|
|
2
|
+
import StrandedController from '../Controller/Stranded';
|
|
3
|
+
import StrandedModel from '../Model/Stranded';
|
|
4
|
+
|
|
5
|
+
export default Track.extend({
|
|
2
6
|
id : 'highlights',
|
|
3
7
|
unsortable : true,
|
|
4
8
|
fixedOrder : true,
|
|
@@ -17,45 +21,47 @@ Genoverse.Track.HighlightRegion = Genoverse.Track.extend({
|
|
|
17
21
|
featureMargin : { top: 13, right: 0, bottom: 0, left: 0 },
|
|
18
22
|
margin : 0,
|
|
19
23
|
|
|
20
|
-
constructor: function () {
|
|
24
|
+
constructor: function (...args) {
|
|
21
25
|
this.colorIndex = 0;
|
|
22
|
-
|
|
26
|
+
|
|
27
|
+
return this.base(...args);
|
|
23
28
|
},
|
|
24
29
|
|
|
25
30
|
addHighlights: function (highlights) {
|
|
26
|
-
|
|
27
|
-
this.model.insertFeature(
|
|
28
|
-
|
|
31
|
+
highlights.forEach(
|
|
32
|
+
highlight => this.model.insertFeature({ label: `${highlight.start}-${highlight.end}`, ...highlight })
|
|
33
|
+
);
|
|
29
34
|
|
|
30
35
|
this.reset();
|
|
31
36
|
},
|
|
32
37
|
|
|
33
38
|
removeHighlights: function (highlights) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
var features, bounds, h;
|
|
39
|
+
const featuresByChr = this.prop('featuresByChr');
|
|
40
|
+
const featuresById = this.prop('featuresById');
|
|
37
41
|
|
|
38
|
-
highlights = highlights ||
|
|
42
|
+
highlights = highlights || Object.values(featuresById);
|
|
39
43
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
highlights.forEach(
|
|
45
|
+
(highlight) => {
|
|
46
|
+
if (highlight.removable === false) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
const features = featuresByChr[highlight.chr];
|
|
51
|
+
const bounds = { x: highlight.start, y: 0, w: highlight.end - highlight.start + 1, h: 1 };
|
|
47
52
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
// RTree.remove only works if the second argument (the object to be removed) === the object found in the tree.
|
|
54
|
+
// Here, while highlight is effectively the same object as the one in the tree, it has been cloned, so the === check fails.
|
|
55
|
+
// To fix this, search for the feature to remove in the location of highlight.
|
|
56
|
+
const h = features.search(bounds).filter(item => item.id === highlight.id);
|
|
52
57
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
if (h.length) {
|
|
59
|
+
features.remove(bounds, h[0]);
|
|
60
|
+
}
|
|
56
61
|
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
delete featuresById[highlight.id];
|
|
63
|
+
}
|
|
64
|
+
);
|
|
59
65
|
|
|
60
66
|
if (this.prop('strand') === 1) {
|
|
61
67
|
this.prop('reverseTrack').removeHighlights(highlights);
|
|
@@ -64,7 +70,7 @@ Genoverse.Track.HighlightRegion = Genoverse.Track.extend({
|
|
|
64
70
|
this.reset();
|
|
65
71
|
},
|
|
66
72
|
|
|
67
|
-
controller:
|
|
73
|
+
controller: StrandedController.extend({
|
|
68
74
|
setDefaults: function () {
|
|
69
75
|
if (this.prop('strand') === -1) {
|
|
70
76
|
this.prop('labels', false);
|
|
@@ -91,8 +97,10 @@ Genoverse.Track.HighlightRegion = Genoverse.Track.extend({
|
|
|
91
97
|
params.background = 'gv-full-height';
|
|
92
98
|
}
|
|
93
99
|
|
|
94
|
-
|
|
100
|
+
const rtn = this.base(params);
|
|
101
|
+
|
|
95
102
|
params.container.addClass(params.background);
|
|
103
|
+
|
|
96
104
|
return rtn;
|
|
97
105
|
},
|
|
98
106
|
|
|
@@ -107,59 +115,65 @@ Genoverse.Track.HighlightRegion = Genoverse.Track.extend({
|
|
|
107
115
|
},
|
|
108
116
|
|
|
109
117
|
populateMenu: function (features) {
|
|
110
|
-
|
|
111
|
-
var location, m;
|
|
118
|
+
const menu = [];
|
|
112
119
|
|
|
113
120
|
if (features.length > 1) {
|
|
114
121
|
menu.push({ title: 'Highlights' });
|
|
115
122
|
}
|
|
116
123
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
124
|
+
features.forEach(
|
|
125
|
+
(feature) => {
|
|
126
|
+
const location = `${feature.start}-${feature.end}`;
|
|
127
|
+
const m = {
|
|
128
|
+
title : feature.label ? feature.label[0] : location,
|
|
129
|
+
start : false,
|
|
130
|
+
};
|
|
123
131
|
|
|
124
|
-
|
|
125
|
-
|
|
132
|
+
m[m.title === location ? 'title' : 'Location'] = `${feature.chr}:${location}`;
|
|
133
|
+
m[`<a class="gv-focus-highlight" href="#" data-chr="${feature.chr}" data-start="${feature.start}" data-end="${feature.end}">Focus here</a>`] = '';
|
|
126
134
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
135
|
+
if (feature.removable !== false) {
|
|
136
|
+
m[`<a class="gv-remove-highlight" href="#" data-id="${feature.id}">Remove this highlight</a>`] = '';
|
|
137
|
+
m['<a class="gv-remove-highlights" href="#">Remove all highlights</a>'] = '';
|
|
138
|
+
}
|
|
131
139
|
|
|
132
|
-
|
|
133
|
-
|
|
140
|
+
menu.push(m);
|
|
141
|
+
}
|
|
142
|
+
);
|
|
134
143
|
|
|
135
144
|
return menu;
|
|
136
145
|
},
|
|
137
146
|
|
|
138
|
-
click: function () {
|
|
147
|
+
click: function (...args) {
|
|
148
|
+
const jQuery = this.browser.jQuery;
|
|
149
|
+
|
|
139
150
|
if (this.prop('strand') !== 1) {
|
|
140
151
|
return;
|
|
141
152
|
}
|
|
142
153
|
|
|
143
|
-
|
|
154
|
+
const menuEl = this.base(...args);
|
|
144
155
|
|
|
145
156
|
if (menuEl && !menuEl.data('highlightEvents')) {
|
|
146
|
-
|
|
157
|
+
const track = this.track;
|
|
147
158
|
|
|
148
159
|
menuEl.find('.gv-remove-highlight').on('click', function () {
|
|
149
|
-
|
|
150
|
-
|
|
160
|
+
const id = jQuery(this).data('id');
|
|
161
|
+
|
|
162
|
+
track.removeHighlights(menuEl.data('feature').filter(f => f.id === id));
|
|
163
|
+
|
|
151
164
|
return false;
|
|
152
165
|
});
|
|
153
166
|
|
|
154
|
-
menuEl.find('.gv-remove-highlights').on('click',
|
|
167
|
+
menuEl.find('.gv-remove-highlights').on('click', () => {
|
|
155
168
|
track.removeHighlights();
|
|
169
|
+
|
|
156
170
|
return false;
|
|
157
171
|
});
|
|
158
172
|
|
|
159
173
|
menuEl.find('.gv-focus-highlight').on('click', function () {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
174
|
+
const data = jQuery(this).data();
|
|
175
|
+
const length = data.end - data.start + 1;
|
|
176
|
+
const context = Math.max(Math.round(length / 4), 25);
|
|
163
177
|
|
|
164
178
|
track.browser.moveTo(data.chr, data.start - context, data.end + context, true);
|
|
165
179
|
|
|
@@ -171,38 +185,39 @@ Genoverse.Track.HighlightRegion = Genoverse.Track.extend({
|
|
|
171
185
|
},
|
|
172
186
|
|
|
173
187
|
getClickedFeatures: function (x, y) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
this.
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
),
|
|
184
|
-
function (f) {
|
|
188
|
+
const seen = {};
|
|
189
|
+
const scale = this.scale;
|
|
190
|
+
const features =
|
|
191
|
+
this.featurePositions.search({ x: x, y: y, w: 1, h: 1 }).concat( // feature positions
|
|
192
|
+
this.labelPositions.search({ x: x, y: y, w: 1, h: 1 }).filter(// plus label positions where the labels are visible
|
|
193
|
+
f => f.position[scale].label.visible !== false
|
|
194
|
+
)
|
|
195
|
+
).filter(
|
|
196
|
+
(f) => {
|
|
185
197
|
// with duplicates removed
|
|
186
|
-
|
|
198
|
+
const rtn = !seen[f.id];
|
|
199
|
+
|
|
187
200
|
seen[f.id] = true;
|
|
201
|
+
|
|
188
202
|
return rtn;
|
|
189
203
|
}
|
|
190
204
|
);
|
|
191
205
|
|
|
192
206
|
return features.length ? [ this.model.sortFeatures(features) ] : false;
|
|
193
|
-
}
|
|
207
|
+
},
|
|
194
208
|
}),
|
|
195
209
|
|
|
196
|
-
model:
|
|
210
|
+
model: StrandedModel.extend({
|
|
197
211
|
url: false,
|
|
198
212
|
|
|
199
213
|
insertFeature: function (feature) {
|
|
200
|
-
feature.id = feature.chr
|
|
214
|
+
feature.id = `${feature.chr}:${feature.start}-${feature.end}`;
|
|
201
215
|
feature.sort = feature.start;
|
|
202
216
|
|
|
203
217
|
if (!feature.color) {
|
|
204
|
-
|
|
205
|
-
|
|
218
|
+
const colors = this.prop('colors');
|
|
219
|
+
|
|
220
|
+
let i = this.prop('colorIndex');
|
|
206
221
|
|
|
207
222
|
feature.color = colors[i++];
|
|
208
223
|
|
|
@@ -214,14 +229,14 @@ Genoverse.Track.HighlightRegion = Genoverse.Track.extend({
|
|
|
214
229
|
}
|
|
215
230
|
},
|
|
216
231
|
|
|
217
|
-
findFeatures: function () {
|
|
218
|
-
return
|
|
219
|
-
}
|
|
232
|
+
findFeatures: function (...args) {
|
|
233
|
+
return Model.prototype.findFeatures.call(this, ...args);
|
|
234
|
+
},
|
|
220
235
|
}),
|
|
221
236
|
|
|
222
|
-
view:
|
|
237
|
+
view: View.extend({
|
|
223
238
|
positionFeatures: function (features, params) {
|
|
224
|
-
|
|
239
|
+
const rtn = this.base(features, params);
|
|
225
240
|
|
|
226
241
|
// featureMargin.top gets used to define params.featureHeight, which is used to determine canvas height.
|
|
227
242
|
// Since featureMargin.top = 13 on forward strand, the canvas has a 13px space at the bottom, meaning there is a gap before the background starts.
|
|
@@ -241,30 +256,40 @@ Genoverse.Track.HighlightRegion = Genoverse.Track.extend({
|
|
|
241
256
|
},
|
|
242
257
|
|
|
243
258
|
drawBackground: function (features, context, params) {
|
|
259
|
+
const jQuery = this.browser.jQuery;
|
|
260
|
+
|
|
244
261
|
if (this.prop('strand') === -1) {
|
|
245
262
|
return;
|
|
246
263
|
}
|
|
247
264
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
265
|
+
features.forEach(
|
|
266
|
+
(feature) => {
|
|
267
|
+
context.fillStyle = feature.color;
|
|
268
|
+
|
|
269
|
+
this.drawFeature(
|
|
270
|
+
jQuery.extend(true, {}, feature, {
|
|
271
|
+
x : feature.position[params.scale].X,
|
|
272
|
+
y : 0,
|
|
273
|
+
width : feature.position[params.scale].width,
|
|
274
|
+
height : context.canvas.height,
|
|
275
|
+
color : this.shadeColor(context.fillStyle, 0.8),
|
|
276
|
+
border : feature.color,
|
|
277
|
+
label : false,
|
|
278
|
+
decorations : true,
|
|
279
|
+
}),
|
|
280
|
+
context,
|
|
281
|
+
false,
|
|
282
|
+
params.scale
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
);
|
|
262
286
|
},
|
|
263
287
|
|
|
264
288
|
decorateFeature: function (feature, context) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
289
|
+
const x1 = feature.x + 0.5;
|
|
290
|
+
const x2 = x1 + feature.width;
|
|
291
|
+
|
|
292
|
+
let draw = false;
|
|
268
293
|
|
|
269
294
|
context.strokeStyle = feature.border;
|
|
270
295
|
context.lineWidth = 2;
|
|
@@ -287,6 +312,6 @@ Genoverse.Track.HighlightRegion = Genoverse.Track.extend({
|
|
|
287
312
|
}
|
|
288
313
|
|
|
289
314
|
context.lineWidth = 1;
|
|
290
|
-
}
|
|
291
|
-
})
|
|
315
|
+
},
|
|
316
|
+
}),
|
|
292
317
|
});
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import Static, { Controller as StaticController, Model as StaticModel, View as StaticView } from './Static';
|
|
2
|
+
|
|
3
|
+
const Controller = StaticController.extend({
|
|
4
|
+
init: function () {
|
|
5
|
+
this.base();
|
|
6
|
+
|
|
7
|
+
this.container.addClass('gv-track-container-legend');
|
|
8
|
+
|
|
9
|
+
this.browser.legends[this.track.id] = this.track;
|
|
10
|
+
|
|
11
|
+
this.track.setTracks();
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
destroy: function () {
|
|
15
|
+
delete this.browser.legends[this.prop('id')];
|
|
16
|
+
this.base();
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const Model = StaticModel.extend({
|
|
21
|
+
findFeatures: function () {
|
|
22
|
+
const bounds = { x: this.browser.scaledStart, y: 0, w: this.width };
|
|
23
|
+
const features = this.track.tracks.flatMap(
|
|
24
|
+
(track) => {
|
|
25
|
+
const featurePositions = track.prop('featurePositions');
|
|
26
|
+
const $bounds = { ...bounds, h: track.prop('height') };
|
|
27
|
+
|
|
28
|
+
return featurePositions ? featurePositions.search($bounds).concat(track.prop('labelPositions').search($bounds)) : [];
|
|
29
|
+
}
|
|
30
|
+
).reduce(
|
|
31
|
+
(acc, feature) => {
|
|
32
|
+
if (Array.isArray(feature.legend)) {
|
|
33
|
+
feature.legend.forEach((legend) => { acc[legend.label] = legend.color; });
|
|
34
|
+
} else if (feature.legend) {
|
|
35
|
+
acc[feature.legend] = feature.legendColor || feature.color;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return acc;
|
|
39
|
+
},
|
|
40
|
+
{}
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
return this.sortFeatures(
|
|
44
|
+
Object.entries(features).map(([ text, color ]) => [ text, color ])
|
|
45
|
+
);
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
sortFeatures: function (features) {
|
|
49
|
+
// sort legend alphabetically
|
|
50
|
+
return features.sort((a, b) => a[0].localeCompare(b[0]));
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const View = StaticView.extend({
|
|
55
|
+
textColor : '#000000',
|
|
56
|
+
labels : 'overlay',
|
|
57
|
+
featureHeight : 12,
|
|
58
|
+
|
|
59
|
+
positionFeatures: function (features, params) {
|
|
60
|
+
if (params.positioned) {
|
|
61
|
+
return features;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const cols = 2;
|
|
65
|
+
const pad = 5;
|
|
66
|
+
const w = 20;
|
|
67
|
+
|
|
68
|
+
let x = 0;
|
|
69
|
+
let y = 0;
|
|
70
|
+
|
|
71
|
+
const xScale = this.width / cols;
|
|
72
|
+
const yScale = this.fontHeight + pad;
|
|
73
|
+
const xOffest = params.xOffset || 0;
|
|
74
|
+
const $features = [];
|
|
75
|
+
|
|
76
|
+
features.forEach(
|
|
77
|
+
([ text, color ]) => {
|
|
78
|
+
const xPos = (x * xScale) + pad;
|
|
79
|
+
const yPos = (y * yScale) + pad;
|
|
80
|
+
const labelWidth = this.context.measureText(text).width;
|
|
81
|
+
|
|
82
|
+
$features.push(
|
|
83
|
+
{ x: xPos + xOffest, y: yPos, width: w, height: this.featureHeight, color: color },
|
|
84
|
+
{ x: xPos + xOffest + pad + w, y: yPos, width: labelWidth + 1, height: this.featureHeight, color: false, labelColor: this.textColor, labelWidth: labelWidth, label: text }
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if (++x === cols) {
|
|
88
|
+
x = 0;
|
|
89
|
+
y++;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
params.height = this.prop('height', features.length ? ((y + (x ? 1 : 0)) * yScale) + pad : 0);
|
|
95
|
+
params.width = this.width;
|
|
96
|
+
params.positioned = true;
|
|
97
|
+
|
|
98
|
+
return this.base($features, params);
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
export default Static.extend({
|
|
103
|
+
isLegend : true, // For duck-typing
|
|
104
|
+
unsortable : true,
|
|
105
|
+
lockToTrack : true, // Always put the legend just below the last track that the legend is for
|
|
106
|
+
removable : false,
|
|
107
|
+
|
|
108
|
+
controller : Controller,
|
|
109
|
+
model : Model,
|
|
110
|
+
view : View,
|
|
111
|
+
|
|
112
|
+
setDefaults: function () {
|
|
113
|
+
this.order = typeof this.order !== 'undefined' ? this.order : 9e99;
|
|
114
|
+
this.id = this.id || 'legend';
|
|
115
|
+
this.type = this.type || 'legend';
|
|
116
|
+
this.base();
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
setEvents: function () {
|
|
120
|
+
this.browser.on({
|
|
121
|
+
'afterAddTracks afterRemoveTracks': function () {
|
|
122
|
+
if (this.destroying) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
Object.values(this.legends).forEach(
|
|
127
|
+
legend => legend.setTracks()
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
this.sortTracks();
|
|
131
|
+
},
|
|
132
|
+
afterRemoveTracks: function (tracks) {
|
|
133
|
+
if (this.destroying) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
tracks.forEach(
|
|
138
|
+
(track) => {
|
|
139
|
+
if (track.legendTrack && track.legendTrack.tracks.length === 0) {
|
|
140
|
+
track.legendTrack.remove();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
Object.values(this.legends).forEach(
|
|
146
|
+
legend => legend.controller.makeImage({})
|
|
147
|
+
);
|
|
148
|
+
},
|
|
149
|
+
afterUpdateTrackOrder: function (e, ui) {
|
|
150
|
+
const track = ui.item.data('track');
|
|
151
|
+
const legendTrack = this.legends[track.id] || track.legendTrack;
|
|
152
|
+
|
|
153
|
+
// If a legend track, or a track with a sortable legend has been reordered, its lockToTrack status is ignored from now on.
|
|
154
|
+
// This allows a legend to initially be locked to a track, but then to be reordered once the browser has been initialized
|
|
155
|
+
if (legendTrack && legendTrack.lockToTrack && legendTrack.unsortable === false) {
|
|
156
|
+
legendTrack.lockToTrack = false;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
Object.values(this.legends).forEach(
|
|
160
|
+
legend => legend.updateOrder()
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
this.sortTracks();
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
this.browser.on(
|
|
168
|
+
{
|
|
169
|
+
afterPositionFeatures: function (features, params) {
|
|
170
|
+
const legend = this.prop('legendTrack');
|
|
171
|
+
|
|
172
|
+
if (legend) {
|
|
173
|
+
setTimeout(() => { legend.controller.makeImage(params); }, 1);
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
afterResize: function (height, userResize) {
|
|
177
|
+
const legend = this.prop('legendTrack');
|
|
178
|
+
|
|
179
|
+
if (legend && userResize === true) {
|
|
180
|
+
legend.controller.makeImage({});
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
afterCheckHeight: function () {
|
|
184
|
+
const legend = this.prop('legendTrack');
|
|
185
|
+
|
|
186
|
+
if (legend) {
|
|
187
|
+
legend.controller.makeImage({});
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
afterSetMVC: function () {
|
|
191
|
+
const legend = this.prop('legendTrack');
|
|
192
|
+
|
|
193
|
+
if (legend && legend.tracks.length) {
|
|
194
|
+
legend.disable();
|
|
195
|
+
|
|
196
|
+
if (this.legend !== false) {
|
|
197
|
+
legend.enable();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
this
|
|
203
|
+
);
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
setTracks: function () {
|
|
207
|
+
const type = this.type;
|
|
208
|
+
|
|
209
|
+
this.tracks = this.browser.tracks.filter(
|
|
210
|
+
(track) => {
|
|
211
|
+
if (track.legendType === type) {
|
|
212
|
+
track.legendTrack = track.legendTrack || this;
|
|
213
|
+
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
).flatMap(
|
|
220
|
+
track => [ track ].concat(
|
|
221
|
+
track.prop('childTracks'),
|
|
222
|
+
track.prop('parentTrack')
|
|
223
|
+
).filter(
|
|
224
|
+
t => t && t !== this && !t.prop('disabled')
|
|
225
|
+
)
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
this.updateOrder();
|
|
229
|
+
|
|
230
|
+
if (typeof this.controller === 'object') {
|
|
231
|
+
this[this.tracks.length ? 'enable' : 'disable']();
|
|
232
|
+
} else {
|
|
233
|
+
this.disabled = !this.tracks.length;
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
updateOrder: function () {
|
|
238
|
+
if (this.lockToTrack) {
|
|
239
|
+
const tracks = this.tracks.filter(t => !t.prop('parentTrack'));
|
|
240
|
+
|
|
241
|
+
if (tracks.length) {
|
|
242
|
+
this.order = tracks[tracks.length - 1].order + 0.1;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
enable: function () {
|
|
248
|
+
this.base();
|
|
249
|
+
this.controller.makeImage({});
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
disable: function () {
|
|
253
|
+
delete this.controller.stringified;
|
|
254
|
+
this.base();
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
export { Controller, Model, View };
|