Skip to content

Commit

Permalink
Merge branch 'one_based_UI', fixes #74.
Browse files Browse the repository at this point in the history
All user-facing coordinates are now displayed as 1-based.
  • Loading branch information
rbuels committed Mar 2, 2012
2 parents 9f17485 + 902b069 commit 8d13807
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 52 deletions.
90 changes: 44 additions & 46 deletions js/Browser.js
Expand Up @@ -171,28 +171,24 @@ Browser.prototype.addRefseqs = function(refSeqs) {
if (this.params.location) {
this.navigateTo(this.params.location);
} else if (oldLocMap[this.refSeq.name]) {
this.navigateTo(this.refSeq.name
+ ":"
+ oldLocMap[this.refSeq.name]);
this.navigateTo( oldLocMap[this.refSeq.name] );
} else if (this.params.defaultLocation){
this.navigateTo(this.params.defaultLocation);
} else {
this.navigateTo(this.refSeq.name
+ ":"
+ ((((this.refSeq.start + this.refSeq.end)
* 0.4) | 0)
+ " .. "
+ (((this.refSeq.start + this.refSeq.end)
* 0.6) | 0)));
this.navigateTo( Util.assembleLocString({
ref: this.refSeq.name,
start: 0.4 * ( this.refSeq.start + this.refSeq.end ),
end: 0.6 * ( this.refSeq.start + this.refSeq.end )
})
);
}

dojo.connect(this.chromList, "onchange", function(event) {
var oldLocMap = dojo.fromJson(dojo.cookie(brwsr.container.id + "-location")) || {};
var newRef = brwsr.allRefs[brwsr.chromList.options[brwsr.chromList.selectedIndex].value];

if (oldLocMap[newRef.name])
brwsr.navigateTo(newRef.name + ":"
+ oldLocMap[newRef.name]);
brwsr.navigateTo( oldLocMap[newRef.name] );
else
brwsr.navigateTo(newRef.name + ":"
+ (((newRef.start + newRef.end) * 0.4) | 0)
Expand Down Expand Up @@ -352,33 +348,29 @@ Browser.prototype.navigateTo = function(loc) {
return;
}

loc = dojo.trim(loc);
// (chromosome) ( start ) ( sep ) ( end )
var matches = String(loc).match(/^(((\S*)\s*:)?\s*(-?[0-9,.]*[0-9])\s*(\.\.|-|\s+))?\s*(-?[0-9,.]+)$/i);
//matches potentially contains location components:
//matches[3] = chromosome (optional)
//matches[4] = start base (optional)
//matches[6] = end base (or center base, if it's the only one)
if (matches) {
if (matches[3]) {
var refName;
for (ref in this.allRefs) {
if ((matches[3].toUpperCase() == ref.toUpperCase())
var location = Util.parseLocString( loc ),
refName;

if( location ) {
if( location.ref ) {
for( ref in this.allRefs ) {
if( location.ref.toUpperCase() == ref.toUpperCase()
||
("CHR" + matches[3].toUpperCase() == ref.toUpperCase())
"CHR" + location.ref.toUpperCase() == ref.toUpperCase()
||
(matches[3].toUpperCase() == "CHR" + ref.toUpperCase())) {
location.ref.toUpperCase() == "CHR" + ref.toUpperCase()
) {

refName = ref;
}
}
if (refName) {
if( refName ) {
dojo.cookie(this.container.id + "-refseq", refName, {expires: 60});
if (refName == this.refSeq.name) {
if(refName == this.refSeq.name) {
//go to given start, end on current refSeq
this.view.setLocation(this.refSeq,
parseInt(matches[4].replace(/[,.]/g, "")),
parseInt(matches[6].replace(/[,.]/g, "")));
location.start,
location.end );
} else {
//new refseq, record open tracks and re-open on new refseq
var curTracks = [];
Expand All @@ -392,26 +384,28 @@ Browser.prototype.navigateTo = function(loc) {
this.refSeq = this.allRefs[refName];
//go to given refseq, start, end
this.view.setLocation(this.refSeq,
parseInt(matches[4].replace(/[,.]/g, "")),
parseInt(matches[6].replace(/[,.]/g, "")));
location.start,
location.end );

this.viewDndWidget.insertNodes(false, curTracks);
this.onVisibleTracksChanged();
}
return;
}
} else if (matches[4]) {
} else if( location.start ) {
//go to start, end on this refseq
this.view.setLocation(this.refSeq,
parseInt(matches[4].replace(/[,.]/g, "")),
parseInt(matches[6].replace(/[,.]/g, "")));
location.start,
location.end );
return;
} else if (matches[6]) {
} else if( location.end ) {
//center at given base
this.view.centerAtBase(parseInt(matches[6].replace(/[,.]/g, "")));
this.view.centerAtBase( location.end );
return;
}
}


//if we get here, we didn't match any expected location format

var brwsr = this;
Expand Down Expand Up @@ -481,11 +475,15 @@ Browser.prototype.showTracks = function(trackNameList) {
};

/**
* @returns {String} string representation of the current location<br>
* @returns {String} locstring representation of the current location<br>
* (suitable for passing to navigateTo)
*/
Browser.prototype.visibleRegion = function() {
return this.view.ref.name + ":" + Math.round(this.view.minVisible()) + ".." + Math.round(this.view.maxVisible());
return Util.assembleLocString({
ref: this.view.ref.name,
start: this.view.minVisible(),
end: this.view.maxVisible()
});
};

/**
Expand Down Expand Up @@ -517,18 +515,18 @@ Browser.prototype.onCoarseMove = function(startbp, endbp) {
//since this method gets triggered by the initial GenomeView.sizeInit,
//we don't want to save whatever location we happen to start at
if (! this.isInitialized) return;
var locString = Util.addCommas(Math.round(startbp)) + " .. " + Util.addCommas(Math.round(endbp));
var locString = Util.assembleLocString({ start: startbp, end: endbp, ref: this.refSeq.name });
this.locationBox.value = locString;
this.goButton.disabled = true;
this.locationBox.blur();
var oldLocMap = dojo.fromJson(dojo.cookie(this.container.id + "-location"));
if ((typeof oldLocMap) != "object") oldLocMap = {};

// update the location cookie
var ckname = this.container.id + "-location";
var oldLocMap = dojo.fromJson( dojo.cookie(ckname ) ) || {};
oldLocMap[this.refSeq.name] = locString;
dojo.cookie(this.container.id + "-location",
dojo.toJson(oldLocMap),
{expires: 60});
dojo.cookie( ckname, dojo.toJson(oldLocMap), {expires: 60});

document.title = this.refSeq.name + ":" + locString;
document.title = locString;
};

/**
Expand Down
3 changes: 2 additions & 1 deletion js/GenomeView.js
Expand Up @@ -785,7 +785,7 @@ GenomeView.prototype.sizeInit = function() {
track.sizeInit(view.overviewStripes,
overviewStripePct);
track.showRange(0, view.overviewStripes - 1,
0, view.overviewStripeBases,
-1, view.overviewStripeBases,
view.overviewBox.w /
(view.ref.end - view.ref.start));
});
Expand Down Expand Up @@ -1024,6 +1024,7 @@ GenomeView.prototype.showVisibleBlocks = function(updateHeight, pos, startX, end

var startBase = Math.round(this.pxToBp((leftVisible * this.stripeWidth)
+ this.offset));
startBase -= 1;
var containerStart = Math.round(this.pxToBp(this.offset));
var containerEnd =
Math.round(this.pxToBp(this.offset
Expand Down
9 changes: 9 additions & 0 deletions js/SequenceTrack.js
Expand Up @@ -53,6 +53,7 @@ SequenceTrack.prototype.setViewInfo = function(genomeView, numBlocks,
this.setLabel(this.key);
};

SequenceTrack.nbsp = String.fromCharCode(160);
SequenceTrack.prototype.fillBlock = function(blockIndex, block,
leftBlock, rightBlock,
leftBase, rightBase,
Expand All @@ -68,6 +69,14 @@ SequenceTrack.prototype.fillBlock = function(blockIndex, block,
this.getRange(leftBase, rightBase,
function(start, end, seq) {
//console.log("adding seq from %d to %d: %s", start, end, seq);

// fill with leading blanks if the
// sequence does not extend all the way
// across our range
for( ; start < 0; start++ ) {
seq = SequenceTrack.nbsp + seq; //nbsp is an "&nbsp;" entity
}

var seqNode = document.createElement("div");
seqNode.className = "sequence";
seqNode.appendChild(document.createTextNode(seq));
Expand Down
2 changes: 1 addition & 1 deletion js/StaticTrack.js
Expand Up @@ -15,7 +15,7 @@ StaticTrack.prototype.fillBlock = function(blockIndex, block,
leftBase, rightBase, scale,
padding, stripeWidth) {
var posLabel = document.createElement("div");
var numtext = Util.addCommas( leftBase );
var numtext = Util.addCommas( leftBase+1 );
posLabel.className = this.labelClass;
posLabel.style.left = "-" + Number(numtext.length)/1.7 + "ex";
posLabel.appendChild( document.createTextNode( numtext ) );
Expand Down
60 changes: 60 additions & 0 deletions js/Util.js
Expand Up @@ -169,6 +169,66 @@ Util.resolveUrl = function(baseUrl, relativeUrl) {
return baseUrl + relativeUrl;
};

Util.parseLocString = function( locstring ) {
locstring = dojo.trim( locstring );

// (chromosome) ( start ) ( sep ) ( end )
var matches = locstring.match(/^(((\S*)\s*:)?\s*(-?[0-9,.]*[0-9])\s*(\.\.|-|\s+))?\s*(-?[0-9,.]+)$/i);
//matches potentially contains location components:
//matches[3] = chromosome (optional)
//matches[4] = start base (optional)
//matches[6] = end base (or center base, if it's the only one)

// parses a number from a locstring that's a coordinate, and
// converts it from 1-based to interbase coordinates
var parseCoord = function( coord ) {
var num = parseInt( String(coord).replace(/[,.]/g, "") );
return typeof num == 'number' && !isNaN(num) ? num : null;
};

if( !matches )
return null;

return {
start: parseCoord( matches[4] )-1,
end: parseCoord( matches[6] ),
ref: matches[3]
};
};

Util.assembleLocString = function( loc_in ) {
var s = '',
types = { start: 'number', end: 'number', ref: 'string' },
location = {}
;

// filter the incoming loc_in to only pay attention to slots that we
// know how to handle
for( var slot in types ) {
if( types[slot] == typeof loc_in[slot]
&& (types[slot] != 'number' || !isNaN(loc_in[slot])) //filter any NaNs
) {
location[slot] = loc_in[slot];
}
}

//finally assembly our string
if( 'ref' in location ) {
s += location.ref;
if( location.start || location.end )
s += ':';
}
if( 'start' in location ) {
s += (Math.round(location.start)+1).toLocaleString();
if( 'end' in location )
s+= '..';
}
if( 'end' in location )
s += Math.round(location.end).toLocaleString();

return s;
};

if (!Array.prototype.reduce)
{
Array.prototype.reduce = function(fun /*, initial*/)
Expand Down
5 changes: 5 additions & 0 deletions release-notes.txt
@@ -1,5 +1,10 @@
{{$NEXT}}

* Coordinates displayed in the user interface are now 1-based closed
coordinates, which are more familiar to most users. Previously,
the labels displayed interbase (i.e. 0-based half-open)
coordinates.

* NON-BACKWARDS-COMPATIBLE improvements to the JSON format used for
track configuration, feature data, and image data
- initial support for a new hook system for greater
Expand Down
9 changes: 5 additions & 4 deletions tests/selenium_tests/volvox_biodb_test.py
Expand Up @@ -75,13 +75,14 @@ def scroll(browser):
def sequence(browser):
do_typed_query( browser, '0..80' );
#turn_on_track( browser, 'DNA' );
assert_element( browser,"/html//div[contains(@class,'sequence')][contains(.,'TCTCtcact')]")
sequence_div_xpath = "/html//div[contains(@class,'sequence')][contains(.,'aacaACGG')]"
assert_element( browser, sequence_div_xpath )
turn_off_track( browser, 'DNA' );
assert_no_element( browser,"/html//div[contains(@class,'sequence')][contains(.,'TCTCtcact')]")
assert_no_element( browser, sequence_div_xpath )
turn_on_track( browser, 'DNA' );
assert_element( browser,"/html//div[contains(@class,'sequence')][contains(.,'TCTCtcact')]")
assert_element( browser, sequence_div_xpath )
do_typed_query( browser, '1..20000');
assert_no_element( browser,"/html//div[contains(@class,'sequence')][contains(.,'TCTCtcact')]")
assert_no_element( browser, sequence_div_xpath )



Expand Down

0 comments on commit 8d13807

Please sign in to comment.