diff --git a/css/dalliance-scoped.css b/css/dalliance-scoped.css index 1e9a2a12..33c89a33 100644 --- a/css/dalliance-scoped.css +++ b/css/dalliance-scoped.css @@ -29,7 +29,7 @@ display: -webkit-inline-flex; flex-direction: column; -webkit-flex-direction: column; - max-height: 500px; + max-height: 1000px; width: 100%; outline: none; } diff --git a/index.js b/index.js new file mode 100644 index 00000000..052684db --- /dev/null +++ b/index.js @@ -0,0 +1,6 @@ +module.exports = { + browser: require('./js/cbrowser').Browser, + chainset: require('./js/chainset').Chainset, + sourcesAreEqual: require('./js/sourcecompare').sourcesAreEqual, + makeElement: require('./js/utils').makeElement +}; diff --git a/js/bedwig.js b/js/bedwig.js index 2c742f4b..27b66699 100644 --- a/js/bedwig.js +++ b/js/bedwig.js @@ -26,6 +26,8 @@ if (typeof(require) !== 'undefined') { var utils = require('./utils'); var shallowCopy = utils.shallowCopy; + + var revalidator = require('revalidator'); } @@ -162,6 +164,87 @@ BedParseSession.prototype.parse = function(line) { BedParseSession.prototype.flush = function() {}; +BedParseSession.prototype.schema = { + properties: { + segment: { + description: 'the name of the region containing the feature', + type: 'string', + required: true + }, + min: { + description: 'the start position of the feature (0-based)', + type: 'integer', + minimum: 1, + required: true + }, + max: { + description: 'the end position of the feature (1-based)', + type: 'integer', + required: true + }, + label: { + description: 'the label for a feature', + type: 'string' + }, + score: { + description: 'score of a feature', + type: 'float' + }, + orientation: { + description: 'strand of feature', + type: 'string', + enum: ['+','-','.'] + }, + itemRGB: { + description: 'rgb color to draw feature', + type: 'string', + pattern: '^rgb\([0-9]+,[0-9]+,[0-9]+)$' + }, + type: { + description: 'type of feature', + type: 'string', + enum: ['transcript','translation'] + }, + readingFrame: { + description: 'open reading frame at the start of the interval', + type: 'integer', + minimum: 0, + maximum: 2 + }, + groups: { + description: 'list of groups that a feature belongs to', + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + required: true + }, + label: { + type: 'string', + required: true + }, + type: { + type: 'string', + required: true + } + } + } + } + } +}; + +BedParseSession.prototype.validate = function(f) { + // use revalidator.validate to check if feature f is valid + var check = revalidator.validate(f, this.schema); + if (check.valid) { + this.sink(f); + } else { + console.log(check.errors); + } +} + function WigParseSession(parser, sink) { this.parser = parser; this.sink = sink; @@ -234,6 +317,42 @@ WigParseSession.prototype.parse = function(line) { WigParseSession.prototype.flush = function() {}; +WigParseSession.prototype.schema = { + properties: { + segment: { + description: 'the name of the region containing the feature', + type: 'string', + required: true + }, + min: { + description: 'the start position of the feature', + type: 'integer', + minimum: 1, + required: true + }, + max: { + description: 'the end position of the feature', + type: 'integer', + required: true + }, + score: { + description: 'score of a feature', + type: 'float', + required: true + } + } +}; + +WigParseSession.prototype.validate = function(f) { + // use revalidator.validate to check if feature f is valid + var check = revalidator.validate(f, this.schema); + if (check.valid) { + this.sink(f); + } else { + console.log(check.errors); + } +} + BedWigParser.prototype.getStyleSheet = function(callback) { var thisB = this; var stylesheet = new DASStylesheet(); diff --git a/js/cbrowser.js b/js/cbrowser.js index c1a631eb..915397f8 100644 --- a/js/cbrowser.js +++ b/js/cbrowser.js @@ -177,14 +177,39 @@ function Browser(opts) { if (opts.baseColors) { this.baseColors = opts.baseColors } else { - this.baseColors = { - A: 'green', - C: 'blue', - G: 'orange', - T: 'red', - '-' : 'hotpink', // deletion - 'I' : 'red' // insertion - }; + if (opts.aminoAcids) { + this.baseColors = { + F: 'rgb(182, 201, 237)', + L: 'rgb(213, 236, 213)', + I: 'rgb(239, 213, 211)', + M: 'rgb(255, 23, 0)', + V: 'rgb(255, 172, 0)', + S: 'rgb(255, 244, 19)', + P: 'rgb(138, 189, 67)', + T: 'rgb(36, 153, 57)', + A: 'rgb(0, 166, 236)', + Y: 'rgb(0, 101, 178)', + H: 'rgb(215, 206, 182)', + Q: 'rgb(252, 176, 124)', + N: 'rgb(159, 148, 186)', + K: 'rgb(133, 117, 67)', + D: 'rgb(108, 242, 51)', + E: 'rgb(0, 253, 255)', + C: 'rgb(248, 129, 51)', + W: 'rgb(243, 68, 252)', + R: 'rgb(207, 207, 207)', + G: 'rgb(166, 213, 227)' + }; + } else { + this.baseColors = { + A: 'green', + C: 'blue', + G: 'orange', + T: 'red', + '-' : 'hotpink', // deletion + 'I' : 'red' // insertion + }; + } } if (opts.viewStart !== undefined && typeof(opts.viewStart) !== 'number') { @@ -2653,6 +2678,7 @@ if (typeof(module) !== 'undefined') { var sa = require('./sourceadapters'); var TwoBitSequenceSource = sa.TwoBitSequenceSource; var EnsemblSequenceSource = sa.EnsemblSequenceSource; + var EnsemblProteinSequenceSource = sa.EnsemblProteinSequenceSource; var DASSequenceSource = sa.DASSequenceSource; var KnownSpace = require('./kspace').KnownSpace; diff --git a/js/memstore.js b/js/memstore.js index 7377fce7..2b17b68b 100644 --- a/js/memstore.js +++ b/js/memstore.js @@ -151,6 +151,7 @@ function MemStoreFeatureSource(source) { this.source = source; FeatureSourceBase.call(this); this.storeHolder = new Awaited(); + this.parser = dalliance_makeParser(source.payload); if (!this.parser) { throw "Unsupported memstore payload: " + source.payload; @@ -164,17 +165,24 @@ function MemStoreFeatureSource(source) { } else { var store = new MemStore(); var features = []; - var lines = resp.split('\n'); - var session = thisB.parser.createSession(function(f) {features.push(f)}); - for (var li = 0; li < lines.length; ++li) { - var line = lines[li]; - if (line.length > 0) { - session.parse(line); - } + + if (err === 'already parsed') { + resp.forEach(function(feat) { + session.validate(feat); + }); } - session.flush(); + else { + var lines = resp.split('\n'); + for (var li = 0; li < lines.length; ++li) { + var line = lines[li]; + if (line.length > 0) { + session.parse(line); + } + } + session.flush(); + } store.addFeatures(features); thisB.storeHolder.provide(store); @@ -191,6 +199,8 @@ MemStoreFeatureSource.prototype._load = function(callback) { return callback(r.result, r.error); } r.readAsText(this.source.blob); + } else if (this.source.features) { + return callback(this.source.features, 'already parsed'); } else { if (this.source.credentials) var opts = {credentials : this.source.credentials}; diff --git a/js/sourceadapters.js b/js/sourceadapters.js index f51f5b5a..6f7fdaa3 100644 --- a/js/sourceadapters.js +++ b/js/sourceadapters.js @@ -1,4 +1,4 @@ -/* -*- mode: javascript; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +//* -*- mode: javascript; c-basic-offset: 4; indent-tabs-mode: nil -*- */ // // Dalliance Genome Explorer @@ -108,7 +108,11 @@ Browser.prototype.createSources = function(config) { if (config.twoBitURI || config.twoBitBlob) { ss = new TwoBitSequenceSource(config); } else if (config.ensemblURI) { - ss = new EnsemblSequenceSource(config); + if (config.aminoAcids) { + ss = new EnsemblProteinSequenceSource(config); + } else { + ss = new EnsemblSequenceSource(config); + } } else { ss = new DASSequenceSource(config); } @@ -590,9 +594,7 @@ TwoBitSequenceSource.prototype.getSeqInfo = function(chr, cnt) { }; function EnsemblSequenceSource(source) { - this.source = source; - // http://data.gramene.org/ensembl/info/assembly/triticum_aestivum/2B?content-type=application/json - // http://data.gramene.org/ensembl/sequence/region/triticum_aestivum/2B:8001..18000:1?content-type=application/json + this.source = source; } EnsemblSequenceSource.prototype.fetch = function(chr, min, max, pool, callback) { @@ -642,6 +644,55 @@ EnsemblSequenceSource.prototype.getSeqInfo = function(chr, cnt) { req.send(); }; +function EnsemblProteinSequenceSource(source) { + this.source = source; +} + +EnsemblProteinSequenceSource.prototype.fetch = function(chr, min, max, pool, callback) { + var url = this.source.ensemblURI + '/sequence/id/' + chr + '?type=protein&content-type=application/json'; + var req = new XMLHttpRequest(); + req.onreadystatechange = function() { + if (req.readyState == 4) { + if (req.status >= 300) { + var err = 'Error code ' + req.status; + try { + var jr = JSON.parse(req.response); + if (jr.error) { + err = jr.error; + } + } catch (ex) {}; + + callback(err, null); + } else { + var jr = JSON.parse(req.response); + var sequence = new DASSequence(chr, 1, jr.seq.length + 1, 'PEP', jr.seq); + return callback(null, sequence); + } + } + } + req.open('GET', url, true); + req.responseType = 'text'; + req.send(''); +} + +EnsemblProteinSequenceSource.prototype.getSeqInfo = function(chr, cnt) { + var url = this.source.ensemblURI + '/sequence/id/' + chr + '?type=protein&content-type=application/json'; + var req = new XMLHttpRequest(); + req.onreadystatechange = function() { + if (req.readyState == 4) { + if (req.status >= 300) { + cnt(); + } else { + var jr = JSON.parse(req.response); + cnt({length:jr.seq.length}); + } + } + } + req.open('GET', url, true); + req.responseType = 'text'; + req.send(''); +} + DASFeatureSource.prototype.getScales = function() { return []; }; @@ -1753,6 +1804,7 @@ if (typeof(module) !== 'undefined') { TwoBitSequenceSource: TwoBitSequenceSource, EnsemblSequenceSource: EnsemblSequenceSource, + EnsemblProteinSequenceSource: EnsemblProteinSequenceSource, DASSequenceSource: DASSequenceSource, MappedFeatureSource: MappedFeatureSource, CachingFeatureSource: CachingFeatureSource, diff --git a/js/vcf.js b/js/vcf.js index 2a47a191..a5137abb 100644 --- a/js/vcf.js +++ b/js/vcf.js @@ -18,6 +18,8 @@ if (typeof(require) !== 'undefined') { var DASStyle = das.DASStyle; var DASFeature = das.DASFeature; var DASGroup = das.DASGroup; + + var revalidator = require('revalidator'); } function VCFParser() { @@ -106,6 +108,68 @@ VCFParseSession.prototype.parse = function(line) { VCFParseSession.prototype.flush = function() {}; +VCFParseSession.prototype.schema = { + properties: { + segment: { + description: 'the name of the region containing the variation', + type: 'string', + required: true + }, + min: { + description: 'the start position', + type: 'integer', + minimum: 1, + required: true + }, + max: { + description: 'the end position', + type: 'integer', + required: true + }, + id: { + description: 'identifier of variant', + type: 'string', + required: true + }, + refAllele: { + description: 'reference allele', + type: 'string', + required: true + }, + altAlleles: { + description: 'list of alternate alleles', + type: 'array', + items: { type: 'string' }, + required: true + }, + info: { + description: 'info', + type: 'object', + required: true + }, + type: { + description: 'type of variation', + type: 'string', + enum: ['insertion','deletion','substitution'], + required: true + }, + insertion: { + description: 'the actual insertion', + type: 'string' + } + } +}; + +VCFParseSession.prototype.validate = function(f) { + // use revalidator.validate to check if feature f is valid + var check = revalidator.validate(f, this.schema); + if (check.valid) { + this.sink(f); + } else { + console.log(check.errors); + } +} + VCFParser.prototype.getStyleSheet = function(callback) { var stylesheet = new DASStylesheet(); diff --git a/package.json b/package.json index 92621e01..4a295ef1 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.13.90-alpha.1", "description": "Fast, embeddable genome visualization", "homepage": "https://www.biodalliance.org/", + "main": "index.js", "license": "BSD-2-Clause", "repository": { "type": "git", @@ -11,6 +12,8 @@ "dependencies": { "es6-promise": "~3.0.2", "jszlib": "dasmoth/jszlib", + "es6-promise": "~0.1.1", + "revalidator": "~0.3.1", "ramda": "^0.21.0" }, "devDependencies": {