Combining changesets for tracks (lots of noise and not a lot of signal). This is a rough prototype of the tracks extension to Galaxy.
@@ -29,6 +29,7 @@
|
||||
<datatype extension="html" type="galaxy.datatypes.images:Html" mimetype="text/html"/>
|
||||
<datatype extension="interval" type="galaxy.datatypes.interval:Interval" display_in_upload="true">
|
||||
<converter file="interval_to_bed_converter.xml" target_datatype="bed"/>
|
||||
<indexer file="interval.xml" />
|
||||
</datatype>
|
||||
<datatype extension="jpg" type="galaxy.datatypes.images:Image" mimetype="image/jpeg"/>
|
||||
<datatype extension="laj" type="galaxy.datatypes.images:Laj"/>
|
||||
|
||||
@@ -18,6 +18,7 @@ import tempfile
|
||||
from bisect import bisect
|
||||
|
||||
def divide( intervals, out_path ):
|
||||
manifest = {}
|
||||
current_file = None
|
||||
lastchrom = ""
|
||||
for line in intervals:
|
||||
@@ -25,6 +26,7 @@ def divide( intervals, out_path ):
|
||||
chrom = line.chrom
|
||||
except AttributeError, e:
|
||||
continue
|
||||
manifest[chrom] = max(manifest.get(chrom,0),line.end)
|
||||
if not lastchrom == chrom:
|
||||
if current_file:
|
||||
current_file.flush()
|
||||
@@ -34,6 +36,7 @@ def divide( intervals, out_path ):
|
||||
lastchrom = chrom
|
||||
current_file.flush()
|
||||
current_file.close()
|
||||
return manifest
|
||||
|
||||
if __name__ == "__main__":
|
||||
options, args = doc_optparse.parse( __doc__ )
|
||||
@@ -56,5 +59,9 @@ if __name__ == "__main__":
|
||||
end_col=end_col_1,
|
||||
strand_col=strand_col_1,
|
||||
fix_strand=True )
|
||||
divide( interval, out_path )
|
||||
manifest = divide( interval, out_path )
|
||||
manifest_file = open( os.path.join( out_path, "manifest.tab" ),"w" )
|
||||
for key, value in manifest.items():
|
||||
print >> manifest_file, "%s\t%s" % (key, value)
|
||||
manifest_file.close()
|
||||
temp_file.close()
|
||||
|
||||
@@ -298,10 +298,10 @@ class Interval( Tabular ):
|
||||
record_start = int(fields[dataset.metadata.startCol-1])
|
||||
record_end = int(fields[dataset.metadata.endCol-1])
|
||||
if record_start < end and record_end > start:
|
||||
window.append( fields ) #Yes I did want to use a generator here, but it doesn't work downstream
|
||||
window.append( (record_start, record_end) ) #Yes I did want to use a generator here, but it doesn't work downstream
|
||||
return window
|
||||
|
||||
def get_track_resolution( self, dataset, data, start, end):
|
||||
def get_track_resolution( self, dataset, start, end):
|
||||
return None
|
||||
|
||||
class Bed( Interval ):
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
1 2
|
||||
3 4
|
||||
@@ -18,9 +18,24 @@ class TrackStoreManager( object ):
|
||||
return TrackStore( path=s.substitute(TemplateSubber(dataset)) )
|
||||
|
||||
class TrackStore( object ):
|
||||
MANIFEST_NAME = "manifest.tab"
|
||||
|
||||
def __init__(self, path=""):
|
||||
self.path = path
|
||||
|
||||
def get_manifest( self ):
|
||||
if not self.exists: raise self.DoesNotExist("TrackStore at %s does not exist." % self.path)
|
||||
manifest_path = os.path.join( self.path, self.MANIFEST_NAME )
|
||||
if os.path.exists( manifest_path ):
|
||||
manifest = {}
|
||||
for line in open( manifest_path ):
|
||||
line = line.rstrip("\n\r")
|
||||
fields = line.split("\t")
|
||||
manifest[fields[0]] = fields[1:]
|
||||
return manifest
|
||||
else:
|
||||
raise self.DoesNotExist( "Manifest for TrackStore object could not be found." )
|
||||
|
||||
def get(self, chrom="chr1", resolution=None, **kwargs):
|
||||
if not self.exists: raise self.DoesNotExist("TrackStore at %s does not exist." % self.path)
|
||||
object_path = self._get_object_path( chrom, resolution )
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
from mako import exceptions
|
||||
from mako.template import Template
|
||||
from mako.lookup import TemplateLookup
|
||||
from galaxy.web.base.controller import *
|
||||
from galaxy.web.framework import simplejson
|
||||
from galaxy import web
|
||||
from galaxy.tracks import messages
|
||||
import mimeparse
|
||||
from galaxy.util.json import to_json_string
|
||||
|
||||
class MultiResponse(object):
|
||||
"""
|
||||
Shamelessly ripped off of a django snippet.
|
||||
"""
|
||||
def __init__(self, handlers):
|
||||
self.handlers = handlers
|
||||
|
||||
def __call__(self, view_func):
|
||||
def wrapper(that, trans, *args, **kwargs):
|
||||
data_resource = view_func(that, trans, *args, **kwargs)
|
||||
content_type = mimeparse.best_match(self.handlers.keys(),
|
||||
trans.request.environ['HTTP_ACCEPT'])
|
||||
response = self.handlers[content_type](data_resource, trans)
|
||||
trans.response.headers['Content-Type'] = "%s" % content_type
|
||||
return response
|
||||
return wrapper
|
||||
|
||||
@classmethod
|
||||
def JSON( cls, data_resource, trans ):
|
||||
return simplejson.dumps( data_resource )
|
||||
|
||||
class XML( object ):
|
||||
def __call__(self, data_resource, trans ):
|
||||
raise NotImplementedError( "XML MultiResponse handler is not implemented." )
|
||||
|
||||
class AMF( object ):
|
||||
def __call__(self, data_resource, trans ):
|
||||
raise NotImplementedError( "XML MultiResponse handler is not implemented." )
|
||||
|
||||
class HTML( object ):
|
||||
def __init__(self, template ):
|
||||
self.template = template
|
||||
|
||||
def __call__(self, data_resource, trans ):
|
||||
return trans.fill_template( self.template, **data_resource )
|
||||
|
||||
class DEBUG( HTML ):
|
||||
def __call__(self, data_resource, trans ):
|
||||
return trans.fill_template( self.template, data_resource=data_resource)
|
||||
|
||||
class WebRoot( BaseController ):
|
||||
|
||||
@web.expose
|
||||
@MultiResponse( {'text/html':MultiResponse.JSON,
|
||||
'text/javascript':MultiResponse.JSON} )
|
||||
def dbkeys(self, trans ):
|
||||
return list(set([x.metadata.dbkey for x in trans.get_history().datasets if not x.deleted]))
|
||||
|
||||
@web.expose
|
||||
@MultiResponse( {'text/html':MultiResponse.JSON,
|
||||
'text/javascript':MultiResponse.JSON} )
|
||||
def chroms(self, trans, dbkey ):
|
||||
db_manifest = os.path.join( trans.app.config.tool_data_path, 'shared','ucsc','chrom', "%s.len" % dbkey )
|
||||
manifest = {}
|
||||
if os.path.exists( db_manifest ):
|
||||
for line in open( db_manifest ):
|
||||
line = line.rstrip("\r\n")
|
||||
fields = line.split("\t")
|
||||
manifest[fields[0]] = int(fields[1])
|
||||
else:
|
||||
# try to fake a manifest by reading track stores
|
||||
datasets = trans.app.model.HistoryDatasetAssociation.filter_by(deleted=False, history_id=trans.history.id).all()
|
||||
for dataset in datasets:
|
||||
if not dataset.metadata.dbkey == dbkey: continue
|
||||
track_store = trans.app.track_store.get( dataset )
|
||||
if track_store.exists:
|
||||
try:
|
||||
for chrom, fields in track_store.get_manifest().items():
|
||||
manifest[chrom] = max(manifest.get(chrom, 0), int(fields[0]))
|
||||
except track_store.DoesNotExist:
|
||||
pass
|
||||
return manifest
|
||||
|
||||
@web.expose
|
||||
@MultiResponse( {'text/html':MultiResponse.JSON,
|
||||
'text/javascript':MultiResponse.JSON} )
|
||||
def list(self, trans, dbkey ):
|
||||
datasets = trans.app.model.HistoryDatasetAssociation.filter_by(deleted=False, history_id=trans.history.id).all()
|
||||
dataset_list = {}
|
||||
for dataset in datasets:
|
||||
if dataset.metadata.dbkey == dbkey and trans.app.datatypes_registry.get_indexers_by_datatype( dataset.extension ):
|
||||
dataset_list[dataset.id] = dataset.name
|
||||
return dataset_list
|
||||
|
||||
@web.expose
|
||||
@MultiResponse( {'text/html':MultiResponse.JSON,
|
||||
'text/javascript':MultiResponse.JSON} )
|
||||
def data(self, trans, dataset_id, chrom="", start="", end=""):
|
||||
dataset = trans.app.model.HistoryDatasetAssociation.get( dataset_id )
|
||||
if not dataset: return messages.NO_DATA
|
||||
if dataset.state == trans.app.model.Job.states.ERROR:
|
||||
return messages.NO_DATA
|
||||
if not dataset.state == trans.app.model.Job.states.OK:
|
||||
return messages.PENDING
|
||||
track_store = trans.app.track_store.get( dataset )
|
||||
if not track_store.exists:
|
||||
# Test if we can make a track
|
||||
indexers = trans.app.datatypes_registry.get_indexers_by_datatype( dataset.extension )
|
||||
if indexers:
|
||||
tool = indexers[0] # They are sorted by class chain so use the top one
|
||||
# If we can, return pending and launch job
|
||||
job = trans.app.model.Job()
|
||||
job.session_id = trans.get_galaxy_session().id
|
||||
job.history_id = trans.history.id
|
||||
job.tool_id = tool.id
|
||||
job.tool_version = "1.0.0"
|
||||
job.add_input_dataset( "input_dataset", dataset )
|
||||
job.add_parameter( "input_dataset", to_json_string( dataset.id ) )
|
||||
# This is odd
|
||||
job.add_output_dataset( "input_dataset", dataset )
|
||||
# create store path, this is rather unclear?
|
||||
track_store.set()
|
||||
job.add_parameter( "store_path", to_json_string( track_store.path ) )
|
||||
job.flush()
|
||||
trans.app.job_manager.job_queue.put( job.id, tool )
|
||||
return messages.PENDING
|
||||
else:
|
||||
return messages.NO_DATA
|
||||
else:
|
||||
if chrom and start and end:
|
||||
resolution = dataset.datatype.get_track_resolution( dataset, int(start), int(end ) )
|
||||
data = track_store.get( chrom, resolution )
|
||||
window = dataset.datatype.get_track_window( dataset, data, int(start), int(end) )
|
||||
return window
|
||||
else:
|
||||
return messages.DATA
|
||||
|
||||
@web.expose
|
||||
def index(self, trans, **kwargs):
|
||||
return trans.fill_template( '/tracks/index.mako' )
|
||||
@@ -0,0 +1,181 @@
|
||||
"""MIME-Type Parser
|
||||
|
||||
This module provides basic functions for handling mime-types. It can handle
|
||||
matching mime-types against a list of media-ranges. See section 14.1 of
|
||||
the HTTP specification [RFC 2616] for a complete explanation.
|
||||
|
||||
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
|
||||
|
||||
Contents:
|
||||
- parse_mime_type(): Parses a mime-type into its component parts.
|
||||
- parse_media_range(): Media-ranges are mime-types with wild-cards and a 'q' quality parameter.
|
||||
- quality(): Determines the quality ('q') of a mime-type when compared against a list of media-ranges.
|
||||
- quality_parsed(): Just like quality() except the second parameter must be pre-parsed.
|
||||
- best_match(): Choose the mime-type with the highest quality ('q') from a list of candidates.
|
||||
"""
|
||||
|
||||
__version__ = "0.1.2"
|
||||
__author__ = 'Joe Gregorio'
|
||||
__email__ = "joe@bitworking.org"
|
||||
__credits__ = ""
|
||||
|
||||
def parse_mime_type(mime_type):
|
||||
"""Carves up a mime-type and returns a tuple of the
|
||||
(type, subtype, params) where 'params' is a dictionary
|
||||
of all the parameters for the media range.
|
||||
For example, the media range 'application/xhtml;q=0.5' would
|
||||
get parsed into:
|
||||
|
||||
('application', 'xhtml', {'q', '0.5'})
|
||||
"""
|
||||
parts = mime_type.split(";")
|
||||
params = dict([tuple([s.strip() for s in param.split("=")])\
|
||||
for param in parts[1:] ])
|
||||
full_type = parts[0].strip()
|
||||
# Java URLConnection class sends an Accept header that includes a single "*"
|
||||
# Turn it into a legal wildcard.
|
||||
if full_type == '*': full_type = '*/*'
|
||||
(type, subtype) = full_type.split("/")
|
||||
return (type.strip(), subtype.strip(), params)
|
||||
|
||||
def parse_media_range(range):
|
||||
"""Carves up a media range and returns a tuple of the
|
||||
(type, subtype, params) where 'params' is a dictionary
|
||||
of all the parameters for the media range.
|
||||
For example, the media range 'application/*;q=0.5' would
|
||||
get parsed into:
|
||||
|
||||
('application', '*', {'q', '0.5'})
|
||||
|
||||
In addition this function also guarantees that there
|
||||
is a value for 'q' in the params dictionary, filling it
|
||||
in with a proper default if necessary.
|
||||
"""
|
||||
(type, subtype, params) = parse_mime_type(range)
|
||||
if not params.has_key('q') or not params['q'] or \
|
||||
not float(params['q']) or float(params['q']) > 1\
|
||||
or float(params['q']) < 0:
|
||||
params['q'] = '1'
|
||||
return (type, subtype, params)
|
||||
|
||||
def fitness_and_quality_parsed(mime_type, parsed_ranges):
|
||||
"""Find the best match for a given mime-type against
|
||||
a list of media_ranges that have already been
|
||||
parsed by parse_media_range(). Returns a tuple of
|
||||
the fitness value and the value of the 'q' quality
|
||||
parameter of the best match, or (-1, 0) if no match
|
||||
was found. Just as for quality_parsed(), 'parsed_ranges'
|
||||
must be a list of parsed media ranges. """
|
||||
best_fitness = -1
|
||||
best_fit_q = 0
|
||||
(target_type, target_subtype, target_params) =\
|
||||
parse_media_range(mime_type)
|
||||
for (type, subtype, params) in parsed_ranges:
|
||||
if (type == target_type or type == '*' or target_type == '*') and \
|
||||
(subtype == target_subtype or subtype == '*' or target_subtype == '*'):
|
||||
param_matches = reduce(lambda x, y: x+y, [1 for (key, value) in \
|
||||
target_params.iteritems() if key != 'q' and \
|
||||
params.has_key(key) and value == params[key]], 0)
|
||||
fitness = (type == target_type) and 100 or 0
|
||||
fitness += (subtype == target_subtype) and 10 or 0
|
||||
fitness += param_matches
|
||||
if fitness > best_fitness:
|
||||
best_fitness = fitness
|
||||
best_fit_q = params['q']
|
||||
|
||||
return best_fitness, float(best_fit_q)
|
||||
|
||||
def quality_parsed(mime_type, parsed_ranges):
|
||||
"""Find the best match for a given mime-type against
|
||||
a list of media_ranges that have already been
|
||||
parsed by parse_media_range(). Returns the
|
||||
'q' quality parameter of the best match, 0 if no
|
||||
match was found. This function bahaves the same as quality()
|
||||
except that 'parsed_ranges' must be a list of
|
||||
parsed media ranges. """
|
||||
return fitness_and_quality_parsed(mime_type, parsed_ranges)[1]
|
||||
|
||||
def quality(mime_type, ranges):
|
||||
"""Returns the quality 'q' of a mime-type when compared
|
||||
against the media-ranges in ranges. For example:
|
||||
|
||||
>>> quality('text/html','text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5')
|
||||
0.7
|
||||
|
||||
"""
|
||||
parsed_ranges = [parse_media_range(r) for r in ranges.split(",")]
|
||||
return quality_parsed(mime_type, parsed_ranges)
|
||||
|
||||
def best_match(supported, header):
|
||||
"""Takes a list of supported mime-types and finds the best
|
||||
match for all the media-ranges listed in header. The value of
|
||||
header must be a string that conforms to the format of the
|
||||
HTTP Accept: header. The value of 'supported' is a list of
|
||||
mime-types.
|
||||
|
||||
>>> best_match(['application/xbel+xml', 'text/xml'], 'text/*;q=0.5,*/*; q=0.1')
|
||||
'text/xml'
|
||||
"""
|
||||
parsed_header = [parse_media_range(r) for r in header.split(",")]
|
||||
weighted_matches = [(fitness_and_quality_parsed(mime_type, parsed_header), mime_type)\
|
||||
for mime_type in supported]
|
||||
weighted_matches.sort()
|
||||
return weighted_matches[-1][0][1] and weighted_matches[-1][1] or ''
|
||||
|
||||
if __name__ == "__main__":
|
||||
import unittest
|
||||
|
||||
class TestMimeParsing(unittest.TestCase):
|
||||
|
||||
def test_parse_media_range(self):
|
||||
self.assert_(('application', 'xml', {'q': '1'}) == parse_media_range('application/xml;q=1'))
|
||||
self.assertEqual(('application', 'xml', {'q': '1'}), parse_media_range('application/xml'))
|
||||
self.assertEqual(('application', 'xml', {'q': '1'}), parse_media_range('application/xml;q='))
|
||||
self.assertEqual(('application', 'xml', {'q': '1'}), parse_media_range('application/xml ; q='))
|
||||
self.assertEqual(('application', 'xml', {'q': '1', 'b': 'other'}), parse_media_range('application/xml ; q=1;b=other'))
|
||||
self.assertEqual(('application', 'xml', {'q': '1', 'b': 'other'}), parse_media_range('application/xml ; q=2;b=other'))
|
||||
# Java URLConnection class sends an Accept header that includes a single *
|
||||
self.assertEqual(('*', '*', {'q': '.2'}), parse_media_range(" *; q=.2"))
|
||||
|
||||
def test_rfc_2616_example(self):
|
||||
accept = "text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5"
|
||||
self.assertEqual(1, quality("text/html;level=1", accept))
|
||||
self.assertEqual(0.7, quality("text/html", accept))
|
||||
self.assertEqual(0.3, quality("text/plain", accept))
|
||||
self.assertEqual(0.5, quality("image/jpeg", accept))
|
||||
self.assertEqual(0.4, quality("text/html;level=2", accept))
|
||||
self.assertEqual(0.7, quality("text/html;level=3", accept))
|
||||
|
||||
def test_best_match(self):
|
||||
mime_types_supported = ['application/xbel+xml', 'application/xml']
|
||||
# direct match
|
||||
self.assertEqual(best_match(mime_types_supported, 'application/xbel+xml'), 'application/xbel+xml')
|
||||
# direct match with a q parameter
|
||||
self.assertEqual(best_match(mime_types_supported, 'application/xbel+xml; q=1'), 'application/xbel+xml')
|
||||
# direct match of our second choice with a q parameter
|
||||
self.assertEqual(best_match(mime_types_supported, 'application/xml; q=1'), 'application/xml')
|
||||
# match using a subtype wildcard
|
||||
self.assertEqual(best_match(mime_types_supported, 'application/*; q=1'), 'application/xml')
|
||||
# match using a type wildcard
|
||||
self.assertEqual(best_match(mime_types_supported, '*/*'), 'application/xml')
|
||||
|
||||
mime_types_supported = ['application/xbel+xml', 'text/xml']
|
||||
# match using a type versus a lower weighted subtype
|
||||
self.assertEqual(best_match(mime_types_supported, 'text/*;q=0.5,*/*; q=0.1'), 'text/xml')
|
||||
# fail to match anything
|
||||
self.assertEqual(best_match(mime_types_supported, 'text/html,application/atom+xml; q=0.9'), '')
|
||||
|
||||
# common AJAX scenario
|
||||
mime_types_supported = ['application/json', 'text/html']
|
||||
self.assertEqual(best_match(mime_types_supported, 'application/json, text/javascript, */*'), 'application/json')
|
||||
# verify fitness ordering
|
||||
self.assertEqual(best_match(mime_types_supported, 'application/json, text/html;q=0.9'), 'application/json')
|
||||
|
||||
def test_support_wildcards(self):
|
||||
mime_types_supported = ['image/*', 'application/xml']
|
||||
# match using a type wildcard
|
||||
self.assertEqual(best_match(mime_types_supported, 'image/png'), 'image/*')
|
||||
# match using a wildcard for both requested and supported
|
||||
self.assertEqual(best_match(mime_types_supported, 'image/*'), 'image/*')
|
||||
|
||||
unittest.main()
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
cd `dirname $0`
|
||||
python -ES ./scripts/paster.py serve universe_wsgi.ini $@
|
||||
python2.52.5 -ES ./scripts/paster.py serve universe_wsgi.ini $@
|
||||
|
||||
|
After Width: | Height: | Size: 463 B |
|
After Width: | Height: | Size: 434 B |
|
After Width: | Height: | Size: 339 B |
|
After Width: | Height: | Size: 356 B |
|
After Width: | Height: | Size: 712 B |
|
After Width: | Height: | Size: 95 B |
|
After Width: | Height: | Size: 120 B |
|
After Width: | Height: | Size: 111 B |
|
After Width: | Height: | Size: 151 B |
@@ -0,0 +1,126 @@
|
||||
/*!
|
||||
jquery.event.drag.js ~ v1.4 ~ Copyright (c) 2008, Three Dub Media (http://threedubmedia.com)
|
||||
Liscensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt
|
||||
*/
|
||||
;(function($){ // secure $ jQuery alias
|
||||
/*******************************************************************************************/
|
||||
// Created: 2008-06-04 | Updated: 2009-01-26
|
||||
/*******************************************************************************************/
|
||||
// Events: drag, dragstart, dragend
|
||||
/*******************************************************************************************/
|
||||
|
||||
// jquery method
|
||||
$.fn.drag = function( fn1, fn2, fn3 ){
|
||||
if ( fn2 ) this.bind('dragstart', fn1 ); // 2+ args
|
||||
if ( fn3 ) this.bind('dragend', fn3 ); // 3 args
|
||||
return !fn1 ? this.trigger('drag') // 0 args
|
||||
: this.bind('drag', fn2 ? fn2 : fn1 ); // 1+ args
|
||||
};
|
||||
|
||||
// local refs
|
||||
var $event = $.event, $special = $event.special,
|
||||
|
||||
// special event configuration
|
||||
drag = $special.drag = {
|
||||
not: ':input', // don't begin to drag on event.targets that match this selector
|
||||
distance: 0, // distance dragged before dragstart
|
||||
which: 1, // mouse button pressed to start drag sequence
|
||||
setup: function( data ){
|
||||
data = $.extend({
|
||||
distance: drag.distance,
|
||||
which: drag.which,
|
||||
not: drag.not
|
||||
}, data || {});
|
||||
data.distance = squared( data.distance ); // x² + y² = distance²
|
||||
$event.add( this, "mousedown", handler, data );
|
||||
},
|
||||
teardown: function(){
|
||||
$event.remove( this, "mousedown", handler );
|
||||
if ( this === drag.dragging ) drag.dragging = drag.proxy = null; // deactivate element
|
||||
selectable( this, true ); // enable text selection
|
||||
}
|
||||
};
|
||||
|
||||
// handle drag-releatd DOM events
|
||||
function handler ( event ){
|
||||
var elem = this, returned, data = event.data || {};
|
||||
// mousemove or mouseup
|
||||
if ( data.elem ){
|
||||
// update event properties...
|
||||
elem = event.dragTarget = data.elem; // drag source element
|
||||
event.dragProxy = drag.proxy || elem; // proxy element or source
|
||||
event.cursorOffsetX = data.pageX - data.left; // mousedown offset
|
||||
event.cursorOffsetY = data.pageY - data.top; // mousedown offset
|
||||
event.offsetX = event.pageX - event.cursorOffsetX; // element offset
|
||||
event.offsetY = event.pageY - event.cursorOffsetY; // element offset
|
||||
}
|
||||
// mousedown, check some initial props to avoid the switch statement
|
||||
else if ( drag.dragging || ( data.which>0 && event.which!=data.which ) ||
|
||||
$( event.target ).is( data.not ) ) return;
|
||||
// handle various events
|
||||
switch ( event.type ){
|
||||
// mousedown, left click, event.target is not restricted, init dragging
|
||||
case 'mousedown':
|
||||
$.extend( data, $( elem ).offset(), {
|
||||
elem: elem, target: event.target,
|
||||
pageX: event.pageX, pageY: event.pageY
|
||||
}); // store some initial attributes
|
||||
$event.add( document, "mousemove mouseup", handler, data );
|
||||
selectable( elem, false ); // disable text selection
|
||||
return false; // prevents text selection in safari
|
||||
// mousemove, check distance, start dragging
|
||||
case !drag.dragging && 'mousemove':
|
||||
if ( squared( event.pageX-data.pageX )
|
||||
+ squared( event.pageY-data.pageY ) // x² + y² = distance²
|
||||
< data.distance ) break; // distance tolerance not reached
|
||||
event.target = data.target; // force target from "mousedown" event (fix distance issue)
|
||||
returned = hijack( event, "dragstart", elem ); // trigger "dragstart", return proxy element
|
||||
if ( returned !== false ){ // "dragstart" not rejected
|
||||
drag.dragging = elem; // activate element
|
||||
drag.proxy = event.dragProxy = $( returned || elem )[0]; // set proxy
|
||||
}
|
||||
// mousemove, dragging
|
||||
case 'mousemove':
|
||||
if ( drag.dragging ){
|
||||
returned = hijack( event, "drag", elem ); // trigger "drag"
|
||||
if ( $special.drop ){ // manage drop events
|
||||
$special.drop.allowed = ( returned !== false ); // prevent drop
|
||||
$special.drop.handler( event ); // "dropstart", "dropend"
|
||||
}
|
||||
if ( returned !== false ) break; // "drag" not rejected, stop
|
||||
event.type = "mouseup"; // helps "drop" handler behave
|
||||
}
|
||||
// mouseup, stop dragging
|
||||
case 'mouseup':
|
||||
$event.remove( document, "mousemove mouseup", handler ); // remove page events
|
||||
if ( drag.dragging ){
|
||||
if ( $special.drop ) $special.drop.handler( event ); // "drop"
|
||||
hijack( event, "dragend", elem ); // trigger "dragend"
|
||||
}
|
||||
selectable( elem, true ); // enable text selection
|
||||
drag.dragging = drag.proxy = data.elem = null; // deactivate element
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// set event type to custom value, and handle it
|
||||
function hijack ( event, type, elem ){
|
||||
event.type = type; // force the event type
|
||||
var result = $event.handle.call( elem, event );
|
||||
return result===false ? false : result || event.result;
|
||||
};
|
||||
|
||||
// return the value squared
|
||||
function squared ( value ){ return Math.pow( value, 2 ); };
|
||||
|
||||
// toggles text selection attributes
|
||||
function selectable ( elem, bool ){
|
||||
if ( !elem ) return; // maybe element was removed ?
|
||||
elem.unselectable = bool ? "off" : "on"; // IE
|
||||
elem.onselectstart = function(){ return bool; }; // IE
|
||||
if ( document.selection && document.selection.empty ) document.selection.empty(); // IE
|
||||
if ( elem.style ) elem.style.MozUserSelect = bool ? "" : "none"; // FF
|
||||
};
|
||||
|
||||
/*******************************************************************************************/
|
||||
})( jQuery ); // confine scope
|
||||
@@ -0,0 +1,5 @@
|
||||
/*
|
||||
jquery.event.drag.js ~ v1.4 ~ Copyright (c) 2008, Three Dub Media (http://threedubmedia.com)
|
||||
Liscensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt
|
||||
*/
|
||||
(function(H){H.fn.drag=function(K,J,I){if(J){this.bind("dragstart",K)}if(I){this.bind("dragend",I)}return !K?this.trigger("drag"):this.bind("drag",J?J:K)};var D=H.event,B=D.special,F=B.drag={not:":input",distance:0,which:1,setup:function(I){I=H.extend({distance:F.distance,which:F.which,not:F.not},I||{});I.distance=G(I.distance);D.add(this,"mousedown",E,I)},teardown:function(){D.remove(this,"mousedown",E);if(this===F.dragging){F.dragging=F.proxy=null}C(this,true)}};function E(K){var J=this,I,L=K.data||{};if(L.elem){J=K.dragTarget=L.elem;K.dragProxy=F.proxy||J;K.cursorOffsetX=L.pageX-L.left;K.cursorOffsetY=L.pageY-L.top;K.offsetX=K.pageX-K.cursorOffsetX;K.offsetY=K.pageY-K.cursorOffsetY}else{if(F.dragging||(L.which>0&&K.which!=L.which)||H(K.target).is(L.not)){return }}switch(K.type){case"mousedown":H.extend(L,H(J).offset(),{elem:J,target:K.target,pageX:K.pageX,pageY:K.pageY});D.add(document,"mousemove mouseup",E,L);C(J,false);return false;case !F.dragging&&"mousemove":if(G(K.pageX-L.pageX)+G(K.pageY-L.pageY)<L.distance){break}K.target=L.target;I=A(K,"dragstart",J);if(I!==false){F.dragging=J;F.proxy=K.dragProxy=H(I||J)[0]}case"mousemove":if(F.dragging){I=A(K,"drag",J);if(B.drop){B.drop.allowed=(I!==false);B.drop.handler(K)}if(I!==false){break}K.type="mouseup"}case"mouseup":D.remove(document,"mousemove mouseup",E);if(F.dragging){if(B.drop){B.drop.handler(K)}A(K,"dragend",J)}C(J,true);F.dragging=F.proxy=L.elem=null;break}}function A(L,J,K){L.type=J;var I=D.handle.call(K,L);return I===false?false:I||L.result}function G(I){return Math.pow(I,2)}function C(J,I){if(!J){return }J.unselectable=I?"off":"on";J.onselectstart=function(){return I};if(document.selection&&document.selection.empty){document.selection.empty()}if(J.style){J.style.MozUserSelect=I?"":"none"}}})(jQuery);
|
||||
@@ -0,0 +1,47 @@
|
||||
/* tracks browser - Galaxy Project - PSU */
|
||||
|
||||
body { font-family:"Lucida Grande MS"; }
|
||||
ul { margin:0; padding:0; }
|
||||
ul li { list-style:none; float:left; }
|
||||
|
||||
.block { display:block; }
|
||||
.float-left { float:left; }
|
||||
.float-right { float:right; }
|
||||
|
||||
.padding-zero { padding:0; }
|
||||
.margin-zero { margin:0; }
|
||||
.overflow { overflow:visible; }
|
||||
.no-overflow { overflow:hidden; }
|
||||
|
||||
.width-hundred-percent { margin:0; width:100%; }
|
||||
.width-eighty-percent { margin:0; width:80%; }
|
||||
.width-sixty-percent { margin:0; width:60%; }
|
||||
.width-fifty-percent { margin:0; width:50%; }
|
||||
.width-forty-percent { margin:0; width:40%; }
|
||||
.width-twenty-percent { margin:0; width:20%; }
|
||||
|
||||
.track, .track-label, .track-canvas { height: 8em; }
|
||||
.track-label { background:#e6e7e8; }
|
||||
.track-canvas { background:#f9f9f9; }
|
||||
|
||||
.history-item { background: #CCFFCC; border: 1px solid #888888; border-left: 5px solid #888888;
|
||||
border-right: none; margin:5px -5px 5px 0; padding: 5px 11px; width:85%; height:3em; }
|
||||
|
||||
#viewport { min-height:20em; border:1px solid #c6c7c8; margin-right:.4em;}
|
||||
#viewport-top { height: 9em; }
|
||||
|
||||
#db-tools { height:4em; }
|
||||
#history-tools { height:4em; }
|
||||
|
||||
#window-macro { height: 4em; }
|
||||
#window-micro { height: 4em; }
|
||||
|
||||
#macro-slider { height: 1em; }
|
||||
#x-axis { height: 4em; background:#E2F1F5;}
|
||||
#tracks-sortable { min-height:15em; }
|
||||
|
||||
#drag-history { border:1px solid #c6c7c8; width: 0%; min-height:20em; position:relative; top:0; }
|
||||
#history-control-bar { height:3em; }
|
||||
|
||||
#dock { position:absolute; bottom:0; left:20%; background: #FFFDF2; border:1px solid #919396;}
|
||||
#dock ul li { margin:0 .4em; }
|
||||
@@ -0,0 +1,39 @@
|
||||
/* SVN FILE: $Id: jamal.js 18 2007-06-13 09:07:32Z teemow $ */
|
||||
/**
|
||||
* To quote Dave Cardwell:
|
||||
* Built on the shoulders of giants:
|
||||
* * John Resig - http://jquery.com/
|
||||
*
|
||||
* Jamal : Javascript MVC Assembly Layout <http://jamal-mvc.com/>
|
||||
* Copyright (c) 2007, Timo Derstappen <http://teemow.com/>
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @filesource
|
||||
* @copyright Copyright (c) 2007, Timo Derstappen
|
||||
* @link
|
||||
* @package jamal
|
||||
* @subpackage jamal.controller
|
||||
* @since Jamal v 0.4
|
||||
* @version $Revision: 40 $
|
||||
* @modifiedby $LastChangedBy: teemow $
|
||||
* @lastmodified $Date: 2007-08-20 18:24:35 +0200 (Mo, 20. Aug 2007) $
|
||||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||
*/
|
||||
|
||||
/**
|
||||
* Jamal app controller
|
||||
*
|
||||
* @public
|
||||
* @cat controller
|
||||
*/
|
||||
jamal.extend(jamal.fn.c.prototype, {
|
||||
components: ['session', 'modal'],
|
||||
|
||||
beforeAction: function() {
|
||||
},
|
||||
afterAction: function() {
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
/* SVN FILE: $Id: jamal.js 18 2007-06-13 09:07:32Z teemow $ */
|
||||
/**
|
||||
* To quote Dave Cardwell:
|
||||
* Built on the shoulders of giants:
|
||||
* * John Resig - http://jquery.com/
|
||||
*
|
||||
* Jamal : Javascript MVC Assembly Layout <http://jamal-mvc.com/>
|
||||
* Copyright (c) 2007, Timo Derstappen <http://teemow.com/>
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @filesource
|
||||
* @copyright Copyright (c) 2007, Timo Derstappen
|
||||
* @link
|
||||
* @package jamal
|
||||
* @subpackage jamal.model
|
||||
* @since Jamal v 0.4
|
||||
* @version $Revision: 40 $
|
||||
* @modifiedby $LastChangedBy: teemow $
|
||||
* @lastmodified $Date: 2007-08-20 18:24:35 +0200 (Mo, 20. Aug 2007) $
|
||||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||
*/
|
||||
|
||||
/**
|
||||
* Jamal app model
|
||||
*
|
||||
* @public
|
||||
* @cat model
|
||||
*/
|
||||
jamal.extend(jamal.fn.m.prototype, {
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
/* SVN FILE: $Id: jamal.js 18 2007-06-13 09:07:32Z teemow $ */
|
||||
/**
|
||||
* To quote Dave Cardwell:
|
||||
* Built on the shoulders of giants:
|
||||
* * John Resig - http://jquery.com/
|
||||
*
|
||||
* Jamal : Javascript MVC Assembly Layout <http://jamal-mvc.com/>
|
||||
* Copyright (c) 2007, Timo Derstappen <http://teemow.com/>
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @filesource
|
||||
* @copyright Copyright (c) 2007, Timo Derstappen
|
||||
* @link
|
||||
* @package jamal
|
||||
* @subpackage jamal.view
|
||||
* @since Jamal v 0.4
|
||||
* @version $Revision: 40 $
|
||||
* @modifiedby $LastChangedBy: teemow $
|
||||
* @lastmodified $Date: 2007-08-20 18:24:35 +0200 (Mo, 20. Aug 2007) $
|
||||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||
*/
|
||||
|
||||
/**
|
||||
* Jamal app view
|
||||
*
|
||||
* @public
|
||||
* @cat view
|
||||
*/
|
||||
jamal.extend(jamal.fn.v.prototype, {
|
||||
});
|
||||
@@ -0,0 +1,123 @@
|
||||
$j.c( {
|
||||
/**
|
||||
* Viewport controller
|
||||
*
|
||||
**/
|
||||
Viewport: {
|
||||
index: function () {
|
||||
|
||||
this.loadDBKeys();
|
||||
|
||||
$j.v.Viewport.initTemplates();
|
||||
$j.v.Viewport.initWindow();
|
||||
$j.v.Viewport.initTracks();
|
||||
$j.v.Viewport.initHistoryPanel();
|
||||
this._registerListeners();
|
||||
|
||||
// initialize axis
|
||||
$j.m.xAxis = new Axis();
|
||||
},
|
||||
/**
|
||||
* Register Listeners
|
||||
* @access private
|
||||
**/
|
||||
_registerListeners: function() {
|
||||
$("a#show-history").bind("click", $j.v.Viewport.showHistory);
|
||||
$("a#hide-history").bind("click", $j.v.Viewport.hideHistory);
|
||||
$("select#dbkey").bind( "change", this.setDBKey );
|
||||
$("select#chrom").bind( "change", this.setChrom );
|
||||
$(window).bind("resize", this.redrawTracks );
|
||||
|
||||
},
|
||||
loadDBKeys: function() {
|
||||
$j.m.Data.getDBKeys( function() {
|
||||
$j.v.Viewport.showDBKeys();
|
||||
});
|
||||
},
|
||||
setDBKey: function(e) {
|
||||
$j.m.Viewport.dbkey = e.target.value;
|
||||
$j.m.Data.getDatasets( function() {
|
||||
// Setup history
|
||||
$j.v.Viewport.showDatasets();
|
||||
});
|
||||
$j.c.Viewport.getChroms();
|
||||
},
|
||||
getChroms: function() {
|
||||
$j.m.Data.getChroms( $j.m.Viewport.dbkey, function() {
|
||||
$j.v.Viewport.showChroms();
|
||||
});
|
||||
},
|
||||
setChrom: function(e) {
|
||||
chrom = $j.m.Viewport.chrom = e.target.value;
|
||||
if( $j.m.Data.dbkeys[$j.m.Viewport.dbkey].chroms[chrom]) {
|
||||
$j.m.Viewport.start = 0;
|
||||
$j.m.Viewport.end = $j.m.Data.dbkeys[$j.m.Viewport.dbkey].chroms[chrom].len;
|
||||
} else {
|
||||
$j.m.Viewport.start = NaN;
|
||||
$j.m.Viewport.end = NaN;
|
||||
}
|
||||
// Update data shown
|
||||
for( track in $j.m.Viewport.tracks ) {
|
||||
var trackObj = $j.m.Viewport.tracks[track];
|
||||
$j.m.Data.getData(trackObj.datasetId, $j.m.Viewport.chrom, $j.m.Viewport.start, $j.m.Viewport.end, function(response) {
|
||||
$j.m.Viewport.tracks[track].redraw();
|
||||
});
|
||||
}
|
||||
$j.m.xAxis.redraw();
|
||||
},
|
||||
addTrack: function(datasetId) {
|
||||
// Add a dataset track to the MVC
|
||||
// Drop->receive->initTrack->this->$j.v.Viewport.addTrack
|
||||
var newTrack = new Track(datasetId);
|
||||
$j.log(datasetId);
|
||||
var loadTrack = function () {
|
||||
$j.m.Data.getDataset( datasetId, function(response) {
|
||||
if( response=="data" ) {
|
||||
if( $j.m.Viewport.chrom=="" ) $j.v.Viewport.getChroms();
|
||||
else $j.m.Data.getData( datasetId,
|
||||
$j.m.Viewport.chrom,
|
||||
$j.m.Viewport.start,
|
||||
$j.m.Viewport.end, function(t) {
|
||||
return function() {
|
||||
t.redraw();
|
||||
}
|
||||
}(newTrack));
|
||||
} else if (response=="pending") {
|
||||
setTimeout(loadTrack, 8000);
|
||||
}
|
||||
});
|
||||
}
|
||||
loadTrack();
|
||||
$j.m.Viewport.tracks.push( newTrack );
|
||||
$j.v.Viewport.addTrack( newTrack );
|
||||
newTrack.redraw();
|
||||
},
|
||||
removeDataset: function(e) {
|
||||
// TODO: Remove a dataset from the MVC
|
||||
},
|
||||
panTo: function(e) {
|
||||
// TODO: Pan.
|
||||
},
|
||||
panBy: function(delta) {
|
||||
$j.m.Viewport.start += delta;
|
||||
$j.m.Viewport.end += delta;
|
||||
$j.c.Viewport.redrawTracks();
|
||||
},
|
||||
zoomIn: function(e) {
|
||||
var range = $j.m.Viewport.end - $j.m.Viewport.start;
|
||||
$j.m.Viewport.end -= range / 4;
|
||||
$j.m.Viewport.start += range / 4;
|
||||
$j.c.Viewport.redrawTracks();
|
||||
},
|
||||
zoomTo: function(e) {
|
||||
// TODO: zoom
|
||||
},
|
||||
redrawTracks: function(e) {
|
||||
for( track in $j.m.Viewport.tracks ) {
|
||||
$j.m.Viewport.tracks[track].redraw();
|
||||
}
|
||||
$j.m.xAxis.redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,68 @@
|
||||
$j.m( {
|
||||
Viewport: {
|
||||
start: NaN,
|
||||
end: NaN,
|
||||
dbkey: "",
|
||||
chrom: "",
|
||||
template: {},
|
||||
tracks: []
|
||||
},
|
||||
Data: {
|
||||
getDBKeys: function (callback) {
|
||||
$j.m.Data.json("/tracks/dbkeys", function (response) {
|
||||
for(item in response)
|
||||
$j.m.Data.dbkeys[response[item]] = {chroms: {}};
|
||||
if($j.m.Viewport.dbkey == "")
|
||||
$j.m.Viewport.dbkey = response[0];
|
||||
if(callback) callback(response);
|
||||
});
|
||||
},
|
||||
getDatasets: function (callback) {
|
||||
$j.m.Data.json("/tracks/list?dbkey="+$j.m.Viewport.dbkey, function (response) {
|
||||
$j.m.Data.datasets = response;
|
||||
if(callback) callback(response);
|
||||
});
|
||||
},
|
||||
getChroms: function (dbkey, callback) {
|
||||
$j.m.Data.json("/tracks/chroms?dbkey="+dbkey, function (response) {
|
||||
for( key in response) {
|
||||
if($j.m.Data.dbkeys[dbkey].chroms[key] == undefined) {
|
||||
$j.m.Data.dbkeys[dbkey].chroms[key] = new Object();
|
||||
}
|
||||
$j.m.Data.dbkeys[dbkey].chroms[key].len = response[key];
|
||||
}
|
||||
if(callback) callback(response);
|
||||
});
|
||||
},
|
||||
getData: function(dataset, chrom, start, end, callback) {
|
||||
var qs = "/tracks/data";
|
||||
qs += "?dataset_id=" + dataset.toString();
|
||||
if(chrom != undefined) qs += "&chrom=" + chrom;
|
||||
if(start != undefined) qs += "&start=" + start.toString();
|
||||
if(end != undefined) qs += "&end=" + end.toString();
|
||||
$j.m.Data.json(qs,
|
||||
function (response) {
|
||||
if($j.m.Data.dbkeys[$j.m.Viewport.dbkey].chroms[chrom] == undefined)
|
||||
$j.m.Data.dbkeys[$j.m.Viewport.dbkey].chroms[chrom] = new Object();
|
||||
if(response.resolution) {
|
||||
if($j.m.Data.dbkeys[$j.m.Viewport.dbkey].chroms[chrom][dataset] == undefined)
|
||||
$j.m.Data.dbkeys[$j.m.Viewport.dbkey].chroms[chrom][dataset] = new Object();
|
||||
$j.m.Data.dbkeys[$j.m.Viewport.dbkey].chroms[chrom][dataset][response.resolution] = response;
|
||||
} else {
|
||||
$j.m.Data.dbkeys[$j.m.Viewport.dbkey].chroms[chrom][dataset] = response;
|
||||
}
|
||||
if(callback) callback(response);
|
||||
});
|
||||
},
|
||||
getDataset: function(dataset, callback) {
|
||||
var qs = "/tracks/data";
|
||||
qs += "?dataset_id=" + dataset.toString();
|
||||
$j.m.Data.json(qs,
|
||||
function(response) {
|
||||
if(callback) callback(response);
|
||||
});
|
||||
},
|
||||
dbkeys: {},
|
||||
datasets: {}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Requires jQuery
|
||||
* testing a change for mercurial
|
||||
*/
|
||||
|
||||
var Track = function (datasetId) {
|
||||
this.elem = $($j.v.Viewport.trackTemplate).clone();
|
||||
this.datasetId = datasetId;
|
||||
this.titleElem = this.elem.find(".track-title");
|
||||
this.titleElem.html($j.m.Data.datasets[datasetId]);
|
||||
this.canvasElem = this.elem.find(".track-canvas");
|
||||
console.log(this.canvasElem);
|
||||
this.renderer = new FeatureRenderer(this.canvasElem.get(0), $j.m.Viewport);
|
||||
|
||||
// Bindings
|
||||
this.canvasElem.bind( "dragstart", function (l_track) {
|
||||
return function (e){
|
||||
this.original_low = $j.m.Viewport.start;
|
||||
};
|
||||
}(this));
|
||||
this.canvasElem.bind( "drag", function( l_track ) {
|
||||
return function(e) {
|
||||
var move_amount = ( e.offsetX - this.offsetLeft ) / this.offsetWidth;
|
||||
var range = $j.m.Viewport.end - $j.m.Viewport.start;
|
||||
var move_bases = Math.round( range * move_amount );
|
||||
var new_low = this.original_low - move_bases;
|
||||
/*if ( new_low < 0 ) {
|
||||
new_low = 0;
|
||||
}*/
|
||||
var new_high = new_low + range;
|
||||
/*if ( new_high > view.length ) {
|
||||
new_high = view.length;
|
||||
new_low = new_high - range;
|
||||
}*/
|
||||
$j.c.Viewport.panBy(new_low-$j.m.Viewport.start);
|
||||
};
|
||||
}(this));
|
||||
this.canvasElem.bind( "dblclick", function( l_track ) {
|
||||
return function(e) {
|
||||
$j.c.Viewport.zoomIn();
|
||||
};
|
||||
}(this));
|
||||
};
|
||||
|
||||
|
||||
$.extend( Track.prototype, {
|
||||
redraw: function () {
|
||||
this.renderer.start = $j.m.Viewport.start;
|
||||
this.renderer.end = $j.m.Viewport.end;
|
||||
this.renderer.clear();
|
||||
this.renderer.updateSize();
|
||||
// tickDistance = Math.floor( Math.pow( 10, Math.floor( Math.log( range ) / Math.log( 10 ) ) ) )
|
||||
this.renderer.xGridLines = Math.floor( Math.pow(10, Math.floor( Math.log( this.renderer.end - this.renderer.start ) / Math.log(10) ) ) );
|
||||
this.renderer.drawGridLines();
|
||||
this.renderer.plot( $j.m.Data.dbkeys[$j.m.Viewport.dbkey].chroms[chrom][this.datasetId] );
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var Axis = function() {
|
||||
this.canvasElem = $("#x-axis")[0];
|
||||
this.renderer = new Renderer( this.canvasElem, $j.m.Viewport );
|
||||
|
||||
this.redraw = function () {
|
||||
this.renderer.start = $j.m.Viewport.start;
|
||||
this.renderer.end = $j.m.Viewport.end;
|
||||
this.renderer.clear();
|
||||
this.renderer.updateSize();
|
||||
this.renderer.xGridLines = Math.floor( Math.pow(10, Math.floor( Math.log( this.renderer.end - this.renderer.start ) / Math.log(10) ) ) );
|
||||
this.renderer.drawGridLines();
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
var Renderer = function (canvas, config) {
|
||||
this.ctx = canvas.getContext("2d");
|
||||
this.canvas = canvas;
|
||||
this.updateSize();
|
||||
|
||||
this.start = config.start || 0;
|
||||
this.end = config.end || 0;
|
||||
this.yMin = config.yMin || 0;
|
||||
this.yMax = config.yMax || this._height;
|
||||
this.scaleX = this._width / (this.end - this.start);
|
||||
this.scaleY = this._height /(this.yMax - this.yMin);
|
||||
this.xGridLines = config.xGridLines || 100000;
|
||||
this.xGridOffset = config.xGridOffset || 0;
|
||||
};
|
||||
|
||||
$.extend( Renderer.prototype, {
|
||||
tX: function (x) {
|
||||
return ( x - this.start ) * this.scaleX;
|
||||
},
|
||||
tY: function (y) {
|
||||
return ( y - this.yMin ) * this.scaleY;
|
||||
},
|
||||
drawGridLines: function() {
|
||||
var lastX = this.end - (this.end % this.xGridLines) + this.xGridOffset;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.setLineWidth(0.1);
|
||||
for(i=lastX; i>this.start; i-= this.xGridLines ) {
|
||||
this.ctx.moveTo( this.tX(i), 0);
|
||||
this.ctx.lineTo( this.tX(i), this._height );
|
||||
}
|
||||
this.ctx.closePath();
|
||||
this.ctx.stroke();
|
||||
return true;
|
||||
},
|
||||
updateSize: function() {
|
||||
this._width = this.ctx.canvas.width = $(this.canvas).width();
|
||||
this._height = this.ctx.canvas.height = $(this.canvas).height();
|
||||
this.scaleX = this._width / (this.end - this.start);
|
||||
this.scaleY = this._height /(this.yMax - this.yMin);
|
||||
},
|
||||
clear: function() {
|
||||
this.ctx.clearRect(0,0,this.ctx.canvas.width, this.ctx.canvas.height);
|
||||
}
|
||||
});
|
||||
|
||||
var PositionRenderer = function(canvas, config) {
|
||||
Renderer.call( this, canvas, config );
|
||||
};
|
||||
|
||||
var FeatureRenderer = function(canvas, config) {
|
||||
Renderer.call( this, canvas, config );
|
||||
this.lanes = [];
|
||||
this.featureHeight = 8;
|
||||
this.featurePadding = 2;
|
||||
};
|
||||
|
||||
$.extend( FeatureRenderer.prototype, Renderer.prototype, {
|
||||
drawRect: function(x1,y1,x2,y2) {
|
||||
x1 = (x1 - this.start) * this.scaleX; //this.tX(x1);
|
||||
x2 = (x2 - this.start) * this.scaleX; //this.tX(x2);
|
||||
this.ctx.fillRect( x1, y1, Math.max(1,x2-x1), y2-y1 );
|
||||
},
|
||||
plot: function(features) {
|
||||
var laneHeight = this.featureHeight + this.featurePadding;
|
||||
this.ctx.setFillColor("#000000");
|
||||
for( idx in features ) {
|
||||
var feature = features[idx];
|
||||
var lane = 0;
|
||||
while( feature[0] < this.lanes[lane] && this.lanes[lane] != undefined ){
|
||||
if( this.lanes && feature[0] < this.lanes[lane] )
|
||||
lane++;
|
||||
}
|
||||
this.lanes[lane] = feature[1];
|
||||
var yOffset = laneHeight * lane;
|
||||
this.drawRect( feature[0], yOffset, feature[1], yOffset+this.featureHeight );
|
||||
}
|
||||
this.lanes = [];
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
$j.v({
|
||||
/**
|
||||
* Class Track
|
||||
* Track in contianer
|
||||
**/
|
||||
Tracks: {
|
||||
children:[],
|
||||
init: function(curAddition) {
|
||||
var curTemplate = $j.v.Viewport.trackTemplate.clone();
|
||||
|
||||
$(curTemplate).attr("id", "item_" + $("#tracks-sortable > li").length);
|
||||
$(curTemplate).find(".track-title").html(curAddition.find(".history-item-title > b").html());
|
||||
return curTemplate;
|
||||
}
|
||||
} // end class Track
|
||||
});
|
||||
@@ -0,0 +1,136 @@
|
||||
$j.v({
|
||||
Viewport: {
|
||||
addView: function(dataset_id) {
|
||||
// $("#viewport").append($j.m.Viewport.TrackTemplate.clone());
|
||||
// $("div.track:last").attr("id", this.get_tid(dataset_id));
|
||||
},
|
||||
renderView: function(dataset_id) {
|
||||
if (!$(this.get_selector(dataset_id)).length)
|
||||
this.addView(dataset_id);
|
||||
},
|
||||
get_selector: function(dataset_id) {
|
||||
return "div.track[@id=" + this.get_tid(dataset_id) + "]";
|
||||
},
|
||||
get_tid: function(dataset_id) {
|
||||
return "t" + dataset_id.toString();
|
||||
},
|
||||
showDBKeys: function() {
|
||||
options = '';
|
||||
for( key in $j.m.Data.dbkeys ) {
|
||||
options += '<option value="'+key+'">'+key+'</option>';
|
||||
}
|
||||
$("select#dbkey").html(options);
|
||||
$("select#dbkey option:first").change();
|
||||
},
|
||||
showChroms: function() {
|
||||
options = '';
|
||||
for( key in $j.m.Data.dbkeys[$j.m.Viewport.dbkey].chroms ) {
|
||||
options += '<option value="'+key+'">'+key+"</option>";
|
||||
}
|
||||
$("select#chrom").html(options);
|
||||
$("select#chrom option:first").change();
|
||||
},
|
||||
showDatasets: function() {
|
||||
options = '';
|
||||
for( key in $j.m.Data.datasets ) {
|
||||
options += '<option value="'+$j.m.Data.datasets[key]+'">'+key+"</option>";
|
||||
}
|
||||
$("select#datasets").html(options);
|
||||
},
|
||||
initTracks: function() {
|
||||
$("#tracks-sortable").sortable({
|
||||
handle: ".track-label",
|
||||
items: 'li',
|
||||
receive: function( event, ui ) {
|
||||
currentAddition = $(".track-history-item"); // only one history added at one time.
|
||||
$j.v.Viewport.addTrack(currentAddition);
|
||||
// on recieve of item from history list
|
||||
}
|
||||
});
|
||||
return false;
|
||||
},
|
||||
addTrack: function(curAddition) {
|
||||
var curTemplate = $j.m.Viewport.template.clone();
|
||||
|
||||
$(curTemplate).attr("id", "item_" + $("#tracks-sortable > li").length);
|
||||
$(curTemplate).find(".track-title").html(curAddition.find(".history-item-title > b").html());
|
||||
|
||||
$("#tracks-sortable .track-history-item:last").remove();
|
||||
$("#tracks-sortable").append(curTemplate);
|
||||
},
|
||||
removeTrack:function() {
|
||||
|
||||
},
|
||||
populateTrack: function() {
|
||||
|
||||
},
|
||||
initHistoryPanel: function() {
|
||||
//$("#drag-history").css("display", "none");
|
||||
$("#tracks-history-sortable").sortable({
|
||||
handle: ".history-item",
|
||||
connectWith: $("#tracks-sortable"),
|
||||
items: 'li',
|
||||
remove: function( event, ui ) {
|
||||
// on remove from history list
|
||||
}
|
||||
});
|
||||
},
|
||||
initOdometer: function() {
|
||||
|
||||
$("#macro-slider").slider({
|
||||
range:true,
|
||||
min: 0,
|
||||
max: 100,
|
||||
orientation: 'horizontal',
|
||||
values:[20, 80],
|
||||
start: function( event, ui ) {
|
||||
// do blur tracks, odometers
|
||||
},
|
||||
slide: function( event, ui ) {
|
||||
$j.log( "slide range: " + $(this).slider("values", 0) + " :: " + $(this).slider("values", 1) );
|
||||
},
|
||||
stop: function( event, ui ) {
|
||||
// $j.log( "range: " + $(this).slider("values", 0) + " :: " + $(this).slider("values", 1) );
|
||||
// $("#micro-scroll-bar").trigger("scroll");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// $("#macro-slider").bind("mousedown", function( event ) {
|
||||
// if(e.pageX > $("#macro-slider").slider("values", 0) && e.pageX > $("#macro-slider").slider("values", 1) ) {
|
||||
// $j.log("mouse down");
|
||||
// }
|
||||
// });
|
||||
// $( "#micro-scroll-bar" ).bind("scroll", function( event ) {
|
||||
// num = Math.floor ( Math.random() * 100 );
|
||||
// $( "#micro-scroll-bar" ).slider('option', 'value', num);
|
||||
// $( "#micro-scroll-content" ).css("margin-left", $("#micro-scroll-content").width() * -(num/100));
|
||||
// });
|
||||
//
|
||||
// $( "#micro-scroll-bar" ).slider({
|
||||
// orientation: 'horizontal',
|
||||
// start: function( event, ui ) {
|
||||
// // do blur tracks, odometers
|
||||
// },
|
||||
// slide: function( event, ui ) {
|
||||
// scrollContent = $("#micro-scroll-content");
|
||||
// scrollPane = $("#micro-scroll-pane");
|
||||
// if( scrollContent.width() > scrollPane.width() ) {
|
||||
// scrollContent.css('margin-left', Math.round( ui.value / 100 * ( scrollPane.width() - scrollContent.width() )) + 'px');
|
||||
// }
|
||||
// else {
|
||||
// scrollContent.css('margin-left', 0);
|
||||
// }
|
||||
// },
|
||||
// change: function( event, ui ) {
|
||||
// // set new positions
|
||||
// $j.log("end: " + ui.value);
|
||||
// }
|
||||
// });
|
||||
this.updateOdometer();
|
||||
},
|
||||
updateOdometer: function() {
|
||||
$j.log("updating odoemeter");
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,107 @@
|
||||
$j.v({
|
||||
/**
|
||||
* Class Viewport
|
||||
* Entire container
|
||||
**/
|
||||
Viewport: {
|
||||
addView: function(dataset_id) {
|
||||
// $("#viewport").append($j.m.Viewport.TrackTemplate.clone());
|
||||
// $("div.track:last").attr("id", this.get_tid(dataset_id));
|
||||
},
|
||||
initTemplates: function () {
|
||||
this.historyTemplate = $(".track-history-item").remove();
|
||||
this.trackTemplate = $(".track-list-item").remove();
|
||||
},
|
||||
showDBKeys: function() {
|
||||
var options = '';
|
||||
for( key in $j.m.Data.dbkeys ) {
|
||||
options += '<option value="'+key+'">'+key+'</option>';
|
||||
}
|
||||
$("select#dbkey").html(options);
|
||||
$("select#dbkey option:first").change();
|
||||
},
|
||||
showChroms: function() {
|
||||
var options = '';
|
||||
for( key in $j.m.Data.dbkeys[$j.m.Viewport.dbkey].chroms ) {
|
||||
options += '<option value="'+key+'">'+key+"</option>";
|
||||
}
|
||||
$("select#chrom").html(options);
|
||||
$("select#chrom option:first").change();
|
||||
},
|
||||
showDatasets: function() {
|
||||
for( dataset in $j.m.Data.datasets ) {
|
||||
var newDataset = $(this.historyTemplate).clone();
|
||||
$("#tracks-history-sortable").append(newDataset);
|
||||
var title = dataset.toString()+": "+$j.m.Data.datasets[dataset];
|
||||
newDataset.find(".history-item-title > b").html(title);
|
||||
newDataset.bind("initTrack", function (d) {
|
||||
return function() {
|
||||
$("#tracks-sortable .track-history-item:last").remove();
|
||||
$j.c.Viewport.addTrack(d);
|
||||
return false;
|
||||
};
|
||||
}(dataset));
|
||||
}
|
||||
},
|
||||
initTracks: function() {
|
||||
$j.log("initializing tracks");
|
||||
$("#tracks-sortable").sortable({
|
||||
handle: ".track-label",
|
||||
items: 'li',
|
||||
over: function( event, ui ) {
|
||||
$("#tracks-sortable").css("border", "2px solid #FFee44");
|
||||
},
|
||||
receive: function( event, ui ) {
|
||||
$("#tracks-sortable").css("border", "none");
|
||||
$("#tracks-sortable .track-history-item:last").trigger("initTrack");
|
||||
},
|
||||
stop: function( event, ui ) {
|
||||
$("#tracks-sortable").css("border", "none");
|
||||
}
|
||||
});
|
||||
return false;
|
||||
},
|
||||
showHistory: function() {
|
||||
$("#viewport").css("width", "80%");
|
||||
$("#drag-history").css("width", "19%");
|
||||
$("#drag-history").css("visibility", "visible");
|
||||
},
|
||||
hideHistory: function() {
|
||||
$("#viewport").css("width", "100%");
|
||||
$("#drag-history").css("visibility", "hidden");
|
||||
},
|
||||
addTrack: function(curAddition) {
|
||||
//var newTrack = $j.v.Tracks.init(curAddition);
|
||||
|
||||
$("#tracks-sortable").append(curAddition.elem);
|
||||
},
|
||||
removeTrack:function(curRemoval) {
|
||||
curRemoval.remove();
|
||||
},
|
||||
populateTrack: function() {
|
||||
|
||||
},
|
||||
initHistoryPanel: function() {
|
||||
$("#tracks-history-sortable").sortable({
|
||||
handle: ".history-item",
|
||||
connectWith: $("#tracks-sortable"),
|
||||
items: 'li',
|
||||
remove: function( event, ui ) {
|
||||
// on remove from history list
|
||||
}
|
||||
});
|
||||
this.hideHistory();
|
||||
},
|
||||
initWindow: function() {
|
||||
$("#macro-slider").css("background", "#e6e7e8");
|
||||
$("#macro-slider").append("<div id='macro-handle'></div>");
|
||||
$("#macro-handle").css("width", "20%");
|
||||
$("#macro-handle").css("height", "100%");
|
||||
$("#macro-handle").css("background", "#cc0000");
|
||||
},
|
||||
updateOdometer: function() {
|
||||
$j.log("updating odoemeter");
|
||||
}
|
||||
|
||||
} // end class Viewport
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
${data_resource}
|
||||
@@ -0,0 +1,104 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>
|
||||
Galaxy
|
||||
</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" />
|
||||
|
||||
<script type='text/javascript' src="${h.url_for('/static/scripts/jquery.js')}"> </script>
|
||||
<script type='text/javascript' src="${h.url_for('/static/scripts/jquery.ui.js')}"> </script>
|
||||
<script type='text/javascript' src="${h.url_for('/static/scripts/jquery.event.drag.js')}"> </script>
|
||||
<script type='text/javascript' src="${h.url_for('/static/scripts/jquery.hoverIntent.js')}"> </script>
|
||||
<script type='text/javascript' src="${h.url_for('/static/scripts/jamal.js')}"> </script>
|
||||
<script type='text/javascript' src="${h.url_for('/static/tracks/app_model.js')}"> </script>
|
||||
<script type='text/javascript' src="${h.url_for('/static/tracks/app_controller.js')}"> </script>
|
||||
<script type='text/javascript' src="${h.url_for('/static/tracks/app_view.js')}"> </script>
|
||||
<script type='text/javascript' src="${h.url_for('/static/tracks/models/tracks.js')}"> </script>
|
||||
<script type='text/javascript' src="${h.url_for('/static/tracks/views/viewport.js')}"> </script>
|
||||
<script type='text/javascript' src="${h.url_for('/static/tracks/views/drawing_api.js')}"> </script>
|
||||
<script type='text/javascript' src="${h.url_for('/static/tracks/controllers/tracks.js')}"> </script>
|
||||
<script type='text/javascript' src="${h.url_for('/static/tracks/views/components.js')}"> </script>
|
||||
<link type="text/css" rel="stylesheet" href="http://ui.jquery.com/testing/themes/base/ui.all.css" />
|
||||
<link href="${h.url_for('/static/tracks/app.css')}" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body class="jamal {controller:'Viewport',action:'index',debug:true}">
|
||||
<div id="viewport" class="float-left">
|
||||
<div id="viewport-top" class="width-hundred-percent block overflow">
|
||||
<div class="float-left width-twenty-percent">
|
||||
<div id="db-tools" class="width-hundred-percent block">
|
||||
<h2 class="margin-zero">db: </h2>
|
||||
<select name="dbkey" id="dbkey"> </select>
|
||||
<select name="chrom" id="chrom"> </select>
|
||||
</div>
|
||||
<div id="history-tools" class="width-hundred-percent block">
|
||||
<a href="#" id="show-history" class="float-left margin-zero padding-zero">
|
||||
<img src="${h.url_for('/static/images/tracks/show_history.gif')}">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="float-left width-eighty-percent">
|
||||
<div id="window-macro" class="width-hundred-percent block">
|
||||
<div id="macro-slider"> </div>
|
||||
</div>
|
||||
<div id="window-micro" class="width-hundred-percent block">
|
||||
<canvas id="x-axis" class="axis width-hundred-percent float-left"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="viewport-inner" class="width-hundred-percent block">
|
||||
<ul id="tracks-sortable" class="width-hundred-percent padding-zero margin-zero">
|
||||
<li class="track-list-item width-hundred-percent" id="item_1">
|
||||
<div id="t11" class="track width-hundred-percent no-overflow margin-zero">
|
||||
<div class="track-label margin-zero float-left width-twenty-percent">
|
||||
<h5 class="track-title">mRNA</h5>
|
||||
</div>
|
||||
<div class="track-track margin-zero float-left width-eighty-percent">
|
||||
<div id="track-graphics">
|
||||
<canvas class="track-canvas width-hundred-percent"> </canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="dock" class="padding-zero width-sixty-percent">
|
||||
<ul>
|
||||
<li><select name="chrom" id="chrom-select"> </select></li>
|
||||
<li><input type="text" id="position-window" /></li>
|
||||
<li><a id="go-btn" href="#"><img src="${h.url_for('/static/images/tracks/go_btn.gif')}"/></a></li>
|
||||
<li><a id="pan-left" href="#"><img src="${h.url_for('/static/images/tracks/pan_left.gif')}"/></a></li>
|
||||
<li><a id="pan-right" href="#"><img src="${h.url_for('/static/images/tracks/pan_right.gif')}"/></a></li>
|
||||
<li><a id="zoom-out-full" href="#"><img src="${h.url_for('/static/images/tracks/zoom_out_full.gif')}"/></a></li>
|
||||
<li><a id="zoom-out" href="#"><img src="${h.url_for('/static/images/tracks/zoom_out.gif')}"/></a></li>
|
||||
<li><a id="zoom-in" href="#"><img src="${h.url_for('/static/images/tracks/zoom_in.gif')}"/></a></li>
|
||||
<li><a id="zoom-in-full" href="#"><img src="${h.url_for('/static/images/tracks/zoom_in_full.gif')}"/></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div id="drag-history" class="float-left margin-zero">
|
||||
<div id="history-control-bar" class="width-hundred-percent">
|
||||
<div class="width-sixty-percent float-left">
|
||||
<h3 class="margin-zero">History</h3>
|
||||
</div>
|
||||
<div class="width-forty-percent float-left">
|
||||
<a id="hide-history" href="#">
|
||||
<img src="${h.url_for('/static/images/tracks/close_btn.gif')}"/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<ul id="tracks-history-sortable">
|
||||
<li class="track-history-item" id="item_1">
|
||||
<div class="history-item" id="history-item-1">
|
||||
<div class="history-edit-panel"> </div>
|
||||
<span class="history-item-title">
|
||||
<b>3: UCSC Main on Human: knownGene (genome)</b>
|
||||
<span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -40,6 +40,9 @@ tool_data_path = tool-data
|
||||
datatype_converters_config_file = datatype_converters_conf.xml
|
||||
datatype_converters_path = %(here)s/lib/galaxy/datatypes/converters
|
||||
|
||||
# Datatype indexers
|
||||
datatype_indexers_path = %(here)s/lib/galaxy/datatypes/indexers
|
||||
|
||||
# Session support (beaker)
|
||||
use_beaker_session = True
|
||||
session_type = file
|
||||
|
||||