@gmod/jbrowse
Version:
JBrowse - client-side genome browser
202 lines (187 loc) • 9.62 kB
JavaScript
define([
'dojo/_base/declare',
'dojo/_base/lang',
'dojo/Deferred',
'dojo/dom-construct',
'dojo/query',
'JBrowse/View/Track/HTMLFeatures'
],
function (
declare,
lang,
Deferred,
domConstruct,
query,
HTMLFeatures
) {
return declare(HTMLFeatures, {
renderFeature: function (feature, uniqueId, block, scale, labelScale, descriptionScale, containerStart, containerEnd) {
var featureNode = this.inherited(arguments);
var thisB = this;
if (featureNode) { // In case the feature was not rendered (too many)
thisB.insertIntrons(featureNode);
thisB.paintNeatFeatures(featureNode);
}
return featureNode;
},
insertIntrons: function (featureNode) {
// ignore if we have already processed this node
if (!dojo.hasClass(featureNode, 'has-neat-introns')) {
// get the subfeatures nodes (only immediate children)
var subNodesX = query('> .subfeature', featureNode);
// filter nodes - eliminate nodes that are splice sites (for Apollo)
var subNodes = [];
for (var i = 0; i < subNodesX.length; i++) {
var attr = dojo.attr(subNodesX[i], 'class');
if (attr.indexOf('splice-site') === -1
&& attr.indexOf('Shine_Dalgarno_sequence') === -1
&& attr.indexOf('stop_codon_read_through') === -1
&& attr.indexOf('intron') === -1) {
subNodes.push(subNodesX[i]);
}
}
if (subNodes.length < 2) {
// apply introns to all feature tracks
var subFeatureIntron = query('div.feature-render', featureNode);
// added to handle apollo annotation classes: https://github.com/GMOD/Apollo/issues/1417
if (subFeatureIntron && subFeatureIntron.length == 1 && subFeatureIntron[0].className.indexOf('annot-apollo') < 0 && subFeatureIntron[0].className.indexOf('annot-render') < 0) {
var left = featureNode.style.left;
var width = featureNode.style.width;
var height = '100%';
var str = '';
str += "<svg class='jb-intron' viewBox='0 0 100 100' preserveAspectRatio='none' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' ";
str += "style='position:absolute;z-index: 15;"; // this must be here and not in CSS file
str += 'left: ' + left + ';width: ' + width + ';height: ' + height + "'>";
str += "<polyline points='0,50 100,50' style='fill:none;stroke:black;stroke-width:5' shape-rendering='optimizeQuality' />";
str += '</svg>';
// note: dojo.create("svg") does not render due to namespace issue between DOM and SVG
domConstruct.place(str, featureNode);
}
} else if (subNodes.length >= 2) {
// identify directionality
var classAttr = dojo.attr(featureNode, 'class');
var direction = 1;
if (classAttr.indexOf('minus') > -1) {
direction = -1;
}
// extract some left & width - more convient to access
for (var i = 0; i < subNodes.length; i++) {
subNodes[i].left = dojo.getStyle(subNodes[i], 'left');
subNodes[i].width = dojo.getStyle(subNodes[i], 'width');
}
// sort the subfeatures
if (subNodes.length >= 2) {
subNodes.sort(function (a, b) { return a.left - b.left; });
}
// insert introns between subfeature gaps
for (var i = 0; i < subNodes.length - 1; ++i) {
var gap = subNodes[i + 1].left - (subNodes[i].left + subNodes[i].width);
if (gap > 0.02) {
var subLeft = subNodes[i].left + subNodes[i].width;
var subWidth = subNodes[i + 1].left - (subNodes[i].left + subNodes[i].width);
var left = subLeft;
var width = subWidth;
var height = '100%';
// invert hat if reverse direction
var dir = '50,5';
if (direction == -1) dir = '50,95';
var str = '';
str += "<svg class='jb-intron' viewBox='0 0 100 100' preserveAspectRatio='none' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' ";
str += "style='position:absolute;z-index: 15;"; // this must be here and not in CSS file
str += 'left: ' + left + '%;width: ' + width + '%;height: ' + height + "'>";
str += "<polyline class='neat-intron' points='0,50 " + dir + " 100,50' shape-rendering='optimizeQuality' />";
str += '</svg>';
// note: dojo.create("svg") does not render due to namespace issue between DOM and SVG
domConstruct.place(str, featureNode);
}
}
}
}
},
/*
* Paint neat features and subfeatures
*/
paintNeatFeatures: function (featureNode) {
// get the subfeature nodes (only immediate children)
var subNodesX = query('> .subfeature', featureNode);
var thisB = this;
// filter nodes - eliminate nodes that are splice sites (for Apollo)
var subNodes = [];
for (var i = 0; i < subNodesX.length; i++) {
var attr = dojo.attr(subNodesX[i], 'class');
if (attr.indexOf('splice-site') === -1) {
subNodes.push(subNodesX[i]);
}
}
// if feature has subfeatures
if (subNodes.length) {
dojo.setStyle(featureNode, {
'background': 'none',
'background-color': 'transparent',
'border-width': '0px'
});
// paint subfeatures
for (var i = 0; i < subNodes.length; i++) {
// if this is Apollo, we have another subfeature level to traverse
if (subNodes[i].childElementCount) {
// get the subfeature nodes (only immediate children)
var childNodes = query('> .subfeature', subNodes[i]);
for (var j = 0; j < childNodes.length; j++) {
thisB.paintSubNode(childNodes[j]);
}
}
// handle the first level subfeature
else {
thisB.paintSubNode(subNodes[i]);
}
}
}
// paint features that have no subfeatures
else {
// ignore if we have already processed node
if (!dojo.hasClass(featureNode, 'neat-feature')) {
var classAttr = dojo.attr(featureNode, 'class');
var color = dojo.getStyle(featureNode, 'background-color');
// update the element with new styling
// if(dojo.hasClass(featureNode,'neat-linear-shading')){
if (this.config.gradient == 1) {
dojo.setStyle(featureNode, {
'background': 'linear-gradient(to bottom, ' + color + ' 0%,#e5e5e5 50%,' + color + ' 100%)'
});
}
dojo.addClass(featureNode, 'neat-feature');
}
}
},
/*
* apply neat modifications to feature sub-nodes
*/
paintSubNode: function (subNode) {
var classAttr = dojo.attr(subNode, 'class');
var color = dojo.getStyle(subNode, 'background-color');
// ignore if we have already processed node
if (!dojo.hasClass(subNode, 'neat-subfeature')) {
// restyle UTR
if (classAttr.indexOf('UTR') > -1) {
dojo.setStyle(subNode, {
'top': '0px',
'border': '1px solid ' + color
});
// mark as neat subfeature
dojo.addClass(subNode, 'neat-UTR');
}
else {
// if(classAttr.indexOf('CDS') > -1 || classAttr.indexOf('exon') > -1) {
if (this.config.gradient == 1) {
dojo.setStyle(subNode, {
'top': '0px',
'background': 'linear-gradient(to bottom, ' + color + ' 0%,#e5e5e5 50%,' + color + ' 100%)'
});
}
}
// mark that we have processed the node
dojo.addClass(subNode, 'neat-subfeature');
}
}
});
});