UNPKG

@gmod/jbrowse

Version:

JBrowse - client-side genome browser

469 lines (438 loc) 18.3 kB
define( [ 'dojo/_base/declare', 'dojo/_base/array', 'dijit/MenuItem', 'JBrowse/View/Dialog/SetTrackHeight', 'JBrowse/Util', 'JBrowse/View/Track/CanvasFeatures', 'JBrowse/View/Track/_AlignmentsMixin' ], function( declare, array, MenuItem, Dialog, Util, CanvasFeatureTrack, AlignmentsMixin ) { return declare( [ CanvasFeatureTrack, AlignmentsMixin ], { _defaultConfig: function() { var c = Util.deepUpdate( dojo.clone( this.inherited(arguments) ), { glyph: 'JBrowse/View/FeatureGlyph/Alignment', maxFeatureGlyphExpansion: 0, maxFeatureScreenDensity: 15, orientationType: 'fr', hideDuplicateReads: true, hideQCFailingReads: true, hideSecondary: true, hideSupplementary: true, hideUnmapped: true, hideUnsplicedReads: false, hideMissingMatepairs: false, hideImproperPairs: false, hideForwardStrand: false, hideReverseStrand: false, useXS: false, useTS: false, useReverseTemplate: false, useReverseTemplateOption: true, viewAsPairs: false, viewAsSpans: false, maxInsertSize: 50000, readCloudLogScale: true, showInterchromosomalArcs: true, showLargeArcs: true, histograms: { description: 'coverage depth', binsPerBlock: 200 }, style: { showLabels: false } } ); c.menuTemplate.push( { "iconClass": "dijitIconUndo", "url": function( track, feature ) { return track.browser.makeCurrentViewURL( { loc: track._nextSegmentViewLoc( feature, 0.8 ), highlight: feature.get('next_segment_position'), tracklist: 0 }); }, "action": "iframeDialog", title: "Open {next_segment_position} in a popup", disabled: function( track, feature ) { return ! feature.get('next_segment_position') || feature.get('paired_feature'); }, "label": "Quick-view mate/next location" }, { "iconClass": "dijitIconUndo", "url": function( track, feature ) { return track.browser.makeCurrentViewURL( { loc: track._nextSegmentViewLoc( feature ), highlight: feature.get('next_segment_position') }); }, "action": "newWindow", title: "Open {next_segment_position} in a new tab", disabled: function( track, feature ) { return ! feature.get('next_segment_position') || feature.get('paired_feature'); }, "label": "Open mate/next location in new tab" } ); return c; }, _trackType: function() { }, // make a locstring for a view of the given feature's next segment // (in a multi-segment read) _nextSegmentViewLoc: function( feature, factor ) { var nextLocStr = feature.get('next_segment_position'); if( ! nextLocStr ) return undefined; var s = nextLocStr.split(':'); var refName = s[0]; var start = parseInt(s[1]); var visibleRegion = this.browser.view.visibleRegion(); var visibleRegionSize = Math.round( (visibleRegion.end - visibleRegion.start + 1 )*(factor||1) ); return Util.assembleLocString( { start: Math.round( start - visibleRegionSize/2 ), end: Math.round( start + visibleRegionSize/2 ), ref: refName }); }, _trackMenuOptions() { var thisB = this; var displayOptions = []; var m = { type: 'dijit/Menu', label: 'Track visualization types', children: [] } var c = { type: 'dijit/Menu', label: 'Coloring options', children: [] } displayOptions.push(m) displayOptions.push(c) m.children.push({ label: 'View as unpaired', type: 'dijit/RadioMenuItem', checked: this.config.glyph == 'JBrowse/View/FeatureGlyph/Alignment', onClick: function(event) { thisB.config.glyph = 'JBrowse/View/FeatureGlyph/Alignment' thisB.browser.publish('/jbrowse/v1/v/tracks/replace', [thisB.config]); } }); m.children.push({ label: 'View as pairs', type: 'dijit/RadioMenuItem', checked: this.config.glyph == 'JBrowse/View/FeatureGlyph/PairedAlignment', onClick: function(event) { thisB.config.glyph = 'JBrowse/View/FeatureGlyph/PairedAlignment' thisB.browser.publish('/jbrowse/v1/v/tracks/replace', [thisB.config]); } }); m.children.push({ label: 'View as arcs', type: 'dijit/RadioMenuItem', checked: this.config.glyph == 'JBrowse/View/FeatureGlyph/PairedArc', onClick: function(event) { thisB.config.glyph = 'JBrowse/View/FeatureGlyph/PairedArc' thisB.browser.publish('/jbrowse/v1/v/tracks/replace', [thisB.config]); } }); m.children.push({ label: 'View as read cloud', type: 'dijit/RadioMenuItem', checked: this.config.glyph == 'JBrowse/View/FeatureGlyph/PairedReadCloud', onClick: function(event) { thisB.config.glyph = 'JBrowse/View/FeatureGlyph/PairedReadCloud' thisB.browser.publish('/jbrowse/v1/v/tracks/replace', [thisB.config]); } }); m.children.push({ label: 'View coverage', type: 'dijit/RadioMenuItem', checked: false, onClick: function(event) { thisB.config.type = 'JBrowse/View/Track/SNPCoverage' thisB.config._oldAlignmentsHeight = thisB.config.style.height thisB.config.style.height = thisB.config._oldSnpCoverageHeight thisB.browser.publish('/jbrowse/v1/v/tracks/replace', [thisB.config]); } }); c.children.push({ label: 'Color by default', type: 'dijit/RadioMenuItem', checked: !!this.config.defaultColor, onClick: function(event) { thisB.clearColorConfig() thisB.config.defaultColor = this.get('checked'); thisB.browser.publish('/jbrowse/v1/v/tracks/replace', [thisB.config]); } }); c.children.push({ label: 'Color by XS tag (RNA-seq orientation)', type: 'dijit/RadioMenuItem', checked: !!this.config.useXS, onClick: function(event) { thisB.clearColorConfig() thisB.config.useXS = this.get('checked'); thisB.browser.publish('/jbrowse/v1/v/tracks/replace', [thisB.config]); } }); c.children.push({ label: 'Color by TS tag (RNA-seq orientation)', type: 'dijit/RadioMenuItem', checked: !!this.config.useTS, onClick: function(event) { thisB.clearColorConfig() thisB.config.useTS = this.get('checked'); thisB.browser.publish('/jbrowse/v1/v/tracks/replace', [thisB.config]); } }); c.children.push({ label: 'Color mate pair as flipped (RNA-seq orientation)', type: 'dijit/RadioMenuItem', checked: !!this.config.useReverseTemplate, onClick: function(event) { thisB.clearColorConfig() thisB.config.useReverseTemplate = this.get('checked'); thisB.browser.publish('/jbrowse/v1/v/tracks/replace', [thisB.config]); } }); c.children.push({ label: 'Color by mapping quality', type: 'dijit/RadioMenuItem', checked: !!this.config.colorByMAPQ, onClick: function(event) { thisB.clearColorConfig() thisB.config.colorByMAPQ = this.get('checked'); thisB.browser.publish('/jbrowse/v1/v/tracks/replace', [thisB.config]); } }); c.children.push({ label: 'Color by orientation', type: 'dijit/RadioMenuItem', checked: !!this.config.colorByOrientation, onClick: function(event) { thisB.clearColorConfig() thisB.config.colorByOrientation = this.get('checked'); thisB.browser.publish('/jbrowse/v1/v/tracks/replace', [thisB.config]); } }); c.children.push({ label: 'Color by insert size', type: 'dijit/RadioMenuItem', checked: !!this.config.colorBySize, onClick: function(event) { thisB.clearColorConfig() thisB.config.colorBySize = this.get('checked'); thisB.browser.publish('/jbrowse/v1/v/tracks/replace', [thisB.config]); } }); c.children.push({ label: 'Color by orientation and insert size', type: 'dijit/RadioMenuItem', checked: !!this.config.colorByOrientationAndSize, onClick: function(event) { thisB.clearColorConfig() thisB.config.colorByOrientationAndSize = this.get('checked'); thisB.browser.publish('/jbrowse/v1/v/tracks/replace', [thisB.config]); } }); if(this.config.glyph == 'JBrowse/View/FeatureGlyph/PairedReadCloud') { displayOptions.push({ type: 'dijit/Menu', label: 'Read cloud options', children: [{ label: 'View log scale', type: 'dijit/CheckedMenuItem', checked: !!this.config.readCloudLogScale, onClick: function(event) { thisB.config.readCloudLogScale = this.get('checked'); thisB.browser.publish('/jbrowse/v1/v/tracks/replace', [thisB.config]); } }, { label: 'Set Y-scale size', onClick: function(event) { new Dialog({ title: 'Set read cloud Y-scale in terms of the maximum expected insert size', msg: ' expected max insert size', maxHeight: Infinity, height: thisB.config.readCloudYScaleMax || 50000, setCallback: ret => { thisB.config.readCloudYScaleMax = ret thisB.browser.publish('/jbrowse/v1/v/tracks/replace', [thisB.config]); } }).show() } }] }); } if(this.config.glyph == 'JBrowse/View/FeatureGlyph/PairedArc') { displayOptions.push({ type: 'dijit/Menu', label: 'Paired arc options', children: [{ label: 'Show interchromosomal', type: 'dijit/CheckedMenuItem', checked: !!this.config.showInterchromosomalArcs, onClick: function(event) { thisB.config.showInterchromosomalArcs = this.get('checked'); thisB.browser.publish('/jbrowse/v1/v/tracks/replace', [thisB.config]); } }, { label: 'Show large arcs', type: 'dijit/CheckedMenuItem', checked: !!this.config.showLargeArcs, onClick: function(event) { thisB.config.showLargeArcs = this.get('checked'); thisB.browser.publish('/jbrowse/v1/v/tracks/replace', [thisB.config]); } }] }); } displayOptions.push({ type: 'dijit/MenuItem', label: 'Re-estimate insert size stats', onClick: function(event) { thisB.insertSizeStats = null thisB.store.cleanStatsCache() thisB.redraw() } }) return Promise.all([ this.inherited(arguments), this._alignmentsFilterTrackMenuOptions(), displayOptions ]) .then( function( options ) { var o = options.shift(); options.unshift({ type: 'dijit/MenuSeparator' } ); return o.concat.apply( o, options ); }); }, clearColorConfig() { Object.assign(this.config, { defaultColor: false, useTS: false, useXS: false, useReverseTemplate: false, colorByMAPQ: false, colorByOrientation: false, colorBySize: false, colorByOrientationAndSize: false }) }, // override getLayout to access addRect method _getLayout() { var layout = this.inherited(arguments); if(this.config.glyph == 'JBrowse/View/FeatureGlyph/PairedReadCloud' || this.config.glyph == 'JBrowse/View/FeatureGlyph/PairedArc') { layout = declare.safeMixin(layout, { addRect: function() { this.pTotalHeight = this.maxHeight; return 0; } }); } return layout; }, fillFeatures( args ) { const finishCallback = args.finishCallback const errorCallback = e => { console.error(e) this._handleError(e, args) finishCallback(e) } if(this.config.viewAsPairs || this.config.viewAsSpans || (this.config.colorByOrientationAndSize || this.config.colorBySize && !this.insertSizeStats)) { let supermethod = this.getInherited(arguments) const blockLen = args.rightBase - args.leftBase let min let max if(this.config.glyph == 'JBrowse/View/FeatureGlyph/PairedArc') { // paired arc the insert size can be large and therefore we request a number of neighboring blocks const numNeighboringBlockFetches = 6 min = Math.max(0, args.leftBase - Math.min(Math.max(blockLen * numNeighboringBlockFetches, this.config.maxInsertSize), 100000)) max = args.rightBase + Math.min(Math.max(blockLen * numNeighboringBlockFetches, this.config.maxInsertSize), 100000) } else { // otherwise we just request based on maxInsertSize min = Math.max(0, args.leftBase - this.config.maxInsertSize) max = args.rightBase + this.config.maxInsertSize } var cachePromise = new Promise((resolve, reject) => { this.store.getFeatures({ ref: this.refSeq.name, start: min, end: max, viewAsPairs: this.config.viewAsPairs, viewAsSpans: this.config.viewAsSpans, maxInsertSize: this.config.maxInsertSize }, () => { /* do nothing except initialize caches on store backend */ }, () => { this.insertSizeStats = this.insertSizeStats || this.store.getInsertSizeStats() resolve() }, reject) }) cachePromise.then(() => { args.finishCallback = () => { finishCallback() this.store.cleanFeatureCache({ ref: this.refSeq.name, start: min, end: max }) } supermethod.call(this, args) }, errorCallback) } else { this.inherited(arguments); } }, constructor() { // automatically set parameters for the track based on glyph types if (this.config.glyph == 'JBrowse/View/FeatureGlyph/PairedArc') { this.config.viewAsSpans = true this.config.viewAsPairs = false } else if (this.config.glyph == 'JBrowse/View/FeatureGlyph/PairedAlignment' || this.config.glyph == 'JBrowse/View/FeatureGlyph/PairedReadCloud') { this.config.viewAsPairs = true this.config.viewAsSpans = false } else { this.config.viewAsPairs = false this.config.viewAsSpans = false } this.insertSizeStats = this.config.insertSizeStats // determine if alternate color scheme in use, otherwise make default var elts = [ 'defaultColor', 'useXS', 'useTS', 'useReverseTemplate', 'colorByOrientation', 'colorBySize', 'colorByOrientationAndSize', 'colorByMAPQ' ] if(!elts.some(e => this.config[e] == true)) { this.config.defaultColor = true } }, renderClickMap() { if (this.config.glyph == 'JBrowse/View/FeatureGlyph/PairedArc' || this.config.glyph == 'JBrowse/View/FeatureGlyph/PairedReadCloud') { return } else { this.inherited(arguments) } } }); });