5 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/567d72ce3a8b/
Changeset: 567d72ce3a8b
User: dannon
Date: 2014-08-26 16:28:00
Summary: Merge 'recent jobs' view from PR466/467. Work in progress.
Affected #: 2 files
diff -r c665ed80227c76001c0a2e767e66d992ed72b4ad -r
567d72ce3a8b4ab3d07e677692d04499bf4c4e36 lib/galaxy/web/base/controllers/admin.py
--- a/lib/galaxy/web/base/controllers/admin.py
+++ b/lib/galaxy/web/base/controllers/admin.py
@@ -1089,6 +1089,11 @@
trans.app.model.Job.state ==
trans.app.model.Job.states.RUNNING,
trans.app.model.Job.state ==
trans.app.model.Job.states.UPLOAD ) ) ) \
.order_by( trans.app.model.Job.table.c.update_time.desc()
)
+ recent_jobs = trans.sa_session.query( trans.app.model.Job ) \
+ .filter( and_( trans.app.model.Job.table.c.update_time
> cutoff_time,
+ or_( trans.app.model.Job.state ==
trans.app.model.Job.states.ERROR,
+ trans.app.model.Job.state ==
trans.app.model.Job.states.OK) ) ) \
+ .order_by( trans.app.model.Job.table.c.update_time.desc()
)
last_updated = {}
for job in jobs:
delta = datetime.utcnow() - job.update_time
@@ -1096,14 +1101,35 @@
last_updated[job.id] = '%s hours' % int( delta.seconds / 60 / 60
)
else:
last_updated[job.id] = '%s minutes' % int( delta.seconds / 60 )
+ finished = {}
+ for job in recent_jobs:
+ delta = datetime.utcnow() - job.update_time
+ if delta > timedelta( minutes=60 ):
+ finished[job.id] = '%s hours' % int( delta.seconds / 60 / 60 )
+ else:
+ finished[job.id] = '%s minutes' % int( delta.seconds / 60 )
return trans.fill_template( '/admin/jobs.mako',
jobs = jobs,
+ recent_jobs = recent_jobs,
last_updated = last_updated,
+ finished = finished,
cutoff = cutoff,
msg = msg,
status = status,
job_lock = job_lock)
+
+ @web.expose
+ @web.require_admin
+ def job_info( self, trans, jobid=None ):
+ job = None
+ if jobid is not None:
+ job = trans.sa_session.query( trans.app.model.Job ).get(jobid)
+ return trans.fill_template( '/webapps/reports/job_info.mako',
+ job=job,
+ message="<a
href='jobs'>Back</a>" )
+
+
## ---- Utility methods -------------------------------------------------------
def get_user( trans, user_id ):
diff -r c665ed80227c76001c0a2e767e66d992ed72b4ad -r
567d72ce3a8b4ab3d07e677692d04499bf4c4e36 templates/admin/jobs.mako
--- a/templates/admin/jobs.mako
+++ b/templates/admin/jobs.mako
@@ -109,6 +109,52 @@
<div class="infomessage">There are no unfinished jobs to show with
current cutoff time.</div><p/>
%endif
+
+%if recent_jobs:
+ <p>
+ Recent Jobs: These jobs have completed
+ </p>
+ <table class="manage-table colored" border="0"
cellspacing="0" cellpadding="0" width="100%">
+ <tr class="header">
+ <td>Job ID</td>
+ <td>User</td>
+ <td>Finished</td>
+ <td>Tool</td>
+ <td>State</td>
+ <td>Inputs</td>
+ <td>Command Line</td>
+ <td>Job Runner</td>
+ <td>PID/Cluster ID</td>
+ </tr>
+ %for job in recent_jobs:
+ <td><a href="${h.url_for( controller="admin",
action="job_info" )}?jobid=${job.id}">${job.id}</a></td>
+ %if job.history and job.history.user:
+ <td>${job.history.user.email}</td>
+ %else:
+ <td>anonymous</td>
+ %endif
+ <td>${finished[job.id]} ago</td>
+ <td>${job.tool_id}</td>
+ <td>${job.state}</td>
+ <%
+ try:
+ inputs = ", ".join( [ '%s %s' % (
da.dataset.id, da.dataset.state ) for da in job.input_datasets ] )
+ except:
+ inputs = 'Unable to determine inputs'
+ %>
+ <td>${inputs}</td>
+ <td>${job.command_line}</td>
+ <td>${job.job_runner_name}</td>
+ <td>${job.job_runner_external_id}</td>
+ </tr>
+ %endfor
+ </table>
+ <p/>
+%else:
+ <div class="infomessage">There are no recently finished jobs to show
with current cutoff time.</div>
+ <p/>
+%endif
+
<form name="jobs" action="${h.url_for(controller='admin',
action='jobs')}" method="POST"><div
class="toolForm"><div class="toolFormTitle">
https://bitbucket.org/galaxy/galaxy-central/commits/44fe3904feb4/
Changeset: 44fe3904feb4
User: dannon
Date: 2014-08-26 16:28:38
Summary: Remove extra p tag from jobs admin.
Affected #: 1 file
diff -r 567d72ce3a8b4ab3d07e677692d04499bf4c4e36 -r
44fe3904feb44dd465f79934f999bc5c3bf99cc7 templates/admin/jobs.mako
--- a/templates/admin/jobs.mako
+++ b/templates/admin/jobs.mako
@@ -33,9 +33,6 @@
report this error".
</p>
-
-<p/>
-
%if jobs:
<form name="jobs" action="${h.url_for(controller='admin',
action='jobs')}" method="POST"><table
class="manage-table colored" border="0" cellspacing="0"
cellpadding="0" width="100%">
https://bitbucket.org/galaxy/galaxy-central/commits/81e2b1d1984e/
Changeset: 81e2b1d1984e
User: dannon
Date: 2014-08-26 16:34:31
Summary: Fix (old) bug causing Jobs grid to display even when there were no jobs to
show in it. Issue was 'jobs' (and the new recent_jobs) was actually a Query
object and not an empty list of results, causing 'if jobs' to evaluate True
regardless of whether or not there were jobs.
Affected #: 1 file
diff -r 44fe3904feb44dd465f79934f999bc5c3bf99cc7 -r
81e2b1d1984e8f4bcdd2d1f82055e815cbced985 lib/galaxy/web/base/controllers/admin.py
--- a/lib/galaxy/web/base/controllers/admin.py
+++ b/lib/galaxy/web/base/controllers/admin.py
@@ -1088,12 +1088,12 @@
trans.app.model.Job.state ==
trans.app.model.Job.states.QUEUED,
trans.app.model.Job.state ==
trans.app.model.Job.states.RUNNING,
trans.app.model.Job.state ==
trans.app.model.Job.states.UPLOAD ) ) ) \
- .order_by( trans.app.model.Job.table.c.update_time.desc()
)
+ .order_by( trans.app.model.Job.table.c.update_time.desc()
).all()
recent_jobs = trans.sa_session.query( trans.app.model.Job ) \
.filter( and_( trans.app.model.Job.table.c.update_time
> cutoff_time,
or_( trans.app.model.Job.state ==
trans.app.model.Job.states.ERROR,
trans.app.model.Job.state ==
trans.app.model.Job.states.OK) ) ) \
- .order_by( trans.app.model.Job.table.c.update_time.desc()
)
+ .order_by( trans.app.model.Job.table.c.update_time.desc()
).all()
last_updated = {}
for job in jobs:
delta = datetime.utcnow() - job.update_time
https://bitbucket.org/galaxy/galaxy-central/commits/314326446ec5/
Changeset: 314326446ec5
User: dannon
Date: 2014-08-26 16:48:12
Summary: Clarify wording, explain cutoff. Add headers to better segment the page and
prevent confusion between the two grids.
Affected #: 1 file
diff -r 81e2b1d1984e8f4bcdd2d1f82055e815cbced985 -r
314326446ec51483341eaacae7902fff44c8ac02 templates/admin/jobs.mako
--- a/templates/admin/jobs.mako
+++ b/templates/admin/jobs.mako
@@ -22,9 +22,11 @@
%endif
<p>
- All unfinished jobs are displayed here. To display only jobs that have not
- had their job state updated recently, set a cutoff value in the 'cutoff'
- box below.
+ Unfinished and recently finished jobs are displayed on this page. The
+ 'cutoff' input box will do two things -- it will limit the display of
+ unfinished jobs to only those jobs that have not had their job state
+ updated recently, and it will limit the recently finished jobs list to only
+ displaying jobs that have finished since the cutoff.
</p><p>
If any jobs are displayed, you may choose to stop them. Your stop message
@@ -35,6 +37,9 @@
%if jobs:
<form name="jobs" action="${h.url_for(controller='admin',
action='jobs')}" method="POST">
+ <h4>
+ Unfinished Jobs: These jobs are unfinished and have had their state updated in
the previous ${cutoff} seconds.
+ </h4><table class="manage-table colored" border="0"
cellspacing="0" cellpadding="0" width="100%"><tr
class="header"><td><input type="checkbox"
onClick="toggle_all(this)"/></td>
@@ -108,9 +113,9 @@
%endif
%if recent_jobs:
- <p>
- Recent Jobs: These jobs have completed
- </p>
+ <h4>
+ Recent Jobs: These jobs have completed in the previous ${cutoff} seconds.
+ </h4><table class="manage-table colored" border="0"
cellspacing="0" cellpadding="0" width="100%"><tr
class="header"><td>Job ID</td>
https://bitbucket.org/galaxy/galaxy-central/commits/3a51eaf209f2/
Changeset: 3a51eaf209f2
User: dannon
Date: 2014-08-26 16:48:35
Summary: Merge.
Affected #: 11 files
diff -r 314326446ec51483341eaacae7902fff44c8ac02 -r
3a51eaf209f2502bf32dbb421ecabb7fe46243ea .hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -18,4 +18,4 @@
81fbe25bd02edcd53065e8e4476dd1dfb5a72cf2 latest_2013.11.04
2a756ca2cb1826db7796018e77d12e2dd7b67603 latest_2014.02.10
ca45b78adb4152fc6e7395514d46eba6b7d0b838 release_2014.08.11
-7e4d21621ce12e13ebbdf9fd3259df58c3ef124c latest_2014.08.11
+7e22f35798522100ff03e1fdd4eced962b292360 latest_2014.08.11
diff -r 314326446ec51483341eaacae7902fff44c8ac02 -r
3a51eaf209f2502bf32dbb421ecabb7fe46243ea
lib/galaxy/webapps/galaxy/controllers/workflow.py
--- a/lib/galaxy/webapps/galaxy/controllers/workflow.py
+++ b/lib/galaxy/webapps/galaxy/controllers/workflow.py
@@ -24,7 +24,7 @@
from galaxy.web.framework import form
from galaxy.web.framework.helpers import grids, time_ago
from galaxy.web.framework.helpers import to_unicode
-from galaxy.workflow.modules import module_factory
+from galaxy.workflow.modules import module_factory, is_tool_module_type
from galaxy.workflow.run import invoke
from galaxy.workflow.run import WorkflowRunConfig
from galaxy.workflow.extract import summarize
@@ -836,8 +836,8 @@
steps_by_external_id = {}
errors = []
for key, step_dict in data['steps'].iteritems():
- is_input = step_dict[ 'type' ] in [ 'data_input',
'data_collection_input' ]
- if not is_input and step_dict['tool_id'] not in
trans.app.toolbox.tools_by_id:
+ is_tool = is_tool_module_type( step_dict[ 'type' ] )
+ if is_tool and step_dict['tool_id'] not in
trans.app.toolbox.tools_by_id:
errors.append("Step %s requires tool '%s'." %
(step_dict['id'], step_dict['tool_id']))
if errors:
return dict( name=workflow.name,
diff -r 314326446ec51483341eaacae7902fff44c8ac02 -r
3a51eaf209f2502bf32dbb421ecabb7fe46243ea lib/galaxy/workflow/modules.py
--- a/lib/galaxy/workflow/modules.py
+++ b/lib/galaxy/workflow/modules.py
@@ -71,9 +71,18 @@
## ---- Configuration time -----------------------------------------------
def get_state( self ):
+ """ Return a serializable representation of the persistable state
of
+ the step - for tools it DefaultToolState.encode returns a string and
+ for inputs a json description is dumped out.
+ """
return None
def get_errors( self ):
+ """ It seems like this is effectively just used as boolean - some
places
+ in the tool shed self.errors is set to boolean, other places
'unavailable',
+ likewise in Galaxy it stores a list containing a string with an unrecognized
+ tool id error message.
+ """
return None
def get_data_inputs( self ):
@@ -292,6 +301,9 @@
@classmethod
def new( Class, trans, tool_id=None ):
module = Class( trans, tool_id )
+ if module.tool is None:
+ error_message = "Attempted to create new workflow module for invalid
tool_id, no tool with id - %s." % tool_id
+ raise Exception( error_message )
module.state = module.tool.new_state( trans, all_pages=True )
return module
@@ -536,5 +548,14 @@
type = step.type
return self.module_types[type].from_workflow_step( trans, step )
-module_types = dict( data_input=InputDataModule,
data_collection_input=InputDataCollectionModule, tool=ToolModule )
+
+def is_tool_module_type( module_type ):
+ return not module_type or module_type == "tool"
+
+
+module_types = dict(
+ data_input=InputDataModule,
+ data_collection_input=InputDataCollectionModule,
+ tool=ToolModule,
+)
module_factory = WorkflowModuleFactory( module_types )
diff -r 314326446ec51483341eaacae7902fff44c8ac02 -r
3a51eaf209f2502bf32dbb421ecabb7fe46243ea lib/galaxy/workflow/run.py
--- a/lib/galaxy/workflow/run.py
+++ b/lib/galaxy/workflow/run.py
@@ -71,7 +71,7 @@
self.trans.sa_session.add( workflow_invocation )
# Not flushing in here, because web controller may create multiple
- # invokations.
+ # invocations.
return self.outputs
def _invoke_step( self, step ):
@@ -211,10 +211,9 @@
"""
replacement = None
if prefixed_name in step.input_connections_by_name:
- outputs = self.outputs
connection = step.input_connections_by_name[ prefixed_name ]
if input.multiple:
- replacement = [ outputs[ c.output_step.id ][ c.output_name ] for c in
connection ]
+ replacement = [ self._replacement_for_connection( c ) for c in connection
]
# If replacement is just one dataset collection, replace tool
# input with dataset collection - tool framework will extract
# datasets properly.
@@ -222,9 +221,12 @@
if isinstance( replacement[ 0 ],
model.HistoryDatasetCollectionAssociation ):
replacement = replacement[ 0 ]
else:
- replacement = outputs[ connection[ 0 ].output_step.id ][ connection[ 0
].output_name ]
+ replacement = self._replacement_for_connection( connection[ 0 ] )
return replacement
+ def _replacement_for_connection( self, connection ):
+ return self.outputs[ connection.output_step.id ][ connection.output_name ]
+
def _populate_state( self ):
# Build the state for each step
for step in self.workflow.steps:
diff -r 314326446ec51483341eaacae7902fff44c8ac02 -r
3a51eaf209f2502bf32dbb421ecabb7fe46243ea static/scripts/packed/viz/trackster/painters.js
--- a/static/scripts/packed/viz/trackster/painters.js
+++ b/static/scripts/packed/viz/trackster/painters.js
@@ -1,1 +1,1 @@
-define(["libs/underscore"],function(_){var
BEFORE=1001,CONTAINS=1002,OVERLAP_START=1003,OVERLAP_END=1004,CONTAINED_BY=1005,AFTER=1006;var
compute_overlap=function(first_region,second_region){var
first_start=first_region[0],first_end=first_region[1],second_start=second_region[0],second_end=second_region[1],overlap;if(first_start<second_start){if(first_end<=second_start){overlap=BEFORE}else{if(first_end<=second_end){overlap=OVERLAP_START}else{overlap=CONTAINS}}}else{if(first_start>second_end){overlap=AFTER}else{if(first_end<=second_end){overlap=CONTAINED_BY}else{overlap=OVERLAP_END}}}return
overlap};var is_overlap=function(first_region,second_region){var
overlap=compute_overlap(first_region,second_region);return(overlap!==BEFORE&&overlap!==AFTER)};var
dashedLine=function(ctx,x1,y1,x2,y2,dashLen){if(dashLen===undefined){dashLen=4}var
dX=x2-x1;var dY=y2-y1;var dashes=Math.floor(Math.sqrt(dX*dX+dY*dY)/dashLen);var
dashX=dX/dashes;var dashY=dY/dashes;var
q;for(q=0;q<dashes;q++,x1+=dashX,y1+=dashY){if(q%2!==0){continue}ctx.fillRect(x1,y1,dashLen,1)}};var
drawDownwardEquilateralTriangle=function(ctx,down_vertex_x,down_vertex_y,side_len){var
x1=down_vertex_x-side_len/2,x2=down_vertex_x+side_len/2,y=down_vertex_y-Math.sqrt(side_len*3/2);ctx.beginPath();ctx.moveTo(x1,y);ctx.lineTo(x2,y);ctx.lineTo(down_vertex_x,down_vertex_y);ctx.lineTo(x1,y);ctx.strokeStyle=this.fillStyle;ctx.fill();ctx.stroke();ctx.closePath()};var
Scaler=function(default_val){this.default_val=(default_val?default_val:1)};Scaler.prototype.gen_val=function(input){return
this.default_val};var
DrawResults=function(options){this.incomplete_features=options.incomplete_features;this.feature_mapper=options.feature_mapper};var
Painter=function(data,view_start,view_end,prefs,mode){this.data=data;this.view_start=view_start;this.view_end=view_end;this.prefs=_.extend({},this.default_prefs,prefs);this.mode=mode};Painter.prototype.default_prefs={};Painter.prototype.draw=function(ctx,width,height,w_scale){};var
LinePainter=function(data,view_start,view_end,prefs,mode){Painter.call(this,data,view_start,view_end,prefs,mode);if(this.prefs.min_value===undefined){this.prefs.min_value=_.min(_.map(this.data,function(d){return
d[1]}))||0}if(this.prefs.max_value===undefined){this.prefs.max_value=_.max(_.map(this.data,function(d){return
d[1]}))||0}};LinePainter.prototype.default_prefs={min_value:undefined,max_value:undefined,mode:"Histogram",color:"#000",overflow_color:"#F66"};LinePainter.prototype.draw=function(ctx,width,height,w_scale){var
in_path=false,min_value=this.prefs.min_value,max_value=this.prefs.max_value,vertical_range=max_value-min_value,height_px=height,view_start=this.view_start,mode=this.mode,data=this.data;ctx.save();var
y_zero=Math.round(height+min_value/vertical_range*height);if(mode!=="Intensity"){ctx.fillStyle="#aaa";ctx.fillRect(0,y_zero,width,1)}ctx.beginPath();var
x_scaled,y,delta_x_px;if(data.length>1){delta_x_px=Math.ceil((data[1][0]-data[0][0])*w_scale)}else{delta_x_px=10}var
painter_color=this.prefs.block_color||this.prefs.color,pref_color=parseInt(painter_color.slice(1),16),pref_r=(pref_color&16711680)>>16,pref_g=(pref_color&65280)>>8,pref_b=pref_color&255,top_overflow=false,bot_overflow=false;for(var
i=0,len=data.length;i<len;i++){ctx.fillStyle=ctx.strokeStyle=painter_color;top_overflow=bot_overflow=false;x_scaled=Math.ceil((data[i][0]-view_start)*w_scale);y=data[i][1];if(y===null){if(in_path&&mode==="Filled"){ctx.lineTo(x_scaled,height_px)}in_path=false;continue}if(y<min_value){bot_overflow=true;y=min_value}else{if(y>max_value){top_overflow=true;y=max_value}}if(mode==="Histogram"){y=Math.round(y/vertical_range*height_px);ctx.fillRect(x_scaled,y_zero,delta_x_px,-y)}else{if(mode==="Intensity"){var
saturation=(y-min_value)/vertical_range,new_r=Math.round(pref_r+(255-pref_r)*(1-saturation)),new_g=Math.round(pref_g+(255-pref_g)*(1-saturation)),new_b=Math.round(pref_b+(255-pref_b)*(1-saturation));ctx.fillStyle="rgb("+new_r+","+new_g+","+new_b+")";ctx.fillRect(x_scaled,0,delta_x_px,height_px)}else{y=Math.round(height_px-(y-min_value)/vertical_range*height_px);if(in_path){ctx.lineTo(x_scaled,y)}else{in_path=true;if(mode==="Filled"){ctx.moveTo(x_scaled,height_px);ctx.lineTo(x_scaled,y)}else{ctx.moveTo(x_scaled,y)}}}}ctx.fillStyle=this.prefs.overflow_color;if(top_overflow||bot_overflow){var
overflow_x;if(mode==="Histogram"||mode==="Intensity"){overflow_x=delta_x_px}else{x_scaled-=2;overflow_x=4}if(top_overflow){ctx.fillRect(x_scaled,0,overflow_x,3)}if(bot_overflow){ctx.fillRect(x_scaled,height_px-3,overflow_x,3)}}ctx.fillStyle=painter_color}if(mode==="Filled"){if(in_path){ctx.lineTo(x_scaled,y_zero);ctx.lineTo(0,y_zero)}ctx.fill()}else{ctx.stroke()}ctx.restore()};var
FeaturePositionMapper=function(slot_height){this.feature_positions={};this.slot_height=slot_height;this.translation=0;this.y_translation=0};FeaturePositionMapper.prototype.map_feature_data=function(feature_data,slot,x_start,x_end){if(!this.feature_positions[slot]){this.feature_positions[slot]=[]}this.feature_positions[slot].push({data:feature_data,x_start:x_start,x_end:x_end})};FeaturePositionMapper.prototype.get_feature_data=function(x,y){var
slot=Math.floor((y-this.y_translation)/this.slot_height),feature_dict;if(!this.feature_positions[slot]){return
null}x+=this.translation;for(var
i=0;i<this.feature_positions[slot].length;i++){feature_dict=this.feature_positions[slot][i];if(x>=feature_dict.x_start&&x<=feature_dict.x_end){return
feature_dict.data}}};var
FeaturePainter=function(data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler){Painter.call(this,data,view_start,view_end,prefs,mode);this.alpha_scaler=(alpha_scaler?alpha_scaler:new
Scaler());this.height_scaler=(height_scaler?height_scaler:new
Scaler());this.max_label_length=200};FeaturePainter.prototype.default_prefs={block_color:"#FFF",connector_color:"#FFF"};_.extend(FeaturePainter.prototype,{get_required_height:function(rows_required,width){var
required_height=this.get_row_height(),y_scale=required_height,mode=this.mode;if(mode==="no_detail"||mode==="Squish"||mode==="Pack"){required_height=rows_required*y_scale}return
required_height+this.get_top_padding(width)},get_top_padding:function(width){return
0},draw:function(ctx,width,height,w_scale,slots){var
data=this.data,view_start=this.view_start,view_end=this.view_end;ctx.save();ctx.fillStyle=this.prefs.block_color;ctx.textAlign="right";var
y_scale=this.get_row_height(),feature_mapper=new
FeaturePositionMapper(y_scale),x_draw_coords,incomplete_features=[];for(var
i=0,len=data.length;i<len;i++){var
feature=data[i],feature_uid=feature[0],feature_start=feature[1],feature_end=feature[2],slot=(slots&&slots[feature_uid]!==undefined?slots[feature_uid]:null);if((this.mode==="Dense"||slot!==null)&&(feature_start<view_end&&feature_end>view_start)){x_draw_coords=this.draw_element(ctx,this.mode,feature,slot,view_start,view_end,w_scale,y_scale,width);feature_mapper.map_feature_data(feature,slot,x_draw_coords[0],x_draw_coords[1]);if(feature_start<view_start||feature_end>view_end){incomplete_features.push(feature)}}}ctx.restore();feature_mapper.y_translation=this.get_top_padding(width);return
new
DrawResults({incomplete_features:incomplete_features,feature_mapper:feature_mapper})},draw_element:function(ctx,mode,feature,slot,tile_low,tile_high,w_scale,y_scale,width){return[0,0]}});var
DENSE_TRACK_HEIGHT=10,NO_DETAIL_TRACK_HEIGHT=3,SQUISH_TRACK_HEIGHT=5,PACK_TRACK_HEIGHT=10,NO_DETAIL_FEATURE_HEIGHT=1,DENSE_FEATURE_HEIGHT=9,SQUISH_FEATURE_HEIGHT=3,PACK_FEATURE_HEIGHT=9,LABEL_SPACING=2,CONNECTOR_COLOR="#ccc";var
LinkedFeaturePainter=function(data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler){FeaturePainter.call(this,data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler);this.draw_background_connector=true;this.draw_individual_connectors=false};_.extend(LinkedFeaturePainter.prototype,FeaturePainter.prototype,{get_row_height:function(){var
mode=this.mode,height;if(mode==="Dense"){height=DENSE_TRACK_HEIGHT}else{if(mode==="no_detail"){height=NO_DETAIL_TRACK_HEIGHT}else{if(mode==="Squish"){height=SQUISH_TRACK_HEIGHT}else{height=PACK_TRACK_HEIGHT}}}return
height},draw_element:function(ctx,mode,feature,slot,tile_low,tile_high,w_scale,y_scale,width){var
feature_uid=feature[0],feature_start=feature[1],feature_end=feature[2],feature_name=feature[3],feature_strand=feature[4],f_start=Math.floor(Math.max(0,(feature_start-tile_low-0.5)*w_scale)),f_end=Math.ceil(Math.min(width,Math.max(0,(feature_end-tile_low-0.5)*w_scale))),draw_start=f_start,draw_end=f_end,y_start=(mode==="Dense"?0:(0+slot))*y_scale+this.get_top_padding(width),thickness,y_start,thick_start=null,thick_end=null,block_color=(!feature_strand||feature_strand==="+"||feature_strand==="."?this.prefs.block_color:this.prefs.reverse_strand_color);label_color=this.prefs.label_color;ctx.globalAlpha=this.alpha_scaler.gen_val(feature);if(mode==="Dense"){slot=1}if(mode==="no_detail"){ctx.fillStyle=block_color;ctx.fillRect(f_start,y_start+5,f_end-f_start,NO_DETAIL_FEATURE_HEIGHT)}else{var
feature_ts=feature[5],feature_te=feature[6],feature_blocks=feature[7],full_height=true;if(feature_ts&&feature_te){thick_start=Math.floor(Math.max(0,(feature_ts-tile_low)*w_scale));thick_end=Math.ceil(Math.min(width,Math.max(0,(feature_te-tile_low)*w_scale)))}var
thin_height,thick_height;if(mode==="Squish"){thin_height=1;thick_height=SQUISH_FEATURE_HEIGHT;full_height=false}else{if(mode==="Dense"){thin_height=5;thick_height=DENSE_FEATURE_HEIGHT}else{thin_height=5;thick_height=PACK_FEATURE_HEIGHT}}if(!feature_blocks){ctx.fillStyle=block_color;ctx.fillRect(f_start,y_start+1,f_end-f_start,thick_height);if(feature_strand&&full_height){if(feature_strand==="+"){ctx.fillStyle=ctx.canvas.manager.get_pattern("right_strand_inv")}else{if(feature_strand==="-"){ctx.fillStyle=ctx.canvas.manager.get_pattern("left_strand_inv")}}ctx.fillRect(f_start,y_start+1,f_end-f_start,thick_height)}}else{var
cur_y_start,cur_height;if(mode==="Squish"||mode==="Dense"){cur_y_start=y_start+Math.floor(SQUISH_FEATURE_HEIGHT/2)+1;cur_height=1}else{if(feature_strand){cur_y_start=y_start;cur_height=thick_height}else{cur_y_start+=(SQUISH_FEATURE_HEIGHT/2)+1;cur_height=1}}if(this.draw_background_connector){if(mode==="Squish"||mode==="Dense"){ctx.fillStyle=CONNECTOR_COLOR}else{if(feature_strand){if(feature_strand==="+"){ctx.fillStyle=ctx.canvas.manager.get_pattern("right_strand")}else{if(feature_strand==="-"){ctx.fillStyle=ctx.canvas.manager.get_pattern("left_strand")}}}else{ctx.fillStyle=CONNECTOR_COLOR}}ctx.fillRect(f_start,cur_y_start,f_end-f_start,cur_height)}var
start_and_height;for(var k=0,k_len=feature_blocks.length;k<k_len;k++){var
block=feature_blocks[k],block_start=Math.floor(Math.max(0,(block[0]-tile_low-0.5)*w_scale)),block_end=Math.ceil(Math.min(width,Math.max((block[1]-tile_low-0.5)*w_scale))),last_block_start,last_block_end;if(block_start>block_end){continue}ctx.fillStyle=block_color;ctx.fillRect(block_start,y_start+(thick_height-thin_height)/2+1,block_end-block_start,thin_height);if(thick_start!==undefined&&feature_te>feature_ts&&!(block_start>thick_end||block_end<thick_start)){var
block_thick_start=Math.max(block_start,thick_start),block_thick_end=Math.min(block_end,thick_end);ctx.fillRect(block_thick_start,y_start+1,block_thick_end-block_thick_start,thick_height);if(feature_blocks.length===1&&mode==="Pack"){if(feature_strand==="+"){ctx.fillStyle=ctx.canvas.manager.get_pattern("right_strand_inv")}else{if(feature_strand==="-"){ctx.fillStyle=ctx.canvas.manager.get_pattern("left_strand_inv")}}if(block_thick_start+14<block_thick_end){block_thick_start+=2;block_thick_end-=2}ctx.fillRect(block_thick_start,y_start+1,block_thick_end-block_thick_start,thick_height)}}if(this.draw_individual_connectors&&last_block_start){this.draw_connector(ctx,last_block_start,last_block_end,block_start,block_end,y_start)}last_block_start=block_start;last_block_end=block_end}if(mode==="Pack"){ctx.globalAlpha=1;ctx.fillStyle="white";var
hscale_factor=this.height_scaler.gen_val(feature),new_height=Math.ceil(thick_height*hscale_factor),ws_height=Math.round((thick_height-new_height)/2);if(hscale_factor!==1){ctx.fillRect(f_start,cur_y_start+1,f_end-f_start,ws_height);ctx.fillRect(f_start,cur_y_start+thick_height-ws_height+1,f_end-f_start,ws_height)}}}ctx.globalAlpha=1;if(feature_name&&mode==="Pack"&&feature_start>tile_low){ctx.fillStyle=label_color;if(tile_low===0&&f_start-ctx.measureText(feature_name).width<0){ctx.textAlign="left";ctx.fillText(feature_name,f_end+LABEL_SPACING,y_start+8,this.max_label_length);draw_end+=ctx.measureText(feature_name).width+LABEL_SPACING}else{ctx.textAlign="right";ctx.fillText(feature_name,f_start-LABEL_SPACING,y_start+8,this.max_label_length);draw_start-=ctx.measureText(feature_name).width+LABEL_SPACING}}}ctx.globalAlpha=1;return[draw_start,draw_end]}});var
ReadPainter=function(data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler,ref_seq,base_color_fn){FeaturePainter.call(this,data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler);this.ref_seq=(ref_seq?ref_seq.data:null);this.base_color_fn=base_color_fn};_.extend(ReadPainter.prototype,FeaturePainter.prototype,{get_row_height:function(){var
height,mode=this.mode;if(mode==="Dense"){height=DENSE_TRACK_HEIGHT}else{if(mode==="Squish"){height=SQUISH_TRACK_HEIGHT}else{height=PACK_TRACK_HEIGHT;if(this.prefs.show_insertions){height*=2}}}return
height},_parse_cigar:function(cigar_str){var cigar_ops="MIDNSHP=X";var
blocks=[[0,0]],cur_block=blocks[0],base_pos=0,parsed_cigar=_.map(cigar_str.match(/[0-9]+[MIDNSHP=X]/g),function(op){var
op_len=parseInt(op.slice(0,-1),10),op_char=op.slice(-1);if(op_char==="N"){if(cur_block[1]!==0){cur_block=[base_pos+op_len,base_pos+op_len];blocks.push(cur_block)}}else{if("ISHP".indexOf(op_char)===-1){cur_block[1]+=op_len;base_pos+=op_len}}return[cigar_ops.indexOf(op_char),op_len]});return{blocks:blocks,cigar:parsed_cigar}},draw_read:function(ctx,mode,w_scale,y_start,tile_low,tile_high,feature_start,cigar,strand,read_seq){var
update_base_offset=function(offset,cig_op,cig_len){if("M=NXD".indexOf(cig_op)!==-1){offset+=cig_len}return
offset},update_seq_offset=function(offset,cig_op,cig_len){if("IX".indexOf(cig_op)!==-1){offset+=cig_len}return
offset},get_draw_coord=function(sequence_coord){return
Math.floor(Math.max(0,(sequence_coord-tile_low-0.5)*w_scale))};ctx.textAlign="center";var
tile_region=[tile_low,tile_high],base_offset=0,seq_offset=0,gap=Math.round(w_scale/2),char_width_px=ctx.canvas.manager.char_width_px,block_color=(strand==="+"?this.prefs.detail_block_color:this.prefs.reverse_strand_color),pack_mode=(mode==="Pack"),draw_height=(pack_mode?PACK_FEATURE_HEIGHT:SQUISH_FEATURE_HEIGHT),rect_y=y_start+1,paint_utils=new
ReadPainterUtils(ctx,draw_height,w_scale,mode),drawing_blocks=[],s_start,s_end;var
draw_last=[];var t=this._parse_cigar(cigar);cigar=t.cigar;drawing_blocks=t.blocks;for(var
i=0;i<drawing_blocks.length;i++){var
block=drawing_blocks[i];if(is_overlap([feature_start+block[0],feature_start+block[1]],tile_region)){s_start=get_draw_coord(feature_start+block[0]);s_end=get_draw_coord(feature_start+block[1]);if(s_start===s_end){s_end+=1}ctx.fillStyle=block_color;ctx.fillRect(s_start,rect_y,s_end-s_start,draw_height)}}for(var
cig_id=0,len=cigar.length;cig_id<len;cig_id++){var
cig=cigar[cig_id],cig_op="MIDNSHP=X"[cig[0]],cig_len=cig[1];var
seq_start=feature_start+base_offset;s_start=get_draw_coord(seq_start);s_end=get_draw_coord(seq_start+cig_len);if(!is_overlap([seq_start,seq_start+cig_len],tile_region)){base_offset=update_base_offset(base_offset,cig_op,cig_len);seq_offset=update_seq_offset(seq_offset,cig_op,cig_len);continue}if(s_start===s_end){s_end+=1}switch(cig_op){case"H":case"S":case"P":break;case"M":base_offset+=cig_len;break;case"=":case"X":var
cur_seq="";if(cig_op==="X"){cur_seq=read_seq.slice(seq_offset,seq_offset+cig_len)}else{if(this.ref_seq){cur_seq=this.ref_seq.slice(Math.max(0,seq_start-tile_low),Math.min(seq_start-tile_low+cig_len,tile_high-tile_low))}}var
start_pos=Math.max(seq_start,tile_low);for(var
c=0;c<cur_seq.length;c++){if(cur_seq&&!this.prefs.show_differences||cig_op==="X"){var
c_start=Math.floor(Math.max(0,(start_pos+c-tile_low)*w_scale));ctx.fillStyle=this.base_color_fn(cur_seq[c]);if(pack_mode&&w_scale>char_width_px){ctx.fillText(cur_seq[c],c_start,y_start+9)}else{if(w_scale>0.05){ctx.fillRect(c_start-gap,rect_y,Math.max(1,Math.round(w_scale)),draw_height)}}}}if(cig_op==="X"){seq_offset+=cig_len}base_offset+=cig_len;break;case"N":ctx.fillStyle=CONNECTOR_COLOR;ctx.fillRect(s_start,rect_y+(draw_height-1)/2,s_end-s_start,1);base_offset+=cig_len;break;case"D":paint_utils.draw_deletion(s_start,rect_y,cig_len);base_offset+=cig_len;break;case"I":var
insert_x_coord=s_start-gap;if(is_overlap([seq_start,seq_start+cig_len],tile_region)){var
seq=read_seq.slice(seq_offset,seq_offset+cig_len);if(this.prefs.show_insertions){var
x_center=s_start-(s_end-s_start)/2;if((mode==="Pack"||this.mode==="Auto")&&read_seq!==undefined&&w_scale>char_width_px){ctx.fillStyle="yellow";ctx.fillRect(x_center-gap,y_start-9,s_end-s_start,9);draw_last[draw_last.length]={type:"triangle",data:[insert_x_coord,y_start+4,5]};ctx.fillStyle=CONNECTOR_COLOR;switch(compute_overlap([seq_start,seq_start+cig_len],tile_region)){case
(OVERLAP_START):seq=seq.slice(tile_low-seq_start);break;case
(OVERLAP_END):seq=seq.slice(0,seq_start-tile_high);break;case (CONTAINED_BY):break;case
(CONTAINS):seq=seq.slice(tile_low-seq_start,seq_start-tile_high);break}for(var
c=0,str_len=seq.length;c<str_len;c++){var
c_start=Math.floor(Math.max(0,(seq_start+c-tile_low)*w_scale));ctx.fillText(seq[c],c_start-(s_end-s_start)/2,y_start)}}else{ctx.fillStyle="yellow";ctx.fillRect(x_center,y_start+(this.mode!=="Dense"?2:5),s_end-s_start,(mode!=="Dense"?SQUISH_FEATURE_HEIGHT:DENSE_FEATURE_HEIGHT))}}else{if((mode==="Pack"||this.mode==="Auto")&&read_seq!==undefined&&w_scale>char_width_px){draw_last.push({type:"text",data:[seq.length,insert_x_coord,y_start+9]})}else{}}}seq_offset+=cig_len;break}}ctx.fillStyle="yellow";var
item,type,data;for(var
i=0;i<draw_last.length;i++){item=draw_last[i];type=item.type;data=item.data;if(type==="text"){ctx.save();ctx.font="bold
"+ctx.font;ctx.fillText(data[0],data[1],data[2]);ctx.restore()}else{if(type==="triangle"){drawDownwardEquilateralTriangle(ctx,data[0],data[1],data[2])}}}},draw_element:function(ctx,mode,feature,slot,tile_low,tile_high,w_scale,y_scale,width){var
feature_uid=feature[0],feature_start=feature[1],feature_end=feature[2],feature_name=feature[3],f_start=Math.floor(Math.max(-0.5*w_scale,(feature_start-tile_low-0.5)*w_scale)),f_end=Math.ceil(Math.min(width,Math.max(0,(feature_end-tile_low-0.5)*w_scale))),y_start=(mode==="Dense"?0:(0+slot))*y_scale,draw_height=(mode==="Pack"?PACK_FEATURE_HEIGHT:SQUISH_FEATURE_HEIGHT),label_color=this.prefs.label_color;if(feature[5]
instanceof Array){var
connector=true;if(feature[4][1]>=tile_low&&feature[4][0]<=tile_high&&feature[4][2]){this.draw_read(ctx,mode,w_scale,y_start,tile_low,tile_high,feature[4][0],feature[4][2],feature[4][3],feature[4][4])}else{connector=false}if(feature[5][1]>=tile_low&&feature[5][0]<=tile_high&&feature[5][2]){this.draw_read(ctx,mode,w_scale,y_start,tile_low,tile_high,feature[5][0],feature[5][2],feature[5][3],feature[5][4])}else{connector=false}var
b1_end=Math.ceil(Math.min(width,Math.max(-0.5*w_scale,(feature[4][1]-tile_low-0.5)*w_scale))),b2_start=Math.floor(Math.max(-0.5*w_scale,(feature[5][0]-tile_low-0.5)*w_scale));if(connector&&b2_start>b1_end){ctx.fillStyle=CONNECTOR_COLOR;var
line_height=y_start+1+(draw_height-1)/2;dashedLine(ctx,b1_end,line_height,b2_start,line_height)}}else{this.draw_read(ctx,mode,w_scale,y_start,tile_low,tile_high,feature_start,feature[4],feature[5],feature[6])}if(mode==="Pack"&&feature_start>=tile_low&&feature_name!=="."){ctx.fillStyle=this.prefs.label_color;if(tile_low===0&&f_start-ctx.measureText(feature_name).width<0){ctx.textAlign="left";ctx.fillText(feature_name,f_end+LABEL_SPACING,y_start+9,this.max_label_length)}else{ctx.textAlign="right";ctx.fillText(feature_name,f_start-LABEL_SPACING,y_start+9,this.max_label_length)}}return[0,0]}});var
ArcLinkedFeaturePainter=function(data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler){LinkedFeaturePainter.call(this,data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler);this.longest_feature_length=this.calculate_longest_feature_length();this.draw_background_connector=false;this.draw_individual_connectors=true};_.extend(ArcLinkedFeaturePainter.prototype,FeaturePainter.prototype,LinkedFeaturePainter.prototype,{calculate_longest_feature_length:function(){var
longest_feature_length=0;for(var i=0,len=this.data.length;i<len;i++){var
feature=this.data[i],feature_start=feature[1],feature_end=feature[2];longest_feature_length=Math.max(longest_feature_length,feature_end-feature_start)}return
longest_feature_length},get_top_padding:function(width){var
view_range=this.view_end-this.view_start,w_scale=width/view_range;return
Math.min(128,Math.ceil((this.longest_feature_length/2)*w_scale))},draw_connector:function(ctx,block1_start,block1_end,block2_start,block2_end,y_start){var
x_center=(block1_end+block2_start)/2,radius=block2_start-x_center;var
angle1=Math.PI,angle2=0;if(radius>0){ctx.beginPath();ctx.arc(x_center,y_start,block2_start-x_center,Math.PI,0);ctx.stroke()}}});var
Color=function(rgb,a){if(Array.isArray(rgb)){this.rgb=rgb}else{if(rgb.length==6){this.rgb=rgb.match(/.{2}/g).map(function(c){return
parseInt(c,16)})}else{if(rgb.length==7){this.rgb=rgb.substring(1,7).match(/.{2}/g).map(function(c){return
parseInt(c,16)})}else{this.rgb=rgb.split("").map(function(c){return
parseInt(c+c,16)})}}}this.alpha=typeof(a)==="number"?a:1};Color.prototype={eval:function(){return
this},toCSS:function(){if(this.alpha<1){return"rgba("+this.rgb.map(function(c){return
Math.round(c)}).concat(this.alpha).join(",
")+")"}else{return"#"+this.rgb.map(function(i){i=Math.round(i);i=(i>255?255:(i<0?0:i)).toString(16);return
i.length===1?"0"+i:i}).join("")}},toHSL:function(){var
r=this.rgb[0]/255,g=this.rgb[1]/255,b=this.rgb[2]/255,a=this.alpha;var
max=Math.max(r,g,b),min=Math.min(r,g,b);var
h,s,l=(max+min)/2,d=max-min;if(max===min){h=s=0}else{s=l>0.5?d/(2-max-min):d/(max+min);switch(max){case
r:h=(g-b)/d+(g<b?6:0);break;case g:h=(b-r)/d+2;break;case
b:h=(r-g)/d+4;break}h/=6}return{h:h*360,s:s,l:l,a:a}},toARGB:function(){var
argb=[Math.round(this.alpha*255)].concat(this.rgb);return"#"+argb.map(function(i){i=Math.round(i);i=(i>255?255:(i<0?0:i)).toString(16);return
i.length===1?"0"+i:i}).join("")},mix:function(color2,weight){color1=this;var
p=weight;var w=p*2-1;var a=color1.toHSL().a-color2.toHSL().a;var
w1=(((w*a==-1)?w:(w+a)/(1+w*a))+1)/2;var w2=1-w1;var
rgb=[color1.rgb[0]*w1+color2.rgb[0]*w2,color1.rgb[1]*w1+color2.rgb[1]*w2,color1.rgb[2]*w1+color2.rgb[2]*w2];var
alpha=color1.alpha*p+color2.alpha*(1-p);return new Color(rgb,alpha)}};var
LinearRamp=function(start_color,end_color,start_value,end_value){this.start_color=new
Color(start_color);this.end_color=new
Color(end_color);this.start_value=start_value;this.end_value=end_value;this.value_range=end_value-start_value};LinearRamp.prototype.map_value=function(value){value=Math.max(value,this.start_value);value=Math.min(value,this.end_value);value=(value-this.start_value)/this.value_range;return
this.start_color.mix(this.end_color,1-value).toCSS()};var
SplitRamp=function(start_color,middle_color,end_color,start_value,end_value){this.positive_ramp=new
LinearRamp(middle_color,end_color,0,end_value);this.negative_ramp=new
LinearRamp(middle_color,start_color,0,-start_value);this.start_value=start_value;this.end_value=end_value};SplitRamp.prototype.map_value=function(value){value=Math.max(value,this.start_value);value=Math.min(value,this.end_value);if(value>=0){return
this.positive_ramp.map_value(value)}else{return this.negative_ramp.map_value(-value)}};var
DiagonalHeatmapPainter=function(data,view_start,view_end,prefs,mode){Painter.call(this,data,view_start,view_end,prefs,mode);var
i,len;if(this.prefs.min_value===undefined){var
min_value=Infinity;for(i=0,len=this.data.length;i<len;i++){min_value=Math.min(min_value,this.data[i][5])}this.prefs.min_value=min_value}if(this.prefs.max_value===undefined){var
max_value=-Infinity;for(i=0,len=this.data.length;i<len;i++){max_value=Math.max(max_value,this.data[i][5])}this.prefs.max_value=max_value}};DiagonalHeatmapPainter.prototype.default_prefs={min_value:undefined,max_value:undefined,mode:"Heatmap",pos_color:"#FF8C00",neg_color:"#4169E1"};DiagonalHeatmapPainter.prototype.draw=function(ctx,width,height,w_scale){var
min_value=this.prefs.min_value,max_value=this.prefs.max_value,value_range=max_value-min_value,height_px=height,view_start=this.view_start,mode=this.mode,data=this.data,invsqrt2=1/Math.sqrt(2);var
ramp=(new
SplitRamp(this.prefs.neg_color,"#FFFFFF",this.prefs.pos_color,min_value,max_value));var
d,s1,e1,s2,e2,value;var
scale=function(p){return(p-view_start)*w_scale};ctx.save();ctx.rotate(-45*Math.PI/180);ctx.scale(invsqrt2,invsqrt2);for(var
i=0,len=data.length;i<len;i++){d=data[i];s1=scale(d[1]);e1=scale(d[2]);s2=scale(d[4]);e2=scale(d[5]);value=d[6];ctx.fillStyle=(ramp.map_value(value));ctx.fillRect(s1,s2,(e1-s1),(e2-s2))}ctx.restore()};var
ReadPainterUtils=function(ctx,row_height,px_per_base,mode){this.ctx=ctx;this.row_height=row_height;this.px_per_base=px_per_base;this.draw_details=(mode==="Pack"||mode==="Auto")&&(px_per_base>=ctx.canvas.manager.char_width_px);this.delete_details_thickness=0.2};_.extend(ReadPainterUtils.prototype,{draw_deletion:function(x,y,len){this.ctx.fillStyle="black";var
thickness=(this.draw_details?this.delete_details_thickness:1)*this.row_height;y+=0.5*(this.row_height-thickness);this.ctx.fillRect(x,y,len*this.px_per_base,thickness)}});var
VariantPainter=function(data,view_start,view_end,prefs,mode,base_color_fn){Painter.call(this,data,view_start,view_end,prefs,mode);this.base_color_fn=base_color_fn;this.divider_height=1};_.extend(VariantPainter.prototype,Painter.prototype,{get_row_height:function(){var
mode=this.mode,height;if(mode==="Dense"){height=DENSE_TRACK_HEIGHT}else{if(mode==="Squish"){height=SQUISH_TRACK_HEIGHT}else{height=PACK_TRACK_HEIGHT}}return
height},get_required_height:function(num_samples){var
height=this.prefs.summary_height;if(num_samples>1&&this.prefs.show_sample_data){height+=this.divider_height+num_samples*this.get_row_height()}return
height},draw:function(ctx,width,height,w_scale){ctx.save();var
get_deletion_info=function(ref,alt){var
ref_len=ref.length,alt_len=alt.length,start=0,len=1,is_delete=false;if(alt==="-"){is_delete=true;len=ref.length}else{if(ref.indexOf(alt)===0&&ref_len>alt_len){is_delete=true;len=ref_len=alt_len;start+=alt_len}}return(is_delete?{start:start,len:len}:null)};var
locus_data,pos,id,ref,alt,qual,filter,sample_gts,allele_counts,variant,draw_x_start,char_x_start,draw_y_start,genotype,base_px=Math.max(1,Math.floor(w_scale)),num_samples=(this.data.length?this.data[0][7].split(",").length:0),row_height=(this.mode==="Squish"?SQUISH_TRACK_HEIGHT:PACK_TRACK_HEIGHT),feature_height=(w_scale<0.1?row_height:(this.mode==="Squish"?SQUISH_FEATURE_HEIGHT:PACK_FEATURE_HEIGHT)),draw_summary=true,paint_utils=new
ReadPainterUtils(ctx,row_height,w_scale,this.mode),j;if(num_samples===1){row_height=feature_height=(w_scale<ctx.canvas.manager.char_width_px?this.prefs.summary_height:row_height);paint_utils.row_height=row_height;draw_summary=false}if(this.prefs.show_sample_data&&draw_summary){ctx.fillStyle="#F3F3F3";ctx.globalAlpha=1;ctx.fillRect(0,this.prefs.summary_height-this.divider_height,width,this.divider_height)}ctx.textAlign="center";for(var
i=0;i<this.data.length;i++){locus_data=this.data[i];pos=locus_data[1];ref=locus_data[3];alt=[locus_data[4].split(",")];sample_gts=locus_data[7].split(",");allele_counts=locus_data.slice(8);alt=_.map(_.flatten(alt),function(a){var
type,alt_info={},delete_info=get_deletion_info(ref,a);if(delete_info){type="deletion";_.extend(alt_info,delete_info)}else{type="snp"}return
_.extend(alt_info,{type:type,value:a,})});if(pos<this.view_start||pos>this.view_end){continue}draw_x_start=Math.floor(Math.max(-0.5*w_scale,(pos-this.view_start-0.5)*w_scale));char_x_start=Math.floor(Math.max(0,(pos-this.view_start)*w_scale));if(draw_summary){ctx.fillStyle="#999999";ctx.globalAlpha=1;ctx.fillRect(draw_x_start,0,base_px,this.prefs.summary_height);draw_y_start=this.prefs.summary_height;for(j=0;j<alt.length;j++){ctx.fillStyle=(alt[j].type==="deletion"?"black":this.base_color_fn(alt[j].value));allele_frac=allele_counts/sample_gts.length;draw_height=Math.ceil(this.prefs.summary_height*allele_frac);ctx.fillRect(draw_x_start,draw_y_start-draw_height,base_px,draw_height);draw_y_start-=draw_height}}if(!this.prefs.show_sample_data){continue}draw_y_start=(draw_summary?this.prefs.summary_height+this.divider_height:0);for(j=0;j<sample_gts.length;j++,draw_y_start+=row_height){genotype=(sample_gts[j]?sample_gts[j].split(/\/|\|/):["0","0"]);variant=null;if(genotype[0]===genotype[1]){if(genotype[0]==="."){}else{if(genotype[0]!=="0"){variant=alt[parseInt(genotype[0],10)-1];ctx.globalAlpha=1}}}else{variant=(genotype[0]!=="0"?genotype[0]:genotype[1]);variant=alt[parseInt(variant,10)-1];ctx.globalAlpha=0.5}if(variant){if(variant.type==="snp"){var
snp=variant.value;ctx.fillStyle=this.base_color_fn(snp);if(paint_utils.draw_details){ctx.fillText(snp,char_x_start,draw_y_start+row_height)}else{ctx.fillRect(draw_x_start,draw_y_start+1,base_px,feature_height)}}else{if(variant.type==="deletion"){paint_utils.draw_deletion(draw_x_start+base_px*variant.start,draw_y_start+1,variant.len)}else{}}}}}ctx.restore()}});return{Scaler:Scaler,LinePainter:LinePainter,LinkedFeaturePainter:LinkedFeaturePainter,ReadPainter:ReadPainter,ArcLinkedFeaturePainter:ArcLinkedFeaturePainter,DiagonalHeatmapPainter:DiagonalHeatmapPainter,VariantPainter:VariantPainter}});
\ No newline at end of file
+define(["libs/underscore"],function(_){var
BEFORE=1001,CONTAINS=1002,OVERLAP_START=1003,OVERLAP_END=1004,CONTAINED_BY=1005,AFTER=1006;var
compute_overlap=function(first_region,second_region){var
first_start=first_region[0],first_end=first_region[1],second_start=second_region[0],second_end=second_region[1],overlap;if(first_start<second_start){if(first_end<=second_start){overlap=BEFORE}else{if(first_end<=second_end){overlap=OVERLAP_START}else{overlap=CONTAINS}}}else{if(first_start>second_end){overlap=AFTER}else{if(first_end<=second_end){overlap=CONTAINED_BY}else{overlap=OVERLAP_END}}}return
overlap};var is_overlap=function(first_region,second_region){var
overlap=compute_overlap(first_region,second_region);return(overlap!==BEFORE&&overlap!==AFTER)};var
dashedLine=function(ctx,x1,y1,x2,y2,dashLen){if(dashLen===undefined){dashLen=4}var
dX=x2-x1;var dY=y2-y1;var dashes=Math.floor(Math.sqrt(dX*dX+dY*dY)/dashLen);var
dashX=dX/dashes;var dashY=dY/dashes;var
q;for(q=0;q<dashes;q++,x1+=dashX,y1+=dashY){if(q%2!==0){continue}ctx.fillRect(x1,y1,dashLen,1)}};var
drawDownwardEquilateralTriangle=function(ctx,down_vertex_x,down_vertex_y,side_len){var
x1=down_vertex_x-side_len/2,x2=down_vertex_x+side_len/2,y=down_vertex_y-Math.sqrt(side_len*3/2);ctx.beginPath();ctx.moveTo(x1,y);ctx.lineTo(x2,y);ctx.lineTo(down_vertex_x,down_vertex_y);ctx.lineTo(x1,y);ctx.strokeStyle=this.fillStyle;ctx.fill();ctx.stroke();ctx.closePath()};var
Scaler=function(default_val){this.default_val=(default_val?default_val:1)};Scaler.prototype.gen_val=function(input){return
this.default_val};var
DrawResults=function(options){this.incomplete_features=options.incomplete_features;this.feature_mapper=options.feature_mapper};var
Painter=function(data,view_start,view_end,prefs,mode){this.data=data;this.view_start=view_start;this.view_end=view_end;this.prefs=_.extend({},this.default_prefs,prefs);this.mode=mode};Painter.prototype.default_prefs={};Painter.prototype.draw=function(ctx,width,height,w_scale){};var
LinePainter=function(data,view_start,view_end,prefs,mode){Painter.call(this,data,view_start,view_end,prefs,mode);if(this.prefs.min_value===undefined){this.prefs.min_value=_.min(_.map(this.data,function(d){return
d[1]}))||0}if(this.prefs.max_value===undefined){this.prefs.max_value=_.max(_.map(this.data,function(d){return
d[1]}))||0}};LinePainter.prototype.default_prefs={min_value:undefined,max_value:undefined,mode:"Histogram",color:"#000",overflow_color:"#F66"};LinePainter.prototype.draw=function(ctx,width,height,w_scale){var
in_path=false,min_value=this.prefs.min_value,max_value=this.prefs.max_value,vertical_range=max_value-min_value,height_px=height,view_start=this.view_start,mode=this.mode,data=this.data;ctx.save();var
y_zero=Math.round(height+min_value/vertical_range*height);if(mode!=="Intensity"){ctx.fillStyle="#aaa";ctx.fillRect(0,y_zero,width,1)}ctx.beginPath();var
x_scaled,y,delta_x_px;if(data.length>1){delta_x_px=Math.ceil((data[1][0]-data[0][0])*w_scale)}else{delta_x_px=10}var
painter_color=this.prefs.block_color||this.prefs.color,pref_color=parseInt(painter_color.slice(1),16),pref_r=(pref_color&16711680)>>16,pref_g=(pref_color&65280)>>8,pref_b=pref_color&255,top_overflow=false,bot_overflow=false;for(var
i=0,len=data.length;i<len;i++){ctx.fillStyle=ctx.strokeStyle=painter_color;top_overflow=bot_overflow=false;x_scaled=Math.ceil((data[i][0]-view_start)*w_scale);y=data[i][1];if(y===null){if(in_path&&mode==="Filled"){ctx.lineTo(x_scaled,height_px)}in_path=false;continue}if(y<min_value){bot_overflow=true;y=min_value}else{if(y>max_value){top_overflow=true;y=max_value}}if(mode==="Histogram"){y=Math.round(y/vertical_range*height_px);ctx.fillRect(x_scaled,y_zero,delta_x_px,-y)}else{if(mode==="Intensity"){var
saturation=(y-min_value)/vertical_range,new_r=Math.round(pref_r+(255-pref_r)*(1-saturation)),new_g=Math.round(pref_g+(255-pref_g)*(1-saturation)),new_b=Math.round(pref_b+(255-pref_b)*(1-saturation));ctx.fillStyle="rgb("+new_r+","+new_g+","+new_b+")";ctx.fillRect(x_scaled,0,delta_x_px,height_px)}else{y=Math.round(height_px-(y-min_value)/vertical_range*height_px);if(in_path){ctx.lineTo(x_scaled,y)}else{in_path=true;if(mode==="Filled"){ctx.moveTo(x_scaled,height_px);ctx.lineTo(x_scaled,y)}else{ctx.moveTo(x_scaled,y)}}}}ctx.fillStyle=this.prefs.overflow_color;if(top_overflow||bot_overflow){var
overflow_x;if(mode==="Histogram"||mode==="Intensity"){overflow_x=delta_x_px}else{x_scaled-=2;overflow_x=4}if(top_overflow){ctx.fillRect(x_scaled,0,overflow_x,3)}if(bot_overflow){ctx.fillRect(x_scaled,height_px-3,overflow_x,3)}}ctx.fillStyle=painter_color}if(mode==="Filled"){if(in_path){ctx.lineTo(x_scaled,y_zero);ctx.lineTo(0,y_zero)}ctx.fill()}else{ctx.stroke()}ctx.restore()};var
FeaturePositionMapper=function(slot_height){this.feature_positions={};this.slot_height=slot_height;this.translation=0;this.y_translation=0};FeaturePositionMapper.prototype.map_feature_data=function(feature_data,slot,x_start,x_end){if(!this.feature_positions[slot]){this.feature_positions[slot]=[]}this.feature_positions[slot].push({data:feature_data,x_start:x_start,x_end:x_end})};FeaturePositionMapper.prototype.get_feature_data=function(x,y){var
slot=Math.floor((y-this.y_translation)/this.slot_height),feature_dict;if(!this.feature_positions[slot]){return
null}x+=this.translation;for(var
i=0;i<this.feature_positions[slot].length;i++){feature_dict=this.feature_positions[slot][i];if(x>=feature_dict.x_start&&x<=feature_dict.x_end){return
feature_dict.data}}};var
FeaturePainter=function(data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler){Painter.call(this,data,view_start,view_end,prefs,mode);this.alpha_scaler=(alpha_scaler?alpha_scaler:new
Scaler());this.height_scaler=(height_scaler?height_scaler:new
Scaler());this.max_label_length=200};FeaturePainter.prototype.default_prefs={block_color:"#FFF",connector_color:"#FFF"};_.extend(FeaturePainter.prototype,{get_required_height:function(rows_required,width){var
required_height=this.get_row_height(),y_scale=required_height,mode=this.mode;if(mode==="no_detail"||mode==="Squish"||mode==="Pack"){required_height=rows_required*y_scale}return
required_height+this.get_top_padding(width)},get_top_padding:function(width){return
0},draw:function(ctx,width,height,w_scale,slots){var
data=this.data,view_start=this.view_start,view_end=this.view_end;ctx.save();ctx.fillStyle=this.prefs.block_color;ctx.textAlign="right";var
y_scale=this.get_row_height(),feature_mapper=new
FeaturePositionMapper(y_scale),x_draw_coords,incomplete_features=[];for(var
i=0,len=data.length;i<len;i++){var
feature=data[i],feature_uid=feature[0],feature_start=feature[1],feature_end=feature[2],slot=(slots&&slots[feature_uid]!==undefined?slots[feature_uid].slot:null);if((this.mode==="Dense"||slot!==null)&&(feature_start<view_end&&feature_end>view_start)){x_draw_coords=this.draw_element(ctx,this.mode,feature,slot,view_start,view_end,w_scale,y_scale,width);feature_mapper.map_feature_data(feature,slot,x_draw_coords[0],x_draw_coords[1]);if(feature_start<view_start||feature_end>view_end){incomplete_features.push(feature)}}}ctx.restore();feature_mapper.y_translation=this.get_top_padding(width);return
new
DrawResults({incomplete_features:incomplete_features,feature_mapper:feature_mapper})},draw_element:function(ctx,mode,feature,slot,tile_low,tile_high,w_scale,y_scale,width){return[0,0]}});var
DENSE_TRACK_HEIGHT=10,NO_DETAIL_TRACK_HEIGHT=3,SQUISH_TRACK_HEIGHT=5,PACK_TRACK_HEIGHT=10,NO_DETAIL_FEATURE_HEIGHT=1,DENSE_FEATURE_HEIGHT=9,SQUISH_FEATURE_HEIGHT=3,PACK_FEATURE_HEIGHT=9,LABEL_SPACING=2,CONNECTOR_COLOR="#ccc";var
LinkedFeaturePainter=function(data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler){FeaturePainter.call(this,data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler);this.draw_background_connector=true;this.draw_individual_connectors=false};_.extend(LinkedFeaturePainter.prototype,FeaturePainter.prototype,{get_row_height:function(){var
mode=this.mode,height;if(mode==="Dense"){height=DENSE_TRACK_HEIGHT}else{if(mode==="no_detail"){height=NO_DETAIL_TRACK_HEIGHT}else{if(mode==="Squish"){height=SQUISH_TRACK_HEIGHT}else{height=PACK_TRACK_HEIGHT}}}return
height},draw_element:function(ctx,mode,feature,slot,tile_low,tile_high,w_scale,y_scale,width){var
feature_uid=feature[0],feature_start=feature[1],feature_end=feature[2],feature_name=feature[3],feature_strand=feature[4],f_start=Math.floor(Math.max(0,(feature_start-tile_low-0.5)*w_scale)),f_end=Math.ceil(Math.min(width,Math.max(0,(feature_end-tile_low-0.5)*w_scale))),draw_start=f_start,draw_end=f_end,y_start=(mode==="Dense"?0:(0+slot))*y_scale+this.get_top_padding(width),thickness,y_start,thick_start=null,thick_end=null,block_color=(!feature_strand||feature_strand==="+"||feature_strand==="."?this.prefs.block_color:this.prefs.reverse_strand_color);label_color=this.prefs.label_color;ctx.globalAlpha=this.alpha_scaler.gen_val(feature);if(mode==="Dense"){slot=1}if(mode==="no_detail"){ctx.fillStyle=block_color;ctx.fillRect(f_start,y_start+5,f_end-f_start,NO_DETAIL_FEATURE_HEIGHT)}else{var
feature_ts=feature[5],feature_te=feature[6],feature_blocks=feature[7],full_height=true;if(feature_ts&&feature_te){thick_start=Math.floor(Math.max(0,(feature_ts-tile_low)*w_scale));thick_end=Math.ceil(Math.min(width,Math.max(0,(feature_te-tile_low)*w_scale)))}var
thin_height,thick_height;if(mode==="Squish"){thin_height=1;thick_height=SQUISH_FEATURE_HEIGHT;full_height=false}else{if(mode==="Dense"){thin_height=5;thick_height=DENSE_FEATURE_HEIGHT}else{thin_height=5;thick_height=PACK_FEATURE_HEIGHT}}if(!feature_blocks){ctx.fillStyle=block_color;ctx.fillRect(f_start,y_start+1,f_end-f_start,thick_height);if(feature_strand&&full_height){if(feature_strand==="+"){ctx.fillStyle=ctx.canvas.manager.get_pattern("right_strand_inv")}else{if(feature_strand==="-"){ctx.fillStyle=ctx.canvas.manager.get_pattern("left_strand_inv")}}ctx.fillRect(f_start,y_start+1,f_end-f_start,thick_height)}}else{var
cur_y_start,cur_height;if(mode==="Squish"||mode==="Dense"){cur_y_start=y_start+Math.floor(SQUISH_FEATURE_HEIGHT/2)+1;cur_height=1}else{if(feature_strand){cur_y_start=y_start;cur_height=thick_height}else{cur_y_start+=(SQUISH_FEATURE_HEIGHT/2)+1;cur_height=1}}if(this.draw_background_connector){if(mode==="Squish"||mode==="Dense"){ctx.fillStyle=CONNECTOR_COLOR}else{if(feature_strand){if(feature_strand==="+"){ctx.fillStyle=ctx.canvas.manager.get_pattern("right_strand")}else{if(feature_strand==="-"){ctx.fillStyle=ctx.canvas.manager.get_pattern("left_strand")}}}else{ctx.fillStyle=CONNECTOR_COLOR}}ctx.fillRect(f_start,cur_y_start,f_end-f_start,cur_height)}var
start_and_height;for(var k=0,k_len=feature_blocks.length;k<k_len;k++){var
block=feature_blocks[k],block_start=Math.floor(Math.max(0,(block[0]-tile_low-0.5)*w_scale)),block_end=Math.ceil(Math.min(width,Math.max((block[1]-tile_low-0.5)*w_scale))),last_block_start,last_block_end;if(block_start>block_end){continue}ctx.fillStyle=block_color;ctx.fillRect(block_start,y_start+(thick_height-thin_height)/2+1,block_end-block_start,thin_height);if(thick_start!==undefined&&feature_te>feature_ts&&!(block_start>thick_end||block_end<thick_start)){var
block_thick_start=Math.max(block_start,thick_start),block_thick_end=Math.min(block_end,thick_end);ctx.fillRect(block_thick_start,y_start+1,block_thick_end-block_thick_start,thick_height);if(feature_blocks.length===1&&mode==="Pack"){if(feature_strand==="+"){ctx.fillStyle=ctx.canvas.manager.get_pattern("right_strand_inv")}else{if(feature_strand==="-"){ctx.fillStyle=ctx.canvas.manager.get_pattern("left_strand_inv")}}if(block_thick_start+14<block_thick_end){block_thick_start+=2;block_thick_end-=2}ctx.fillRect(block_thick_start,y_start+1,block_thick_end-block_thick_start,thick_height)}}if(this.draw_individual_connectors&&last_block_start){this.draw_connector(ctx,last_block_start,last_block_end,block_start,block_end,y_start)}last_block_start=block_start;last_block_end=block_end}if(mode==="Pack"){ctx.globalAlpha=1;ctx.fillStyle="white";var
hscale_factor=this.height_scaler.gen_val(feature),new_height=Math.ceil(thick_height*hscale_factor),ws_height=Math.round((thick_height-new_height)/2);if(hscale_factor!==1){ctx.fillRect(f_start,cur_y_start+1,f_end-f_start,ws_height);ctx.fillRect(f_start,cur_y_start+thick_height-ws_height+1,f_end-f_start,ws_height)}}}ctx.globalAlpha=1;if(feature_name&&mode==="Pack"&&feature_start>tile_low){ctx.fillStyle=label_color;if(tile_low===0&&f_start-ctx.measureText(feature_name).width<0){ctx.textAlign="left";ctx.fillText(feature_name,f_end+LABEL_SPACING,y_start+8,this.max_label_length);draw_end+=ctx.measureText(feature_name).width+LABEL_SPACING}else{ctx.textAlign="right";ctx.fillText(feature_name,f_start-LABEL_SPACING,y_start+8,this.max_label_length);draw_start-=ctx.measureText(feature_name).width+LABEL_SPACING}}}ctx.globalAlpha=1;return[draw_start,draw_end]}});var
ReadPainter=function(data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler,ref_seq,base_color_fn){FeaturePainter.call(this,data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler);this.ref_seq=(ref_seq?ref_seq.data:null);this.base_color_fn=base_color_fn};_.extend(ReadPainter.prototype,FeaturePainter.prototype,{get_row_height:function(){var
height,mode=this.mode;if(mode==="Dense"){height=DENSE_TRACK_HEIGHT}else{if(mode==="Squish"){height=SQUISH_TRACK_HEIGHT}else{height=PACK_TRACK_HEIGHT;if(this.prefs.show_insertions){height*=2}}}return
height},_parse_cigar:function(cigar_str){var cigar_ops="MIDNSHP=X";var
blocks=[[0,0]],cur_block=blocks[0],base_pos=0,parsed_cigar=_.map(cigar_str.match(/[0-9]+[MIDNSHP=X]/g),function(op){var
op_len=parseInt(op.slice(0,-1),10),op_char=op.slice(-1);if(op_char==="N"){if(cur_block[1]!==0){cur_block=[base_pos+op_len,base_pos+op_len];blocks.push(cur_block)}}else{if("ISHP".indexOf(op_char)===-1){cur_block[1]+=op_len;base_pos+=op_len}}return[cigar_ops.indexOf(op_char),op_len]});return{blocks:blocks,cigar:parsed_cigar}},draw_read:function(ctx,mode,w_scale,y_start,tile_low,tile_high,feature_start,cigar,strand,read_seq){var
update_base_offset=function(offset,cig_op,cig_len){if("M=NXD".indexOf(cig_op)!==-1){offset+=cig_len}return
offset},update_seq_offset=function(offset,cig_op,cig_len){if("IX".indexOf(cig_op)!==-1){offset+=cig_len}return
offset},get_draw_coord=function(sequence_coord){return
Math.floor(Math.max(0,(sequence_coord-tile_low-0.5)*w_scale))};ctx.textAlign="center";var
tile_region=[tile_low,tile_high],base_offset=0,seq_offset=0,gap=Math.round(w_scale/2),char_width_px=ctx.canvas.manager.char_width_px,block_color=(strand==="+"?this.prefs.detail_block_color:this.prefs.reverse_strand_color),pack_mode=(mode==="Pack"),draw_height=(pack_mode?PACK_FEATURE_HEIGHT:SQUISH_FEATURE_HEIGHT),rect_y=y_start+1,paint_utils=new
ReadPainterUtils(ctx,draw_height,w_scale,mode),drawing_blocks=[],s_start,s_end;var
draw_last=[];var t=this._parse_cigar(cigar);cigar=t.cigar;drawing_blocks=t.blocks;for(var
i=0;i<drawing_blocks.length;i++){var
block=drawing_blocks[i];if(is_overlap([feature_start+block[0],feature_start+block[1]],tile_region)){s_start=get_draw_coord(feature_start+block[0]);s_end=get_draw_coord(feature_start+block[1]);if(s_start===s_end){s_end+=1}ctx.fillStyle=block_color;ctx.fillRect(s_start,rect_y,s_end-s_start,draw_height)}}for(var
cig_id=0,len=cigar.length;cig_id<len;cig_id++){var
cig=cigar[cig_id],cig_op="MIDNSHP=X"[cig[0]],cig_len=cig[1];var
seq_start=feature_start+base_offset;s_start=get_draw_coord(seq_start);s_end=get_draw_coord(seq_start+cig_len);if(!is_overlap([seq_start,seq_start+cig_len],tile_region)){base_offset=update_base_offset(base_offset,cig_op,cig_len);seq_offset=update_seq_offset(seq_offset,cig_op,cig_len);continue}if(s_start===s_end){s_end+=1}switch(cig_op){case"H":case"S":case"P":break;case"M":base_offset+=cig_len;break;case"=":case"X":var
cur_seq="";if(cig_op==="X"){cur_seq=read_seq.slice(seq_offset,seq_offset+cig_len)}else{if(this.ref_seq){cur_seq=this.ref_seq.slice(Math.max(0,seq_start-tile_low),Math.min(seq_start-tile_low+cig_len,tile_high-tile_low))}}var
start_pos=Math.max(seq_start,tile_low);for(var
c=0;c<cur_seq.length;c++){if(cur_seq&&!this.prefs.show_differences||cig_op==="X"){var
c_start=Math.floor(Math.max(0,(start_pos+c-tile_low)*w_scale));ctx.fillStyle=this.base_color_fn(cur_seq[c]);if(pack_mode&&w_scale>char_width_px){ctx.fillText(cur_seq[c],c_start,y_start+9)}else{if(w_scale>0.05){ctx.fillRect(c_start-gap,rect_y,Math.max(1,Math.round(w_scale)),draw_height)}}}}if(cig_op==="X"){seq_offset+=cig_len}base_offset+=cig_len;break;case"N":ctx.fillStyle=CONNECTOR_COLOR;ctx.fillRect(s_start,rect_y+(draw_height-1)/2,s_end-s_start,1);base_offset+=cig_len;break;case"D":paint_utils.draw_deletion(s_start,rect_y,cig_len);base_offset+=cig_len;break;case"I":var
insert_x_coord=s_start-gap;if(is_overlap([seq_start,seq_start+cig_len],tile_region)){var
seq=read_seq.slice(seq_offset,seq_offset+cig_len);if(this.prefs.show_insertions){var
x_center=s_start-(s_end-s_start)/2;if((mode==="Pack"||this.mode==="Auto")&&read_seq!==undefined&&w_scale>char_width_px){ctx.fillStyle="yellow";ctx.fillRect(x_center-gap,y_start-9,s_end-s_start,9);draw_last[draw_last.length]={type:"triangle",data:[insert_x_coord,y_start+4,5]};ctx.fillStyle=CONNECTOR_COLOR;switch(compute_overlap([seq_start,seq_start+cig_len],tile_region)){case
(OVERLAP_START):seq=seq.slice(tile_low-seq_start);break;case
(OVERLAP_END):seq=seq.slice(0,seq_start-tile_high);break;case (CONTAINED_BY):break;case
(CONTAINS):seq=seq.slice(tile_low-seq_start,seq_start-tile_high);break}for(var
c=0,str_len=seq.length;c<str_len;c++){var
c_start=Math.floor(Math.max(0,(seq_start+c-tile_low)*w_scale));ctx.fillText(seq[c],c_start-(s_end-s_start)/2,y_start)}}else{ctx.fillStyle="yellow";ctx.fillRect(x_center,y_start+(this.mode!=="Dense"?2:5),s_end-s_start,(mode!=="Dense"?SQUISH_FEATURE_HEIGHT:DENSE_FEATURE_HEIGHT))}}else{if((mode==="Pack"||this.mode==="Auto")&&read_seq!==undefined&&w_scale>char_width_px){draw_last.push({type:"text",data:[seq.length,insert_x_coord,y_start+9]})}else{}}}seq_offset+=cig_len;break}}ctx.fillStyle="yellow";var
item,type,data;for(var
i=0;i<draw_last.length;i++){item=draw_last[i];type=item.type;data=item.data;if(type==="text"){ctx.save();ctx.font="bold
"+ctx.font;ctx.fillText(data[0],data[1],data[2]);ctx.restore()}else{if(type==="triangle"){drawDownwardEquilateralTriangle(ctx,data[0],data[1],data[2])}}}},draw_element:function(ctx,mode,feature,slot,tile_low,tile_high,w_scale,y_scale,width){var
feature_uid=feature[0],feature_start=feature[1],feature_end=feature[2],feature_name=feature[3],f_start=Math.floor(Math.max(-0.5*w_scale,(feature_start-tile_low-0.5)*w_scale)),f_end=Math.ceil(Math.min(width,Math.max(0,(feature_end-tile_low-0.5)*w_scale))),y_start=(mode==="Dense"?0:(0+slot))*y_scale,draw_height=(mode==="Pack"?PACK_FEATURE_HEIGHT:SQUISH_FEATURE_HEIGHT),label_color=this.prefs.label_color;if(feature[5]
instanceof Array){var
connector=true;if(feature[4][1]>=tile_low&&feature[4][0]<=tile_high&&feature[4][2]){this.draw_read(ctx,mode,w_scale,y_start,tile_low,tile_high,feature[4][0],feature[4][2],feature[4][3],feature[4][4])}else{connector=false}if(feature[5][1]>=tile_low&&feature[5][0]<=tile_high&&feature[5][2]){this.draw_read(ctx,mode,w_scale,y_start,tile_low,tile_high,feature[5][0],feature[5][2],feature[5][3],feature[5][4])}else{connector=false}var
b1_end=Math.ceil(Math.min(width,Math.max(-0.5*w_scale,(feature[4][1]-tile_low-0.5)*w_scale))),b2_start=Math.floor(Math.max(-0.5*w_scale,(feature[5][0]-tile_low-0.5)*w_scale));if(connector&&b2_start>b1_end){ctx.fillStyle=CONNECTOR_COLOR;var
line_height=y_start+1+(draw_height-1)/2;dashedLine(ctx,b1_end,line_height,b2_start,line_height)}}else{this.draw_read(ctx,mode,w_scale,y_start,tile_low,tile_high,feature_start,feature[4],feature[5],feature[6])}if(mode==="Pack"&&feature_start>=tile_low&&feature_name!=="."){ctx.fillStyle=this.prefs.label_color;if(tile_low===0&&f_start-ctx.measureText(feature_name).width<0){ctx.textAlign="left";ctx.fillText(feature_name,f_end+LABEL_SPACING,y_start+9,this.max_label_length)}else{ctx.textAlign="right";ctx.fillText(feature_name,f_start-LABEL_SPACING,y_start+9,this.max_label_length)}}return[0,0]}});var
ArcLinkedFeaturePainter=function(data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler){LinkedFeaturePainter.call(this,data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler);this.longest_feature_length=this.calculate_longest_feature_length();this.draw_background_connector=false;this.draw_individual_connectors=true};_.extend(ArcLinkedFeaturePainter.prototype,FeaturePainter.prototype,LinkedFeaturePainter.prototype,{calculate_longest_feature_length:function(){var
longest_feature_length=0;for(var i=0,len=this.data.length;i<len;i++){var
feature=this.data[i],feature_start=feature[1],feature_end=feature[2];longest_feature_length=Math.max(longest_feature_length,feature_end-feature_start)}return
longest_feature_length},get_top_padding:function(width){var
view_range=this.view_end-this.view_start,w_scale=width/view_range;return
Math.min(128,Math.ceil((this.longest_feature_length/2)*w_scale))},draw_connector:function(ctx,block1_start,block1_end,block2_start,block2_end,y_start){var
x_center=(block1_end+block2_start)/2,radius=block2_start-x_center;var
angle1=Math.PI,angle2=0;if(radius>0){ctx.beginPath();ctx.arc(x_center,y_start,block2_start-x_center,Math.PI,0);ctx.stroke()}}});var
Color=function(rgb,a){if(Array.isArray(rgb)){this.rgb=rgb}else{if(rgb.length==6){this.rgb=rgb.match(/.{2}/g).map(function(c){return
parseInt(c,16)})}else{if(rgb.length==7){this.rgb=rgb.substring(1,7).match(/.{2}/g).map(function(c){return
parseInt(c,16)})}else{this.rgb=rgb.split("").map(function(c){return
parseInt(c+c,16)})}}}this.alpha=typeof(a)==="number"?a:1};Color.prototype={eval:function(){return
this},toCSS:function(){if(this.alpha<1){return"rgba("+this.rgb.map(function(c){return
Math.round(c)}).concat(this.alpha).join(",
")+")"}else{return"#"+this.rgb.map(function(i){i=Math.round(i);i=(i>255?255:(i<0?0:i)).toString(16);return
i.length===1?"0"+i:i}).join("")}},toHSL:function(){var
r=this.rgb[0]/255,g=this.rgb[1]/255,b=this.rgb[2]/255,a=this.alpha;var
max=Math.max(r,g,b),min=Math.min(r,g,b);var
h,s,l=(max+min)/2,d=max-min;if(max===min){h=s=0}else{s=l>0.5?d/(2-max-min):d/(max+min);switch(max){case
r:h=(g-b)/d+(g<b?6:0);break;case g:h=(b-r)/d+2;break;case
b:h=(r-g)/d+4;break}h/=6}return{h:h*360,s:s,l:l,a:a}},toARGB:function(){var
argb=[Math.round(this.alpha*255)].concat(this.rgb);return"#"+argb.map(function(i){i=Math.round(i);i=(i>255?255:(i<0?0:i)).toString(16);return
i.length===1?"0"+i:i}).join("")},mix:function(color2,weight){color1=this;var
p=weight;var w=p*2-1;var a=color1.toHSL().a-color2.toHSL().a;var
w1=(((w*a==-1)?w:(w+a)/(1+w*a))+1)/2;var w2=1-w1;var
rgb=[color1.rgb[0]*w1+color2.rgb[0]*w2,color1.rgb[1]*w1+color2.rgb[1]*w2,color1.rgb[2]*w1+color2.rgb[2]*w2];var
alpha=color1.alpha*p+color2.alpha*(1-p);return new Color(rgb,alpha)}};var
LinearRamp=function(start_color,end_color,start_value,end_value){this.start_color=new
Color(start_color);this.end_color=new
Color(end_color);this.start_value=start_value;this.end_value=end_value;this.value_range=end_value-start_value};LinearRamp.prototype.map_value=function(value){value=Math.max(value,this.start_value);value=Math.min(value,this.end_value);value=(value-this.start_value)/this.value_range;return
this.start_color.mix(this.end_color,1-value).toCSS()};var
SplitRamp=function(start_color,middle_color,end_color,start_value,end_value){this.positive_ramp=new
LinearRamp(middle_color,end_color,0,end_value);this.negative_ramp=new
LinearRamp(middle_color,start_color,0,-start_value);this.start_value=start_value;this.end_value=end_value};SplitRamp.prototype.map_value=function(value){value=Math.max(value,this.start_value);value=Math.min(value,this.end_value);if(value>=0){return
this.positive_ramp.map_value(value)}else{return this.negative_ramp.map_value(-value)}};var
DiagonalHeatmapPainter=function(data,view_start,view_end,prefs,mode){Painter.call(this,data,view_start,view_end,prefs,mode);var
i,len;if(this.prefs.min_value===undefined){var
min_value=Infinity;for(i=0,len=this.data.length;i<len;i++){min_value=Math.min(min_value,this.data[i][5])}this.prefs.min_value=min_value}if(this.prefs.max_value===undefined){var
max_value=-Infinity;for(i=0,len=this.data.length;i<len;i++){max_value=Math.max(max_value,this.data[i][5])}this.prefs.max_value=max_value}};DiagonalHeatmapPainter.prototype.default_prefs={min_value:undefined,max_value:undefined,mode:"Heatmap",pos_color:"#FF8C00",neg_color:"#4169E1"};DiagonalHeatmapPainter.prototype.draw=function(ctx,width,height,w_scale){var
min_value=this.prefs.min_value,max_value=this.prefs.max_value,value_range=max_value-min_value,height_px=height,view_start=this.view_start,mode=this.mode,data=this.data,invsqrt2=1/Math.sqrt(2);var
ramp=(new
SplitRamp(this.prefs.neg_color,"#FFFFFF",this.prefs.pos_color,min_value,max_value));var
d,s1,e1,s2,e2,value;var
scale=function(p){return(p-view_start)*w_scale};ctx.save();ctx.rotate(-45*Math.PI/180);ctx.scale(invsqrt2,invsqrt2);for(var
i=0,len=data.length;i<len;i++){d=data[i];s1=scale(d[1]);e1=scale(d[2]);s2=scale(d[4]);e2=scale(d[5]);value=d[6];ctx.fillStyle=(ramp.map_value(value));ctx.fillRect(s1,s2,(e1-s1),(e2-s2))}ctx.restore()};var
ReadPainterUtils=function(ctx,row_height,px_per_base,mode){this.ctx=ctx;this.row_height=row_height;this.px_per_base=px_per_base;this.draw_details=(mode==="Pack"||mode==="Auto")&&(px_per_base>=ctx.canvas.manager.char_width_px);this.delete_details_thickness=0.2};_.extend(ReadPainterUtils.prototype,{draw_deletion:function(x,y,len){this.ctx.fillStyle="black";var
thickness=(this.draw_details?this.delete_details_thickness:1)*this.row_height;y+=0.5*(this.row_height-thickness);this.ctx.fillRect(x,y,len*this.px_per_base,thickness)}});var
VariantPainter=function(data,view_start,view_end,prefs,mode,base_color_fn){Painter.call(this,data,view_start,view_end,prefs,mode);this.base_color_fn=base_color_fn;this.divider_height=1};_.extend(VariantPainter.prototype,Painter.prototype,{get_row_height:function(){var
mode=this.mode,height;if(mode==="Dense"){height=DENSE_TRACK_HEIGHT}else{if(mode==="Squish"){height=SQUISH_TRACK_HEIGHT}else{height=PACK_TRACK_HEIGHT}}return
height},get_required_height:function(num_samples){var
height=this.prefs.summary_height;if(num_samples>1&&this.prefs.show_sample_data){height+=this.divider_height+num_samples*this.get_row_height()}return
height},draw:function(ctx,width,height,w_scale){ctx.save();var
get_deletion_info=function(ref,alt){var
ref_len=ref.length,alt_len=alt.length,start=0,len=1,is_delete=false;if(alt==="-"){is_delete=true;len=ref.length}else{if(ref.indexOf(alt)===0&&ref_len>alt_len){is_delete=true;len=ref_len=alt_len;start+=alt_len}}return(is_delete?{start:start,len:len}:null)};var
locus_data,pos,id,ref,alt,qual,filter,sample_gts,allele_counts,variant,draw_x_start,char_x_start,draw_y_start,genotype,base_px=Math.max(1,Math.floor(w_scale)),num_samples=(this.data.length?this.data[0][7].split(",").length:0),row_height=(this.mode==="Squish"?SQUISH_TRACK_HEIGHT:PACK_TRACK_HEIGHT),feature_height=(w_scale<0.1?row_height:(this.mode==="Squish"?SQUISH_FEATURE_HEIGHT:PACK_FEATURE_HEIGHT)),draw_summary=true,paint_utils=new
ReadPainterUtils(ctx,row_height,w_scale,this.mode),j;if(num_samples===1){row_height=feature_height=(w_scale<ctx.canvas.manager.char_width_px?this.prefs.summary_height:row_height);paint_utils.row_height=row_height;draw_summary=false}if(this.prefs.show_sample_data&&draw_summary){ctx.fillStyle="#F3F3F3";ctx.globalAlpha=1;ctx.fillRect(0,this.prefs.summary_height-this.divider_height,width,this.divider_height)}ctx.textAlign="center";for(var
i=0;i<this.data.length;i++){locus_data=this.data[i];pos=locus_data[1];ref=locus_data[3];alt=[locus_data[4].split(",")];sample_gts=locus_data[7].split(",");allele_counts=locus_data.slice(8);alt=_.map(_.flatten(alt),function(a){var
type,alt_info={},delete_info=get_deletion_info(ref,a);if(delete_info){type="deletion";_.extend(alt_info,delete_info)}else{type="snp"}return
_.extend(alt_info,{type:type,value:a,})});if(pos<this.view_start||pos>this.view_end){continue}draw_x_start=Math.floor(Math.max(-0.5*w_scale,(pos-this.view_start-0.5)*w_scale));char_x_start=Math.floor(Math.max(0,(pos-this.view_start)*w_scale));if(draw_summary){ctx.fillStyle="#999999";ctx.globalAlpha=1;ctx.fillRect(draw_x_start,0,base_px,this.prefs.summary_height);draw_y_start=this.prefs.summary_height;for(j=0;j<alt.length;j++){ctx.fillStyle=(alt[j].type==="deletion"?"black":this.base_color_fn(alt[j].value));allele_frac=allele_counts/sample_gts.length;draw_height=Math.ceil(this.prefs.summary_height*allele_frac);ctx.fillRect(draw_x_start,draw_y_start-draw_height,base_px,draw_height);draw_y_start-=draw_height}}if(!this.prefs.show_sample_data){continue}draw_y_start=(draw_summary?this.prefs.summary_height+this.divider_height:0);for(j=0;j<sample_gts.length;j++,draw_y_start+=row_height){genotype=(sample_gts[j]?sample_gts[j].split(/\/|\|/):["0","0"]);variant=null;if(genotype[0]===genotype[1]){if(genotype[0]==="."){}else{if(genotype[0]!=="0"){variant=alt[parseInt(genotype[0],10)-1];ctx.globalAlpha=1}}}else{variant=(genotype[0]!=="0"?genotype[0]:genotype[1]);variant=alt[parseInt(variant,10)-1];ctx.globalAlpha=0.5}if(variant){if(variant.type==="snp"){var
snp=variant.value;ctx.fillStyle=this.base_color_fn(snp);if(paint_utils.draw_details){ctx.fillText(snp,char_x_start,draw_y_start+row_height)}else{ctx.fillRect(draw_x_start,draw_y_start+1,base_px,feature_height)}}else{if(variant.type==="deletion"){paint_utils.draw_deletion(draw_x_start+base_px*variant.start,draw_y_start+1,variant.len)}else{}}}}}ctx.restore()}});return{Scaler:Scaler,LinePainter:LinePainter,LinkedFeaturePainter:LinkedFeaturePainter,ReadPainter:ReadPainter,ArcLinkedFeaturePainter:ArcLinkedFeaturePainter,DiagonalHeatmapPainter:DiagonalHeatmapPainter,VariantPainter:VariantPainter}});
\ No newline at end of file
diff -r 314326446ec51483341eaacae7902fff44c8ac02 -r
3a51eaf209f2502bf32dbb421ecabb7fe46243ea static/scripts/packed/viz/trackster/slotting.js
--- a/static/scripts/packed/viz/trackster/slotting.js
+++ b/static/scripts/packed/viz/trackster/slotting.js
@@ -1,1 +1,1 @@
-define(["libs/underscore"],function(b){var e=b.extend;var c=2,a=5;var
d=function(i,h,f,g){this.slots={};this.start_end_dct={};this.w_scale=i;this.mode=h;this.include_label=(h==="Pack");this.max_rows=f;this.measureText=g};e(d.prototype,{slot_features:function(m){var
p=this.w_scale,h=this.start_end_dct,x=[],z=[],n=0,y=this.max_rows;for(var
v=0,w=m.length;v<w;v++){var
k=m[v],o=k[0];if(this.slots[o]!==undefined){n=Math.max(n,this.slots[o]);z.push(this.slots[o])}else{x.push(v)}}var
q=function(E,F){for(var D=0;D<=y;D++){var B=false,G=h[D];if(G!==undefined){for(var
A=0,C=G.length;A<C;A++){var
i=G[A];if(F>i[0]&&E<i[1]){B=true;break}}}if(!B){return D}}return -1};for(var
v=0,w=x.length;v<w;v++){var
k=m[x[v]],o=k[0],t=k[1],f=k[2],r=k[3],g=Math.floor(t*p),l=Math.ceil(f*p),u=this.measureText(r).width,j;if(r!==undefined&&this.include_label){u+=(c+a);if(g-u>=0){g-=u;j="left"}else{l+=u;j="right"}}var
s=q(g,l);if(s>=0){if(h[s]===undefined){h[s]=[]}h[s].push([g,l]);this.slots[o]=s;n=Math.max(n,s)}}return
n+1}});return{FeatureSlotter:d}});
\ No newline at end of file
+define(["libs/underscore"],function(c){var f=c.extend;var d=2,b=5;var
a=function(h,g){this.slot=h;this.feature=g};var
e=function(j,i,g,h){this.slots={};this.start_end_dct={};this.w_scale=j;this.mode=i;this.include_label=(i==="Pack");this.max_rows=g;this.measureText=h};f(e.prototype,{_get_draw_coords:function(j){var
h=Math.floor(j[1]*this.w_scale),i=Math.ceil(j[2]*this.w_scale),g=j[3],k;if(g!==undefined&&this.include_label){var
l=this.measureText(g).width+(d+b);if(h-l>=0){h-=l;k="left"}else{i+=l;k="right"}}return[h,i]},_find_slot:function(j){var
o=j[0],n=j[1];for(var l=0;l<=this.max_rows;l++){var
p=false,m=this.start_end_dct[l];if(m!==undefined){for(var g=0,h=m.length;g<h;g++){var
i=m[g];if(n>i[0]&&o<i[1]){p=true;break}}}if(!p){return l}}return
-1},slot_features:function(h){var q=this.start_end_dct,v=[],m=0,x,l;for(var
o=0,t=h.length;o<t;o++){x=h[o];l=x[0];var
g=this.slots[l];if(g){if(x[1]<g.feature[1]||g.feature[2]<x[2]){console.log(x[3],g.slot,this._find_slot(this._get_draw_coords(x)));var
s=this._get_draw_coords(g.feature),p=this._get_draw_coords(x),j=this.start_end_dct[g.slot];for(var
n=0;n<j.length;n++){var
w=j[n];if(w[0]===s[0]&&w[1]===s[1]){j[n]=p}}}m=Math.max(m,this.slots[l].slot)}else{v.push(o)}}for(var
o=0,t=v.length;o<t;o++){x=h[v[o]];l=x[0];var r=this._get_draw_coords(x);var
u=this._find_slot(r);if(u>=0){if(q[u]===undefined){q[u]=[]}q[u].push(r);this.slots[l]=new
a(u,x);m=Math.max(m,u)}}return m+1}});return{FeatureSlotter:e}});
\ No newline at end of file
diff -r 314326446ec51483341eaacae7902fff44c8ac02 -r
3a51eaf209f2502bf32dbb421ecabb7fe46243ea static/scripts/viz/trackster/painters.js
--- a/static/scripts/viz/trackster/painters.js
+++ b/static/scripts/viz/trackster/painters.js
@@ -396,7 +396,7 @@
feature_end = feature[2],
// Slot valid only if features are slotted and this feature is slotted;
// feature may not be due to lack of space.
- slot = (slots && slots[feature_uid] !== undefined ?
slots[feature_uid] : null);
+ slot = (slots && slots[feature_uid] !== undefined ?
slots[feature_uid].slot : null);
// Draw feature if (a) mode is dense or feature is slotted (as it must be for
all non-dense modes) and
// (b) there's overlap between the feature and drawing region.
diff -r 314326446ec51483341eaacae7902fff44c8ac02 -r
3a51eaf209f2502bf32dbb421ecabb7fe46243ea static/scripts/viz/trackster/slotting.js
--- a/static/scripts/viz/trackster/slotting.js
+++ b/static/scripts/viz/trackster/slotting.js
@@ -7,6 +7,14 @@
PACK_SPACING = 5;
/**
+ * Hold slotting information for a feature.
+ */
+var SlottedInfo = function(slot, feature) {
+ this.slot = slot;
+ this.feature = feature;
+};
+
+/**
* FeatureSlotter determines slots in which to draw features for vertical
* packing.
*
@@ -28,118 +36,148 @@
* the largest slot required for the passed set of features is returned
*/
extend( FeatureSlotter.prototype, {
+ /**
+ * Get drawing coordinate for a feature.
+ */
+ _get_draw_coords: function(feature) {
+ // Get initial draw coordinates using w_scale.
+ var draw_start = Math.floor(feature[1] * this.w_scale),
+ draw_end = Math.ceil(feature[2] * this.w_scale),
+ f_name = feature[3],
+ text_align;
+
+ // Update start, end drawing locations to include feature name.
+ // Try to put the name on the left, if not, put on right.
+ if (f_name !== undefined && this.include_label ) {
+ // Add gap for label spacing and extra pack space padding
+ // TODO: Fix constants
+ var text_len = this.measureText(f_name).width + (LABEL_SPACING +
PACK_SPACING);
+ if (draw_start - text_len >= 0) {
+ draw_start -= text_len;
+ text_align = "left";
+ } else {
+ draw_end += text_len;
+ text_align = "right";
+ }
+ }
+
+ /*
+ if (slot_num < 0) {
+
+ TODO: this is not yet working --
+ console.log(feature_uid, "looking for slot with text on the
right");
+ // Slot not found. If text was on left, try on right and see
+ // if slot can be found.
+ // TODO: are there any checks we need to do to ensure that text
+ // will fit on tile?
+ if (text_align === "left") {
+ draw_start -= text_len;
+ draw_end -= text_len;
+ text_align = "right";
+ slot_num = find_slot(draw_start, draw_end);
+ }
+ if (slot_num >= 0) {
+ console.log(feature_uid, "found slot with text on the right");
+ }
+
+ }
+ */
+
+ return [draw_start, draw_end];
+ },
+
+ /**
+ * Find the first slot such that current feature doesn't overlap any other
features in that slot.
+ * Returns -1 if no slot was found.
+ */
+ _find_slot: function(draw_coords) {
+ // TODO: Use a data structure for faster searching of available slots.
+ var draw_start = draw_coords[0],
+ draw_end = draw_coords[1];
+ for (var slot_num = 0; slot_num <= this.max_rows; slot_num++) {
+ var has_overlap = false,
+ slot = this.start_end_dct[slot_num];
+ if (slot !== undefined) {
+ // Iterate through features already in slot to see if current feature
will fit.
+ for (var k = 0, k_len = slot.length; k < k_len; k++) {
+ var s_e = slot[k];
+ if (draw_end > s_e[0] && draw_start < s_e[1]) {
+ // There is overlap
+ has_overlap = true;
+ break;
+ }
+ }
+ }
+ if (!has_overlap) {
+ return slot_num;
+ }
+ }
+ return -1;
+ },
+
+ /**
+ * Slot features.
+ */
slot_features: function( features ) {
- var w_scale = this.w_scale,
- start_end_dct = this.start_end_dct,
+ var start_end_dct = this.start_end_dct,
undone = [],
- slotted = [],
- highest_slot = 0,
- max_rows = this.max_rows;
-
- // If feature already exists in slots (from previously seen tiles), use the same
slot,
- // otherwise if not seen, add to "undone" list for slot calculation.
-
- // TODO: Should calculate zoom tile index, which will improve performance
- // by only having to look at a smaller subset
- // if (this.start_end_dct[0] === undefined) { this.start_end_dct[0] = []; }
+ highest_slot = 0,
+ feature,
+ feature_uid;
+
+ // Loop through features to (a) find those that are not yet slotted and (b)
update
+ // those that are slotted if new information is availabe. For (a), features
already
+ // slotted (based on slotting from other tiles) will retain their current slot.
for (var i = 0, len = features.length; i < len; i++) {
- var feature = features[i],
- feature_uid = feature[0];
- if (this.slots[feature_uid] !== undefined) {
- highest_slot = Math.max(highest_slot, this.slots[feature_uid]);
- slotted.push(this.slots[feature_uid]);
- } else {
+ feature = features[i];
+ feature_uid = feature[0];
+ var slotted_info = this.slots[feature_uid];
+
+ // Separate and handle slotted vs. unslotted features.
+ if (slotted_info) {
+ // Feature is slotted; if feature now has larger start/end coordinates,
+ // update drawing coordinates.
+ if (feature[1] < slotted_info.feature[1] || slotted_info.feature[2]
< feature[2]) {
+ console.log(feature[3], slotted_info.slot,
this._find_slot(this._get_draw_coords(feature)));
+ // Feature has changed (e.g. a single read now has its pair), so
recalculate its
+ // drawing coordinates.
+ var old_draw_coords = this._get_draw_coords(slotted_info.feature),
+ new_draw_coords = this._get_draw_coords(feature),
+ slotted_coords = this.start_end_dct[slotted_info.slot];
+ for (var k = 0; k < slotted_coords.length; k++) {
+ var dc = slotted_coords[k];
+ if (dc[0] === old_draw_coords[0] && dc[1] ===
old_draw_coords[1]) {
+ // Replace old drawing coordinates with new ones.
+ slotted_coords[k] = new_draw_coords;
+ }
+ }
+ }
+ highest_slot = Math.max(highest_slot, this.slots[feature_uid].slot);
+ }
+ else {
undone.push(i);
}
}
-
+
// Slot unslotted features.
- // Find the first slot such that current feature doesn't overlap any other
features in that slot.
- // Returns -1 if no slot was found.
- var find_slot = function(f_start, f_end) {
- // TODO: Fix constants
- for (var slot_num = 0; slot_num <= max_rows; slot_num++) {
- var has_overlap = false,
- slot = start_end_dct[slot_num];
- if (slot !== undefined) {
- // Iterate through features already in slot to see if current feature
will fit.
- for (var k = 0, k_len = slot.length; k < k_len; k++) {
- var s_e = slot[k];
- if (f_end > s_e[0] && f_start < s_e[1]) {
- // There is overlap
- has_overlap = true;
- break;
- }
- }
- }
- if (!has_overlap) {
- return slot_num;
- }
- }
- return -1;
- };
-
// Do slotting.
for (var i = 0, len = undone.length; i < len; i++) {
- var feature = features[undone[i]],
- feature_uid = feature[0],
- feature_start = feature[1],
- feature_end = feature[2],
- feature_name = feature[3],
- // Where to start, end drawing on screen.
- f_start = Math.floor( feature_start * w_scale ),
- f_end = Math.ceil( feature_end * w_scale ),
- text_len = this.measureText(feature_name).width,
- text_align;
-
- // Update start, end drawing locations to include feature name.
- // Try to put the name on the left, if not, put on right.
- if (feature_name !== undefined && this.include_label ) {
- // Add gap for label spacing and extra pack space padding
- // TODO: Fix constants
- text_len += (LABEL_SPACING + PACK_SPACING);
- if (f_start - text_len >= 0) {
- f_start -= text_len;
- text_align = "left";
- } else {
- f_end += text_len;
- text_align = "right";
- }
- }
+ feature = features[undone[i]];
+ feature_uid = feature[0];
+ var draw_coords = this._get_draw_coords(feature);
// Find slot.
- var slot_num = find_slot(f_start, f_end);
+ var slot_num = this._find_slot(draw_coords);
- /*
- if (slot_num < 0) {
-
- TODO: this is not yet working --
- console.log(feature_uid, "looking for slot with text on the
right");
- // Slot not found. If text was on left, try on right and see
- // if slot can be found.
- // TODO: are there any checks we need to do to ensure that text
- // will fit on tile?
- if (text_align === "left") {
- f_start -= text_len;
- f_end -= text_len;
- text_align = "right";
- slot_num = find_slot(f_start, f_end);
- }
- if (slot_num >= 0) {
- console.log(feature_uid, "found slot with text on the
right");
- }
-
- }
- */
// Do slotting.
if (slot_num >= 0) {
// Add current feature to slot.
if (start_end_dct[slot_num] === undefined) {
start_end_dct[slot_num] = [];
}
- start_end_dct[slot_num].push([f_start, f_end]);
- this.slots[feature_uid] = slot_num;
+ start_end_dct[slot_num].push(draw_coords);
+ this.slots[feature_uid] = new SlottedInfo(slot_num, feature);
highest_slot = Math.max(highest_slot, slot_num);
}
}
diff -r 314326446ec51483341eaacae7902fff44c8ac02 -r
3a51eaf209f2502bf32dbb421ecabb7fe46243ea test/unit/workflows/test_modules.py
--- /dev/null
+++ b/test/unit/workflows/test_modules.py
@@ -0,0 +1,39 @@
+from galaxy import eggs
+eggs.require( "mock" )
+
+
+import mock
+
+from galaxy.workflow import modules
+from .workflow_support import MockTrans
+
+
+def test_input_has_no_errors():
+ trans = MockTrans()
+ input_step_module = modules.module_factory.new( trans, 'data_input' )
+ assert not input_step_module.get_errors()
+
+
+def test_valid_new_tool_has_no_errors():
+ trans = MockTrans()
+ mock_tool = mock.Mock()
+ trans.app.toolbox.tools[ "cat1" ] = mock_tool
+ tool_module = modules.module_factory.new( trans, 'tool',
tool_id="cat1" )
+ assert not tool_module.get_errors()
+
+
+def test_missing_tool_has_errors():
+ trans = MockTrans()
+ tool_dict = { "type": "tool", "tool_id":
"cat1" }
+ tool_module = modules.module_factory.from_dict( trans, tool_dict )
+ assert tool_module.get_errors()
+
+
+def test_cannot_create_tool_modules_for_missing_tools():
+ trans = MockTrans()
+ exception = False
+ try:
+ modules.module_factory.new( trans, 'tool', tool_id="cat1" )
+ except Exception:
+ exception = True
+ assert exception
diff -r 314326446ec51483341eaacae7902fff44c8ac02 -r
3a51eaf209f2502bf32dbb421ecabb7fe46243ea test/unit/workflows/workflow_support.py
--- a/test/unit/workflows/workflow_support.py
+++ b/test/unit/workflows/workflow_support.py
@@ -17,3 +17,14 @@
"sqlite:///:memory:",
create_tables=True
)
+ self.toolbox = TestToolbox()
+
+
+class TestToolbox( object ):
+
+ def __init__( self ):
+ self.tools = {}
+
+ def get_tool( self, tool_id ):
+ # Real tool box returns None of missing tool also
+ return self.tools.get( tool_id, None )
Repository URL:
https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from
bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.