Skip to content

Commit

Permalink
Add fasta viewer and editor
Browse files Browse the repository at this point in the history
  • Loading branch information
Seraff committed Jul 23, 2019
1 parent a4f5e85 commit d299fd9
Show file tree
Hide file tree
Showing 10 changed files with 668 additions and 286 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# fangorn
Newick + Fasta = Fangorn ❤️
27 changes: 27 additions & 0 deletions css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,32 @@
transform:rotate(135deg);
}

.btn-pressed {
background-color:#ddd;
background-image:none;
}

.btn:disabled,
.btn[disabled] {
background-color:red !important;
border-color: #C9C8C9;
background-color: #D2D0D2;
background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0,#D2D0D2),color-stop(100%,#29a03b));
background-image: -webkit-linear-gradient(top,#D2D0D2 0,#D2D0D2 100%);
background-image: linear-gradient(to bottom,#D2D0D2 0,#D2D0D2 100%);
color: #F7F7F7;
}

/* phylotree */

.node-selected {
fill: #3182bd !important;
}

.fasta-node-selected {
color: #3182bd;
}

.branch-selected {
stroke: #999 !important;
stroke-width: 2px;
Expand All @@ -38,3 +58,10 @@
stroke-width: 2;
stroke-dasharray: 5, 5;
}

dialog {
padding: 0px;
border-width: 1px;
border-color: #B1B1B1;
width: 400px;
}
53 changes: 42 additions & 11 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ <h1 class="title">Fangorn</h1>
<div class="toolbar-actions">

<div class="btn-group">
<button id="mark-node" class="btn btn-default">
<span class="ui-text fa fa-pencil"></span>
<button id="mark-node" class="btn btn-default mark-button" title="Remove sequence from fasta file">
<span class="icon icon-logout"></span>
</button>
<button id="unmark-node" class="btn btn-default">
<span class="ui-text fa icon-pencil-alt-solid-crossed"></span>
<button id="unmark-node" class="btn btn-default mark-button" title="Return sequence to fasta file">
<span class="icon icon-login"></span>
</button>
</div>

Expand Down Expand Up @@ -58,30 +58,61 @@ <h1 class="title">Fangorn</h1>
</button>
</div>

<button class="btn btn-default pull-right" title="Show fasta">
<button id="annotate-node-action" class="btn btn-default" title="Annotate node">
<span class="icon icon-pencil"></span>
</button>

<button id="show-fasta-action" class="btn btn-default pull-right" title="Show fasta">
<span class="icon icon-book-open"></span>
</button>

<button id="search-action" class="btn btn-default pull-right" title="Search">
<span class="icon icon-search"></span>
</button>

<button id="save-fasta-action" class="btn btn-positive pull-right" title="Save fasta">
Save fasta
</button>
</div>
</header>

<div class="window-content">
<div class="padded-more">
<!-- CONTENT -->

<svg id="tree_display" />

<!-- END CONTENT -->
<div class="pane-group">
<div class="pane">
<div class="padded" id="main-tree-container">
<svg id="tree_display" />
</div>
</div>
<div class="pane-one-third sidebar padded" id="fasta-panel" style="display: none; overflow: auto">
</div>
</div>
</div>

<footer class="toolbar toolbar-footer">
</footer>
</div>

<dialog id="annotate-dialog">
<header class="toolbar toolbar-header">
<h1 class="title">Edit the node title</h1>
</header>

<div class="padded-more">
<form>
<div class="form-group">
<input id="seq-title-input" type="text" class="form-control" placeholder="Title">
</div>
</form>
</div>

<footer class="toolbar toolbar-footer">
<div class="toolbar-actions">
<button id="annotation-dialog-save" class="btn btn-primary pull-right">Save</button>
<button id="annotation-dialog-cancel" class="btn btn-default pull-right">Cancel</button>
</div>
</footer>
</dialog>

<script>if (typeof module === 'object') {window.module = module; module = undefined;}</script>
<script src="js/vendor/jquery-3.4.0.min.js"></script>
<script src="js/vendor/underscore-min.js"></script>
Expand Down
114 changes: 93 additions & 21 deletions js/fangorn.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
const fs = require('fs');
const Fasta = require('biojs-io-fasta');
const Node = require('./node.js');
const FastaRepresentation = require('./fasta_representation.js');
const fs = require('fs');

function Fangorn(){
var fangorn = this;
var _nodes = null;
var _tree_content = null;
var _tree = null;
var _fasta_seqs = null;
var _fasta_path = null;
var _fasta = null;

fangorn.get_tree = function(){
return _tree;
}

fangorn.tree_is_loaded = function(){
return _tree != null;
}

fangorn.get_nodes = function(){
return _nodes;
}
Expand All @@ -22,10 +25,22 @@ function Fangorn(){
return _nodes.filter(function(node){ return node.is_leaf() });
}

fangorn.get_leave_names = function(){
return fangorn.get_leaves().map(function(node){ return node.name });
}

fangorn.nodes_are_loaded = function(){
return _nodes.length > 0;
}

fangorn.get_fasta = function(){
return _fasta;
}

fangorn.get_selection = function(){
return fangorn.get_tree().get_selection();
}

fangorn.init_phylotree = function(str){
_tree = d3.layout
.phylotree()
Expand Down Expand Up @@ -55,6 +70,15 @@ function Fangorn(){
_tree.style_nodes(nodeStyler);
_tree(str).layout();
d3.select(".phylotree-container").attr("align","center");

document.addEventListener('selection_modified', function(e){
fangorn.update_fasta_sidebar()
fangorn.dispatch_state_update();
});

_fasta = null;
fangorn.update_fasta_sidebar();
fangorn.dispatch_state_update();
}

fangorn.load_tree = function(path){
Expand All @@ -74,31 +98,79 @@ function Fangorn(){
}

fangorn.load_fasta = function(path){
if (!nodes_are_loaded()){
return false;
}
_fasta = FastaRepresentation(path);
_fasta.addEventListener('loaded', function(e){
if (!_fasta.check_consistency(fangorn.get_leave_names())){
_fasta = null;
} else {
fangorn.each_leaf(function(leaf){
leaf.apply_fasta(_fasta.sequences[leaf.name]);
})
fangorn.update_fasta_sidebar()
fangorn.dispatch_state_update();
}
});
}

var contents;
fangorn.save_fasta = function(){
var content = '';

try {
contents = fs.readFileSync(path, 'utf8');
} catch(err) {
console.error(err);
fangorn.each_leaf(function(leaf){
var raw_fasta = leaf.raw_fasta_entry();
if (raw_fasta != null)
content += raw_fasta;
});

console.log(content);

fs.writeFile(_fasta.out_path, content, function(err) {
if(err) {
return console.error(err);
}
});
}

fangorn.fasta_is_loaded = function(){
return _fasta != null;
}

fangorn.update_fasta_sidebar = function(){
var content = '';

if (fangorn.fasta_is_loaded()){
content = '<b class="ui-text">' + _fasta._out_filename + '</b></br>'
fangorn.each_leaf(function(leaf){
content += leaf.fasta_bar_entry()
});
} else {
content += '<b class="ui-text">No fasta loaded...</b>'
}

_fasta_seqs = Fasta.parse(contents);
$('#fasta-panel').html(content);
}

return _fasta_seqs ? true : false;
fangorn.dispatch_state_update = function(){
var event = new Event('fangorn_state_update');
document.dispatchEvent(event);
}

// check_fasta = function(){
// if (_fasta_seqs == null){
// return true;
// }
fangorn.each_leaf = function(f){
fangorn.get_leaves().forEach(function(leaf){
f(leaf);
})
}

// var
// _fasta_seqs.filter(function(seq){ });
// }
fangorn.update_node_title = function(node, title){
if (!fangorn.fasta_is_loaded())
return null;

var new_id = FastaRepresentation.extract_id(title);
node.name = new_id;
node.fasta.id = new_id;
node.fasta.title = title;

fangorn.update_fasta_sidebar()
}

return this;
}
Expand Down
69 changes: 66 additions & 3 deletions js/fasta_representation.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,73 @@
const Fasta = require('biojs-io-fasta');

var Fasta = require('bionode-fasta')
const fs = require('fs');
const p = require('path');
/**
* FastaRepresentation class doesn't know anything about Fangorn
* Stores fasta sequence
*/

function FastaRepresentation(){
function FastaRepresentation(path){
var fasta = this;
var _path = path;

fasta.sequences = {};

fasta._path = path;
fasta._src_filename = p.posix.basename(path);

f_name_array = fasta._src_filename.split('.')
f_name_array.splice(f_name_array.length - 1, 0, 'fangorn');

fasta._out_filename = f_name_array.join('.')
fasta.out_path = p.join(p.dirname(path), fasta._out_filename)

fasta.read_from_file = function(path){
try {
Fasta.obj(path)
.on('data', function(data) {
id = data.id.split(/\s/)[0];
data.title = data.id;
data.id = id
fasta.sequences[id] = data;
}).on('end', function(){
var event = new Event('loaded');
fasta.dispatchEvent(event);
});
} catch(err) {
console.error(err);
return false;
}
return true;
}

fasta.check_consistency = function(ids){
if (ids.length != Object.keys(fasta.sequences).length){
return false;
}

ids.forEach(function(id){
if (!fasta.sequences.hasOwnProperty(id))
return false;
})

return true;
}

fasta.each_sequence = function(f){
for (var k in fasta.sequences){
if (fasta.sequences.hasOwnProperty(k)) {
f(fasta.sequences[k]);
}
}
}

fasta.read_from_file(path);
return fasta;
}

FastaRepresentation.extract_id = function(title){
return title.split(/\s/)[0]
}


module.exports = FastaRepresentation;
Loading

0 comments on commit d299fd9

Please # to comment.