galaxy-commits
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
September 2012
- 1 participants
- 161 discussions
commit/galaxy-central: smcmanus: This is the migration script for the Job and Task tables' exit_code column.
by Bitbucket 28 Sep '12
by Bitbucket 28 Sep '12
28 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/e86e9e97daf8/
changeset: e86e9e97daf8
user: smcmanus
date: 2012-09-29 06:37:46
summary: This is the migration script for the Job and Task tables' exit_code column.
affected #: 1 file
diff -r f76eaed275e6999fbe0cb575f95647fb168ed37f -r e86e9e97daf8f4b682cfe1fe5c12c8a408fd9cc3 lib/galaxy/model/migrate/versions/0107_add_exit_code_to_job_and_task.py
--- /dev/null
+++ b/lib/galaxy/model/migrate/versions/0107_add_exit_code_to_job_and_task.py
@@ -0,0 +1,70 @@
+"""
+Add the exit_code column to the Job and Task tables.
+"""
+
+from sqlalchemy import *
+from sqlalchemy.orm import *
+from migrate import *
+from migrate.changeset import *
+
+import logging
+log = logging.getLogger( __name__ )
+
+# Need our custom types, but don't import anything else from model
+from galaxy.model.custom_types import *
+
+metadata = MetaData( migrate_engine )
+db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) )
+
+# There was a bug when only one column was used for both tables,
+# so create separate columns.
+exit_code_job_col = Column( "exit_code", Integer, nullable=True )
+exit_code_task_col = Column( "exit_code", Integer, nullable=True )
+
+def display_migration_details():
+ print ""
+ print "This migration script adds a 'handler' column to the Job table."
+
+def upgrade():
+ print __doc__
+ metadata.reflect()
+
+ # Add the exit_code column to the Job table.
+ try:
+ job_table = Table( "job", metadata, autoload=True )
+ exit_code_job_col.create( job_table )
+ assert exit_code_job_col is job_table.c.exit_code
+ except Exception, e:
+ print str(e)
+ log.error( "Adding column 'exit_code' to job table failed: %s" % str( e ) )
+ return
+
+ # Add the exit_code column to the Task table.
+ try:
+ task_table = Table( "task", metadata, autoload=True )
+ exit_code_task_col.create( task_table )
+ assert exit_code_task_col is task_table.c.exit_code
+ except Exception, e:
+ print str(e)
+ log.error( "Adding column 'exit_code' to task table failed: %s" % str( e ) )
+ return
+
+def downgrade():
+ metadata.reflect()
+
+ # Drop the Job table's exit_code column.
+ try:
+ job_table = Table( "job", metadata, autoload=True )
+ exit_code_col = job_table.c.exit_code
+ exit_code_col.drop()
+ except Exception, e:
+ log.debug( "Dropping 'exit_code' column from job table failed: %s" % ( str( e ) ) )
+
+ # Drop the Job table's exit_code column.
+ try:
+ task_table = Table( "task", metadata, autoload=True )
+ exit_code_col = task_table.c.exit_code
+ exit_code_col.drop()
+ except Exception, e:
+ log.debug( "Dropping 'exit_code' column from task table failed: %s" % ( str( e ) ) )
+
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.
1
0
commit/galaxy-central: smcmanus: Added exit_code columns for the Job and Task tables, as well as a migration script
by Bitbucket 28 Sep '12
by Bitbucket 28 Sep '12
28 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/f76eaed275e6/
changeset: f76eaed275e6
user: smcmanus
date: 2012-09-29 05:26:53
summary: Added exit_code columns for the Job and Task tables, as well as a migration script
affected #: 2 files
diff -r 578c082d9b46689268aa4a0cd4d29327f84d2791 -r f76eaed275e6999fbe0cb575f95647fb168ed37f lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -134,6 +134,7 @@
self.post_job_actions = []
self.imported = False
self.handler = None
+ self.exit_code = 0
# TODO: Add accessors for members defined in SQL Alchemy for the Job table and
# for the mapper defined to the Job table.
@@ -319,6 +320,7 @@
# SM: Using default empty strings avoids None exceptions later on.
self.stdout = ""
self.stderr = ""
+ self.exit_code = 0
self.prepare_input_files_cmd = prepare_files_cmd
def get_param_values( self, app ):
diff -r 578c082d9b46689268aa4a0cd4d29327f84d2791 -r f76eaed275e6999fbe0cb575f95647fb168ed37f lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -435,6 +435,7 @@
Column( "runner_name", String( 255 ) ),
Column( "stdout", TEXT ),
Column( "stderr", TEXT ),
+ Column( "exit_code", Integer, nullable=True ),
Column( "traceback", TEXT ),
Column( "session_id", Integer, ForeignKey( "galaxy_session.id" ), index=True, nullable=True ),
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=True ),
@@ -529,6 +530,7 @@
Column( "runner_name", String( 255 ) ),
Column( "stdout", TEXT ),
Column( "stderr", TEXT ),
+ Column( "exit_code", Integer, nullable=True ),
Column( "info", TrimmedString ( 255 ) ),
Column( "traceback", TEXT ),
Column( "job_id", Integer, ForeignKey( "job.id" ), index=True, nullable=False ),
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.
1
0
28 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/578c082d9b46/
changeset: 578c082d9b46
user: james_taylor
date: 2012-09-28 23:55:02
summary: Lookup templates in 'templates/webapps/${webapp.name}/' first before looking in the root directory. This avoids the ned to specify the webapp name in the template path. Still needs to be updated in nearly all templates
affected #: 10 files
diff -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 -r 578c082d9b46689268aa4a0cd4d29327f84d2791 lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py
+++ b/lib/galaxy/web/framework/__init__.py
@@ -220,13 +220,23 @@
base.WebApplication.__init__( self )
self.set_transaction_factory( lambda e: self.transaction_chooser( e, galaxy_app, session_cookie ) )
# Mako support
- self.mako_template_lookup = mako.lookup.TemplateLookup(
- directories = [ galaxy_app.config.template_path ] ,
+ self.mako_template_lookup = self.create_mako_template_lookup( galaxy_app, name )
+ # Security helper
+ self.security = galaxy_app.security
+
+ def create_mako_template_lookup( self, galaxy_app, name ):
+ paths = []
+ # First look in webapp specific directory
+ if name is not None:
+ paths.append( os.path.join( galaxy_app.config.template_path, 'webapps', name ) )
+ # Then look in root directory
+ paths.append( galaxy_app.config.template_path )
+ # Create TemplateLookup with a small cache
+ return mako.lookup.TemplateLookup(
+ directories = paths,
module_directory = galaxy_app.config.template_cache,
collection_size = 500,
output_encoding = 'utf-8' )
- # Security helper
- self.security = galaxy_app.security
def handle_controller_exception( self, e, trans, **kwargs ):
diff -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 -r 578c082d9b46689268aa4a0cd4d29327f84d2791 templates/base/base_panels.mako
--- /dev/null
+++ b/templates/base/base_panels.mako
@@ -0,0 +1,299 @@
+<!DOCTYPE HTML>
+
+<%
+ self.has_left_panel=True
+ self.has_right_panel=True
+ self.message_box_visible=False
+ self.overlay_visible=False
+ self.message_box_class=""
+ self.active_view=None
+ self.body_class=""
+ self.require_javascript=False
+%>
+
+<%def name="init()">
+ ## Override
+</%def>
+
+## Default stylesheets
+<%def name="stylesheets()">
+ ${h.css('base','panel_layout','jquery.rating')}
+ <style type="text/css">
+ body, html {
+ overflow: hidden;
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ height: 100%;
+ }
+ #center {
+ %if not self.has_left_panel:
+ left: 0 !important;
+ %endif
+ %if not self.has_right_panel:
+ right: 0 !important;
+ %endif
+ }
+ %if self.message_box_visible:
+ #left, #left-border, #center, #right-border, #right
+ {
+ top: 64px;
+ }
+ %endif
+ </style>
+</%def>
+
+## Default javascripts
+<%def name="javascripts()">
+ <!--[if lt IE 7]>
+ ${h.js( 'libs/IE/IE7', 'libs/IE/ie7-recalc' )}
+ <![endif]-->
+ ${h.js(
+ 'libs/jquery/jquery',
+ 'libs/json2',
+ 'libs/bootstrap',
+ 'libs/underscore',
+ 'libs/backbone/backbone',
+ 'libs/backbone/backbone-relational',
+ 'libs/handlebars.runtime',
+ 'mvc/ui',
+ 'galaxy.base'
+ )}
+ <script type="text/javascript">
+ // Set up needed paths.
+ var galaxy_paths = new GalaxyPaths({
+ root_path: '${h.url_for( "/" )}',
+ image_path: '${h.url_for( "/static/images" )}',
+
+ tool_url: '${h.url_for( controller="/api/tools" )}',
+ history_url: '${h.url_for( controller="/api/histories" )}',
+
+ datasets_url: '${h.url_for( controller="/api/datasets" )}',
+ sweepster_url: '${h.url_for( controller="/visualization", action="sweepster" )}',
+ visualization_url: '${h.url_for( controller="/visualization", action="save" )}',
+ });
+ </script>
+</%def>
+
+## Default late-load javascripts
+<%def name="late_javascripts()">
+ ## Scripts can be loaded later since they progressively add features to
+ ## the panels, but do not change layout
+ ${h.js( 'libs/jquery/jquery.event.drag', 'libs/jquery/jquery.event.hover', 'libs/jquery/jquery.form', 'libs/jquery/jquery.rating', 'galaxy.panels' )}
+ <script type="text/javascript">
+
+ ensure_dd_helper();
+
+ %if self.has_left_panel:
+ var lp = new Panel( { panel: $("#left"), center: $("#center"), drag: $("#left > .unified-panel-footer > .drag" ), toggle: $("#left > .unified-panel-footer > .panel-collapse" ) } );
+ force_left_panel = function( x ) { lp.force_panel( x ) };
+ %endif
+
+ %if self.has_right_panel:
+ var rp = new Panel( { panel: $("#right"), center: $("#center"), drag: $("#right > .unified-panel-footer > .drag" ), toggle: $("#right > .unified-panel-footer > .panel-collapse" ), right: true } );
+ window.handle_minwidth_hint = function( x ) { rp.handle_minwidth_hint( x ) };
+ force_right_panel = function( x ) { rp.force_panel( x ) };
+ %endif
+
+ </script>
+ ## Handle AJAX (actually hidden iframe) upload tool
+ <![if !IE]>
+ <script type="text/javascript">
+ var upload_form_error = function( msg ) {
+ if ( ! $("iframe#galaxy_main").contents().find("body").find("div[class='errormessage']").size() ) {
+ $("iframe#galaxy_main").contents().find("body").prepend( '<div class="errormessage" name="upload_error">' + msg + '</div><p/>' );
+ } else {
+ $("iframe#galaxy_main").contents().find("body").find("div[class='errormessage']").text( msg );
+ }
+ }
+ var uploads_in_progress = 0;
+ jQuery( function() {
+ $("iframe#galaxy_main").load( function() {
+ $(this).contents().find("form").each( function() {
+ if ( $(this).find("input[galaxy-ajax-upload]").length > 0 ){
+ $(this).submit( function() {
+ // Only bother using a hidden iframe if there's a file (e.g. big data) upload
+ var file_upload = false;
+ $(this).find("input[galaxy-ajax-upload]").each( function() {
+ if ( $(this).val() != '' ) {
+ file_upload = true;
+ }
+ });
+ if ( ! file_upload ) {
+ return true;
+ }
+ // Make a synchronous request to create the datasets first
+ var async_datasets;
+ var upload_error = false;
+ $.ajax( {
+ async: false,
+ type: "POST",
+ url: "${h.url_for(controller='/tool_runner', action='upload_async_create')}",
+ data: $(this).formSerialize(),
+ dataType: "json",
+ success: function(array_obj, status) {
+ if (array_obj.length > 0) {
+ if (array_obj[0] == 'error') {
+ upload_error = true;
+ upload_form_error(array_obj[1]);
+ } else {
+ async_datasets = array_obj.join();
+ }
+ } else {
+ // ( gvk 1/22/10 ) FIXME: this block is never entered, so there may be a bug somewhere
+ // I've done some debugging like checking to see if array_obj is undefined, but have not
+ // tracked down the behavior that will result in this block being entered. I believe the
+ // intent was to have this block entered if the upload button is clicked on the upload
+ // form but no file was selected.
+ upload_error = true;
+ upload_form_error( 'No data was entered in the upload form. You may choose to upload a file, paste some data directly in the data box, or enter URL(s) to fetch data.' );
+ }
+ }
+ } );
+ if (upload_error == true) {
+ return false;
+ } else {
+ $(this).find("input[name=async_datasets]").val( async_datasets );
+ $(this).append("<input type='hidden' name='ajax_upload' value='true'>");
+ }
+ // iframe submit is required for nginx (otherwise the encoding is wrong)
+ $(this).ajaxSubmit( { iframe: true,
+ complete: function (xhr, stat) {
+ uploads_in_progress--;
+ if (uploads_in_progress == 0) {
+ window.onbeforeunload = null;
+ }
+ }
+ } );
+ uploads_in_progress++;
+ window.onbeforeunload = function() { return "Navigating away from the Galaxy analysis interface will interrupt the file upload(s) currently in progress. Do you really want to do this?"; }
+ if ( $(this).find("input[name='folder_id']").val() != undefined ) {
+ var library_id = $(this).find("input[name='library_id']").val();
+ var show_deleted = $(this).find("input[name='show_deleted']").val();
+ if ( location.pathname.indexOf( 'admin' ) != -1 ) {
+ $("iframe#galaxy_main").attr("src","${h.url_for( controller='library_common', action='browse_library' )}?cntrller=library_admin&id=" + library_id + "&created_ldda_ids=" + async_datasets + "&show_deleted=" + show_deleted);
+ } else {
+ $("iframe#galaxy_main").attr("src","${h.url_for( controller='library_common', action='browse_library' )}?cntrller=library&id=" + library_id + "&created_ldda_ids=" + async_datasets + "&show_deleted=" + show_deleted);
+ }
+ } else {
+ $("iframe#galaxy_main").attr("src","${h.url_for(controller='tool_runner', action='upload_async_message')}");
+ }
+ return false;
+ });
+ }
+ });
+ });
+ });
+ </script>
+ <![endif]>
+</%def>
+
+## Masthead
+<%def name="masthead()">
+ ## Override
+</%def>
+
+<%def name="overlay( title='', content='', visible=False )">
+ <%def name="title()"></%def>
+ <%def name="content()"></%def>
+
+ <%
+ if visible:
+ display = "style='display: block;'"
+ overlay_class = "in"
+ else:
+ display = "style='display: none;'"
+ overlay_class = ""
+ %>
+
+ <div id="overlay" ${display}>
+
+ <div id="overlay-background" class="modal-backdrop fade ${overlay_class}"></div>
+
+ <div id="dialog-box" class="modal dialog-box" border="0" ${display}>
+ <div class="modal-header">
+ <span><h3 class='title'>${title}</h3></span>
+ </div>
+ <div class="modal-body">${content}</div>
+ <div class="modal-footer">
+ <div class="buttons" style="float: right;"></div>
+ <div class="extra_buttons" style=""></div>
+ <div style="clear: both;"></div>
+ </div>
+ </div>
+
+ </div>
+</%def>
+
+## Messagebox
+<%def name="message_box_content()">
+</%def>
+
+## Document
+<html>
+ ${self.init()}
+ <head>
+ <title>${self.title()}</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ ## For mobile browsers, don't scale up
+ <meta name = "viewport" content = "maximum-scale=1.0">
+ ## Force IE to standards mode, and prefer Google Chrome Frame if the user has already installed it
+ <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
+ ${self.stylesheets()}
+ ${self.javascripts()}
+ </head>
+
+ <body scroll="no" class="${self.body_class}">
+ %if self.require_javascript:
+ <noscript>
+ <div class="overlay overlay-background">
+ <div class="modal dialog-box" border="0">
+ <div class="modal-header"><h3 class="title">Javascript Required</h3></div>
+ <div class="modal-body">The Galaxy analysis interface requires a browser with Javascript enabled. <br> Please enable Javascript and refresh this page</div>
+ </div>
+ </div>
+ </noscript>
+ %endif
+ <div id="everything" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; min-width: 600px;">
+ ## Background displays first
+ <div id="background"></div>
+ ## Layer iframes over backgrounds
+ <div id="masthead" class="navbar navbar-fixed-top">
+ <div class="masthead-inner navbar-inner">
+ ${self.masthead()}
+ </div>
+ </div>
+ <div id="messagebox" class="panel-${self.message_box_class}-message">
+ %if self.message_box_visible:
+ ${self.message_box_content()}
+ %endif
+ </div>
+ ${self.overlay(visible=self.overlay_visible)}
+ %if self.has_left_panel:
+ <div id="left">
+ ${self.left_panel()}
+ <div class="unified-panel-footer">
+ <div class="panel-collapse"></span></div>
+ <div class="drag"></div>
+ </div>
+ </div>
+ %endif
+ <div id="center">
+ ${self.center_panel()}
+ </div>
+ %if self.has_right_panel:
+ <div id="right">
+ ${self.right_panel()}
+ <div class="unified-panel-footer">
+ <div class="panel-collapse right"></span></div>
+ <div class="drag"></div>
+ </div>
+ </div>
+ %endif
+ </div>
+ ## Allow other body level elements
+ </body>
+ ## Scripts can be loaded later since they progressively add features to
+ ## the panels, but do not change layout
+ ${self.late_javascripts()}
+</html>
diff -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 -r 578c082d9b46689268aa4a0cd4d29327f84d2791 templates/base_panels.mako
--- a/templates/base_panels.mako
+++ /dev/null
@@ -1,299 +0,0 @@
-<!DOCTYPE HTML>
-
-<%
- self.has_left_panel=True
- self.has_right_panel=True
- self.message_box_visible=False
- self.overlay_visible=False
- self.message_box_class=""
- self.active_view=None
- self.body_class=""
- self.require_javascript=False
-%>
-
-<%def name="init()">
- ## Override
-</%def>
-
-## Default stylesheets
-<%def name="stylesheets()">
- ${h.css('base','panel_layout','jquery.rating')}
- <style type="text/css">
- body, html {
- overflow: hidden;
- margin: 0;
- padding: 0;
- width: 100%;
- height: 100%;
- }
- #center {
- %if not self.has_left_panel:
- left: 0 !important;
- %endif
- %if not self.has_right_panel:
- right: 0 !important;
- %endif
- }
- %if self.message_box_visible:
- #left, #left-border, #center, #right-border, #right
- {
- top: 64px;
- }
- %endif
- </style>
-</%def>
-
-## Default javascripts
-<%def name="javascripts()">
- <!--[if lt IE 7]>
- ${h.js( 'libs/IE/IE7', 'libs/IE/ie7-recalc' )}
- <![endif]-->
- ${h.js(
- 'libs/jquery/jquery',
- 'libs/json2',
- 'libs/bootstrap',
- 'libs/underscore',
- 'libs/backbone/backbone',
- 'libs/backbone/backbone-relational',
- 'libs/handlebars.runtime',
- 'mvc/ui',
- 'galaxy.base'
- )}
- <script type="text/javascript">
- // Set up needed paths.
- var galaxy_paths = new GalaxyPaths({
- root_path: '${h.url_for( "/" )}',
- image_path: '${h.url_for( "/static/images" )}',
-
- tool_url: '${h.url_for( controller="/api/tools" )}',
- history_url: '${h.url_for( controller="/api/histories" )}',
-
- datasets_url: '${h.url_for( controller="/api/datasets" )}',
- sweepster_url: '${h.url_for( controller="/visualization", action="sweepster" )}',
- visualization_url: '${h.url_for( controller="/visualization", action="save" )}',
- });
- </script>
-</%def>
-
-## Default late-load javascripts
-<%def name="late_javascripts()">
- ## Scripts can be loaded later since they progressively add features to
- ## the panels, but do not change layout
- ${h.js( 'libs/jquery/jquery.event.drag', 'libs/jquery/jquery.event.hover', 'libs/jquery/jquery.form', 'libs/jquery/jquery.rating', 'galaxy.panels' )}
- <script type="text/javascript">
-
- ensure_dd_helper();
-
- %if self.has_left_panel:
- var lp = new Panel( { panel: $("#left"), center: $("#center"), drag: $("#left > .unified-panel-footer > .drag" ), toggle: $("#left > .unified-panel-footer > .panel-collapse" ) } );
- force_left_panel = function( x ) { lp.force_panel( x ) };
- %endif
-
- %if self.has_right_panel:
- var rp = new Panel( { panel: $("#right"), center: $("#center"), drag: $("#right > .unified-panel-footer > .drag" ), toggle: $("#right > .unified-panel-footer > .panel-collapse" ), right: true } );
- window.handle_minwidth_hint = function( x ) { rp.handle_minwidth_hint( x ) };
- force_right_panel = function( x ) { rp.force_panel( x ) };
- %endif
-
- </script>
- ## Handle AJAX (actually hidden iframe) upload tool
- <![if !IE]>
- <script type="text/javascript">
- var upload_form_error = function( msg ) {
- if ( ! $("iframe#galaxy_main").contents().find("body").find("div[class='errormessage']").size() ) {
- $("iframe#galaxy_main").contents().find("body").prepend( '<div class="errormessage" name="upload_error">' + msg + '</div><p/>' );
- } else {
- $("iframe#galaxy_main").contents().find("body").find("div[class='errormessage']").text( msg );
- }
- }
- var uploads_in_progress = 0;
- jQuery( function() {
- $("iframe#galaxy_main").load( function() {
- $(this).contents().find("form").each( function() {
- if ( $(this).find("input[galaxy-ajax-upload]").length > 0 ){
- $(this).submit( function() {
- // Only bother using a hidden iframe if there's a file (e.g. big data) upload
- var file_upload = false;
- $(this).find("input[galaxy-ajax-upload]").each( function() {
- if ( $(this).val() != '' ) {
- file_upload = true;
- }
- });
- if ( ! file_upload ) {
- return true;
- }
- // Make a synchronous request to create the datasets first
- var async_datasets;
- var upload_error = false;
- $.ajax( {
- async: false,
- type: "POST",
- url: "${h.url_for(controller='/tool_runner', action='upload_async_create')}",
- data: $(this).formSerialize(),
- dataType: "json",
- success: function(array_obj, status) {
- if (array_obj.length > 0) {
- if (array_obj[0] == 'error') {
- upload_error = true;
- upload_form_error(array_obj[1]);
- } else {
- async_datasets = array_obj.join();
- }
- } else {
- // ( gvk 1/22/10 ) FIXME: this block is never entered, so there may be a bug somewhere
- // I've done some debugging like checking to see if array_obj is undefined, but have not
- // tracked down the behavior that will result in this block being entered. I believe the
- // intent was to have this block entered if the upload button is clicked on the upload
- // form but no file was selected.
- upload_error = true;
- upload_form_error( 'No data was entered in the upload form. You may choose to upload a file, paste some data directly in the data box, or enter URL(s) to fetch data.' );
- }
- }
- } );
- if (upload_error == true) {
- return false;
- } else {
- $(this).find("input[name=async_datasets]").val( async_datasets );
- $(this).append("<input type='hidden' name='ajax_upload' value='true'>");
- }
- // iframe submit is required for nginx (otherwise the encoding is wrong)
- $(this).ajaxSubmit( { iframe: true,
- complete: function (xhr, stat) {
- uploads_in_progress--;
- if (uploads_in_progress == 0) {
- window.onbeforeunload = null;
- }
- }
- } );
- uploads_in_progress++;
- window.onbeforeunload = function() { return "Navigating away from the Galaxy analysis interface will interrupt the file upload(s) currently in progress. Do you really want to do this?"; }
- if ( $(this).find("input[name='folder_id']").val() != undefined ) {
- var library_id = $(this).find("input[name='library_id']").val();
- var show_deleted = $(this).find("input[name='show_deleted']").val();
- if ( location.pathname.indexOf( 'admin' ) != -1 ) {
- $("iframe#galaxy_main").attr("src","${h.url_for( controller='library_common', action='browse_library' )}?cntrller=library_admin&id=" + library_id + "&created_ldda_ids=" + async_datasets + "&show_deleted=" + show_deleted);
- } else {
- $("iframe#galaxy_main").attr("src","${h.url_for( controller='library_common', action='browse_library' )}?cntrller=library&id=" + library_id + "&created_ldda_ids=" + async_datasets + "&show_deleted=" + show_deleted);
- }
- } else {
- $("iframe#galaxy_main").attr("src","${h.url_for(controller='tool_runner', action='upload_async_message')}");
- }
- return false;
- });
- }
- });
- });
- });
- </script>
- <![endif]>
-</%def>
-
-## Masthead
-<%def name="masthead()">
- ## Override
-</%def>
-
-<%def name="overlay( title='', content='', visible=False )">
- <%def name="title()"></%def>
- <%def name="content()"></%def>
-
- <%
- if visible:
- display = "style='display: block;'"
- overlay_class = "in"
- else:
- display = "style='display: none;'"
- overlay_class = ""
- %>
-
- <div id="overlay" ${display}>
-
- <div id="overlay-background" class="modal-backdrop fade ${overlay_class}"></div>
-
- <div id="dialog-box" class="modal dialog-box" border="0" ${display}>
- <div class="modal-header">
- <span><h3 class='title'>${title}</h3></span>
- </div>
- <div class="modal-body">${content}</div>
- <div class="modal-footer">
- <div class="buttons" style="float: right;"></div>
- <div class="extra_buttons" style=""></div>
- <div style="clear: both;"></div>
- </div>
- </div>
-
- </div>
-</%def>
-
-## Messagebox
-<%def name="message_box_content()">
-</%def>
-
-## Document
-<html>
- ${self.init()}
- <head>
- <title>${self.title()}</title>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- ## For mobile browsers, don't scale up
- <meta name = "viewport" content = "maximum-scale=1.0">
- ## Force IE to standards mode, and prefer Google Chrome Frame if the user has already installed it
- <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
- ${self.stylesheets()}
- ${self.javascripts()}
- </head>
-
- <body scroll="no" class="${self.body_class}">
- %if self.require_javascript:
- <noscript>
- <div class="overlay overlay-background">
- <div class="modal dialog-box" border="0">
- <div class="modal-header"><h3 class="title">Javascript Required</h3></div>
- <div class="modal-body">The Galaxy analysis interface requires a browser with Javascript enabled. <br> Please enable Javascript and refresh this page</div>
- </div>
- </div>
- </noscript>
- %endif
- <div id="everything" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; min-width: 600px;">
- ## Background displays first
- <div id="background"></div>
- ## Layer iframes over backgrounds
- <div id="masthead" class="navbar navbar-fixed-top">
- <div class="masthead-inner navbar-inner">
- ${self.masthead()}
- </div>
- </div>
- <div id="messagebox" class="panel-${self.message_box_class}-message">
- %if self.message_box_visible:
- ${self.message_box_content()}
- %endif
- </div>
- ${self.overlay(visible=self.overlay_visible)}
- %if self.has_left_panel:
- <div id="left">
- ${self.left_panel()}
- <div class="unified-panel-footer">
- <div class="panel-collapse"></span></div>
- <div class="drag"></div>
- </div>
- </div>
- %endif
- <div id="center">
- ${self.center_panel()}
- </div>
- %if self.has_right_panel:
- <div id="right">
- ${self.right_panel()}
- <div class="unified-panel-footer">
- <div class="panel-collapse right"></span></div>
- <div class="drag"></div>
- </div>
- </div>
- %endif
- </div>
- ## Allow other body level elements
- </body>
- ## Scripts can be loaded later since they progressively add features to
- ## the panels, but do not change layout
- ${self.late_javascripts()}
-</html>
diff -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 -r 578c082d9b46689268aa4a0cd4d29327f84d2791 templates/user/dbkeys.mako
--- a/templates/user/dbkeys.mako
+++ b/templates/user/dbkeys.mako
@@ -1,7 +1,7 @@
<%!
def inherit(context):
if context.get('use_panels'):
- return '/webapps/%s/base_panels.mako' % t.webapp.name
+ return '/base_panels.mako'
else:
return '/base.mako'
%>
diff -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 -r 578c082d9b46689268aa4a0cd4d29327f84d2791 templates/user/login.mako
--- a/templates/user/login.mako
+++ b/templates/user/login.mako
@@ -1,7 +1,7 @@
<%!
def inherit(context):
if context.get('use_panels'):
- return '/webapps/%s/base_panels.mako' % context.get('t').webapp.name
+ return '/base_panels.mako'
else:
return '/base.mako'
%>
diff -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 -r 578c082d9b46689268aa4a0cd4d29327f84d2791 templates/user/logout.mako
--- a/templates/user/logout.mako
+++ b/templates/user/logout.mako
@@ -1,8 +1,4 @@
-<%!
- def inherit(context):
- return '/webapps/%s/base_panels.mako' % context.get('t').webapp.name
-%>
-<%inherit file="${inherit(context)}"/>
+<%inherit file="/base_panels.mako"/><%namespace file="/message.mako" import="render_msg" />
diff -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 -r 578c082d9b46689268aa4a0cd4d29327f84d2791 templates/user/openid_associate.mako
--- a/templates/user/openid_associate.mako
+++ b/templates/user/openid_associate.mako
@@ -1,7 +1,7 @@
<%!
def inherit(context):
if context.get('use_panels'):
- return '/webapps/%s/base_panels.mako' % t.webapp.name
+ return '/base_panels.mako'
else:
return '/base.mako'
%>
diff -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 -r 578c082d9b46689268aa4a0cd4d29327f84d2791 templates/webapps/community/base_panels.mako
--- a/templates/webapps/community/base_panels.mako
+++ b/templates/webapps/community/base_panels.mako
@@ -1,4 +1,4 @@
-<%inherit file="/base_panels.mako"/>
+<%inherit file="/base/base_panels.mako"/>
## Default title
<%def name="title()">Galaxy Tool Shed</%def>
diff -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 -r 578c082d9b46689268aa4a0cd4d29327f84d2791 templates/webapps/galaxy/base_panels.mako
--- a/templates/webapps/galaxy/base_panels.mako
+++ b/templates/webapps/galaxy/base_panels.mako
@@ -1,4 +1,4 @@
-<%inherit file="/base_panels.mako"/>
+<%inherit file="/base/base_panels.mako"/>
## Default title
<%def name="title()">Galaxy</%def>
diff -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 -r 578c082d9b46689268aa4a0cd4d29327f84d2791 templates/webapps/reports/base_panels.mako
--- a/templates/webapps/reports/base_panels.mako
+++ b/templates/webapps/reports/base_panels.mako
@@ -1,4 +1,4 @@
-<%inherit file="/base_panels.mako"/>
+<%inherit file="/base/base_panels.mako"/>
## Default title
<%def name="title()">Galaxy Reports</%def>
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.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/2ca0e99d6d6d/
changeset: 2ca0e99d6d6d
user: james_taylor
date: 2012-09-28 23:31:30
summary: Pull base Admin controller out into its own module, begin removing as much gratuitous passing around of the webapp parameter as possible
affected #: 20 files
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r 2ca0e99d6d6d940af0712e9c56b9f238fd59635c lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -1525,1191 +1525,8 @@
class ControllerUnavailable( Exception ):
pass
-class Admin( object ):
- # Override these
- user_list_grid = None
- role_list_grid = None
- group_list_grid = None
- quota_list_grid = None
- repository_list_grid = None
- tool_version_list_grid = None
- delete_operation = None
- undelete_operation = None
- purge_operation = None
-
- @web.expose
- @web.require_admin
- def index( self, trans, **kwd ):
- webapp = get_webapp( trans, **kwd )
- message = kwd.get( 'message', '' )
- status = kwd.get( 'status', 'done' )
- if webapp == 'galaxy':
- installed_repositories = trans.sa_session.query( trans.model.ToolShedRepository ).first()
- installing_repository_ids = get_ids_of_tool_shed_repositories_being_installed( trans, as_string=True )
- return trans.fill_template( '/webapps/galaxy/admin/index.mako',
- webapp=webapp,
- installed_repositories=installed_repositories,
- installing_repository_ids=installing_repository_ids,
- message=message,
- status=status )
- else:
- return trans.fill_template( '/webapps/community/admin/index.mako',
- webapp=webapp,
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def center( self, trans, **kwd ):
- webapp = get_webapp( trans, **kwd )
- message = kwd.get( 'message', '' )
- status = kwd.get( 'status', 'done' )
- if webapp == 'galaxy':
- return trans.fill_template( '/webapps/galaxy/admin/center.mako',
- message=message,
- status=status )
- else:
- return trans.fill_template( '/webapps/community/admin/center.mako',
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def reload_tool( self, trans, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- toolbox = self.app.toolbox
- if params.get( 'reload_tool_button', False ):
- tool_id = params.tool_id
- message, status = toolbox.reload_tool_by_id( tool_id )
- return trans.fill_template( '/admin/reload_tool.mako',
- toolbox=toolbox,
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def tool_versions( self, trans, **kwd ):
- if 'message' not in kwd or not kwd[ 'message' ]:
- kwd[ 'message' ] = 'Tool ids for tools that are currently loaded into the tool panel are highlighted in green (click to display).'
- return self.tool_version_list_grid( trans, **kwd )
- # Galaxy Role Stuff
- @web.expose
- @web.require_admin
- def roles( self, trans, **kwargs ):
- if 'operation' in kwargs:
- operation = kwargs['operation'].lower()
- if operation == "roles":
- return self.role( trans, **kwargs )
- if operation == "create":
- return self.create_role( trans, **kwargs )
- if operation == "delete":
- return self.mark_role_deleted( trans, **kwargs )
- if operation == "undelete":
- return self.undelete_role( trans, **kwargs )
- if operation == "purge":
- return self.purge_role( trans, **kwargs )
- if operation == "manage users and groups":
- return self.manage_users_and_groups_for_role( trans, **kwargs )
- if operation == "rename":
- return self.rename_role( trans, **kwargs )
- # Render the list view
- return self.role_list_grid( trans, **kwargs )
- @web.expose
- @web.require_admin
- def create_role( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- name = util.restore_text( params.get( 'name', '' ) )
- description = util.restore_text( params.get( 'description', '' ) )
- in_users = util.listify( params.get( 'in_users', [] ) )
- out_users = util.listify( params.get( 'out_users', [] ) )
- in_groups = util.listify( params.get( 'in_groups', [] ) )
- out_groups = util.listify( params.get( 'out_groups', [] ) )
- create_group_for_role = params.get( 'create_group_for_role', '' )
- create_group_for_role_checked = CheckboxField.is_checked( create_group_for_role )
- ok = True
- if params.get( 'create_role_button', False ):
- if not name or not description:
- message = "Enter a valid name and a description."
- status = 'error'
- ok = False
- elif trans.sa_session.query( trans.app.model.Role ).filter( trans.app.model.Role.table.c.name==name ).first():
- message = "Role names must be unique and a role with that name already exists, so choose another name."
- status = 'error'
- ok = False
- else:
- # Create the role
- role = trans.app.model.Role( name=name, description=description, type=trans.app.model.Role.types.ADMIN )
- trans.sa_session.add( role )
- # Create the UserRoleAssociations
- for user in [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in in_users ]:
- ura = trans.app.model.UserRoleAssociation( user, role )
- trans.sa_session.add( ura )
- # Create the GroupRoleAssociations
- for group in [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in in_groups ]:
- gra = trans.app.model.GroupRoleAssociation( group, role )
- trans.sa_session.add( gra )
- if create_group_for_role_checked:
- # Create the group
- group = trans.app.model.Group( name=name )
- trans.sa_session.add( group )
- # Associate the group with the role
- gra = trans.model.GroupRoleAssociation( group, role )
- trans.sa_session.add( gra )
- num_in_groups = len( in_groups ) + 1
- else:
- num_in_groups = len( in_groups )
- trans.sa_session.flush()
- message = "Role '%s' has been created with %d associated users and %d associated groups. " \
- % ( role.name, len( in_users ), num_in_groups )
- if create_group_for_role_checked:
- message += 'One of the groups associated with this role is the newly created group with the same name.'
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- if ok:
- for user in trans.sa_session.query( trans.app.model.User ) \
- .filter( trans.app.model.User.table.c.deleted==False ) \
- .order_by( trans.app.model.User.table.c.email ):
- out_users.append( ( user.id, user.email ) )
- for group in trans.sa_session.query( trans.app.model.Group ) \
- .filter( trans.app.model.Group.table.c.deleted==False ) \
- .order_by( trans.app.model.Group.table.c.name ):
- out_groups.append( ( group.id, group.name ) )
- return trans.fill_template( '/admin/dataset_security/role/role_create.mako',
- webapp=webapp,
- name=name,
- description=description,
- in_users=in_users,
- out_users=out_users,
- in_groups=in_groups,
- out_groups=out_groups,
- create_group_for_role_checked=create_group_for_role_checked,
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def rename_role( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- id = params.get( 'id', None )
- if not id:
- message = "No role ids received for renaming"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=message,
- status='error' ) )
- role = get_role( trans, id )
- if params.get( 'rename_role_button', False ):
- old_name = role.name
- new_name = util.restore_text( params.name )
- new_description = util.restore_text( params.description )
- if not new_name:
- message = 'Enter a valid name'
- status='error'
- else:
- existing_role = trans.sa_session.query( trans.app.model.Role ).filter( trans.app.model.Role.table.c.name==new_name ).first()
- if existing_role and existing_role.id != role.id:
- message = 'A role with that name already exists'
- status = 'error'
- else:
- if not ( role.name == new_name and role.description == new_description ):
- role.name = new_name
- role.description = new_description
- trans.sa_session.add( role )
- trans.sa_session.flush()
- message = "Role '%s' has been renamed to '%s'" % ( old_name, new_name )
- return trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- return trans.fill_template( '/admin/dataset_security/role/role_rename.mako',
- role=role,
- webapp=webapp,
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def manage_users_and_groups_for_role( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- id = params.get( 'id', None )
- if not id:
- message = "No role ids received for managing users and groups"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=message,
- status='error' ) )
- role = get_role( trans, id )
- if params.get( 'role_members_edit_button', False ):
- in_users = [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in util.listify( params.in_users ) ]
- for ura in role.users:
- user = trans.sa_session.query( trans.app.model.User ).get( ura.user_id )
- if user not in in_users:
- # Delete DefaultUserPermissions for previously associated users that have been removed from the role
- for dup in user.default_permissions:
- if role == dup.role:
- trans.sa_session.delete( dup )
- # Delete DefaultHistoryPermissions for previously associated users that have been removed from the role
- for history in user.histories:
- for dhp in history.default_permissions:
- if role == dhp.role:
- trans.sa_session.delete( dhp )
- trans.sa_session.flush()
- in_groups = [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in util.listify( params.in_groups ) ]
- trans.app.security_agent.set_entity_role_associations( roles=[ role ], users=in_users, groups=in_groups )
- trans.sa_session.refresh( role )
- message = "Role '%s' has been updated with %d associated users and %d associated groups" % ( role.name, len( in_users ), len( in_groups ) )
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status=status ) )
- in_users = []
- out_users = []
- in_groups = []
- out_groups = []
- for user in trans.sa_session.query( trans.app.model.User ) \
- .filter( trans.app.model.User.table.c.deleted==False ) \
- .order_by( trans.app.model.User.table.c.email ):
- if user in [ x.user for x in role.users ]:
- in_users.append( ( user.id, user.email ) )
- else:
- out_users.append( ( user.id, user.email ) )
- for group in trans.sa_session.query( trans.app.model.Group ) \
- .filter( trans.app.model.Group.table.c.deleted==False ) \
- .order_by( trans.app.model.Group.table.c.name ):
- if group in [ x.group for x in role.groups ]:
- in_groups.append( ( group.id, group.name ) )
- else:
- out_groups.append( ( group.id, group.name ) )
- library_dataset_actions = {}
- if webapp == 'galaxy':
- # Build a list of tuples that are LibraryDatasetDatasetAssociationss followed by a list of actions
- # whose DatasetPermissions is associated with the Role
- # [ ( LibraryDatasetDatasetAssociation [ action, action ] ) ]
- for dp in role.dataset_actions:
- for ldda in trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ) \
- .filter( trans.app.model.LibraryDatasetDatasetAssociation.dataset_id==dp.dataset_id ):
- root_found = False
- folder_path = ''
- folder = ldda.library_dataset.folder
- while not root_found:
- folder_path = '%s / %s' % ( folder.name, folder_path )
- if not folder.parent:
- root_found = True
- else:
- folder = folder.parent
- folder_path = '%s %s' % ( folder_path, ldda.name )
- library = trans.sa_session.query( trans.app.model.Library ) \
- .filter( trans.app.model.Library.table.c.root_folder_id == folder.id ) \
- .first()
- if library not in library_dataset_actions:
- library_dataset_actions[ library ] = {}
- try:
- library_dataset_actions[ library ][ folder_path ].append( dp.action )
- except:
- library_dataset_actions[ library ][ folder_path ] = [ dp.action ]
- return trans.fill_template( '/admin/dataset_security/role/role.mako',
- role=role,
- in_users=in_users,
- out_users=out_users,
- in_groups=in_groups,
- out_groups=out_groups,
- library_dataset_actions=library_dataset_actions,
- webapp=webapp,
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def mark_role_deleted( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- id = kwd.get( 'id', None )
- if not id:
- message = "No role ids received for deleting"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=message,
- status='error' ) )
- ids = util.listify( id )
- message = "Deleted %d roles: " % len( ids )
- for role_id in ids:
- role = get_role( trans, role_id )
- role.deleted = True
- trans.sa_session.add( role )
- trans.sa_session.flush()
- message += " %s " % role.name
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- @web.expose
- @web.require_admin
- def undelete_role( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- id = kwd.get( 'id', None )
- if not id:
- message = "No role ids received for undeleting"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=message,
- status='error' ) )
- ids = util.listify( id )
- count = 0
- undeleted_roles = ""
- for role_id in ids:
- role = get_role( trans, role_id )
- if not role.deleted:
- message = "Role '%s' has not been deleted, so it cannot be undeleted." % role.name
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- role.deleted = False
- trans.sa_session.add( role )
- trans.sa_session.flush()
- count += 1
- undeleted_roles += " %s" % role.name
- message = "Undeleted %d roles: %s" % ( count, undeleted_roles )
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- @web.expose
- @web.require_admin
- def purge_role( self, trans, **kwd ):
- # This method should only be called for a Role that has previously been deleted.
- # Purging a deleted Role deletes all of the following from the database:
- # - UserRoleAssociations where role_id == Role.id
- # - DefaultUserPermissions where role_id == Role.id
- # - DefaultHistoryPermissions where role_id == Role.id
- # - GroupRoleAssociations where role_id == Role.id
- # - DatasetPermissionss where role_id == Role.id
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- id = kwd.get( 'id', None )
- if not id:
- message = "No role ids received for purging"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- ids = util.listify( id )
- message = "Purged %d roles: " % len( ids )
- for role_id in ids:
- role = get_role( trans, role_id )
- if not role.deleted:
- message = "Role '%s' has not been deleted, so it cannot be purged." % role.name
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- # Delete UserRoleAssociations
- for ura in role.users:
- user = trans.sa_session.query( trans.app.model.User ).get( ura.user_id )
- # Delete DefaultUserPermissions for associated users
- for dup in user.default_permissions:
- if role == dup.role:
- trans.sa_session.delete( dup )
- # Delete DefaultHistoryPermissions for associated users
- for history in user.histories:
- for dhp in history.default_permissions:
- if role == dhp.role:
- trans.sa_session.delete( dhp )
- trans.sa_session.delete( ura )
- # Delete GroupRoleAssociations
- for gra in role.groups:
- trans.sa_session.delete( gra )
- # Delete DatasetPermissionss
- for dp in role.dataset_actions:
- trans.sa_session.delete( dp )
- trans.sa_session.flush()
- message += " %s " % role.name
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
-
- # Galaxy Group Stuff
- @web.expose
- @web.require_admin
- def groups( self, trans, **kwargs ):
- if 'operation' in kwargs:
- operation = kwargs['operation'].lower()
- if operation == "groups":
- return self.group( trans, **kwargs )
- if operation == "create":
- return self.create_group( trans, **kwargs )
- if operation == "delete":
- return self.mark_group_deleted( trans, **kwargs )
- if operation == "undelete":
- return self.undelete_group( trans, **kwargs )
- if operation == "purge":
- return self.purge_group( trans, **kwargs )
- if operation == "manage users and roles":
- return self.manage_users_and_roles_for_group( trans, **kwargs )
- if operation == "rename":
- return self.rename_group( trans, **kwargs )
- # Render the list view
- return self.group_list_grid( trans, **kwargs )
- @web.expose
- @web.require_admin
- def rename_group( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- id = params.get( 'id', None )
- if not id:
- message = "No group ids received for renaming"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=message,
- status='error' ) )
- group = get_group( trans, id )
- if params.get( 'rename_group_button', False ):
- old_name = group.name
- new_name = util.restore_text( params.name )
- if not new_name:
- message = 'Enter a valid name'
- status = 'error'
- else:
- existing_group = trans.sa_session.query( trans.app.model.Group ).filter( trans.app.model.Group.table.c.name==new_name ).first()
- if existing_group and existing_group.id != group.id:
- message = 'A group with that name already exists'
- status = 'error'
- else:
- if group.name != new_name:
- group.name = new_name
- trans.sa_session.add( group )
- trans.sa_session.flush()
- message = "Group '%s' has been renamed to '%s'" % ( old_name, new_name )
- return trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- return trans.fill_template( '/admin/dataset_security/group/group_rename.mako',
- group=group,
- webapp=webapp,
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def manage_users_and_roles_for_group( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- group = get_group( trans, params.id )
- if params.get( 'group_roles_users_edit_button', False ):
- in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( params.in_roles ) ]
- in_users = [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in util.listify( params.in_users ) ]
- trans.app.security_agent.set_entity_group_associations( groups=[ group ], roles=in_roles, users=in_users )
- trans.sa_session.refresh( group )
- message += "Group '%s' has been updated with %d associated roles and %d associated users" % ( group.name, len( in_roles ), len( in_users ) )
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status=status ) )
- in_roles = []
- out_roles = []
- in_users = []
- out_users = []
- for role in trans.sa_session.query(trans.app.model.Role ) \
- .filter( trans.app.model.Role.table.c.deleted==False ) \
- .order_by( trans.app.model.Role.table.c.name ):
- if role in [ x.role for x in group.roles ]:
- in_roles.append( ( role.id, role.name ) )
- else:
- out_roles.append( ( role.id, role.name ) )
- for user in trans.sa_session.query( trans.app.model.User ) \
- .filter( trans.app.model.User.table.c.deleted==False ) \
- .order_by( trans.app.model.User.table.c.email ):
- if user in [ x.user for x in group.users ]:
- in_users.append( ( user.id, user.email ) )
- else:
- out_users.append( ( user.id, user.email ) )
- message += 'Group %s is currently associated with %d roles and %d users' % ( group.name, len( in_roles ), len( in_users ) )
- return trans.fill_template( '/admin/dataset_security/group/group.mako',
- group=group,
- in_roles=in_roles,
- out_roles=out_roles,
- in_users=in_users,
- out_users=out_users,
- webapp=webapp,
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def create_group( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- name = util.restore_text( params.get( 'name', '' ) )
- in_users = util.listify( params.get( 'in_users', [] ) )
- out_users = util.listify( params.get( 'out_users', [] ) )
- in_roles = util.listify( params.get( 'in_roles', [] ) )
- out_roles = util.listify( params.get( 'out_roles', [] ) )
- create_role_for_group = params.get( 'create_role_for_group', '' )
- create_role_for_group_checked = CheckboxField.is_checked( create_role_for_group )
- ok = True
- if params.get( 'create_group_button', False ):
- if not name:
- message = "Enter a valid name."
- status = 'error'
- ok = False
- elif trans.sa_session.query( trans.app.model.Group ).filter( trans.app.model.Group.table.c.name==name ).first():
- message = "Group names must be unique and a group with that name already exists, so choose another name."
- status = 'error'
- ok = False
- else:
- # Create the group
- group = trans.app.model.Group( name=name )
- trans.sa_session.add( group )
- trans.sa_session.flush()
- # Create the UserRoleAssociations
- for user in [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in in_users ]:
- uga = trans.app.model.UserGroupAssociation( user, group )
- trans.sa_session.add( uga )
- # Create the GroupRoleAssociations
- for role in [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in in_roles ]:
- gra = trans.app.model.GroupRoleAssociation( group, role )
- trans.sa_session.add( gra )
- if create_role_for_group_checked:
- # Create the role
- role = trans.app.model.Role( name=name, description='Role for group %s' % name )
- trans.sa_session.add( role )
- # Associate the role with the group
- gra = trans.model.GroupRoleAssociation( group, role )
- trans.sa_session.add( gra )
- num_in_roles = len( in_roles ) + 1
- else:
- num_in_roles = len( in_roles )
- trans.sa_session.flush()
- message = "Group '%s' has been created with %d associated users and %d associated roles. " \
- % ( group.name, len( in_users ), num_in_roles )
- if create_role_for_group_checked:
- message += 'One of the roles associated with this group is the newly created role with the same name.'
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
-
-
- if ok:
- for user in trans.sa_session.query( trans.app.model.User ) \
- .filter( trans.app.model.User.table.c.deleted==False ) \
- .order_by( trans.app.model.User.table.c.email ):
- out_users.append( ( user.id, user.email ) )
- for role in trans.sa_session.query( trans.app.model.Role ) \
- .filter( trans.app.model.Role.table.c.deleted==False ) \
- .order_by( trans.app.model.Role.table.c.name ):
- out_roles.append( ( role.id, role.name ) )
- return trans.fill_template( '/admin/dataset_security/group/group_create.mako',
- webapp=webapp,
- name=name,
- in_users=in_users,
- out_users=out_users,
- in_roles=in_roles,
- out_roles=out_roles,
- create_role_for_group_checked=create_role_for_group_checked,
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def mark_group_deleted( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- id = params.get( 'id', None )
- if not id:
- message = "No group ids received for marking deleted"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=message,
- status='error' ) )
- ids = util.listify( id )
- message = "Deleted %d groups: " % len( ids )
- for group_id in ids:
- group = get_group( trans, group_id )
- group.deleted = True
- trans.sa_session.add( group )
- trans.sa_session.flush()
- message += " %s " % group.name
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- @web.expose
- @web.require_admin
- def undelete_group( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- id = kwd.get( 'id', None )
- if not id:
- message = "No group ids received for undeleting"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=message,
- status='error' ) )
- ids = util.listify( id )
- count = 0
- undeleted_groups = ""
- for group_id in ids:
- group = get_group( trans, group_id )
- if not group.deleted:
- message = "Group '%s' has not been deleted, so it cannot be undeleted." % group.name
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- group.deleted = False
- trans.sa_session.add( group )
- trans.sa_session.flush()
- count += 1
- undeleted_groups += " %s" % group.name
- message = "Undeleted %d groups: %s" % ( count, undeleted_groups )
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- @web.expose
- @web.require_admin
- def purge_group( self, trans, **kwd ):
- # This method should only be called for a Group that has previously been deleted.
- # Purging a deleted Group simply deletes all UserGroupAssociations and GroupRoleAssociations.
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- id = kwd.get( 'id', None )
- if not id:
- message = "No group ids received for purging"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- ids = util.listify( id )
- message = "Purged %d groups: " % len( ids )
- for group_id in ids:
- group = get_group( trans, group_id )
- if not group.deleted:
- # We should never reach here, but just in case there is a bug somewhere...
- message = "Group '%s' has not been deleted, so it cannot be purged." % group.name
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- # Delete UserGroupAssociations
- for uga in group.users:
- trans.sa_session.delete( uga )
- # Delete GroupRoleAssociations
- for gra in group.roles:
- trans.sa_session.delete( gra )
- trans.sa_session.flush()
- message += " %s " % group.name
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
-
- # Galaxy User Stuff
- @web.expose
- @web.require_admin
- def create_new_user( self, trans, **kwd ):
- webapp = get_webapp( trans, **kwd )
- return trans.response.send_redirect( web.url_for( controller='user',
- action='create',
- cntrller='admin',
- webapp=webapp ) )
- @web.expose
- @web.require_admin
- def reset_user_password( self, trans, **kwd ):
- webapp = get_webapp( trans, **kwd )
- user_id = kwd.get( 'id', None )
- if not user_id:
- message = "No users received for resetting passwords."
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=message,
- status='error' ) )
- user_ids = util.listify( user_id )
- if 'reset_user_password_button' in kwd:
- message = ''
- status = ''
- for user_id in user_ids:
- user = get_user( trans, user_id )
- password = kwd.get( 'password', None )
- confirm = kwd.get( 'confirm' , None )
- if len( password ) < 6:
- message = "Use a password of at least 6 characters."
- status = 'error'
- break
- elif password != confirm:
- message = "Passwords do not match."
- status = 'error'
- break
- else:
- user.set_password_cleartext( password )
- trans.sa_session.add( user )
- trans.sa_session.flush()
- if not message and not status:
- message = "Passwords reset for %d %s." % ( len( user_ids ), inflector.cond_plural( len( user_ids ), 'user' ) )
- status = 'done'
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status=status ) )
- users = [ get_user( trans, user_id ) for user_id in user_ids ]
- if len( user_ids ) > 1:
- user_id = ','.join( user_ids )
- return trans.fill_template( '/admin/user/reset_password.mako',
- id=user_id,
- users=users,
- password='',
- confirm='',
- webapp=webapp )
- @web.expose
- @web.require_admin
- def mark_user_deleted( self, trans, **kwd ):
- webapp = get_webapp( trans, **kwd )
- id = kwd.get( 'id', None )
- if not id:
- message = "No user ids received for deleting"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=message,
- status='error' ) )
- ids = util.listify( id )
- message = "Deleted %d users: " % len( ids )
- for user_id in ids:
- user = get_user( trans, user_id )
- user.deleted = True
- trans.sa_session.add( user )
- trans.sa_session.flush()
- message += " %s " % user.email
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- @web.expose
- @web.require_admin
- def undelete_user( self, trans, **kwd ):
- webapp = get_webapp( trans, **kwd )
- id = kwd.get( 'id', None )
- if not id:
- message = "No user ids received for undeleting"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=message,
- status='error' ) )
- ids = util.listify( id )
- count = 0
- undeleted_users = ""
- for user_id in ids:
- user = get_user( trans, user_id )
- if not user.deleted:
- message = "User '%s' has not been deleted, so it cannot be undeleted." % user.email
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- user.deleted = False
- trans.sa_session.add( user )
- trans.sa_session.flush()
- count += 1
- undeleted_users += " %s" % user.email
- message = "Undeleted %d users: %s" % ( count, undeleted_users )
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- @web.expose
- @web.require_admin
- def purge_user( self, trans, **kwd ):
- # This method should only be called for a User that has previously been deleted.
- # We keep the User in the database ( marked as purged ), and stuff associated
- # with the user's private role in case we want the ability to unpurge the user
- # some time in the future.
- # Purging a deleted User deletes all of the following:
- # - History where user_id = User.id
- # - HistoryDatasetAssociation where history_id = History.id
- # - Dataset where HistoryDatasetAssociation.dataset_id = Dataset.id
- # - UserGroupAssociation where user_id == User.id
- # - UserRoleAssociation where user_id == User.id EXCEPT FOR THE PRIVATE ROLE
- # - UserAddress where user_id == User.id
- # Purging Histories and Datasets must be handled via the cleanup_datasets.py script
- webapp = get_webapp( trans, **kwd )
- id = kwd.get( 'id', None )
- if not id:
- message = "No user ids received for purging"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- ids = util.listify( id )
- message = "Purged %d users: " % len( ids )
- for user_id in ids:
- user = get_user( trans, user_id )
- if not user.deleted:
- # We should never reach here, but just in case there is a bug somewhere...
- message = "User '%s' has not been deleted, so it cannot be purged." % user.email
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- private_role = trans.app.security_agent.get_private_user_role( user )
- # Delete History
- for h in user.active_histories:
- trans.sa_session.refresh( h )
- for hda in h.active_datasets:
- # Delete HistoryDatasetAssociation
- d = trans.sa_session.query( trans.app.model.Dataset ).get( hda.dataset_id )
- # Delete Dataset
- if not d.deleted:
- d.deleted = True
- trans.sa_session.add( d )
- hda.deleted = True
- trans.sa_session.add( hda )
- h.deleted = True
- trans.sa_session.add( h )
- # Delete UserGroupAssociations
- for uga in user.groups:
- trans.sa_session.delete( uga )
- # Delete UserRoleAssociations EXCEPT FOR THE PRIVATE ROLE
- for ura in user.roles:
- if ura.role_id != private_role.id:
- trans.sa_session.delete( ura )
- # Delete UserAddresses
- for address in user.addresses:
- trans.sa_session.delete( address )
- # Purge the user
- user.purged = True
- trans.sa_session.add( user )
- trans.sa_session.flush()
- message += "%s " % user.email
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- @web.expose
- @web.require_admin
- def users( self, trans, **kwd ):
- if 'operation' in kwd:
- operation = kwd['operation'].lower()
- if operation == "roles":
- return self.user( trans, **kwd )
- elif operation == "reset password":
- return self.reset_user_password( trans, **kwd )
- elif operation == "delete":
- return self.mark_user_deleted( trans, **kwd )
- elif operation == "undelete":
- return self.undelete_user( trans, **kwd )
- elif operation == "purge":
- return self.purge_user( trans, **kwd )
- elif operation == "create":
- return self.create_new_user( trans, **kwd )
- elif operation == "information":
- user_id = kwd.get( 'id', None )
- if not user_id:
- kwd[ 'message' ] = util.sanitize_text( "Invalid user id (%s) received" % str( user_id ) )
- kwd[ 'status' ] = 'error'
- else:
- return trans.response.send_redirect( web.url_for( controller='user',
- action='manage_user_info',
- cntrller='admin',
- **kwd ) )
- elif operation == "manage roles and groups":
- return self.manage_roles_and_groups_for_user( trans, **kwd )
- if trans.app.config.allow_user_deletion:
- if self.delete_operation not in self.user_list_grid.operations:
- self.user_list_grid.operations.append( self.delete_operation )
- if self.undelete_operation not in self.user_list_grid.operations:
- self.user_list_grid.operations.append( self.undelete_operation )
- if self.purge_operation not in self.user_list_grid.operations:
- self.user_list_grid.operations.append( self.purge_operation )
- # Render the list view
- return self.user_list_grid( trans, **kwd )
- @web.expose
- @web.require_admin
- def name_autocomplete_data( self, trans, q=None, limit=None, timestamp=None ):
- """Return autocomplete data for user emails"""
- ac_data = ""
- for user in trans.sa_session.query( User ).filter_by( deleted=False ).filter( func.lower( User.email ).like( q.lower() + "%" ) ):
- ac_data = ac_data + user.email + "\n"
- return ac_data
- @web.expose
- @web.require_admin
- def manage_roles_and_groups_for_user( self, trans, **kwd ):
- webapp = get_webapp( trans, **kwd )
- user_id = kwd.get( 'id', None )
- message = ''
- status = ''
- if not user_id:
- message += "Invalid user id (%s) received" % str( user_id )
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- user = get_user( trans, user_id )
- private_role = trans.app.security_agent.get_private_user_role( user )
- if kwd.get( 'user_roles_groups_edit_button', False ):
- # Make sure the user is not dis-associating himself from his private role
- out_roles = kwd.get( 'out_roles', [] )
- if out_roles:
- out_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( out_roles ) ]
- if private_role in out_roles:
- message += "You cannot eliminate a user's private role association. "
- status = 'error'
- in_roles = kwd.get( 'in_roles', [] )
- if in_roles:
- in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( in_roles ) ]
- out_groups = kwd.get( 'out_groups', [] )
- if out_groups:
- out_groups = [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in util.listify( out_groups ) ]
- in_groups = kwd.get( 'in_groups', [] )
- if in_groups:
- in_groups = [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in util.listify( in_groups ) ]
- if in_roles:
- trans.app.security_agent.set_entity_user_associations( users=[ user ], roles=in_roles, groups=in_groups )
- trans.sa_session.refresh( user )
- message += "User '%s' has been updated with %d associated roles and %d associated groups (private roles are not displayed)" % \
- ( user.email, len( in_roles ), len( in_groups ) )
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- in_roles = []
- out_roles = []
- in_groups = []
- out_groups = []
- for role in trans.sa_session.query( trans.app.model.Role ).filter( trans.app.model.Role.table.c.deleted==False ) \
- .order_by( trans.app.model.Role.table.c.name ):
- if role in [ x.role for x in user.roles ]:
- in_roles.append( ( role.id, role.name ) )
- elif role.type != trans.app.model.Role.types.PRIVATE:
- # There is a 1 to 1 mapping between a user and a PRIVATE role, so private roles should
- # not be listed in the roles form fields, except for the currently selected user's private
- # role, which should always be in in_roles. The check above is added as an additional
- # precaution, since for a period of time we were including private roles in the form fields.
- out_roles.append( ( role.id, role.name ) )
- for group in trans.sa_session.query( trans.app.model.Group ).filter( trans.app.model.Group.table.c.deleted==False ) \
- .order_by( trans.app.model.Group.table.c.name ):
- if group in [ x.group for x in user.groups ]:
- in_groups.append( ( group.id, group.name ) )
- else:
- out_groups.append( ( group.id, group.name ) )
- message += "User '%s' is currently associated with %d roles and is a member of %d groups" % \
- ( user.email, len( in_roles ), len( in_groups ) )
- if not status:
- status = 'done'
- return trans.fill_template( '/admin/user/user.mako',
- user=user,
- in_roles=in_roles,
- out_roles=out_roles,
- in_groups=in_groups,
- out_groups=out_groups,
- webapp=webapp,
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def memdump( self, trans, ids = 'None', sorts = 'None', pages = 'None', new_id = None, new_sort = None, **kwd ):
- if self.app.memdump is None:
- return trans.show_error_message( "Memdump is not enabled (set <code>use_memdump = True</code> in universe_wsgi.ini)" )
- heap = self.app.memdump.get()
- p = util.Params( kwd )
- msg = None
- if p.dump:
- heap = self.app.memdump.get( update = True )
- msg = "Heap dump complete"
- elif p.setref:
- self.app.memdump.setref()
- msg = "Reference point set (dump to see delta from this point)"
- ids = ids.split( ',' )
- sorts = sorts.split( ',' )
- if new_id is not None:
- ids.append( new_id )
- sorts.append( 'None' )
- elif new_sort is not None:
- sorts[-1] = new_sort
- breadcrumb = "<a href='%s' class='breadcrumb'>heap</a>" % web.url_for()
- # new lists so we can assemble breadcrumb links
- new_ids = []
- new_sorts = []
- for id, sort in zip( ids, sorts ):
- new_ids.append( id )
- if id != 'None':
- breadcrumb += "<a href='%s' class='breadcrumb'>[%s]</a>" % ( web.url_for( ids=','.join( new_ids ), sorts=','.join( new_sorts ) ), id )
- heap = heap[int(id)]
- new_sorts.append( sort )
- if sort != 'None':
- breadcrumb += "<a href='%s' class='breadcrumb'>.by('%s')</a>" % ( web.url_for( ids=','.join( new_ids ), sorts=','.join( new_sorts ) ), sort )
- heap = heap.by( sort )
- ids = ','.join( new_ids )
- sorts = ','.join( new_sorts )
- if p.theone:
- breadcrumb += ".theone"
- heap = heap.theone
- return trans.fill_template( '/admin/memdump.mako', heap = heap, ids = ids, sorts = sorts, breadcrumb = breadcrumb, msg = msg )
-
- @web.expose
- @web.require_admin
- def jobs( self, trans, stop = [], stop_msg = None, cutoff = 180, job_lock = None, ajl_submit = None, **kwd ):
- deleted = []
- msg = None
- status = None
- if self.app.config.job_manager != self.app.config.server_name:
- return trans.show_error_message( 'This Galaxy instance (%s) is not the job manager (%s). If using multiple servers, please directly access the job manager instance to manage jobs.' % (self.app.config.server_name, self.app.config.job_manager) )
- job_ids = util.listify( stop )
- if job_ids and stop_msg in [ None, '' ]:
- msg = 'Please enter an error message to display to the user describing why the job was terminated'
- status = 'error'
- elif job_ids:
- if stop_msg[-1] not in string.punctuation:
- stop_msg += '.'
- for job_id in job_ids:
- trans.app.job_manager.job_stop_queue.put( job_id, error_msg="This job was stopped by an administrator: %s For more information or help" % stop_msg )
- deleted.append( str( job_id ) )
- if deleted:
- msg = 'Queued job'
- if len( deleted ) > 1:
- msg += 's'
- msg += ' for deletion: '
- msg += ', '.join( deleted )
- status = 'done'
- if ajl_submit:
- if job_lock == 'on':
- trans.app.job_manager.job_queue.job_lock = True
- else:
- trans.app.job_manager.job_queue.job_lock = False
- cutoff_time = datetime.utcnow() - timedelta( seconds=int( cutoff ) )
- 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.NEW,
- 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() )
- last_updated = {}
- for job in jobs:
- delta = datetime.utcnow() - job.update_time
- if delta > timedelta( minutes=60 ):
- last_updated[job.id] = '%s hours' % int( delta.seconds / 60 / 60 )
- else:
- last_updated[job.id] = '%s minutes' % int( delta.seconds / 60 )
- return trans.fill_template( '/admin/jobs.mako',
- jobs = jobs,
- last_updated = last_updated,
- cutoff = cutoff,
- msg = msg,
- status = status,
- job_lock = trans.app.job_manager.job_queue.job_lock )
-
## ---- Utility methods -------------------------------------------------------
-def get_ids_of_tool_shed_repositories_being_installed( trans, as_string=False ):
- installing_repository_ids = []
- new_status = trans.model.ToolShedRepository.installation_status.NEW
- cloning_status = trans.model.ToolShedRepository.installation_status.CLONING
- setting_tool_versions_status = trans.model.ToolShedRepository.installation_status.SETTING_TOOL_VERSIONS
- installing_dependencies_status = trans.model.ToolShedRepository.installation_status.INSTALLING_TOOL_DEPENDENCIES
- loading_datatypes_status = trans.model.ToolShedRepository.installation_status.LOADING_PROPRIETARY_DATATYPES
- for tool_shed_repository in trans.sa_session.query( trans.model.ToolShedRepository ) \
- .filter( or_( trans.model.ToolShedRepository.status == new_status,
- trans.model.ToolShedRepository.status == cloning_status,
- trans.model.ToolShedRepository.status == setting_tool_versions_status,
- trans.model.ToolShedRepository.status == installing_dependencies_status,
- trans.model.ToolShedRepository.status == loading_datatypes_status ) ):
- installing_repository_ids.append( trans.security.encode_id( tool_shed_repository.id ) )
- if as_string:
- return ','.join( installing_repository_ids )
- return installing_repository_ids
-
-def get_user( trans, user_id ):
- """Get a User from the database by id."""
- user = trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( user_id ) )
- if not user:
- return trans.show_error_message( "User not found for id (%s)" % str( user_id ) )
- return user
-def get_user_by_username( trans, username ):
- """Get a user from the database by username"""
- # TODO: Add exception handling here.
- return trans.sa_session.query( trans.model.User ) \
- .filter( trans.model.User.table.c.username == username ) \
- .one()
-def get_role( trans, id ):
- """Get a Role from the database by id."""
- # Load user from database
- id = trans.security.decode_id( id )
- role = trans.sa_session.query( trans.model.Role ).get( id )
- if not role:
- return trans.show_error_message( "Role not found for id (%s)" % str( id ) )
- return role
-def get_group( trans, id ):
- """Get a Group from the database by id."""
- # Load user from database
- id = trans.security.decode_id( id )
- group = trans.sa_session.query( trans.model.Group ).get( id )
- if not group:
- return trans.show_error_message( "Group not found for id (%s)" % str( id ) )
- return group
-def get_quota( trans, id ):
- """Get a Quota from the database by id."""
- # Load user from database
- id = trans.security.decode_id( id )
- quota = trans.sa_session.query( trans.model.Quota ).get( id )
- return quota
-def get_webapp( trans, **kwd ):
- """Get the value of the webapp, can be one of 'community', 'galaxy', 'reports', 'demo_sequencer'."""
- if 'webapp' in kwd:
- return kwd[ 'webapp' ]
- if 'webapp' in trans.environ:
- return trans.environ[ 'webapp' ]
- # The default is galaxy.
- return 'galaxy'
def sort_by_attr( seq, attr ):
"""
Sort the sequence of objects by object's attribute
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r 2ca0e99d6d6d940af0712e9c56b9f238fd59635c lib/galaxy/web/base/controllers/admin.py
--- /dev/null
+++ b/lib/galaxy/web/base/controllers/admin.py
@@ -0,0 +1,1114 @@
+from datetime import date, datetime, timedelta
+
+from galaxy import config, tools, web, util
+from galaxy.model.orm import *
+
+class Admin( object ):
+ # Override these
+ user_list_grid = None
+ role_list_grid = None
+ group_list_grid = None
+ quota_list_grid = None
+ repository_list_grid = None
+ tool_version_list_grid = None
+ delete_operation = None
+ undelete_operation = None
+ purge_operation = None
+
+ @web.expose
+ @web.require_admin
+ def index( self, trans, **kwd ):
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ if trans.webapp.name == 'galaxy':
+ installed_repositories = trans.sa_session.query( trans.model.ToolShedRepository ).first()
+ installing_repository_ids = get_ids_of_tool_shed_repositories_being_installed( trans, as_string=True )
+ return trans.fill_template( '/webapps/galaxy/admin/index.mako',
+ installed_repositories=installed_repositories,
+ installing_repository_ids=installing_repository_ids,
+ message=message,
+ status=status )
+ else:
+ return trans.fill_template( '/webapps/community/admin/index.mako',
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def center( self, trans, **kwd ):
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ if trans.webapp.name == 'galaxy':
+ return trans.fill_template( '/webapps/galaxy/admin/center.mako',
+ message=message,
+ status=status )
+ else:
+ return trans.fill_template( '/webapps/community/admin/center.mako',
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def reload_tool( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ toolbox = self.app.toolbox
+ if params.get( 'reload_tool_button', False ):
+ tool_id = params.tool_id
+ message, status = toolbox.reload_tool_by_id( tool_id )
+ return trans.fill_template( '/admin/reload_tool.mako',
+ toolbox=toolbox,
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def tool_versions( self, trans, **kwd ):
+ if 'message' not in kwd or not kwd[ 'message' ]:
+ kwd[ 'message' ] = 'Tool ids for tools that are currently loaded into the tool panel are highlighted in green (click to display).'
+ return self.tool_version_list_grid( trans, **kwd )
+ # Galaxy Role Stuff
+ @web.expose
+ @web.require_admin
+ def roles( self, trans, **kwargs ):
+ if 'operation' in kwargs:
+ operation = kwargs['operation'].lower()
+ if operation == "roles":
+ return self.role( trans, **kwargs )
+ if operation == "create":
+ return self.create_role( trans, **kwargs )
+ if operation == "delete":
+ return self.mark_role_deleted( trans, **kwargs )
+ if operation == "undelete":
+ return self.undelete_role( trans, **kwargs )
+ if operation == "purge":
+ return self.purge_role( trans, **kwargs )
+ if operation == "manage users and groups":
+ return self.manage_users_and_groups_for_role( trans, **kwargs )
+ if operation == "rename":
+ return self.rename_role( trans, **kwargs )
+ # Render the list view
+ return self.role_list_grid( trans, **kwargs )
+ @web.expose
+ @web.require_admin
+ def create_role( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ name = util.restore_text( params.get( 'name', '' ) )
+ description = util.restore_text( params.get( 'description', '' ) )
+ in_users = util.listify( params.get( 'in_users', [] ) )
+ out_users = util.listify( params.get( 'out_users', [] ) )
+ in_groups = util.listify( params.get( 'in_groups', [] ) )
+ out_groups = util.listify( params.get( 'out_groups', [] ) )
+ create_group_for_role = params.get( 'create_group_for_role', '' )
+ create_group_for_role_checked = CheckboxField.is_checked( create_group_for_role )
+ ok = True
+ if params.get( 'create_role_button', False ):
+ if not name or not description:
+ message = "Enter a valid name and a description."
+ status = 'error'
+ ok = False
+ elif trans.sa_session.query( trans.app.model.Role ).filter( trans.app.model.Role.table.c.name==name ).first():
+ message = "Role names must be unique and a role with that name already exists, so choose another name."
+ status = 'error'
+ ok = False
+ else:
+ # Create the role
+ role = trans.app.model.Role( name=name, description=description, type=trans.app.model.Role.types.ADMIN )
+ trans.sa_session.add( role )
+ # Create the UserRoleAssociations
+ for user in [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in in_users ]:
+ ura = trans.app.model.UserRoleAssociation( user, role )
+ trans.sa_session.add( ura )
+ # Create the GroupRoleAssociations
+ for group in [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in in_groups ]:
+ gra = trans.app.model.GroupRoleAssociation( group, role )
+ trans.sa_session.add( gra )
+ if create_group_for_role_checked:
+ # Create the group
+ group = trans.app.model.Group( name=name )
+ trans.sa_session.add( group )
+ # Associate the group with the role
+ gra = trans.model.GroupRoleAssociation( group, role )
+ trans.sa_session.add( gra )
+ num_in_groups = len( in_groups ) + 1
+ else:
+ num_in_groups = len( in_groups )
+ trans.sa_session.flush()
+ message = "Role '%s' has been created with %d associated users and %d associated groups. " \
+ % ( role.name, len( in_users ), num_in_groups )
+ if create_group_for_role_checked:
+ message += 'One of the groups associated with this role is the newly created group with the same name.'
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ if ok:
+ for user in trans.sa_session.query( trans.app.model.User ) \
+ .filter( trans.app.model.User.table.c.deleted==False ) \
+ .order_by( trans.app.model.User.table.c.email ):
+ out_users.append( ( user.id, user.email ) )
+ for group in trans.sa_session.query( trans.app.model.Group ) \
+ .filter( trans.app.model.Group.table.c.deleted==False ) \
+ .order_by( trans.app.model.Group.table.c.name ):
+ out_groups.append( ( group.id, group.name ) )
+ return trans.fill_template( '/admin/dataset_security/role/role_create.mako',
+ name=name,
+ description=description,
+ in_users=in_users,
+ out_users=out_users,
+ in_groups=in_groups,
+ out_groups=out_groups,
+ create_group_for_role_checked=create_group_for_role_checked,
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def rename_role( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ id = params.get( 'id', None )
+ if not id:
+ message = "No role ids received for renaming"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=message,
+ status='error' ) )
+ role = get_role( trans, id )
+ if params.get( 'rename_role_button', False ):
+ old_name = role.name
+ new_name = util.restore_text( params.name )
+ new_description = util.restore_text( params.description )
+ if not new_name:
+ message = 'Enter a valid name'
+ status='error'
+ else:
+ existing_role = trans.sa_session.query( trans.app.model.Role ).filter( trans.app.model.Role.table.c.name==new_name ).first()
+ if existing_role and existing_role.id != role.id:
+ message = 'A role with that name already exists'
+ status = 'error'
+ else:
+ if not ( role.name == new_name and role.description == new_description ):
+ role.name = new_name
+ role.description = new_description
+ trans.sa_session.add( role )
+ trans.sa_session.flush()
+ message = "Role '%s' has been renamed to '%s'" % ( old_name, new_name )
+ return trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ return trans.fill_template( '/admin/dataset_security/role/role_rename.mako',
+ role=role,
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def manage_users_and_groups_for_role( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ id = params.get( 'id', None )
+ if not id:
+ message = "No role ids received for managing users and groups"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=message,
+ status='error' ) )
+ role = get_role( trans, id )
+ if params.get( 'role_members_edit_button', False ):
+ in_users = [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in util.listify( params.in_users ) ]
+ for ura in role.users:
+ user = trans.sa_session.query( trans.app.model.User ).get( ura.user_id )
+ if user not in in_users:
+ # Delete DefaultUserPermissions for previously associated users that have been removed from the role
+ for dup in user.default_permissions:
+ if role == dup.role:
+ trans.sa_session.delete( dup )
+ # Delete DefaultHistoryPermissions for previously associated users that have been removed from the role
+ for history in user.histories:
+ for dhp in history.default_permissions:
+ if role == dhp.role:
+ trans.sa_session.delete( dhp )
+ trans.sa_session.flush()
+ in_groups = [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in util.listify( params.in_groups ) ]
+ trans.app.security_agent.set_entity_role_associations( roles=[ role ], users=in_users, groups=in_groups )
+ trans.sa_session.refresh( role )
+ message = "Role '%s' has been updated with %d associated users and %d associated groups" % ( role.name, len( in_users ), len( in_groups ) )
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=util.sanitize_text( message ),
+ status=status ) )
+ in_users = []
+ out_users = []
+ in_groups = []
+ out_groups = []
+ for user in trans.sa_session.query( trans.app.model.User ) \
+ .filter( trans.app.model.User.table.c.deleted==False ) \
+ .order_by( trans.app.model.User.table.c.email ):
+ if user in [ x.user for x in role.users ]:
+ in_users.append( ( user.id, user.email ) )
+ else:
+ out_users.append( ( user.id, user.email ) )
+ for group in trans.sa_session.query( trans.app.model.Group ) \
+ .filter( trans.app.model.Group.table.c.deleted==False ) \
+ .order_by( trans.app.model.Group.table.c.name ):
+ if group in [ x.group for x in role.groups ]:
+ in_groups.append( ( group.id, group.name ) )
+ else:
+ out_groups.append( ( group.id, group.name ) )
+ library_dataset_actions = {}
+ if trans.webapp.name == 'galaxy':
+ # Build a list of tuples that are LibraryDatasetDatasetAssociationss followed by a list of actions
+ # whose DatasetPermissions is associated with the Role
+ # [ ( LibraryDatasetDatasetAssociation [ action, action ] ) ]
+ for dp in role.dataset_actions:
+ for ldda in trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ) \
+ .filter( trans.app.model.LibraryDatasetDatasetAssociation.dataset_id==dp.dataset_id ):
+ root_found = False
+ folder_path = ''
+ folder = ldda.library_dataset.folder
+ while not root_found:
+ folder_path = '%s / %s' % ( folder.name, folder_path )
+ if not folder.parent:
+ root_found = True
+ else:
+ folder = folder.parent
+ folder_path = '%s %s' % ( folder_path, ldda.name )
+ library = trans.sa_session.query( trans.app.model.Library ) \
+ .filter( trans.app.model.Library.table.c.root_folder_id == folder.id ) \
+ .first()
+ if library not in library_dataset_actions:
+ library_dataset_actions[ library ] = {}
+ try:
+ library_dataset_actions[ library ][ folder_path ].append( dp.action )
+ except:
+ library_dataset_actions[ library ][ folder_path ] = [ dp.action ]
+ return trans.fill_template( '/admin/dataset_security/role/role.mako',
+ role=role,
+ in_users=in_users,
+ out_users=out_users,
+ in_groups=in_groups,
+ out_groups=out_groups,
+ library_dataset_actions=library_dataset_actions,
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def mark_role_deleted( self, trans, **kwd ):
+ params = util.Params( kwd )
+ id = kwd.get( 'id', None )
+ if not id:
+ message = "No role ids received for deleting"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=message,
+ status='error' ) )
+ ids = util.listify( id )
+ message = "Deleted %d roles: " % len( ids )
+ for role_id in ids:
+ role = get_role( trans, role_id )
+ role.deleted = True
+ trans.sa_session.add( role )
+ trans.sa_session.flush()
+ message += " %s " % role.name
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ @web.expose
+ @web.require_admin
+ def undelete_role( self, trans, **kwd ):
+ params = util.Params( kwd )
+ id = kwd.get( 'id', None )
+ if not id:
+ message = "No role ids received for undeleting"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=message,
+ status='error' ) )
+ ids = util.listify( id )
+ count = 0
+ undeleted_roles = ""
+ for role_id in ids:
+ role = get_role( trans, role_id )
+ if not role.deleted:
+ message = "Role '%s' has not been deleted, so it cannot be undeleted." % role.name
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ role.deleted = False
+ trans.sa_session.add( role )
+ trans.sa_session.flush()
+ count += 1
+ undeleted_roles += " %s" % role.name
+ message = "Undeleted %d roles: %s" % ( count, undeleted_roles )
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ @web.expose
+ @web.require_admin
+ def purge_role( self, trans, **kwd ):
+ # This method should only be called for a Role that has previously been deleted.
+ # Purging a deleted Role deletes all of the following from the database:
+ # - UserRoleAssociations where role_id == Role.id
+ # - DefaultUserPermissions where role_id == Role.id
+ # - DefaultHistoryPermissions where role_id == Role.id
+ # - GroupRoleAssociations where role_id == Role.id
+ # - DatasetPermissionss where role_id == Role.id
+ params = util.Params( kwd )
+ id = kwd.get( 'id', None )
+ if not id:
+ message = "No role ids received for purging"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ ids = util.listify( id )
+ message = "Purged %d roles: " % len( ids )
+ for role_id in ids:
+ role = get_role( trans, role_id )
+ if not role.deleted:
+ message = "Role '%s' has not been deleted, so it cannot be purged." % role.name
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ # Delete UserRoleAssociations
+ for ura in role.users:
+ user = trans.sa_session.query( trans.app.model.User ).get( ura.user_id )
+ # Delete DefaultUserPermissions for associated users
+ for dup in user.default_permissions:
+ if role == dup.role:
+ trans.sa_session.delete( dup )
+ # Delete DefaultHistoryPermissions for associated users
+ for history in user.histories:
+ for dhp in history.default_permissions:
+ if role == dhp.role:
+ trans.sa_session.delete( dhp )
+ trans.sa_session.delete( ura )
+ # Delete GroupRoleAssociations
+ for gra in role.groups:
+ trans.sa_session.delete( gra )
+ # Delete DatasetPermissionss
+ for dp in role.dataset_actions:
+ trans.sa_session.delete( dp )
+ trans.sa_session.flush()
+ message += " %s " % role.name
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+
+ # Galaxy Group Stuff
+ @web.expose
+ @web.require_admin
+ def groups( self, trans, **kwargs ):
+ if 'operation' in kwargs:
+ operation = kwargs['operation'].lower()
+ if operation == "groups":
+ return self.group( trans, **kwargs )
+ if operation == "create":
+ return self.create_group( trans, **kwargs )
+ if operation == "delete":
+ return self.mark_group_deleted( trans, **kwargs )
+ if operation == "undelete":
+ return self.undelete_group( trans, **kwargs )
+ if operation == "purge":
+ return self.purge_group( trans, **kwargs )
+ if operation == "manage users and roles":
+ return self.manage_users_and_roles_for_group( trans, **kwargs )
+ if operation == "rename":
+ return self.rename_group( trans, **kwargs )
+ # Render the list view
+ return self.group_list_grid( trans, **kwargs )
+ @web.expose
+ @web.require_admin
+ def rename_group( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ id = params.get( 'id', None )
+ if not id:
+ message = "No group ids received for renaming"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=message,
+ status='error' ) )
+ group = get_group( trans, id )
+ if params.get( 'rename_group_button', False ):
+ old_name = group.name
+ new_name = util.restore_text( params.name )
+ if not new_name:
+ message = 'Enter a valid name'
+ status = 'error'
+ else:
+ existing_group = trans.sa_session.query( trans.app.model.Group ).filter( trans.app.model.Group.table.c.name==new_name ).first()
+ if existing_group and existing_group.id != group.id:
+ message = 'A group with that name already exists'
+ status = 'error'
+ else:
+ if group.name != new_name:
+ group.name = new_name
+ trans.sa_session.add( group )
+ trans.sa_session.flush()
+ message = "Group '%s' has been renamed to '%s'" % ( old_name, new_name )
+ return trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ return trans.fill_template( '/admin/dataset_security/group/group_rename.mako',
+ group=group,
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def manage_users_and_roles_for_group( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ group = get_group( trans, params.id )
+ if params.get( 'group_roles_users_edit_button', False ):
+ in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( params.in_roles ) ]
+ in_users = [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in util.listify( params.in_users ) ]
+ trans.app.security_agent.set_entity_group_associations( groups=[ group ], roles=in_roles, users=in_users )
+ trans.sa_session.refresh( group )
+ message += "Group '%s' has been updated with %d associated roles and %d associated users" % ( group.name, len( in_roles ), len( in_users ) )
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=util.sanitize_text( message ),
+ status=status ) )
+ in_roles = []
+ out_roles = []
+ in_users = []
+ out_users = []
+ for role in trans.sa_session.query(trans.app.model.Role ) \
+ .filter( trans.app.model.Role.table.c.deleted==False ) \
+ .order_by( trans.app.model.Role.table.c.name ):
+ if role in [ x.role for x in group.roles ]:
+ in_roles.append( ( role.id, role.name ) )
+ else:
+ out_roles.append( ( role.id, role.name ) )
+ for user in trans.sa_session.query( trans.app.model.User ) \
+ .filter( trans.app.model.User.table.c.deleted==False ) \
+ .order_by( trans.app.model.User.table.c.email ):
+ if user in [ x.user for x in group.users ]:
+ in_users.append( ( user.id, user.email ) )
+ else:
+ out_users.append( ( user.id, user.email ) )
+ message += 'Group %s is currently associated with %d roles and %d users' % ( group.name, len( in_roles ), len( in_users ) )
+ return trans.fill_template( '/admin/dataset_security/group/group.mako',
+ group=group,
+ in_roles=in_roles,
+ out_roles=out_roles,
+ in_users=in_users,
+ out_users=out_users,
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def create_group( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ name = util.restore_text( params.get( 'name', '' ) )
+ in_users = util.listify( params.get( 'in_users', [] ) )
+ out_users = util.listify( params.get( 'out_users', [] ) )
+ in_roles = util.listify( params.get( 'in_roles', [] ) )
+ out_roles = util.listify( params.get( 'out_roles', [] ) )
+ create_role_for_group = params.get( 'create_role_for_group', '' )
+ create_role_for_group_checked = CheckboxField.is_checked( create_role_for_group )
+ ok = True
+ if params.get( 'create_group_button', False ):
+ if not name:
+ message = "Enter a valid name."
+ status = 'error'
+ ok = False
+ elif trans.sa_session.query( trans.app.model.Group ).filter( trans.app.model.Group.table.c.name==name ).first():
+ message = "Group names must be unique and a group with that name already exists, so choose another name."
+ status = 'error'
+ ok = False
+ else:
+ # Create the group
+ group = trans.app.model.Group( name=name )
+ trans.sa_session.add( group )
+ trans.sa_session.flush()
+ # Create the UserRoleAssociations
+ for user in [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in in_users ]:
+ uga = trans.app.model.UserGroupAssociation( user, group )
+ trans.sa_session.add( uga )
+ # Create the GroupRoleAssociations
+ for role in [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in in_roles ]:
+ gra = trans.app.model.GroupRoleAssociation( group, role )
+ trans.sa_session.add( gra )
+ if create_role_for_group_checked:
+ # Create the role
+ role = trans.app.model.Role( name=name, description='Role for group %s' % name )
+ trans.sa_session.add( role )
+ # Associate the role with the group
+ gra = trans.model.GroupRoleAssociation( group, role )
+ trans.sa_session.add( gra )
+ num_in_roles = len( in_roles ) + 1
+ else:
+ num_in_roles = len( in_roles )
+ trans.sa_session.flush()
+ message = "Group '%s' has been created with %d associated users and %d associated roles. " \
+ % ( group.name, len( in_users ), num_in_roles )
+ if create_role_for_group_checked:
+ message += 'One of the roles associated with this group is the newly created role with the same name.'
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+
+
+ if ok:
+ for user in trans.sa_session.query( trans.app.model.User ) \
+ .filter( trans.app.model.User.table.c.deleted==False ) \
+ .order_by( trans.app.model.User.table.c.email ):
+ out_users.append( ( user.id, user.email ) )
+ for role in trans.sa_session.query( trans.app.model.Role ) \
+ .filter( trans.app.model.Role.table.c.deleted==False ) \
+ .order_by( trans.app.model.Role.table.c.name ):
+ out_roles.append( ( role.id, role.name ) )
+ return trans.fill_template( '/admin/dataset_security/group/group_create.mako',
+ name=name,
+ in_users=in_users,
+ out_users=out_users,
+ in_roles=in_roles,
+ out_roles=out_roles,
+ create_role_for_group_checked=create_role_for_group_checked,
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def mark_group_deleted( self, trans, **kwd ):
+ params = util.Params( kwd )
+ id = params.get( 'id', None )
+ if not id:
+ message = "No group ids received for marking deleted"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=message,
+ status='error' ) )
+ ids = util.listify( id )
+ message = "Deleted %d groups: " % len( ids )
+ for group_id in ids:
+ group = get_group( trans, group_id )
+ group.deleted = True
+ trans.sa_session.add( group )
+ trans.sa_session.flush()
+ message += " %s " % group.name
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ @web.expose
+ @web.require_admin
+ def undelete_group( self, trans, **kwd ):
+ params = util.Params( kwd )
+ id = kwd.get( 'id', None )
+ if not id:
+ message = "No group ids received for undeleting"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=message,
+ status='error' ) )
+ ids = util.listify( id )
+ count = 0
+ undeleted_groups = ""
+ for group_id in ids:
+ group = get_group( trans, group_id )
+ if not group.deleted:
+ message = "Group '%s' has not been deleted, so it cannot be undeleted." % group.name
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ group.deleted = False
+ trans.sa_session.add( group )
+ trans.sa_session.flush()
+ count += 1
+ undeleted_groups += " %s" % group.name
+ message = "Undeleted %d groups: %s" % ( count, undeleted_groups )
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ @web.expose
+ @web.require_admin
+ def purge_group( self, trans, **kwd ):
+ # This method should only be called for a Group that has previously been deleted.
+ # Purging a deleted Group simply deletes all UserGroupAssociations and GroupRoleAssociations.
+ params = util.Params( kwd )
+ id = kwd.get( 'id', None )
+ if not id:
+ message = "No group ids received for purging"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ ids = util.listify( id )
+ message = "Purged %d groups: " % len( ids )
+ for group_id in ids:
+ group = get_group( trans, group_id )
+ if not group.deleted:
+ # We should never reach here, but just in case there is a bug somewhere...
+ message = "Group '%s' has not been deleted, so it cannot be purged." % group.name
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ # Delete UserGroupAssociations
+ for uga in group.users:
+ trans.sa_session.delete( uga )
+ # Delete GroupRoleAssociations
+ for gra in group.roles:
+ trans.sa_session.delete( gra )
+ trans.sa_session.flush()
+ message += " %s " % group.name
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+
+ # Galaxy User Stuff
+ @web.expose
+ @web.require_admin
+ def create_new_user( self, trans, **kwd ):
+ return trans.response.send_redirect( web.url_for( controller='user',
+ action='create',
+ cntrller='admin' ) )
+ @web.expose
+ @web.require_admin
+ def reset_user_password( self, trans, **kwd ):
+ user_id = kwd.get( 'id', None )
+ if not user_id:
+ message = "No users received for resetting passwords."
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=message,
+ status='error' ) )
+ user_ids = util.listify( user_id )
+ if 'reset_user_password_button' in kwd:
+ message = ''
+ status = ''
+ for user_id in user_ids:
+ user = get_user( trans, user_id )
+ password = kwd.get( 'password', None )
+ confirm = kwd.get( 'confirm' , None )
+ if len( password ) < 6:
+ message = "Use a password of at least 6 characters."
+ status = 'error'
+ break
+ elif password != confirm:
+ message = "Passwords do not match."
+ status = 'error'
+ break
+ else:
+ user.set_password_cleartext( password )
+ trans.sa_session.add( user )
+ trans.sa_session.flush()
+ if not message and not status:
+ message = "Passwords reset for %d %s." % ( len( user_ids ), inflector.cond_plural( len( user_ids ), 'user' ) )
+ status = 'done'
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=util.sanitize_text( message ),
+ status=status ) )
+ users = [ get_user( trans, user_id ) for user_id in user_ids ]
+ if len( user_ids ) > 1:
+ user_id = ','.join( user_ids )
+ return trans.fill_template( '/admin/user/reset_password.mako',
+ id=user_id,
+ users=users,
+ password='',
+ confirm='' )
+ @web.expose
+ @web.require_admin
+ def mark_user_deleted( self, trans, **kwd ):
+ id = kwd.get( 'id', None )
+ if not id:
+ message = "No user ids received for deleting"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=message,
+ status='error' ) )
+ ids = util.listify( id )
+ message = "Deleted %d users: " % len( ids )
+ for user_id in ids:
+ user = get_user( trans, user_id )
+ user.deleted = True
+ trans.sa_session.add( user )
+ trans.sa_session.flush()
+ message += " %s " % user.email
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ @web.expose
+ @web.require_admin
+ def undelete_user( self, trans, **kwd ):
+ id = kwd.get( 'id', None )
+ if not id:
+ message = "No user ids received for undeleting"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=message,
+ status='error' ) )
+ ids = util.listify( id )
+ count = 0
+ undeleted_users = ""
+ for user_id in ids:
+ user = get_user( trans, user_id )
+ if not user.deleted:
+ message = "User '%s' has not been deleted, so it cannot be undeleted." % user.email
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ user.deleted = False
+ trans.sa_session.add( user )
+ trans.sa_session.flush()
+ count += 1
+ undeleted_users += " %s" % user.email
+ message = "Undeleted %d users: %s" % ( count, undeleted_users )
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ @web.expose
+ @web.require_admin
+ def purge_user( self, trans, **kwd ):
+ # This method should only be called for a User that has previously been deleted.
+ # We keep the User in the database ( marked as purged ), and stuff associated
+ # with the user's private role in case we want the ability to unpurge the user
+ # some time in the future.
+ # Purging a deleted User deletes all of the following:
+ # - History where user_id = User.id
+ # - HistoryDatasetAssociation where history_id = History.id
+ # - Dataset where HistoryDatasetAssociation.dataset_id = Dataset.id
+ # - UserGroupAssociation where user_id == User.id
+ # - UserRoleAssociation where user_id == User.id EXCEPT FOR THE PRIVATE ROLE
+ # - UserAddress where user_id == User.id
+ # Purging Histories and Datasets must be handled via the cleanup_datasets.py script
+ id = kwd.get( 'id', None )
+ if not id:
+ message = "No user ids received for purging"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ ids = util.listify( id )
+ message = "Purged %d users: " % len( ids )
+ for user_id in ids:
+ user = get_user( trans, user_id )
+ if not user.deleted:
+ # We should never reach here, but just in case there is a bug somewhere...
+ message = "User '%s' has not been deleted, so it cannot be purged." % user.email
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ private_role = trans.app.security_agent.get_private_user_role( user )
+ # Delete History
+ for h in user.active_histories:
+ trans.sa_session.refresh( h )
+ for hda in h.active_datasets:
+ # Delete HistoryDatasetAssociation
+ d = trans.sa_session.query( trans.app.model.Dataset ).get( hda.dataset_id )
+ # Delete Dataset
+ if not d.deleted:
+ d.deleted = True
+ trans.sa_session.add( d )
+ hda.deleted = True
+ trans.sa_session.add( hda )
+ h.deleted = True
+ trans.sa_session.add( h )
+ # Delete UserGroupAssociations
+ for uga in user.groups:
+ trans.sa_session.delete( uga )
+ # Delete UserRoleAssociations EXCEPT FOR THE PRIVATE ROLE
+ for ura in user.roles:
+ if ura.role_id != private_role.id:
+ trans.sa_session.delete( ura )
+ # Delete UserAddresses
+ for address in user.addresses:
+ trans.sa_session.delete( address )
+ # Purge the user
+ user.purged = True
+ trans.sa_session.add( user )
+ trans.sa_session.flush()
+ message += "%s " % user.email
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ @web.expose
+ @web.require_admin
+ def users( self, trans, **kwd ):
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
+ if operation == "roles":
+ return self.user( trans, **kwd )
+ elif operation == "reset password":
+ return self.reset_user_password( trans, **kwd )
+ elif operation == "delete":
+ return self.mark_user_deleted( trans, **kwd )
+ elif operation == "undelete":
+ return self.undelete_user( trans, **kwd )
+ elif operation == "purge":
+ return self.purge_user( trans, **kwd )
+ elif operation == "create":
+ return self.create_new_user( trans, **kwd )
+ elif operation == "information":
+ user_id = kwd.get( 'id', None )
+ if not user_id:
+ kwd[ 'message' ] = util.sanitize_text( "Invalid user id (%s) received" % str( user_id ) )
+ kwd[ 'status' ] = 'error'
+ else:
+ return trans.response.send_redirect( web.url_for( controller='user',
+ action='manage_user_info',
+ cntrller='admin',
+ **kwd ) )
+ elif operation == "manage roles and groups":
+ return self.manage_roles_and_groups_for_user( trans, **kwd )
+ if trans.app.config.allow_user_deletion:
+ if self.delete_operation not in self.user_list_grid.operations:
+ self.user_list_grid.operations.append( self.delete_operation )
+ if self.undelete_operation not in self.user_list_grid.operations:
+ self.user_list_grid.operations.append( self.undelete_operation )
+ if self.purge_operation not in self.user_list_grid.operations:
+ self.user_list_grid.operations.append( self.purge_operation )
+ # Render the list view
+ return self.user_list_grid( trans, **kwd )
+ @web.expose
+ @web.require_admin
+ def name_autocomplete_data( self, trans, q=None, limit=None, timestamp=None ):
+ """Return autocomplete data for user emails"""
+ ac_data = ""
+ for user in trans.sa_session.query( User ).filter_by( deleted=False ).filter( func.lower( User.email ).like( q.lower() + "%" ) ):
+ ac_data = ac_data + user.email + "\n"
+ return ac_data
+ @web.expose
+ @web.require_admin
+ def manage_roles_and_groups_for_user( self, trans, **kwd ):
+ user_id = kwd.get( 'id', None )
+ message = ''
+ status = ''
+ if not user_id:
+ message += "Invalid user id (%s) received" % str( user_id )
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ user = get_user( trans, user_id )
+ private_role = trans.app.security_agent.get_private_user_role( user )
+ if kwd.get( 'user_roles_groups_edit_button', False ):
+ # Make sure the user is not dis-associating himself from his private role
+ out_roles = kwd.get( 'out_roles', [] )
+ if out_roles:
+ out_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( out_roles ) ]
+ if private_role in out_roles:
+ message += "You cannot eliminate a user's private role association. "
+ status = 'error'
+ in_roles = kwd.get( 'in_roles', [] )
+ if in_roles:
+ in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( in_roles ) ]
+ out_groups = kwd.get( 'out_groups', [] )
+ if out_groups:
+ out_groups = [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in util.listify( out_groups ) ]
+ in_groups = kwd.get( 'in_groups', [] )
+ if in_groups:
+ in_groups = [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in util.listify( in_groups ) ]
+ if in_roles:
+ trans.app.security_agent.set_entity_user_associations( users=[ user ], roles=in_roles, groups=in_groups )
+ trans.sa_session.refresh( user )
+ message += "User '%s' has been updated with %d associated roles and %d associated groups (private roles are not displayed)" % \
+ ( user.email, len( in_roles ), len( in_groups ) )
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ in_roles = []
+ out_roles = []
+ in_groups = []
+ out_groups = []
+ for role in trans.sa_session.query( trans.app.model.Role ).filter( trans.app.model.Role.table.c.deleted==False ) \
+ .order_by( trans.app.model.Role.table.c.name ):
+ if role in [ x.role for x in user.roles ]:
+ in_roles.append( ( role.id, role.name ) )
+ elif role.type != trans.app.model.Role.types.PRIVATE:
+ # There is a 1 to 1 mapping between a user and a PRIVATE role, so private roles should
+ # not be listed in the roles form fields, except for the currently selected user's private
+ # role, which should always be in in_roles. The check above is added as an additional
+ # precaution, since for a period of time we were including private roles in the form fields.
+ out_roles.append( ( role.id, role.name ) )
+ for group in trans.sa_session.query( trans.app.model.Group ).filter( trans.app.model.Group.table.c.deleted==False ) \
+ .order_by( trans.app.model.Group.table.c.name ):
+ if group in [ x.group for x in user.groups ]:
+ in_groups.append( ( group.id, group.name ) )
+ else:
+ out_groups.append( ( group.id, group.name ) )
+ message += "User '%s' is currently associated with %d roles and is a member of %d groups" % \
+ ( user.email, len( in_roles ), len( in_groups ) )
+ if not status:
+ status = 'done'
+ return trans.fill_template( '/admin/user/user.mako',
+ user=user,
+ in_roles=in_roles,
+ out_roles=out_roles,
+ in_groups=in_groups,
+ out_groups=out_groups,
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def memdump( self, trans, ids = 'None', sorts = 'None', pages = 'None', new_id = None, new_sort = None, **kwd ):
+ if self.app.memdump is None:
+ return trans.show_error_message( "Memdump is not enabled (set <code>use_memdump = True</code> in universe_wsgi.ini)" )
+ heap = self.app.memdump.get()
+ p = util.Params( kwd )
+ msg = None
+ if p.dump:
+ heap = self.app.memdump.get( update = True )
+ msg = "Heap dump complete"
+ elif p.setref:
+ self.app.memdump.setref()
+ msg = "Reference point set (dump to see delta from this point)"
+ ids = ids.split( ',' )
+ sorts = sorts.split( ',' )
+ if new_id is not None:
+ ids.append( new_id )
+ sorts.append( 'None' )
+ elif new_sort is not None:
+ sorts[-1] = new_sort
+ breadcrumb = "<a href='%s' class='breadcrumb'>heap</a>" % web.url_for()
+ # new lists so we can assemble breadcrumb links
+ new_ids = []
+ new_sorts = []
+ for id, sort in zip( ids, sorts ):
+ new_ids.append( id )
+ if id != 'None':
+ breadcrumb += "<a href='%s' class='breadcrumb'>[%s]</a>" % ( web.url_for( ids=','.join( new_ids ), sorts=','.join( new_sorts ) ), id )
+ heap = heap[int(id)]
+ new_sorts.append( sort )
+ if sort != 'None':
+ breadcrumb += "<a href='%s' class='breadcrumb'>.by('%s')</a>" % ( web.url_for( ids=','.join( new_ids ), sorts=','.join( new_sorts ) ), sort )
+ heap = heap.by( sort )
+ ids = ','.join( new_ids )
+ sorts = ','.join( new_sorts )
+ if p.theone:
+ breadcrumb += ".theone"
+ heap = heap.theone
+ return trans.fill_template( '/admin/memdump.mako', heap = heap, ids = ids, sorts = sorts, breadcrumb = breadcrumb, msg = msg )
+
+ @web.expose
+ @web.require_admin
+ def jobs( self, trans, stop = [], stop_msg = None, cutoff = 180, job_lock = None, ajl_submit = None, **kwd ):
+ deleted = []
+ msg = None
+ status = None
+ if self.app.config.job_manager != self.app.config.server_name:
+ return trans.show_error_message( 'This Galaxy instance (%s) is not the job manager (%s). If using multiple servers, please directly access the job manager instance to manage jobs.' % (self.app.config.server_name, self.app.config.job_manager) )
+ job_ids = util.listify( stop )
+ if job_ids and stop_msg in [ None, '' ]:
+ msg = 'Please enter an error message to display to the user describing why the job was terminated'
+ status = 'error'
+ elif job_ids:
+ if stop_msg[-1] not in string.punctuation:
+ stop_msg += '.'
+ for job_id in job_ids:
+ trans.app.job_manager.job_stop_queue.put( job_id, error_msg="This job was stopped by an administrator: %s For more information or help" % stop_msg )
+ deleted.append( str( job_id ) )
+ if deleted:
+ msg = 'Queued job'
+ if len( deleted ) > 1:
+ msg += 's'
+ msg += ' for deletion: '
+ msg += ', '.join( deleted )
+ status = 'done'
+ if ajl_submit:
+ if job_lock == 'on':
+ trans.app.job_manager.job_queue.job_lock = True
+ else:
+ trans.app.job_manager.job_queue.job_lock = False
+ cutoff_time = datetime.utcnow() - timedelta( seconds=int( cutoff ) )
+ 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.NEW,
+ 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() )
+ last_updated = {}
+ for job in jobs:
+ delta = datetime.utcnow() - job.update_time
+ if delta > timedelta( minutes=60 ):
+ last_updated[job.id] = '%s hours' % int( delta.seconds / 60 / 60 )
+ else:
+ last_updated[job.id] = '%s minutes' % int( delta.seconds / 60 )
+ return trans.fill_template( '/admin/jobs.mako',
+ jobs = jobs,
+ last_updated = last_updated,
+ cutoff = cutoff,
+ msg = msg,
+ status = status,
+ job_lock = trans.app.job_manager.job_queue.job_lock )
+
+## ---- Utility methods -------------------------------------------------------
+
+def get_ids_of_tool_shed_repositories_being_installed( trans, as_string=False ):
+ installing_repository_ids = []
+ new_status = trans.model.ToolShedRepository.installation_status.NEW
+ cloning_status = trans.model.ToolShedRepository.installation_status.CLONING
+ setting_tool_versions_status = trans.model.ToolShedRepository.installation_status.SETTING_TOOL_VERSIONS
+ installing_dependencies_status = trans.model.ToolShedRepository.installation_status.INSTALLING_TOOL_DEPENDENCIES
+ loading_datatypes_status = trans.model.ToolShedRepository.installation_status.LOADING_PROPRIETARY_DATATYPES
+ for tool_shed_repository in trans.sa_session.query( trans.model.ToolShedRepository ) \
+ .filter( or_( trans.model.ToolShedRepository.status == new_status,
+ trans.model.ToolShedRepository.status == cloning_status,
+ trans.model.ToolShedRepository.status == setting_tool_versions_status,
+ trans.model.ToolShedRepository.status == installing_dependencies_status,
+ trans.model.ToolShedRepository.status == loading_datatypes_status ) ):
+ installing_repository_ids.append( trans.security.encode_id( tool_shed_repository.id ) )
+ if as_string:
+ return ','.join( installing_repository_ids )
+ return installing_repository_ids
+
+def get_user( trans, user_id ):
+ """Get a User from the database by id."""
+ user = trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( user_id ) )
+ if not user:
+ return trans.show_error_message( "User not found for id (%s)" % str( user_id ) )
+ return user
+def get_user_by_username( trans, username ):
+ """Get a user from the database by username"""
+ # TODO: Add exception handling here.
+ return trans.sa_session.query( trans.model.User ) \
+ .filter( trans.model.User.table.c.username == username ) \
+ .one()
+def get_role( trans, id ):
+ """Get a Role from the database by id."""
+ # Load user from database
+ id = trans.security.decode_id( id )
+ role = trans.sa_session.query( trans.model.Role ).get( id )
+ if not role:
+ return trans.show_error_message( "Role not found for id (%s)" % str( id ) )
+ return role
+def get_group( trans, id ):
+ """Get a Group from the database by id."""
+ # Load user from database
+ id = trans.security.decode_id( id )
+ group = trans.sa_session.query( trans.model.Group ).get( id )
+ if not group:
+ return trans.show_error_message( "Group not found for id (%s)" % str( id ) )
+ return group
+def get_quota( trans, id ):
+ """Get a Quota from the database by id."""
+ # Load user from database
+ id = trans.security.decode_id( id )
+ quota = trans.sa_session.query( trans.model.Quota ).get( id )
+ return quota
\ No newline at end of file
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r 2ca0e99d6d6d940af0712e9c56b9f238fd59635c lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py
+++ b/lib/galaxy/web/framework/__init__.py
@@ -607,7 +607,7 @@
Update the session cookie to match the current session.
"""
self.set_cookie( self.security.encode_guid( self.galaxy_session.session_key ), name=name, path=self.app.config.cookie_path )
- def handle_user_login( self, user, webapp ):
+ def handle_user_login( self, user ):
"""
Login a new user (possibly newly created)
- create a new session
@@ -621,7 +621,7 @@
prev_galaxy_session.is_valid = False
# Define a new current_session
self.galaxy_session = self.__create_new_session( prev_galaxy_session, user )
- if webapp == 'galaxy':
+ if self.webapp.name == 'galaxy':
cookie_name = 'galaxysession'
# Associated the current user's last accessed history (if exists) with their new session
history = None
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r 2ca0e99d6d6d940af0712e9c56b9f238fd59635c lib/galaxy/web/framework/helpers/grids.py
--- a/lib/galaxy/web/framework/helpers/grids.py
+++ b/lib/galaxy/web/framework/helpers/grids.py
@@ -53,7 +53,8 @@
def __call__( self, trans, **kwargs ):
# Get basics.
- webapp = get_webapp( trans, **kwargs )
+ # FIXME: pretty sure this is only here to pass along, can likely be eliminated
+ webapp = trans.webapp.name
status = kwargs.get( 'status', None )
message = kwargs.get( 'message', None )
# Build a base filter and sort key that is the combination of the saved state and defaults.
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r 2ca0e99d6d6d940af0712e9c56b9f238fd59635c lib/galaxy/webapps/community/controllers/admin.py
--- a/lib/galaxy/webapps/community/controllers/admin.py
+++ b/lib/galaxy/webapps/community/controllers/admin.py
@@ -1,4 +1,5 @@
from galaxy.web.base.controller import *
+from galaxy.web.base.controllers.admin import Admin
from galaxy.webapps.community import model
from galaxy.model.orm import *
from galaxy.web.framework.helpers import time_ago, iff, grids
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r 2ca0e99d6d6d940af0712e9c56b9f238fd59635c lib/galaxy/webapps/galaxy/api/quotas.py
--- a/lib/galaxy/webapps/galaxy/api/quotas.py
+++ b/lib/galaxy/webapps/galaxy/api/quotas.py
@@ -2,7 +2,8 @@
API operations on Quota objects.
"""
import logging
-from galaxy.web.base.controller import BaseAPIController, Admin, UsesQuotaMixin, url_for
+from galaxy.web.base.controller import BaseAPIController, UsesQuotaMixin, url_for
+from galaxy.web.base.controllers.admin import Admin
from galaxy import web, util
from elementtree.ElementTree import XML
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r 2ca0e99d6d6d940af0712e9c56b9f238fd59635c lib/galaxy/webapps/galaxy/controllers/admin.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin.py
@@ -1,4 +1,5 @@
from galaxy.web.base.controller import *
+from galaxy.web.base.controllers.admin import Admin
from galaxy import model
from galaxy.model.orm import *
from galaxy.web.framework.helpers import time_ago, iff, grids
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r 2ca0e99d6d6d940af0712e9c56b9f238fd59635c lib/galaxy/webapps/galaxy/controllers/user.py
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -14,7 +14,7 @@
from galaxy.security.validate_user_input import validate_email, validate_publicname, validate_password, transform_publicname
from galaxy.util.json import from_json_string, to_json_string
from galaxy.web import url_for
-from galaxy.web.base.controller import BaseUIController, UsesFormDefinitionsMixin, get_webapp
+from galaxy.web.base.controller import BaseUIController, UsesFormDefinitionsMixin
from galaxy.web.form_builder import CheckboxField, build_select_field
from galaxy.web.framework.helpers import time_ago, grids
@@ -49,10 +49,10 @@
installed_len_files = None
@web.expose
- def index( self, trans, cntrller, webapp='galaxy', **kwd ):
- return trans.fill_template( '/user/index.mako', cntrller=cntrller, webapp=webapp )
+ def index( self, trans, cntrller, **kwd ):
+ return trans.fill_template( '/user/index.mako', cntrller=cntrller )
@web.expose
- def openid_auth( self, trans, webapp='galaxy', **kwd ):
+ def openid_auth( self, trans, **kwd ):
'''Handles user request to access an OpenID provider'''
if not trans.app.config.enable_openid:
return trans.show_error_message( 'OpenID authentication is not enabled in this instance of Galaxy' )
@@ -102,7 +102,7 @@
message=message,
status='error' ) )
@web.expose
- def openid_process( self, trans, webapp='galaxy', **kwd ):
+ def openid_process( self, trans, **kwd ):
'''Handle's response from OpenID Providers'''
if not trans.app.config.enable_openid:
return trans.show_error_message( 'OpenID authentication is not enabled in this instance of Galaxy' )
@@ -178,7 +178,7 @@
message=message,
status=status ) )
elif user_openid.user:
- trans.handle_user_login( user_openid.user, webapp )
+ trans.handle_user_login( user_openid.user )
trans.log_event( "User logged in via OpenID: %s" % display_identifier )
openid_provider_obj.post_authentication( trans, trans.app.openid_manager, info )
if not redirect:
@@ -222,7 +222,7 @@
message=message,
status=status ) )
@web.expose
- def openid_associate( self, trans, cntrller='user', webapp='galaxy', **kwd ):
+ def openid_associate( self, trans, cntrller='user', **kwd ):
'''Associates a user with an OpenID log in'''
if not trans.app.config.enable_openid:
return trans.show_error_message( 'OpenID authentication is not enabled in this instance of Galaxy' )
@@ -241,7 +241,7 @@
elif is_admin:
return trans.show_error_message( 'Associating OpenIDs with accounts cannot be done by administrators.' )
if kwd.get( 'login_button', False ):
- message, status, user, success = self.__validate_login( trans, webapp, **kwd )
+ message, status, user, success = self.__validate_login( trans, **kwd )
if success:
openid_objs = []
for openid in openids:
@@ -285,7 +285,7 @@
error = 'User registration is disabled. Please contact your Galaxy administrator for an account.'
else:
# Check email and password validity
- error = self.__validate( trans, params, email, password, confirm, username, webapp )
+ error = self.__validate( trans, params, email, password, confirm, username )
if not error:
# all the values are valid
message, status, user, success = self.__register( trans,
@@ -330,7 +330,7 @@
else:
message = error
status = 'error'
- if webapp == 'galaxy':
+ if trans.webapp.name == 'galaxy':
user_type_form_definition = self.__get_user_type_form_definition( trans, user=user, **kwd )
user_type_fd_id = params.get( 'user_type_fd_id', 'none' )
if user_type_fd_id == 'none' and user_type_form_definition is not None:
@@ -342,7 +342,6 @@
user_type_form_definition = None
widgets = []
return trans.fill_template( '/user/openid_associate.mako',
- webapp=webapp,
cntrller=cntrller,
email=email,
password='',
@@ -362,7 +361,7 @@
openids=openids )
@web.expose
@web.require_login( 'manage OpenIDs' )
- def openid_disassociate( self, trans, webapp='galaxy', **kwd ):
+ def openid_disassociate( self, trans, **kwd ):
'''Disassociates a user with an OpenID'''
if not trans.app.config.enable_openid:
return trans.show_error_message( 'OpenID authentication is not enabled in this instance of Galaxy' )
@@ -404,7 +403,7 @@
@web.expose
@web.require_login( 'manage OpenIDs' )
- def openid_manage( self, trans, webapp='galaxy', **kwd ):
+ def openid_manage( self, trans, **kwd ):
'''Manage OpenIDs for user'''
if not trans.app.config.enable_openid:
return trans.show_error_message( 'OpenID authentication is not enabled in this instance of Galaxy' )
@@ -421,7 +420,7 @@
return self.user_openid_grid( trans, **kwd )
@web.expose
- def login( self, trans, webapp='galaxy', redirect_url='', refresh_frames=[], **kwd ):
+ def login( self, trans, redirect_url='', refresh_frames=[], **kwd ):
'''Handle Galaxy Log in'''
redirect = kwd.get( 'redirect', trans.request.referer ).strip()
use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
@@ -430,16 +429,13 @@
header = ''
user = None
email = kwd.get( 'email', '' )
- #Sanitize webapp login here, once, since it can be reflected to the user in messages/etc.
- #Only text is valid.
- webapp = util.sanitize_text(webapp)
if kwd.get( 'login_button', False ):
- if webapp == 'galaxy' and not refresh_frames:
+ if trans.webapp.name == 'galaxy' and not refresh_frames:
if trans.app.config.require_login:
refresh_frames = [ 'masthead', 'history', 'tools' ]
else:
refresh_frames = [ 'masthead', 'history' ]
- message, status, user, success = self.__validate_login( trans, webapp, **kwd )
+ message, status, user, success = self.__validate_login( trans, **kwd )
if success and redirect and not redirect.startswith( trans.request.base + url_for( controller='user', action='logout' ) ):
redirect_url = redirect
elif success:
@@ -447,18 +443,17 @@
if not user and trans.app.config.require_login:
if trans.app.config.allow_user_creation:
create_account_str = " If you don't already have an account, <a href='%s'>you may create one</a>." % \
- web.url_for( action='create', cntrller='user', webapp=webapp )
- if webapp == 'galaxy':
+ web.url_for( action='create', cntrller='user' )
+ if trans.webapp.name == 'galaxy':
header = require_login_template % ( "Galaxy instance", create_account_str )
else:
header = require_login_template % ( "Galaxy tool shed", create_account_str )
else:
- if webapp == 'galaxy':
+ if trans.webapp.name == 'galaxy':
header = require_login_template % ( "Galaxy instance", "" )
else:
header = require_login_template % ( "Galaxy tool shed", "" )
return trans.fill_template( '/user/login.mako',
- webapp=webapp,
email=email,
header=header,
use_panels=use_panels,
@@ -469,7 +464,7 @@
status=status,
openid_providers=trans.app.openid_providers,
active_view="user" )
- def __validate_login( self, trans, webapp='galaxy', **kwd ):
+ def __validate_login( self, trans, **kwd ):
message = kwd.get( 'message', '' )
status = kwd.get( 'status', 'done' )
email = kwd.get( 'email', '' )
@@ -490,8 +485,8 @@
message = "Invalid password"
status = 'error'
else:
- trans.handle_user_login( user, webapp )
- if webapp == 'galaxy':
+ trans.handle_user_login( user )
+ if trans.webapp.name == 'galaxy':
trans.log_event( "User logged in" )
message = 'You are now logged in as %s.<br>You can <a target="_top" href="%s">go back to the page you were visiting</a> or <a target="_top" href="%s">go to the home page</a>.' % \
( user.email, redirect, url_for( '/' ) )
@@ -501,8 +496,8 @@
return ( message, status, user, success )
@web.expose
- def logout( self, trans, webapp='galaxy', logout_all=False ):
- if webapp == 'galaxy':
+ def logout( self, trans, logout_all=False ):
+ if trans.webapp.name == 'galaxy':
if trans.app.config.require_login:
refresh_frames = [ 'masthead', 'history', 'tools' ]
else:
@@ -515,7 +510,6 @@
message = 'You have been logged out.<br>You can log in again, <a target="_top" href="%s">go back to the page you were visiting</a> or <a target="_top" href="%s">go to the home page</a>.' % \
( trans.request.referer, url_for( '/' ) )
return trans.fill_template( '/user/logout.mako',
- webapp=webapp,
refresh_frames=refresh_frames,
message=message,
status='done',
@@ -526,7 +520,6 @@
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
- webapp = get_webapp( trans, **kwd )
use_panels = util.string_as_bool( kwd.get( 'use_panels', True ) )
email = util.restore_text( params.get( 'email', '' ) )
# Do not sanitize passwords, so take from kwd
@@ -543,7 +536,7 @@
status = 'error'
else:
if not refresh_frames:
- if webapp == 'galaxy':
+ if trans.webapp.name == 'galaxy':
if trans.app.config.require_login:
refresh_frames = [ 'masthead', 'history', 'tools' ]
else:
@@ -553,19 +546,19 @@
# Create the user, save all the user info and login to Galaxy
if params.get( 'create_user_button', False ):
# Check email and password validity
- message = self.__validate( trans, params, email, password, confirm, username, webapp )
+ message = self.__validate( trans, params, email, password, confirm, username )
if not message:
# All the values are valid
message, status, user, success = self.__register( trans,
cntrller,
subscribe_checked,
**kwd )
- if webapp == 'community':
+ if trans.webapp.name == 'community':
redirect_url = url_for( '/' )
if success and not is_admin:
# The handle_user_login() method has a call to the history_set_default_permissions() method
# (needed when logging in with a history), user needs to have default permissions set before logging in
- trans.handle_user_login( user, webapp )
+ trans.handle_user_login( user )
trans.log_event( "User created a new account" )
trans.log_event( "User logged in" )
if success and is_admin:
@@ -577,7 +570,7 @@
status=status ) )
else:
status = 'error'
- if webapp == 'galaxy':
+ if trans.webapp.name == 'galaxy':
user_type_form_definition = self.__get_user_type_form_definition( trans, user=None, **kwd )
user_type_fd_id = params.get( 'user_type_fd_id', 'none' )
if user_type_fd_id == 'none' and user_type_form_definition is not None:
@@ -596,7 +589,6 @@
user_type_fd_id_select_field=user_type_fd_id_select_field,
user_type_form_definition=user_type_form_definition,
widgets=widgets,
- webapp=webapp,
use_panels=use_panels,
redirect=redirect,
redirect_url=redirect_url,
@@ -608,7 +600,6 @@
email = util.restore_text( kwd.get( 'email', '' ) )
password = kwd.get( 'password', '' )
username = util.restore_text( kwd.get( 'username', '' ) )
- webapp = get_webapp( trans, **kwd )
status = kwd.get( 'status', 'done' )
is_admin = cntrller == 'admin' and trans.user_is_admin()
user = trans.app.model.User( email=email )
@@ -618,7 +609,7 @@
trans.sa_session.flush()
trans.app.security_agent.create_private_user_role( user )
error = ''
- if webapp == 'galaxy':
+ if trans.webapp.name == 'galaxy':
# We set default user permissions, before we log in and set the default history permissions
trans.app.security_agent.user_set_default_permissions( user,
default_access_private=trans.app.config.new_user_dataset_access_role_default_private )
@@ -654,7 +645,7 @@
if not error and not is_admin:
# The handle_user_login() method has a call to the history_set_default_permissions() method
# (needed when logging in with a history), user needs to have default permissions set before logging in
- trans.handle_user_login( user, webapp )
+ trans.handle_user_login( user )
trans.log_event( "User created a new account" )
trans.log_event( "User logged in" )
elif not error:
@@ -706,14 +697,13 @@
user = trans.user
if not user:
raise AssertionError, "The user id (%s) is not valid" % str( user_id )
- webapp = get_webapp( trans, **kwd )
email = util.restore_text( params.get( 'email', user.email ) )
username = util.restore_text( params.get( 'username', '' ) )
if not username:
username = user.username
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
- if webapp == 'galaxy':
+ if trans.webapp.name == 'galaxy':
user_type_form_definition = self.__get_user_type_form_definition( trans, user=user, **kwd )
user_type_fd_id = params.get( 'user_type_fd_id', 'none' )
if user_type_fd_id == 'none' and user_type_form_definition is not None:
@@ -742,7 +732,6 @@
widgets=widgets,
addresses=addresses,
show_filter=show_filter,
- webapp=webapp,
message=message,
status=status )
else:
@@ -751,7 +740,6 @@
user=user,
email=email,
username=username,
- webapp=webapp,
message=message,
status=status )
@@ -761,7 +749,6 @@
def edit_username( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
is_admin = cntrller == 'admin' and trans.user_is_admin()
- webapp = get_webapp( trans, **kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
user_id = params.get( 'user_id', None )
@@ -784,14 +771,12 @@
cntrller=cntrller,
user=user,
username=user.username,
- webapp=webapp,
message=message,
status=status )
@web.expose
def edit_info( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
is_admin = cntrller == 'admin' and trans.user_is_admin()
- webapp = get_webapp( trans, **kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
user_id = params.get( 'user_id', None )
@@ -885,7 +870,7 @@
trans.sa_session.add( user )
trans.sa_session.flush()
message = "The user information has been updated with the changes."
- if user and webapp == 'galaxy' and is_admin:
+ if user and trans.webapp.name == 'galaxy' and is_admin:
kwd[ 'user_id' ] = trans.security.encode_id( user.id )
kwd[ 'id' ] = user_id
if message:
@@ -897,7 +882,7 @@
cntrller=cntrller,
**kwd ) )
@web.expose
- def reset_password( self, trans, email=None, webapp='galaxy', **kwd ):
+ def reset_password( self, trans, email=None, **kwd ):
if trans.app.config.smtp_server is None:
return trans.show_error_message( "Mail is not configured for this Galaxy instance. Please contact an administrator." )
message = util.restore_text( kwd.get( 'message', '' ) )
@@ -941,12 +926,11 @@
elif email is None:
email = ""
return trans.fill_template( '/user/reset_password.mako',
- webapp=webapp,
message=message,
status=status )
- def __validate( self, trans, params, email, password, confirm, username, webapp ):
+ def __validate( self, trans, params, email, password, confirm, username ):
# If coming from the community webapp, we'll require a public user name
- if webapp == 'community' and not username:
+ if trans.webapp.name == 'community' and not username:
return "A public user name is required"
message = validate_email( trans, email )
if not message:
@@ -954,7 +938,7 @@
if not message and username:
message = validate_publicname( trans, username )
if not message:
- if webapp == 'galaxy':
+ if trans.webapp.name == 'galaxy':
if self.get_all_forms( trans,
filter=dict( deleted=False ),
form_type=trans.app.model.FormDefinition.types.USER_INFO ):
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r 2ca0e99d6d6d940af0712e9c56b9f238fd59635c templates/user/dbkeys.mako
--- a/templates/user/dbkeys.mako
+++ b/templates/user/dbkeys.mako
@@ -1,11 +1,7 @@
<%!
def inherit(context):
if context.get('use_panels'):
- if context.get('webapp'):
- webapp = context.get('webapp')
- else:
- webapp = 'galaxy'
- return '/webapps/%s/base_panels.mako' % webapp
+ return '/webapps/%s/base_panels.mako' % t.webapp.name
else:
return '/base.mako'
%>
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r 2ca0e99d6d6d940af0712e9c56b9f238fd59635c templates/user/index.mako
--- a/templates/user/index.mako
+++ b/templates/user/index.mako
@@ -9,27 +9,27 @@
<h2>${_('User preferences')}</h2><p>You are currently logged in as ${trans.user.email}.</p><ul>
- %if webapp == 'galaxy':
- <li><a href="${h.url_for( controller='user', action='manage_user_info', cntrller=cntrller, webapp=webapp )}">${_('Manage your information')}</a></li>
- <li><a href="${h.url_for( controller='user', action='set_default_permissions', cntrller=cntrller, webapp=webapp )}">${_('Change default permissions')}</a> for new histories</li>
- <li><a href="${h.url_for( controller='user', action='api_keys', cntrller=cntrller, webapp=webapp )}">${_('Manage your API keys')}</a></li>
+ %if t.webapp.name == 'galaxy':
+ <li><a href="${h.url_for( controller='user', action='manage_user_info', cntrller=cntrller )}">${_('Manage your information')}</a></li>
+ <li><a href="${h.url_for( controller='user', action='set_default_permissions', cntrller=cntrller )}">${_('Change default permissions')}</a> for new histories</li>
+ <li><a href="${h.url_for( controller='user', action='api_keys', cntrller=cntrller )}">${_('Manage your API keys')}</a></li>
%if trans.app.config.enable_openid:
- <li><a href="${h.url_for( controller='user', action='openid_manage', cntrller=cntrller, webapp=webapp )}">${_('Manage OpenIDs')}</a> linked to your account</li>
+ <li><a href="${h.url_for( controller='user', action='openid_manage', cntrller=cntrller )}">${_('Manage OpenIDs')}</a> linked to your account</li>
%endif
%if trans.app.config.use_remote_user:
%if trans.app.config.remote_user_logout_href:
<li><a href="${trans.app.config.remote_user_logout_href}" target="_top">${_('Logout')}</a></li>
%endif
%else:
- <li><a href="${h.url_for( controller='user', action='logout', webapp=webapp, logout_all=True )}" target="_top">${_('Logout')}</a> ${_('of all user sessions')}</li>
+ <li><a href="${h.url_for( controller='user', action='logout', logout_all=True )}" target="_top">${_('Logout')}</a> ${_('of all user sessions')}</li>
%endif
%else:
- <li><a href="${h.url_for( controller='user', action='manage_user_info', cntrller=cntrller, webapp=webapp )}">${_('Manage your information')}</a></li>
- <li><a href="${h.url_for( controller='repository', action='manage_email_alerts', cntrller=cntrller, webapp=webapp )}">${_('Manage your email alerts')}</a></li>
- <li><a href="${h.url_for( controller='user', action='logout', webapp=webapp, logout_all=True )}" target="_top">${_('Logout')}</a> ${_('of all user sessions')}</li>
+ <li><a href="${h.url_for( controller='user', action='manage_user_info', cntrller=cntrller )}">${_('Manage your information')}</a></li>
+ <li><a href="${h.url_for( controller='repository', action='manage_email_alerts', cntrller=cntrller )}">${_('Manage your email alerts')}</a></li>
+ <li><a href="${h.url_for( controller='user', action='logout', logout_all=True )}" target="_top">${_('Logout')}</a> ${_('of all user sessions')}</li>
%endif
</ul>
- %if webapp == 'galaxy':
+ %if t.webapp.name == 'galaxy':
<p>
You are using <strong>${trans.user.get_disk_usage( nice_size=True )}</strong> of disk space in this Galaxy instance.
%if trans.app.config.enable_quotas:
@@ -43,7 +43,7 @@
<p>${n_('You are currently not logged in.')}</p>
%endif
<ul>
- <li><a href="${h.url_for( action='login', webapp=webapp )}">${_('Login')}</li>
- <li><a href="${h.url_for( action='create', cntrller='user', webapp=webapp )}">${_('Register')}</a></li>
+ <li><a href="${h.url_for( action='login' )}">${_('Login')}</li>
+ <li><a href="${h.url_for( action='create', cntrller='user' )}">${_('Register')}</a></li></ul>
%endif
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r 2ca0e99d6d6d940af0712e9c56b9f238fd59635c templates/user/info.mako
--- a/templates/user/info.mako
+++ b/templates/user/info.mako
@@ -7,13 +7,12 @@
%if not is_admin:
<ul class="manage-table-actions"><li>
- <a class="action-button" href="${h.url_for( controller='user', action='index', cntrller=cntrller, webapp=webapp )}">User preferences</a>
+ <a class="action-button" href="${h.url_for( controller='user', action='index', cntrller=cntrller )}">User preferences</a></li></ul>
%endif
<div class="toolForm"><form name="login_info" id="login_info" action="${h.url_for( controller='user', action='edit_info', cntrller=cntrller, user_id=trans.security.encode_id( user.id ) )}" method="post" >
- <input type="hidden" name="webapp" value="${webapp}" size="40"/><div class="toolFormTitle">Login Information</div><div class="form-row"><label>Email address:</label>
@@ -21,7 +20,7 @@
</div><div class="form-row"><label>Public name:</label>
- %if webapp == 'community':
+ %if t.webapp.name == 'community':
%if user.active_repositories:
<input type="hidden" name="username" value="${username}"/>
${username}
@@ -54,7 +53,6 @@
<p></p><div class="toolForm"><form name="change_password" id="change_password" action="${h.url_for( controller='user', action='edit_info', cntrller=cntrller, user_id=trans.security.encode_id( user.id ) )}" method="post" >
- <input type="hidden" name="webapp" value="${webapp}" size="40"/><div class="toolFormTitle">Change Password</div>
%if not is_admin:
<div class="form-row">
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r 2ca0e99d6d6d940af0712e9c56b9f238fd59635c templates/user/login.mako
--- a/templates/user/login.mako
+++ b/templates/user/login.mako
@@ -1,11 +1,7 @@
<%!
def inherit(context):
if context.get('use_panels'):
- if context.get('webapp'):
- webapp = context.get('webapp')
- else:
- webapp = 'galaxy'
- return '/webapps/%s/base_panels.mako' % webapp
+ return '/webapps/%s/base_panels.mako' % context.get('t').webapp.name
else:
return '/base.mako'
%>
@@ -82,14 +78,13 @@
<div class="form-row"><label>Email address:</label><input type="text" name="email" value="${email | h}" size="40"/>
- <input type="hidden" name="webapp" value="${webapp | h}" size="40"/><input type="hidden" name="redirect" value="${redirect | h}" size="40"/></div><div class="form-row"><label>Password:</label><input type="password" name="password" value="" size="40"/><div class="toolParamHelp" style="clear: both;">
- <a href="${h.url_for( controller='user', action='reset_password', webapp=webapp, use_panels=use_panels )}">Forgot password? Reset here</a>
+ <a href="${h.url_for( controller='user', action='reset_password', use_panels=use_panels )}">Forgot password? Reset here</a></div></div><div class="form-row">
@@ -107,7 +102,6 @@
<div class="form-row"><label>OpenID URL:</label><input type="text" name="openid_url" size="60" style="background-image:url('${h.url_for( '/static/images/openid-16x16.gif' )}' ); background-repeat: no-repeat; padding-right: 20px; background-position: 99% 50%;"/>
- <input type="hidden" name="webapp" value="${webapp | h}" size="40"/><input type="hidden" name="redirect" value="${redirect | h}" size="40"/></div><div class="form-row">
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r 2ca0e99d6d6d940af0712e9c56b9f238fd59635c templates/user/logout.mako
--- a/templates/user/logout.mako
+++ b/templates/user/logout.mako
@@ -1,10 +1,6 @@
<%!
def inherit(context):
- if context.get('webapp'):
- webapp = context.get('webapp')
- else:
- webapp = 'galaxy'
- return '/webapps/%s/base_panels.mako' % webapp
+ return '/webapps/%s/base_panels.mako' % context.get('t').webapp.name
%><%inherit file="${inherit(context)}"/><%namespace file="/message.mako" import="render_msg" />
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r 2ca0e99d6d6d940af0712e9c56b9f238fd59635c templates/user/openid_associate.mako
--- a/templates/user/openid_associate.mako
+++ b/templates/user/openid_associate.mako
@@ -1,11 +1,7 @@
<%!
def inherit(context):
if context.get('use_panels'):
- if context.get('webapp'):
- webapp = context.get('webapp')
- else:
- webapp = 'galaxy'
- return '/webapps/%s/base_panels.mako' % webapp
+ return '/webapps/%s/base_panels.mako' % t.webapp.name
else:
return '/base.mako'
%>
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r 2ca0e99d6d6d940af0712e9c56b9f238fd59635c templates/user/register.mako
--- a/templates/user/register.mako
+++ b/templates/user/register.mako
@@ -43,7 +43,6 @@
<div class="form-row"><label>Email address:</label><input type="text" name="email" value="${email | h}" size="40"/>
- <input type="hidden" name="webapp" value="${webapp | h}" size="40"/><input type="hidden" name="redirect" value="${redirect | h}" size="40"/></div><div class="form-row">
@@ -57,7 +56,7 @@
<div class="form-row"><label>Public name:</label><input type="text" name="username" size="40" value="${username |h}"/>
- %if webapp == 'galaxy':
+ %if t.webapp.name == 'galaxy':
<div class="toolParamHelp" style="clear: both;">
Your public name is an identifier that will be used to generate addresses for information
you share publicly. Public names must be at least four characters in length and contain only lower-case
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r 2ca0e99d6d6d940af0712e9c56b9f238fd59635c templates/user/reset_password.mako
--- a/templates/user/reset_password.mako
+++ b/templates/user/reset_password.mako
@@ -11,7 +11,6 @@
<div class="form-row"><label>Email:</label><input type="text" name="email" value="" size="40"/>
- <input type="hidden" name="webapp" value="${webapp}" size="40"/></div><div style="clear: both"></div><div class="form-row">
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r 2ca0e99d6d6d940af0712e9c56b9f238fd59635c templates/user/username.mako
--- a/templates/user/username.mako
+++ b/templates/user/username.mako
@@ -5,7 +5,6 @@
<h2>Manage Public Name</h2><div class="toolForm"><form name="username" id="username" action="${h.url_for( controller='user', action='edit_username', cntrller=cntrller, user_id=trans.security.encode_id( user.id ) )}" method="post" >
- <input type="hidden" name="webapp" value="${webapp}" size="40"/><div class="toolFormTitle">Login Information</div><div class="form-row"><label>Public name:</label>
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r 2ca0e99d6d6d940af0712e9c56b9f238fd59635c templates/webapps/galaxy/admin/index.mako
--- a/templates/webapps/galaxy/admin/index.mako
+++ b/templates/webapps/galaxy/admin/index.mako
@@ -42,11 +42,11 @@
<div class="toolSectionTitle">Security</div><div class="toolSectionBody"><div class="toolSectionBg">
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='users', webapp=webapp )}" target="galaxy_main">Manage users</a></div>
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='groups', webapp=webapp )}" target="galaxy_main">Manage groups</a></div>
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='roles', webapp=webapp )}" target="galaxy_main">Manage roles</a></div>
+ <div class="toolTitle"><a href="${h.url_for( controller='admin', action='users' )}" target="galaxy_main">Manage users</a></div>
+ <div class="toolTitle"><a href="${h.url_for( controller='admin', action='groups' )}" target="galaxy_main">Manage groups</a></div>
+ <div class="toolTitle"><a href="${h.url_for( controller='admin', action='roles' )}" target="galaxy_main">Manage roles</a></div>
%if trans.app.config.allow_user_impersonation:
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='impersonate', webapp=webapp )}" target="galaxy_main">Impersonate a user</a></div>
+ <div class="toolTitle"><a href="${h.url_for( controller='admin', action='impersonate' )}" target="galaxy_main">Impersonate a user</a></div>
%endif
</div></div>
@@ -54,7 +54,7 @@
<div class="toolSectionTitle">Data</div><div class="toolSectionBody"><div class="toolSectionBg">
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='quotas', webapp=webapp )}" target="galaxy_main">Manage quotas</a></div>
+ <div class="toolTitle"><a href="${h.url_for( controller='admin', action='quotas' )}" target="galaxy_main">Manage quotas</a></div><div class="toolTitle"><a href="${h.url_for( controller='library_admin', action='browse_libraries' )}" target="galaxy_main">Manage data libraries</a></div>
%if trans.app.config.enable_beta_job_managers:
<div class="toolTitle"><a href="${h.url_for( controller='data_admin', action='manage_data' )}" target="galaxy_main">Manage local data</a></div>
@@ -111,6 +111,6 @@
</%def><%def name="center_panel()">
- <% center_url = h.url_for( controller='admin', action='center', webapp='galaxy', message=message, status=status ) %>
+ <% center_url = h.url_for( controller='admin', action='center', message=message, status=status ) %><iframe name="galaxy_main" id="galaxy_main" frameborder="0" style="position: absolute; width: 100%; height: 100%;" src="${center_url}"></iframe></%def>
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r 2ca0e99d6d6d940af0712e9c56b9f238fd59635c templates/webapps/galaxy/base_panels.mako
--- a/templates/webapps/galaxy/base_panels.mako
+++ b/templates/webapps/galaxy/base_panels.mako
@@ -136,7 +136,7 @@
# Menu for user who is not logged in.
menu_options = [ [ _("Login"), h.url_for( controller='/user', action='login' ), "galaxy_main" ] ]
if app.config.allow_user_creation:
- menu_options.append( [ _("Register"), h.url_for( controller='/user', action='create', cntrller='user', webapp='galaxy' ), "galaxy_main" ] )
+ menu_options.append( [ _("Register"), h.url_for( controller='/user', action='create', cntrller='user' ), "galaxy_main" ] )
extra_class = "loggedout-only"
visible = ( trans.user == None )
tab( "user", _("User"), None, visible=visible, menu_options=menu_options )
@@ -151,20 +151,20 @@
if app.config.remote_user_logout_href:
menu_options.append( [ _('Logout'), app.config.remote_user_logout_href, "_top" ] )
else:
- menu_options.append( [ _('Preferences'), h.url_for( controller='/user', action='index', cntrller='user', webapp='galaxy' ), "galaxy_main" ] )
+ menu_options.append( [ _('Preferences'), h.url_for( controller='/user', action='index', cntrller='user' ), "galaxy_main" ] )
menu_options.append( [ 'Custom Builds', h.url_for( controller='/user', action='dbkeys' ), "galaxy_main" ] )
if app.config.require_login:
- logout_url = h.url_for( controller='/root', action='index', m_c='user', m_a='logout', webapp='galaxy' )
+ logout_url = h.url_for( controller='/root', action='index', m_c='user', m_a='logout' )
else:
- logout_url = h.url_for( controller='/user', action='logout', webapp='galaxy' )
+ logout_url = h.url_for( controller='/user', action='logout' )
menu_options.append( [ 'Logout', logout_url, "_top" ] )
menu_options.append( None )
menu_options.append( [ _('Saved Histories'), h.url_for( controller='/history', action='list' ), "galaxy_main" ] )
menu_options.append( [ _('Saved Datasets'), h.url_for( controller='/dataset', action='list' ), "galaxy_main" ] )
menu_options.append( [ _('Saved Pages'), h.url_for( controller='/page', action='list' ), "_top" ] )
- menu_options.append( [ _('API Keys'), h.url_for( controller='/user', action='api_keys', cntrller='user', webapp='galaxy' ), "galaxy_main" ] )
+ menu_options.append( [ _('API Keys'), h.url_for( controller='/user', action='api_keys', cntrller='user' ), "galaxy_main" ] )
if app.config.use_remote_user:
- menu_options.append( [ _('Public Name'), h.url_for( controller='/user', action='edit_username', cntrller='user', webapp='galaxy' ), "galaxy_main" ] )
+ menu_options.append( [ _('Public Name'), h.url_for( controller='/user', action='edit_username', cntrller='user' ), "galaxy_main" ] )
extra_class = "loggedin-only"
visible = ( trans.user != None )
https://bitbucket.org/galaxy/galaxy-central/changeset/5b977db5fc50/
changeset: 5b977db5fc50
user: james_taylor
date: 2012-09-28 23:37:31
summary: Automated merge with https://bitbucket.org/galaxy/galaxy-central
affected #: 20 files
diff -r ee2feed164a9efc9201409b3e6191e2707ec3e18 -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -1525,1191 +1525,8 @@
class ControllerUnavailable( Exception ):
pass
-class Admin( object ):
- # Override these
- user_list_grid = None
- role_list_grid = None
- group_list_grid = None
- quota_list_grid = None
- repository_list_grid = None
- tool_version_list_grid = None
- delete_operation = None
- undelete_operation = None
- purge_operation = None
-
- @web.expose
- @web.require_admin
- def index( self, trans, **kwd ):
- webapp = get_webapp( trans, **kwd )
- message = kwd.get( 'message', '' )
- status = kwd.get( 'status', 'done' )
- if webapp == 'galaxy':
- installed_repositories = trans.sa_session.query( trans.model.ToolShedRepository ).first()
- installing_repository_ids = get_ids_of_tool_shed_repositories_being_installed( trans, as_string=True )
- return trans.fill_template( '/webapps/galaxy/admin/index.mako',
- webapp=webapp,
- installed_repositories=installed_repositories,
- installing_repository_ids=installing_repository_ids,
- message=message,
- status=status )
- else:
- return trans.fill_template( '/webapps/community/admin/index.mako',
- webapp=webapp,
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def center( self, trans, **kwd ):
- webapp = get_webapp( trans, **kwd )
- message = kwd.get( 'message', '' )
- status = kwd.get( 'status', 'done' )
- if webapp == 'galaxy':
- return trans.fill_template( '/webapps/galaxy/admin/center.mako',
- message=message,
- status=status )
- else:
- return trans.fill_template( '/webapps/community/admin/center.mako',
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def reload_tool( self, trans, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- toolbox = self.app.toolbox
- if params.get( 'reload_tool_button', False ):
- tool_id = params.tool_id
- message, status = toolbox.reload_tool_by_id( tool_id )
- return trans.fill_template( '/admin/reload_tool.mako',
- toolbox=toolbox,
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def tool_versions( self, trans, **kwd ):
- if 'message' not in kwd or not kwd[ 'message' ]:
- kwd[ 'message' ] = 'Tool ids for tools that are currently loaded into the tool panel are highlighted in green (click to display).'
- return self.tool_version_list_grid( trans, **kwd )
- # Galaxy Role Stuff
- @web.expose
- @web.require_admin
- def roles( self, trans, **kwargs ):
- if 'operation' in kwargs:
- operation = kwargs['operation'].lower()
- if operation == "roles":
- return self.role( trans, **kwargs )
- if operation == "create":
- return self.create_role( trans, **kwargs )
- if operation == "delete":
- return self.mark_role_deleted( trans, **kwargs )
- if operation == "undelete":
- return self.undelete_role( trans, **kwargs )
- if operation == "purge":
- return self.purge_role( trans, **kwargs )
- if operation == "manage users and groups":
- return self.manage_users_and_groups_for_role( trans, **kwargs )
- if operation == "rename":
- return self.rename_role( trans, **kwargs )
- # Render the list view
- return self.role_list_grid( trans, **kwargs )
- @web.expose
- @web.require_admin
- def create_role( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- name = util.restore_text( params.get( 'name', '' ) )
- description = util.restore_text( params.get( 'description', '' ) )
- in_users = util.listify( params.get( 'in_users', [] ) )
- out_users = util.listify( params.get( 'out_users', [] ) )
- in_groups = util.listify( params.get( 'in_groups', [] ) )
- out_groups = util.listify( params.get( 'out_groups', [] ) )
- create_group_for_role = params.get( 'create_group_for_role', '' )
- create_group_for_role_checked = CheckboxField.is_checked( create_group_for_role )
- ok = True
- if params.get( 'create_role_button', False ):
- if not name or not description:
- message = "Enter a valid name and a description."
- status = 'error'
- ok = False
- elif trans.sa_session.query( trans.app.model.Role ).filter( trans.app.model.Role.table.c.name==name ).first():
- message = "Role names must be unique and a role with that name already exists, so choose another name."
- status = 'error'
- ok = False
- else:
- # Create the role
- role = trans.app.model.Role( name=name, description=description, type=trans.app.model.Role.types.ADMIN )
- trans.sa_session.add( role )
- # Create the UserRoleAssociations
- for user in [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in in_users ]:
- ura = trans.app.model.UserRoleAssociation( user, role )
- trans.sa_session.add( ura )
- # Create the GroupRoleAssociations
- for group in [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in in_groups ]:
- gra = trans.app.model.GroupRoleAssociation( group, role )
- trans.sa_session.add( gra )
- if create_group_for_role_checked:
- # Create the group
- group = trans.app.model.Group( name=name )
- trans.sa_session.add( group )
- # Associate the group with the role
- gra = trans.model.GroupRoleAssociation( group, role )
- trans.sa_session.add( gra )
- num_in_groups = len( in_groups ) + 1
- else:
- num_in_groups = len( in_groups )
- trans.sa_session.flush()
- message = "Role '%s' has been created with %d associated users and %d associated groups. " \
- % ( role.name, len( in_users ), num_in_groups )
- if create_group_for_role_checked:
- message += 'One of the groups associated with this role is the newly created group with the same name.'
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- if ok:
- for user in trans.sa_session.query( trans.app.model.User ) \
- .filter( trans.app.model.User.table.c.deleted==False ) \
- .order_by( trans.app.model.User.table.c.email ):
- out_users.append( ( user.id, user.email ) )
- for group in trans.sa_session.query( trans.app.model.Group ) \
- .filter( trans.app.model.Group.table.c.deleted==False ) \
- .order_by( trans.app.model.Group.table.c.name ):
- out_groups.append( ( group.id, group.name ) )
- return trans.fill_template( '/admin/dataset_security/role/role_create.mako',
- webapp=webapp,
- name=name,
- description=description,
- in_users=in_users,
- out_users=out_users,
- in_groups=in_groups,
- out_groups=out_groups,
- create_group_for_role_checked=create_group_for_role_checked,
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def rename_role( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- id = params.get( 'id', None )
- if not id:
- message = "No role ids received for renaming"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=message,
- status='error' ) )
- role = get_role( trans, id )
- if params.get( 'rename_role_button', False ):
- old_name = role.name
- new_name = util.restore_text( params.name )
- new_description = util.restore_text( params.description )
- if not new_name:
- message = 'Enter a valid name'
- status='error'
- else:
- existing_role = trans.sa_session.query( trans.app.model.Role ).filter( trans.app.model.Role.table.c.name==new_name ).first()
- if existing_role and existing_role.id != role.id:
- message = 'A role with that name already exists'
- status = 'error'
- else:
- if not ( role.name == new_name and role.description == new_description ):
- role.name = new_name
- role.description = new_description
- trans.sa_session.add( role )
- trans.sa_session.flush()
- message = "Role '%s' has been renamed to '%s'" % ( old_name, new_name )
- return trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- return trans.fill_template( '/admin/dataset_security/role/role_rename.mako',
- role=role,
- webapp=webapp,
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def manage_users_and_groups_for_role( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- id = params.get( 'id', None )
- if not id:
- message = "No role ids received for managing users and groups"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=message,
- status='error' ) )
- role = get_role( trans, id )
- if params.get( 'role_members_edit_button', False ):
- in_users = [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in util.listify( params.in_users ) ]
- for ura in role.users:
- user = trans.sa_session.query( trans.app.model.User ).get( ura.user_id )
- if user not in in_users:
- # Delete DefaultUserPermissions for previously associated users that have been removed from the role
- for dup in user.default_permissions:
- if role == dup.role:
- trans.sa_session.delete( dup )
- # Delete DefaultHistoryPermissions for previously associated users that have been removed from the role
- for history in user.histories:
- for dhp in history.default_permissions:
- if role == dhp.role:
- trans.sa_session.delete( dhp )
- trans.sa_session.flush()
- in_groups = [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in util.listify( params.in_groups ) ]
- trans.app.security_agent.set_entity_role_associations( roles=[ role ], users=in_users, groups=in_groups )
- trans.sa_session.refresh( role )
- message = "Role '%s' has been updated with %d associated users and %d associated groups" % ( role.name, len( in_users ), len( in_groups ) )
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status=status ) )
- in_users = []
- out_users = []
- in_groups = []
- out_groups = []
- for user in trans.sa_session.query( trans.app.model.User ) \
- .filter( trans.app.model.User.table.c.deleted==False ) \
- .order_by( trans.app.model.User.table.c.email ):
- if user in [ x.user for x in role.users ]:
- in_users.append( ( user.id, user.email ) )
- else:
- out_users.append( ( user.id, user.email ) )
- for group in trans.sa_session.query( trans.app.model.Group ) \
- .filter( trans.app.model.Group.table.c.deleted==False ) \
- .order_by( trans.app.model.Group.table.c.name ):
- if group in [ x.group for x in role.groups ]:
- in_groups.append( ( group.id, group.name ) )
- else:
- out_groups.append( ( group.id, group.name ) )
- library_dataset_actions = {}
- if webapp == 'galaxy':
- # Build a list of tuples that are LibraryDatasetDatasetAssociationss followed by a list of actions
- # whose DatasetPermissions is associated with the Role
- # [ ( LibraryDatasetDatasetAssociation [ action, action ] ) ]
- for dp in role.dataset_actions:
- for ldda in trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ) \
- .filter( trans.app.model.LibraryDatasetDatasetAssociation.dataset_id==dp.dataset_id ):
- root_found = False
- folder_path = ''
- folder = ldda.library_dataset.folder
- while not root_found:
- folder_path = '%s / %s' % ( folder.name, folder_path )
- if not folder.parent:
- root_found = True
- else:
- folder = folder.parent
- folder_path = '%s %s' % ( folder_path, ldda.name )
- library = trans.sa_session.query( trans.app.model.Library ) \
- .filter( trans.app.model.Library.table.c.root_folder_id == folder.id ) \
- .first()
- if library not in library_dataset_actions:
- library_dataset_actions[ library ] = {}
- try:
- library_dataset_actions[ library ][ folder_path ].append( dp.action )
- except:
- library_dataset_actions[ library ][ folder_path ] = [ dp.action ]
- return trans.fill_template( '/admin/dataset_security/role/role.mako',
- role=role,
- in_users=in_users,
- out_users=out_users,
- in_groups=in_groups,
- out_groups=out_groups,
- library_dataset_actions=library_dataset_actions,
- webapp=webapp,
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def mark_role_deleted( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- id = kwd.get( 'id', None )
- if not id:
- message = "No role ids received for deleting"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=message,
- status='error' ) )
- ids = util.listify( id )
- message = "Deleted %d roles: " % len( ids )
- for role_id in ids:
- role = get_role( trans, role_id )
- role.deleted = True
- trans.sa_session.add( role )
- trans.sa_session.flush()
- message += " %s " % role.name
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- @web.expose
- @web.require_admin
- def undelete_role( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- id = kwd.get( 'id', None )
- if not id:
- message = "No role ids received for undeleting"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=message,
- status='error' ) )
- ids = util.listify( id )
- count = 0
- undeleted_roles = ""
- for role_id in ids:
- role = get_role( trans, role_id )
- if not role.deleted:
- message = "Role '%s' has not been deleted, so it cannot be undeleted." % role.name
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- role.deleted = False
- trans.sa_session.add( role )
- trans.sa_session.flush()
- count += 1
- undeleted_roles += " %s" % role.name
- message = "Undeleted %d roles: %s" % ( count, undeleted_roles )
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- @web.expose
- @web.require_admin
- def purge_role( self, trans, **kwd ):
- # This method should only be called for a Role that has previously been deleted.
- # Purging a deleted Role deletes all of the following from the database:
- # - UserRoleAssociations where role_id == Role.id
- # - DefaultUserPermissions where role_id == Role.id
- # - DefaultHistoryPermissions where role_id == Role.id
- # - GroupRoleAssociations where role_id == Role.id
- # - DatasetPermissionss where role_id == Role.id
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- id = kwd.get( 'id', None )
- if not id:
- message = "No role ids received for purging"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- ids = util.listify( id )
- message = "Purged %d roles: " % len( ids )
- for role_id in ids:
- role = get_role( trans, role_id )
- if not role.deleted:
- message = "Role '%s' has not been deleted, so it cannot be purged." % role.name
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- # Delete UserRoleAssociations
- for ura in role.users:
- user = trans.sa_session.query( trans.app.model.User ).get( ura.user_id )
- # Delete DefaultUserPermissions for associated users
- for dup in user.default_permissions:
- if role == dup.role:
- trans.sa_session.delete( dup )
- # Delete DefaultHistoryPermissions for associated users
- for history in user.histories:
- for dhp in history.default_permissions:
- if role == dhp.role:
- trans.sa_session.delete( dhp )
- trans.sa_session.delete( ura )
- # Delete GroupRoleAssociations
- for gra in role.groups:
- trans.sa_session.delete( gra )
- # Delete DatasetPermissionss
- for dp in role.dataset_actions:
- trans.sa_session.delete( dp )
- trans.sa_session.flush()
- message += " %s " % role.name
- trans.response.send_redirect( web.url_for( controller='admin',
- action='roles',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
-
- # Galaxy Group Stuff
- @web.expose
- @web.require_admin
- def groups( self, trans, **kwargs ):
- if 'operation' in kwargs:
- operation = kwargs['operation'].lower()
- if operation == "groups":
- return self.group( trans, **kwargs )
- if operation == "create":
- return self.create_group( trans, **kwargs )
- if operation == "delete":
- return self.mark_group_deleted( trans, **kwargs )
- if operation == "undelete":
- return self.undelete_group( trans, **kwargs )
- if operation == "purge":
- return self.purge_group( trans, **kwargs )
- if operation == "manage users and roles":
- return self.manage_users_and_roles_for_group( trans, **kwargs )
- if operation == "rename":
- return self.rename_group( trans, **kwargs )
- # Render the list view
- return self.group_list_grid( trans, **kwargs )
- @web.expose
- @web.require_admin
- def rename_group( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- id = params.get( 'id', None )
- if not id:
- message = "No group ids received for renaming"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=message,
- status='error' ) )
- group = get_group( trans, id )
- if params.get( 'rename_group_button', False ):
- old_name = group.name
- new_name = util.restore_text( params.name )
- if not new_name:
- message = 'Enter a valid name'
- status = 'error'
- else:
- existing_group = trans.sa_session.query( trans.app.model.Group ).filter( trans.app.model.Group.table.c.name==new_name ).first()
- if existing_group and existing_group.id != group.id:
- message = 'A group with that name already exists'
- status = 'error'
- else:
- if group.name != new_name:
- group.name = new_name
- trans.sa_session.add( group )
- trans.sa_session.flush()
- message = "Group '%s' has been renamed to '%s'" % ( old_name, new_name )
- return trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- return trans.fill_template( '/admin/dataset_security/group/group_rename.mako',
- group=group,
- webapp=webapp,
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def manage_users_and_roles_for_group( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- group = get_group( trans, params.id )
- if params.get( 'group_roles_users_edit_button', False ):
- in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( params.in_roles ) ]
- in_users = [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in util.listify( params.in_users ) ]
- trans.app.security_agent.set_entity_group_associations( groups=[ group ], roles=in_roles, users=in_users )
- trans.sa_session.refresh( group )
- message += "Group '%s' has been updated with %d associated roles and %d associated users" % ( group.name, len( in_roles ), len( in_users ) )
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status=status ) )
- in_roles = []
- out_roles = []
- in_users = []
- out_users = []
- for role in trans.sa_session.query(trans.app.model.Role ) \
- .filter( trans.app.model.Role.table.c.deleted==False ) \
- .order_by( trans.app.model.Role.table.c.name ):
- if role in [ x.role for x in group.roles ]:
- in_roles.append( ( role.id, role.name ) )
- else:
- out_roles.append( ( role.id, role.name ) )
- for user in trans.sa_session.query( trans.app.model.User ) \
- .filter( trans.app.model.User.table.c.deleted==False ) \
- .order_by( trans.app.model.User.table.c.email ):
- if user in [ x.user for x in group.users ]:
- in_users.append( ( user.id, user.email ) )
- else:
- out_users.append( ( user.id, user.email ) )
- message += 'Group %s is currently associated with %d roles and %d users' % ( group.name, len( in_roles ), len( in_users ) )
- return trans.fill_template( '/admin/dataset_security/group/group.mako',
- group=group,
- in_roles=in_roles,
- out_roles=out_roles,
- in_users=in_users,
- out_users=out_users,
- webapp=webapp,
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def create_group( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- name = util.restore_text( params.get( 'name', '' ) )
- in_users = util.listify( params.get( 'in_users', [] ) )
- out_users = util.listify( params.get( 'out_users', [] ) )
- in_roles = util.listify( params.get( 'in_roles', [] ) )
- out_roles = util.listify( params.get( 'out_roles', [] ) )
- create_role_for_group = params.get( 'create_role_for_group', '' )
- create_role_for_group_checked = CheckboxField.is_checked( create_role_for_group )
- ok = True
- if params.get( 'create_group_button', False ):
- if not name:
- message = "Enter a valid name."
- status = 'error'
- ok = False
- elif trans.sa_session.query( trans.app.model.Group ).filter( trans.app.model.Group.table.c.name==name ).first():
- message = "Group names must be unique and a group with that name already exists, so choose another name."
- status = 'error'
- ok = False
- else:
- # Create the group
- group = trans.app.model.Group( name=name )
- trans.sa_session.add( group )
- trans.sa_session.flush()
- # Create the UserRoleAssociations
- for user in [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in in_users ]:
- uga = trans.app.model.UserGroupAssociation( user, group )
- trans.sa_session.add( uga )
- # Create the GroupRoleAssociations
- for role in [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in in_roles ]:
- gra = trans.app.model.GroupRoleAssociation( group, role )
- trans.sa_session.add( gra )
- if create_role_for_group_checked:
- # Create the role
- role = trans.app.model.Role( name=name, description='Role for group %s' % name )
- trans.sa_session.add( role )
- # Associate the role with the group
- gra = trans.model.GroupRoleAssociation( group, role )
- trans.sa_session.add( gra )
- num_in_roles = len( in_roles ) + 1
- else:
- num_in_roles = len( in_roles )
- trans.sa_session.flush()
- message = "Group '%s' has been created with %d associated users and %d associated roles. " \
- % ( group.name, len( in_users ), num_in_roles )
- if create_role_for_group_checked:
- message += 'One of the roles associated with this group is the newly created role with the same name.'
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
-
-
- if ok:
- for user in trans.sa_session.query( trans.app.model.User ) \
- .filter( trans.app.model.User.table.c.deleted==False ) \
- .order_by( trans.app.model.User.table.c.email ):
- out_users.append( ( user.id, user.email ) )
- for role in trans.sa_session.query( trans.app.model.Role ) \
- .filter( trans.app.model.Role.table.c.deleted==False ) \
- .order_by( trans.app.model.Role.table.c.name ):
- out_roles.append( ( role.id, role.name ) )
- return trans.fill_template( '/admin/dataset_security/group/group_create.mako',
- webapp=webapp,
- name=name,
- in_users=in_users,
- out_users=out_users,
- in_roles=in_roles,
- out_roles=out_roles,
- create_role_for_group_checked=create_role_for_group_checked,
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def mark_group_deleted( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- id = params.get( 'id', None )
- if not id:
- message = "No group ids received for marking deleted"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=message,
- status='error' ) )
- ids = util.listify( id )
- message = "Deleted %d groups: " % len( ids )
- for group_id in ids:
- group = get_group( trans, group_id )
- group.deleted = True
- trans.sa_session.add( group )
- trans.sa_session.flush()
- message += " %s " % group.name
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- @web.expose
- @web.require_admin
- def undelete_group( self, trans, **kwd ):
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- id = kwd.get( 'id', None )
- if not id:
- message = "No group ids received for undeleting"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=message,
- status='error' ) )
- ids = util.listify( id )
- count = 0
- undeleted_groups = ""
- for group_id in ids:
- group = get_group( trans, group_id )
- if not group.deleted:
- message = "Group '%s' has not been deleted, so it cannot be undeleted." % group.name
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- group.deleted = False
- trans.sa_session.add( group )
- trans.sa_session.flush()
- count += 1
- undeleted_groups += " %s" % group.name
- message = "Undeleted %d groups: %s" % ( count, undeleted_groups )
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- @web.expose
- @web.require_admin
- def purge_group( self, trans, **kwd ):
- # This method should only be called for a Group that has previously been deleted.
- # Purging a deleted Group simply deletes all UserGroupAssociations and GroupRoleAssociations.
- params = util.Params( kwd )
- webapp = get_webapp( trans, **kwd )
- id = kwd.get( 'id', None )
- if not id:
- message = "No group ids received for purging"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- ids = util.listify( id )
- message = "Purged %d groups: " % len( ids )
- for group_id in ids:
- group = get_group( trans, group_id )
- if not group.deleted:
- # We should never reach here, but just in case there is a bug somewhere...
- message = "Group '%s' has not been deleted, so it cannot be purged." % group.name
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- # Delete UserGroupAssociations
- for uga in group.users:
- trans.sa_session.delete( uga )
- # Delete GroupRoleAssociations
- for gra in group.roles:
- trans.sa_session.delete( gra )
- trans.sa_session.flush()
- message += " %s " % group.name
- trans.response.send_redirect( web.url_for( controller='admin',
- action='groups',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
-
- # Galaxy User Stuff
- @web.expose
- @web.require_admin
- def create_new_user( self, trans, **kwd ):
- webapp = get_webapp( trans, **kwd )
- return trans.response.send_redirect( web.url_for( controller='user',
- action='create',
- cntrller='admin',
- webapp=webapp ) )
- @web.expose
- @web.require_admin
- def reset_user_password( self, trans, **kwd ):
- webapp = get_webapp( trans, **kwd )
- user_id = kwd.get( 'id', None )
- if not user_id:
- message = "No users received for resetting passwords."
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=message,
- status='error' ) )
- user_ids = util.listify( user_id )
- if 'reset_user_password_button' in kwd:
- message = ''
- status = ''
- for user_id in user_ids:
- user = get_user( trans, user_id )
- password = kwd.get( 'password', None )
- confirm = kwd.get( 'confirm' , None )
- if len( password ) < 6:
- message = "Use a password of at least 6 characters."
- status = 'error'
- break
- elif password != confirm:
- message = "Passwords do not match."
- status = 'error'
- break
- else:
- user.set_password_cleartext( password )
- trans.sa_session.add( user )
- trans.sa_session.flush()
- if not message and not status:
- message = "Passwords reset for %d %s." % ( len( user_ids ), inflector.cond_plural( len( user_ids ), 'user' ) )
- status = 'done'
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status=status ) )
- users = [ get_user( trans, user_id ) for user_id in user_ids ]
- if len( user_ids ) > 1:
- user_id = ','.join( user_ids )
- return trans.fill_template( '/admin/user/reset_password.mako',
- id=user_id,
- users=users,
- password='',
- confirm='',
- webapp=webapp )
- @web.expose
- @web.require_admin
- def mark_user_deleted( self, trans, **kwd ):
- webapp = get_webapp( trans, **kwd )
- id = kwd.get( 'id', None )
- if not id:
- message = "No user ids received for deleting"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=message,
- status='error' ) )
- ids = util.listify( id )
- message = "Deleted %d users: " % len( ids )
- for user_id in ids:
- user = get_user( trans, user_id )
- user.deleted = True
- trans.sa_session.add( user )
- trans.sa_session.flush()
- message += " %s " % user.email
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- @web.expose
- @web.require_admin
- def undelete_user( self, trans, **kwd ):
- webapp = get_webapp( trans, **kwd )
- id = kwd.get( 'id', None )
- if not id:
- message = "No user ids received for undeleting"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=message,
- status='error' ) )
- ids = util.listify( id )
- count = 0
- undeleted_users = ""
- for user_id in ids:
- user = get_user( trans, user_id )
- if not user.deleted:
- message = "User '%s' has not been deleted, so it cannot be undeleted." % user.email
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- user.deleted = False
- trans.sa_session.add( user )
- trans.sa_session.flush()
- count += 1
- undeleted_users += " %s" % user.email
- message = "Undeleted %d users: %s" % ( count, undeleted_users )
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- @web.expose
- @web.require_admin
- def purge_user( self, trans, **kwd ):
- # This method should only be called for a User that has previously been deleted.
- # We keep the User in the database ( marked as purged ), and stuff associated
- # with the user's private role in case we want the ability to unpurge the user
- # some time in the future.
- # Purging a deleted User deletes all of the following:
- # - History where user_id = User.id
- # - HistoryDatasetAssociation where history_id = History.id
- # - Dataset where HistoryDatasetAssociation.dataset_id = Dataset.id
- # - UserGroupAssociation where user_id == User.id
- # - UserRoleAssociation where user_id == User.id EXCEPT FOR THE PRIVATE ROLE
- # - UserAddress where user_id == User.id
- # Purging Histories and Datasets must be handled via the cleanup_datasets.py script
- webapp = get_webapp( trans, **kwd )
- id = kwd.get( 'id', None )
- if not id:
- message = "No user ids received for purging"
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- ids = util.listify( id )
- message = "Purged %d users: " % len( ids )
- for user_id in ids:
- user = get_user( trans, user_id )
- if not user.deleted:
- # We should never reach here, but just in case there is a bug somewhere...
- message = "User '%s' has not been deleted, so it cannot be purged." % user.email
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- private_role = trans.app.security_agent.get_private_user_role( user )
- # Delete History
- for h in user.active_histories:
- trans.sa_session.refresh( h )
- for hda in h.active_datasets:
- # Delete HistoryDatasetAssociation
- d = trans.sa_session.query( trans.app.model.Dataset ).get( hda.dataset_id )
- # Delete Dataset
- if not d.deleted:
- d.deleted = True
- trans.sa_session.add( d )
- hda.deleted = True
- trans.sa_session.add( hda )
- h.deleted = True
- trans.sa_session.add( h )
- # Delete UserGroupAssociations
- for uga in user.groups:
- trans.sa_session.delete( uga )
- # Delete UserRoleAssociations EXCEPT FOR THE PRIVATE ROLE
- for ura in user.roles:
- if ura.role_id != private_role.id:
- trans.sa_session.delete( ura )
- # Delete UserAddresses
- for address in user.addresses:
- trans.sa_session.delete( address )
- # Purge the user
- user.purged = True
- trans.sa_session.add( user )
- trans.sa_session.flush()
- message += "%s " % user.email
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- @web.expose
- @web.require_admin
- def users( self, trans, **kwd ):
- if 'operation' in kwd:
- operation = kwd['operation'].lower()
- if operation == "roles":
- return self.user( trans, **kwd )
- elif operation == "reset password":
- return self.reset_user_password( trans, **kwd )
- elif operation == "delete":
- return self.mark_user_deleted( trans, **kwd )
- elif operation == "undelete":
- return self.undelete_user( trans, **kwd )
- elif operation == "purge":
- return self.purge_user( trans, **kwd )
- elif operation == "create":
- return self.create_new_user( trans, **kwd )
- elif operation == "information":
- user_id = kwd.get( 'id', None )
- if not user_id:
- kwd[ 'message' ] = util.sanitize_text( "Invalid user id (%s) received" % str( user_id ) )
- kwd[ 'status' ] = 'error'
- else:
- return trans.response.send_redirect( web.url_for( controller='user',
- action='manage_user_info',
- cntrller='admin',
- **kwd ) )
- elif operation == "manage roles and groups":
- return self.manage_roles_and_groups_for_user( trans, **kwd )
- if trans.app.config.allow_user_deletion:
- if self.delete_operation not in self.user_list_grid.operations:
- self.user_list_grid.operations.append( self.delete_operation )
- if self.undelete_operation not in self.user_list_grid.operations:
- self.user_list_grid.operations.append( self.undelete_operation )
- if self.purge_operation not in self.user_list_grid.operations:
- self.user_list_grid.operations.append( self.purge_operation )
- # Render the list view
- return self.user_list_grid( trans, **kwd )
- @web.expose
- @web.require_admin
- def name_autocomplete_data( self, trans, q=None, limit=None, timestamp=None ):
- """Return autocomplete data for user emails"""
- ac_data = ""
- for user in trans.sa_session.query( User ).filter_by( deleted=False ).filter( func.lower( User.email ).like( q.lower() + "%" ) ):
- ac_data = ac_data + user.email + "\n"
- return ac_data
- @web.expose
- @web.require_admin
- def manage_roles_and_groups_for_user( self, trans, **kwd ):
- webapp = get_webapp( trans, **kwd )
- user_id = kwd.get( 'id', None )
- message = ''
- status = ''
- if not user_id:
- message += "Invalid user id (%s) received" % str( user_id )
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='error' ) )
- user = get_user( trans, user_id )
- private_role = trans.app.security_agent.get_private_user_role( user )
- if kwd.get( 'user_roles_groups_edit_button', False ):
- # Make sure the user is not dis-associating himself from his private role
- out_roles = kwd.get( 'out_roles', [] )
- if out_roles:
- out_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( out_roles ) ]
- if private_role in out_roles:
- message += "You cannot eliminate a user's private role association. "
- status = 'error'
- in_roles = kwd.get( 'in_roles', [] )
- if in_roles:
- in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( in_roles ) ]
- out_groups = kwd.get( 'out_groups', [] )
- if out_groups:
- out_groups = [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in util.listify( out_groups ) ]
- in_groups = kwd.get( 'in_groups', [] )
- if in_groups:
- in_groups = [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in util.listify( in_groups ) ]
- if in_roles:
- trans.app.security_agent.set_entity_user_associations( users=[ user ], roles=in_roles, groups=in_groups )
- trans.sa_session.refresh( user )
- message += "User '%s' has been updated with %d associated roles and %d associated groups (private roles are not displayed)" % \
- ( user.email, len( in_roles ), len( in_groups ) )
- trans.response.send_redirect( web.url_for( controller='admin',
- action='users',
- webapp=webapp,
- message=util.sanitize_text( message ),
- status='done' ) )
- in_roles = []
- out_roles = []
- in_groups = []
- out_groups = []
- for role in trans.sa_session.query( trans.app.model.Role ).filter( trans.app.model.Role.table.c.deleted==False ) \
- .order_by( trans.app.model.Role.table.c.name ):
- if role in [ x.role for x in user.roles ]:
- in_roles.append( ( role.id, role.name ) )
- elif role.type != trans.app.model.Role.types.PRIVATE:
- # There is a 1 to 1 mapping between a user and a PRIVATE role, so private roles should
- # not be listed in the roles form fields, except for the currently selected user's private
- # role, which should always be in in_roles. The check above is added as an additional
- # precaution, since for a period of time we were including private roles in the form fields.
- out_roles.append( ( role.id, role.name ) )
- for group in trans.sa_session.query( trans.app.model.Group ).filter( trans.app.model.Group.table.c.deleted==False ) \
- .order_by( trans.app.model.Group.table.c.name ):
- if group in [ x.group for x in user.groups ]:
- in_groups.append( ( group.id, group.name ) )
- else:
- out_groups.append( ( group.id, group.name ) )
- message += "User '%s' is currently associated with %d roles and is a member of %d groups" % \
- ( user.email, len( in_roles ), len( in_groups ) )
- if not status:
- status = 'done'
- return trans.fill_template( '/admin/user/user.mako',
- user=user,
- in_roles=in_roles,
- out_roles=out_roles,
- in_groups=in_groups,
- out_groups=out_groups,
- webapp=webapp,
- message=message,
- status=status )
- @web.expose
- @web.require_admin
- def memdump( self, trans, ids = 'None', sorts = 'None', pages = 'None', new_id = None, new_sort = None, **kwd ):
- if self.app.memdump is None:
- return trans.show_error_message( "Memdump is not enabled (set <code>use_memdump = True</code> in universe_wsgi.ini)" )
- heap = self.app.memdump.get()
- p = util.Params( kwd )
- msg = None
- if p.dump:
- heap = self.app.memdump.get( update = True )
- msg = "Heap dump complete"
- elif p.setref:
- self.app.memdump.setref()
- msg = "Reference point set (dump to see delta from this point)"
- ids = ids.split( ',' )
- sorts = sorts.split( ',' )
- if new_id is not None:
- ids.append( new_id )
- sorts.append( 'None' )
- elif new_sort is not None:
- sorts[-1] = new_sort
- breadcrumb = "<a href='%s' class='breadcrumb'>heap</a>" % web.url_for()
- # new lists so we can assemble breadcrumb links
- new_ids = []
- new_sorts = []
- for id, sort in zip( ids, sorts ):
- new_ids.append( id )
- if id != 'None':
- breadcrumb += "<a href='%s' class='breadcrumb'>[%s]</a>" % ( web.url_for( ids=','.join( new_ids ), sorts=','.join( new_sorts ) ), id )
- heap = heap[int(id)]
- new_sorts.append( sort )
- if sort != 'None':
- breadcrumb += "<a href='%s' class='breadcrumb'>.by('%s')</a>" % ( web.url_for( ids=','.join( new_ids ), sorts=','.join( new_sorts ) ), sort )
- heap = heap.by( sort )
- ids = ','.join( new_ids )
- sorts = ','.join( new_sorts )
- if p.theone:
- breadcrumb += ".theone"
- heap = heap.theone
- return trans.fill_template( '/admin/memdump.mako', heap = heap, ids = ids, sorts = sorts, breadcrumb = breadcrumb, msg = msg )
-
- @web.expose
- @web.require_admin
- def jobs( self, trans, stop = [], stop_msg = None, cutoff = 180, job_lock = None, ajl_submit = None, **kwd ):
- deleted = []
- msg = None
- status = None
- if self.app.config.job_manager != self.app.config.server_name:
- return trans.show_error_message( 'This Galaxy instance (%s) is not the job manager (%s). If using multiple servers, please directly access the job manager instance to manage jobs.' % (self.app.config.server_name, self.app.config.job_manager) )
- job_ids = util.listify( stop )
- if job_ids and stop_msg in [ None, '' ]:
- msg = 'Please enter an error message to display to the user describing why the job was terminated'
- status = 'error'
- elif job_ids:
- if stop_msg[-1] not in string.punctuation:
- stop_msg += '.'
- for job_id in job_ids:
- trans.app.job_manager.job_stop_queue.put( job_id, error_msg="This job was stopped by an administrator: %s For more information or help" % stop_msg )
- deleted.append( str( job_id ) )
- if deleted:
- msg = 'Queued job'
- if len( deleted ) > 1:
- msg += 's'
- msg += ' for deletion: '
- msg += ', '.join( deleted )
- status = 'done'
- if ajl_submit:
- if job_lock == 'on':
- trans.app.job_manager.job_queue.job_lock = True
- else:
- trans.app.job_manager.job_queue.job_lock = False
- cutoff_time = datetime.utcnow() - timedelta( seconds=int( cutoff ) )
- 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.NEW,
- 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() )
- last_updated = {}
- for job in jobs:
- delta = datetime.utcnow() - job.update_time
- if delta > timedelta( minutes=60 ):
- last_updated[job.id] = '%s hours' % int( delta.seconds / 60 / 60 )
- else:
- last_updated[job.id] = '%s minutes' % int( delta.seconds / 60 )
- return trans.fill_template( '/admin/jobs.mako',
- jobs = jobs,
- last_updated = last_updated,
- cutoff = cutoff,
- msg = msg,
- status = status,
- job_lock = trans.app.job_manager.job_queue.job_lock )
-
## ---- Utility methods -------------------------------------------------------
-def get_ids_of_tool_shed_repositories_being_installed( trans, as_string=False ):
- installing_repository_ids = []
- new_status = trans.model.ToolShedRepository.installation_status.NEW
- cloning_status = trans.model.ToolShedRepository.installation_status.CLONING
- setting_tool_versions_status = trans.model.ToolShedRepository.installation_status.SETTING_TOOL_VERSIONS
- installing_dependencies_status = trans.model.ToolShedRepository.installation_status.INSTALLING_TOOL_DEPENDENCIES
- loading_datatypes_status = trans.model.ToolShedRepository.installation_status.LOADING_PROPRIETARY_DATATYPES
- for tool_shed_repository in trans.sa_session.query( trans.model.ToolShedRepository ) \
- .filter( or_( trans.model.ToolShedRepository.status == new_status,
- trans.model.ToolShedRepository.status == cloning_status,
- trans.model.ToolShedRepository.status == setting_tool_versions_status,
- trans.model.ToolShedRepository.status == installing_dependencies_status,
- trans.model.ToolShedRepository.status == loading_datatypes_status ) ):
- installing_repository_ids.append( trans.security.encode_id( tool_shed_repository.id ) )
- if as_string:
- return ','.join( installing_repository_ids )
- return installing_repository_ids
-
-def get_user( trans, user_id ):
- """Get a User from the database by id."""
- user = trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( user_id ) )
- if not user:
- return trans.show_error_message( "User not found for id (%s)" % str( user_id ) )
- return user
-def get_user_by_username( trans, username ):
- """Get a user from the database by username"""
- # TODO: Add exception handling here.
- return trans.sa_session.query( trans.model.User ) \
- .filter( trans.model.User.table.c.username == username ) \
- .one()
-def get_role( trans, id ):
- """Get a Role from the database by id."""
- # Load user from database
- id = trans.security.decode_id( id )
- role = trans.sa_session.query( trans.model.Role ).get( id )
- if not role:
- return trans.show_error_message( "Role not found for id (%s)" % str( id ) )
- return role
-def get_group( trans, id ):
- """Get a Group from the database by id."""
- # Load user from database
- id = trans.security.decode_id( id )
- group = trans.sa_session.query( trans.model.Group ).get( id )
- if not group:
- return trans.show_error_message( "Group not found for id (%s)" % str( id ) )
- return group
-def get_quota( trans, id ):
- """Get a Quota from the database by id."""
- # Load user from database
- id = trans.security.decode_id( id )
- quota = trans.sa_session.query( trans.model.Quota ).get( id )
- return quota
-def get_webapp( trans, **kwd ):
- """Get the value of the webapp, can be one of 'community', 'galaxy', 'reports', 'demo_sequencer'."""
- if 'webapp' in kwd:
- return kwd[ 'webapp' ]
- if 'webapp' in trans.environ:
- return trans.environ[ 'webapp' ]
- # The default is galaxy.
- return 'galaxy'
def sort_by_attr( seq, attr ):
"""
Sort the sequence of objects by object's attribute
diff -r ee2feed164a9efc9201409b3e6191e2707ec3e18 -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 lib/galaxy/web/base/controllers/admin.py
--- /dev/null
+++ b/lib/galaxy/web/base/controllers/admin.py
@@ -0,0 +1,1114 @@
+from datetime import date, datetime, timedelta
+
+from galaxy import config, tools, web, util
+from galaxy.model.orm import *
+
+class Admin( object ):
+ # Override these
+ user_list_grid = None
+ role_list_grid = None
+ group_list_grid = None
+ quota_list_grid = None
+ repository_list_grid = None
+ tool_version_list_grid = None
+ delete_operation = None
+ undelete_operation = None
+ purge_operation = None
+
+ @web.expose
+ @web.require_admin
+ def index( self, trans, **kwd ):
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ if trans.webapp.name == 'galaxy':
+ installed_repositories = trans.sa_session.query( trans.model.ToolShedRepository ).first()
+ installing_repository_ids = get_ids_of_tool_shed_repositories_being_installed( trans, as_string=True )
+ return trans.fill_template( '/webapps/galaxy/admin/index.mako',
+ installed_repositories=installed_repositories,
+ installing_repository_ids=installing_repository_ids,
+ message=message,
+ status=status )
+ else:
+ return trans.fill_template( '/webapps/community/admin/index.mako',
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def center( self, trans, **kwd ):
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ if trans.webapp.name == 'galaxy':
+ return trans.fill_template( '/webapps/galaxy/admin/center.mako',
+ message=message,
+ status=status )
+ else:
+ return trans.fill_template( '/webapps/community/admin/center.mako',
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def reload_tool( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ toolbox = self.app.toolbox
+ if params.get( 'reload_tool_button', False ):
+ tool_id = params.tool_id
+ message, status = toolbox.reload_tool_by_id( tool_id )
+ return trans.fill_template( '/admin/reload_tool.mako',
+ toolbox=toolbox,
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def tool_versions( self, trans, **kwd ):
+ if 'message' not in kwd or not kwd[ 'message' ]:
+ kwd[ 'message' ] = 'Tool ids for tools that are currently loaded into the tool panel are highlighted in green (click to display).'
+ return self.tool_version_list_grid( trans, **kwd )
+ # Galaxy Role Stuff
+ @web.expose
+ @web.require_admin
+ def roles( self, trans, **kwargs ):
+ if 'operation' in kwargs:
+ operation = kwargs['operation'].lower()
+ if operation == "roles":
+ return self.role( trans, **kwargs )
+ if operation == "create":
+ return self.create_role( trans, **kwargs )
+ if operation == "delete":
+ return self.mark_role_deleted( trans, **kwargs )
+ if operation == "undelete":
+ return self.undelete_role( trans, **kwargs )
+ if operation == "purge":
+ return self.purge_role( trans, **kwargs )
+ if operation == "manage users and groups":
+ return self.manage_users_and_groups_for_role( trans, **kwargs )
+ if operation == "rename":
+ return self.rename_role( trans, **kwargs )
+ # Render the list view
+ return self.role_list_grid( trans, **kwargs )
+ @web.expose
+ @web.require_admin
+ def create_role( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ name = util.restore_text( params.get( 'name', '' ) )
+ description = util.restore_text( params.get( 'description', '' ) )
+ in_users = util.listify( params.get( 'in_users', [] ) )
+ out_users = util.listify( params.get( 'out_users', [] ) )
+ in_groups = util.listify( params.get( 'in_groups', [] ) )
+ out_groups = util.listify( params.get( 'out_groups', [] ) )
+ create_group_for_role = params.get( 'create_group_for_role', '' )
+ create_group_for_role_checked = CheckboxField.is_checked( create_group_for_role )
+ ok = True
+ if params.get( 'create_role_button', False ):
+ if not name or not description:
+ message = "Enter a valid name and a description."
+ status = 'error'
+ ok = False
+ elif trans.sa_session.query( trans.app.model.Role ).filter( trans.app.model.Role.table.c.name==name ).first():
+ message = "Role names must be unique and a role with that name already exists, so choose another name."
+ status = 'error'
+ ok = False
+ else:
+ # Create the role
+ role = trans.app.model.Role( name=name, description=description, type=trans.app.model.Role.types.ADMIN )
+ trans.sa_session.add( role )
+ # Create the UserRoleAssociations
+ for user in [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in in_users ]:
+ ura = trans.app.model.UserRoleAssociation( user, role )
+ trans.sa_session.add( ura )
+ # Create the GroupRoleAssociations
+ for group in [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in in_groups ]:
+ gra = trans.app.model.GroupRoleAssociation( group, role )
+ trans.sa_session.add( gra )
+ if create_group_for_role_checked:
+ # Create the group
+ group = trans.app.model.Group( name=name )
+ trans.sa_session.add( group )
+ # Associate the group with the role
+ gra = trans.model.GroupRoleAssociation( group, role )
+ trans.sa_session.add( gra )
+ num_in_groups = len( in_groups ) + 1
+ else:
+ num_in_groups = len( in_groups )
+ trans.sa_session.flush()
+ message = "Role '%s' has been created with %d associated users and %d associated groups. " \
+ % ( role.name, len( in_users ), num_in_groups )
+ if create_group_for_role_checked:
+ message += 'One of the groups associated with this role is the newly created group with the same name.'
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ if ok:
+ for user in trans.sa_session.query( trans.app.model.User ) \
+ .filter( trans.app.model.User.table.c.deleted==False ) \
+ .order_by( trans.app.model.User.table.c.email ):
+ out_users.append( ( user.id, user.email ) )
+ for group in trans.sa_session.query( trans.app.model.Group ) \
+ .filter( trans.app.model.Group.table.c.deleted==False ) \
+ .order_by( trans.app.model.Group.table.c.name ):
+ out_groups.append( ( group.id, group.name ) )
+ return trans.fill_template( '/admin/dataset_security/role/role_create.mako',
+ name=name,
+ description=description,
+ in_users=in_users,
+ out_users=out_users,
+ in_groups=in_groups,
+ out_groups=out_groups,
+ create_group_for_role_checked=create_group_for_role_checked,
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def rename_role( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ id = params.get( 'id', None )
+ if not id:
+ message = "No role ids received for renaming"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=message,
+ status='error' ) )
+ role = get_role( trans, id )
+ if params.get( 'rename_role_button', False ):
+ old_name = role.name
+ new_name = util.restore_text( params.name )
+ new_description = util.restore_text( params.description )
+ if not new_name:
+ message = 'Enter a valid name'
+ status='error'
+ else:
+ existing_role = trans.sa_session.query( trans.app.model.Role ).filter( trans.app.model.Role.table.c.name==new_name ).first()
+ if existing_role and existing_role.id != role.id:
+ message = 'A role with that name already exists'
+ status = 'error'
+ else:
+ if not ( role.name == new_name and role.description == new_description ):
+ role.name = new_name
+ role.description = new_description
+ trans.sa_session.add( role )
+ trans.sa_session.flush()
+ message = "Role '%s' has been renamed to '%s'" % ( old_name, new_name )
+ return trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ return trans.fill_template( '/admin/dataset_security/role/role_rename.mako',
+ role=role,
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def manage_users_and_groups_for_role( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ id = params.get( 'id', None )
+ if not id:
+ message = "No role ids received for managing users and groups"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=message,
+ status='error' ) )
+ role = get_role( trans, id )
+ if params.get( 'role_members_edit_button', False ):
+ in_users = [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in util.listify( params.in_users ) ]
+ for ura in role.users:
+ user = trans.sa_session.query( trans.app.model.User ).get( ura.user_id )
+ if user not in in_users:
+ # Delete DefaultUserPermissions for previously associated users that have been removed from the role
+ for dup in user.default_permissions:
+ if role == dup.role:
+ trans.sa_session.delete( dup )
+ # Delete DefaultHistoryPermissions for previously associated users that have been removed from the role
+ for history in user.histories:
+ for dhp in history.default_permissions:
+ if role == dhp.role:
+ trans.sa_session.delete( dhp )
+ trans.sa_session.flush()
+ in_groups = [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in util.listify( params.in_groups ) ]
+ trans.app.security_agent.set_entity_role_associations( roles=[ role ], users=in_users, groups=in_groups )
+ trans.sa_session.refresh( role )
+ message = "Role '%s' has been updated with %d associated users and %d associated groups" % ( role.name, len( in_users ), len( in_groups ) )
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=util.sanitize_text( message ),
+ status=status ) )
+ in_users = []
+ out_users = []
+ in_groups = []
+ out_groups = []
+ for user in trans.sa_session.query( trans.app.model.User ) \
+ .filter( trans.app.model.User.table.c.deleted==False ) \
+ .order_by( trans.app.model.User.table.c.email ):
+ if user in [ x.user for x in role.users ]:
+ in_users.append( ( user.id, user.email ) )
+ else:
+ out_users.append( ( user.id, user.email ) )
+ for group in trans.sa_session.query( trans.app.model.Group ) \
+ .filter( trans.app.model.Group.table.c.deleted==False ) \
+ .order_by( trans.app.model.Group.table.c.name ):
+ if group in [ x.group for x in role.groups ]:
+ in_groups.append( ( group.id, group.name ) )
+ else:
+ out_groups.append( ( group.id, group.name ) )
+ library_dataset_actions = {}
+ if trans.webapp.name == 'galaxy':
+ # Build a list of tuples that are LibraryDatasetDatasetAssociationss followed by a list of actions
+ # whose DatasetPermissions is associated with the Role
+ # [ ( LibraryDatasetDatasetAssociation [ action, action ] ) ]
+ for dp in role.dataset_actions:
+ for ldda in trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ) \
+ .filter( trans.app.model.LibraryDatasetDatasetAssociation.dataset_id==dp.dataset_id ):
+ root_found = False
+ folder_path = ''
+ folder = ldda.library_dataset.folder
+ while not root_found:
+ folder_path = '%s / %s' % ( folder.name, folder_path )
+ if not folder.parent:
+ root_found = True
+ else:
+ folder = folder.parent
+ folder_path = '%s %s' % ( folder_path, ldda.name )
+ library = trans.sa_session.query( trans.app.model.Library ) \
+ .filter( trans.app.model.Library.table.c.root_folder_id == folder.id ) \
+ .first()
+ if library not in library_dataset_actions:
+ library_dataset_actions[ library ] = {}
+ try:
+ library_dataset_actions[ library ][ folder_path ].append( dp.action )
+ except:
+ library_dataset_actions[ library ][ folder_path ] = [ dp.action ]
+ return trans.fill_template( '/admin/dataset_security/role/role.mako',
+ role=role,
+ in_users=in_users,
+ out_users=out_users,
+ in_groups=in_groups,
+ out_groups=out_groups,
+ library_dataset_actions=library_dataset_actions,
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def mark_role_deleted( self, trans, **kwd ):
+ params = util.Params( kwd )
+ id = kwd.get( 'id', None )
+ if not id:
+ message = "No role ids received for deleting"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=message,
+ status='error' ) )
+ ids = util.listify( id )
+ message = "Deleted %d roles: " % len( ids )
+ for role_id in ids:
+ role = get_role( trans, role_id )
+ role.deleted = True
+ trans.sa_session.add( role )
+ trans.sa_session.flush()
+ message += " %s " % role.name
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ @web.expose
+ @web.require_admin
+ def undelete_role( self, trans, **kwd ):
+ params = util.Params( kwd )
+ id = kwd.get( 'id', None )
+ if not id:
+ message = "No role ids received for undeleting"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=message,
+ status='error' ) )
+ ids = util.listify( id )
+ count = 0
+ undeleted_roles = ""
+ for role_id in ids:
+ role = get_role( trans, role_id )
+ if not role.deleted:
+ message = "Role '%s' has not been deleted, so it cannot be undeleted." % role.name
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ role.deleted = False
+ trans.sa_session.add( role )
+ trans.sa_session.flush()
+ count += 1
+ undeleted_roles += " %s" % role.name
+ message = "Undeleted %d roles: %s" % ( count, undeleted_roles )
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ @web.expose
+ @web.require_admin
+ def purge_role( self, trans, **kwd ):
+ # This method should only be called for a Role that has previously been deleted.
+ # Purging a deleted Role deletes all of the following from the database:
+ # - UserRoleAssociations where role_id == Role.id
+ # - DefaultUserPermissions where role_id == Role.id
+ # - DefaultHistoryPermissions where role_id == Role.id
+ # - GroupRoleAssociations where role_id == Role.id
+ # - DatasetPermissionss where role_id == Role.id
+ params = util.Params( kwd )
+ id = kwd.get( 'id', None )
+ if not id:
+ message = "No role ids received for purging"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ ids = util.listify( id )
+ message = "Purged %d roles: " % len( ids )
+ for role_id in ids:
+ role = get_role( trans, role_id )
+ if not role.deleted:
+ message = "Role '%s' has not been deleted, so it cannot be purged." % role.name
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ # Delete UserRoleAssociations
+ for ura in role.users:
+ user = trans.sa_session.query( trans.app.model.User ).get( ura.user_id )
+ # Delete DefaultUserPermissions for associated users
+ for dup in user.default_permissions:
+ if role == dup.role:
+ trans.sa_session.delete( dup )
+ # Delete DefaultHistoryPermissions for associated users
+ for history in user.histories:
+ for dhp in history.default_permissions:
+ if role == dhp.role:
+ trans.sa_session.delete( dhp )
+ trans.sa_session.delete( ura )
+ # Delete GroupRoleAssociations
+ for gra in role.groups:
+ trans.sa_session.delete( gra )
+ # Delete DatasetPermissionss
+ for dp in role.dataset_actions:
+ trans.sa_session.delete( dp )
+ trans.sa_session.flush()
+ message += " %s " % role.name
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='roles',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+
+ # Galaxy Group Stuff
+ @web.expose
+ @web.require_admin
+ def groups( self, trans, **kwargs ):
+ if 'operation' in kwargs:
+ operation = kwargs['operation'].lower()
+ if operation == "groups":
+ return self.group( trans, **kwargs )
+ if operation == "create":
+ return self.create_group( trans, **kwargs )
+ if operation == "delete":
+ return self.mark_group_deleted( trans, **kwargs )
+ if operation == "undelete":
+ return self.undelete_group( trans, **kwargs )
+ if operation == "purge":
+ return self.purge_group( trans, **kwargs )
+ if operation == "manage users and roles":
+ return self.manage_users_and_roles_for_group( trans, **kwargs )
+ if operation == "rename":
+ return self.rename_group( trans, **kwargs )
+ # Render the list view
+ return self.group_list_grid( trans, **kwargs )
+ @web.expose
+ @web.require_admin
+ def rename_group( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ id = params.get( 'id', None )
+ if not id:
+ message = "No group ids received for renaming"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=message,
+ status='error' ) )
+ group = get_group( trans, id )
+ if params.get( 'rename_group_button', False ):
+ old_name = group.name
+ new_name = util.restore_text( params.name )
+ if not new_name:
+ message = 'Enter a valid name'
+ status = 'error'
+ else:
+ existing_group = trans.sa_session.query( trans.app.model.Group ).filter( trans.app.model.Group.table.c.name==new_name ).first()
+ if existing_group and existing_group.id != group.id:
+ message = 'A group with that name already exists'
+ status = 'error'
+ else:
+ if group.name != new_name:
+ group.name = new_name
+ trans.sa_session.add( group )
+ trans.sa_session.flush()
+ message = "Group '%s' has been renamed to '%s'" % ( old_name, new_name )
+ return trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ return trans.fill_template( '/admin/dataset_security/group/group_rename.mako',
+ group=group,
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def manage_users_and_roles_for_group( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ group = get_group( trans, params.id )
+ if params.get( 'group_roles_users_edit_button', False ):
+ in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( params.in_roles ) ]
+ in_users = [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in util.listify( params.in_users ) ]
+ trans.app.security_agent.set_entity_group_associations( groups=[ group ], roles=in_roles, users=in_users )
+ trans.sa_session.refresh( group )
+ message += "Group '%s' has been updated with %d associated roles and %d associated users" % ( group.name, len( in_roles ), len( in_users ) )
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=util.sanitize_text( message ),
+ status=status ) )
+ in_roles = []
+ out_roles = []
+ in_users = []
+ out_users = []
+ for role in trans.sa_session.query(trans.app.model.Role ) \
+ .filter( trans.app.model.Role.table.c.deleted==False ) \
+ .order_by( trans.app.model.Role.table.c.name ):
+ if role in [ x.role for x in group.roles ]:
+ in_roles.append( ( role.id, role.name ) )
+ else:
+ out_roles.append( ( role.id, role.name ) )
+ for user in trans.sa_session.query( trans.app.model.User ) \
+ .filter( trans.app.model.User.table.c.deleted==False ) \
+ .order_by( trans.app.model.User.table.c.email ):
+ if user in [ x.user for x in group.users ]:
+ in_users.append( ( user.id, user.email ) )
+ else:
+ out_users.append( ( user.id, user.email ) )
+ message += 'Group %s is currently associated with %d roles and %d users' % ( group.name, len( in_roles ), len( in_users ) )
+ return trans.fill_template( '/admin/dataset_security/group/group.mako',
+ group=group,
+ in_roles=in_roles,
+ out_roles=out_roles,
+ in_users=in_users,
+ out_users=out_users,
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def create_group( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ name = util.restore_text( params.get( 'name', '' ) )
+ in_users = util.listify( params.get( 'in_users', [] ) )
+ out_users = util.listify( params.get( 'out_users', [] ) )
+ in_roles = util.listify( params.get( 'in_roles', [] ) )
+ out_roles = util.listify( params.get( 'out_roles', [] ) )
+ create_role_for_group = params.get( 'create_role_for_group', '' )
+ create_role_for_group_checked = CheckboxField.is_checked( create_role_for_group )
+ ok = True
+ if params.get( 'create_group_button', False ):
+ if not name:
+ message = "Enter a valid name."
+ status = 'error'
+ ok = False
+ elif trans.sa_session.query( trans.app.model.Group ).filter( trans.app.model.Group.table.c.name==name ).first():
+ message = "Group names must be unique and a group with that name already exists, so choose another name."
+ status = 'error'
+ ok = False
+ else:
+ # Create the group
+ group = trans.app.model.Group( name=name )
+ trans.sa_session.add( group )
+ trans.sa_session.flush()
+ # Create the UserRoleAssociations
+ for user in [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in in_users ]:
+ uga = trans.app.model.UserGroupAssociation( user, group )
+ trans.sa_session.add( uga )
+ # Create the GroupRoleAssociations
+ for role in [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in in_roles ]:
+ gra = trans.app.model.GroupRoleAssociation( group, role )
+ trans.sa_session.add( gra )
+ if create_role_for_group_checked:
+ # Create the role
+ role = trans.app.model.Role( name=name, description='Role for group %s' % name )
+ trans.sa_session.add( role )
+ # Associate the role with the group
+ gra = trans.model.GroupRoleAssociation( group, role )
+ trans.sa_session.add( gra )
+ num_in_roles = len( in_roles ) + 1
+ else:
+ num_in_roles = len( in_roles )
+ trans.sa_session.flush()
+ message = "Group '%s' has been created with %d associated users and %d associated roles. " \
+ % ( group.name, len( in_users ), num_in_roles )
+ if create_role_for_group_checked:
+ message += 'One of the roles associated with this group is the newly created role with the same name.'
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+
+
+ if ok:
+ for user in trans.sa_session.query( trans.app.model.User ) \
+ .filter( trans.app.model.User.table.c.deleted==False ) \
+ .order_by( trans.app.model.User.table.c.email ):
+ out_users.append( ( user.id, user.email ) )
+ for role in trans.sa_session.query( trans.app.model.Role ) \
+ .filter( trans.app.model.Role.table.c.deleted==False ) \
+ .order_by( trans.app.model.Role.table.c.name ):
+ out_roles.append( ( role.id, role.name ) )
+ return trans.fill_template( '/admin/dataset_security/group/group_create.mako',
+ name=name,
+ in_users=in_users,
+ out_users=out_users,
+ in_roles=in_roles,
+ out_roles=out_roles,
+ create_role_for_group_checked=create_role_for_group_checked,
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def mark_group_deleted( self, trans, **kwd ):
+ params = util.Params( kwd )
+ id = params.get( 'id', None )
+ if not id:
+ message = "No group ids received for marking deleted"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=message,
+ status='error' ) )
+ ids = util.listify( id )
+ message = "Deleted %d groups: " % len( ids )
+ for group_id in ids:
+ group = get_group( trans, group_id )
+ group.deleted = True
+ trans.sa_session.add( group )
+ trans.sa_session.flush()
+ message += " %s " % group.name
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ @web.expose
+ @web.require_admin
+ def undelete_group( self, trans, **kwd ):
+ params = util.Params( kwd )
+ id = kwd.get( 'id', None )
+ if not id:
+ message = "No group ids received for undeleting"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=message,
+ status='error' ) )
+ ids = util.listify( id )
+ count = 0
+ undeleted_groups = ""
+ for group_id in ids:
+ group = get_group( trans, group_id )
+ if not group.deleted:
+ message = "Group '%s' has not been deleted, so it cannot be undeleted." % group.name
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ group.deleted = False
+ trans.sa_session.add( group )
+ trans.sa_session.flush()
+ count += 1
+ undeleted_groups += " %s" % group.name
+ message = "Undeleted %d groups: %s" % ( count, undeleted_groups )
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ @web.expose
+ @web.require_admin
+ def purge_group( self, trans, **kwd ):
+ # This method should only be called for a Group that has previously been deleted.
+ # Purging a deleted Group simply deletes all UserGroupAssociations and GroupRoleAssociations.
+ params = util.Params( kwd )
+ id = kwd.get( 'id', None )
+ if not id:
+ message = "No group ids received for purging"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ ids = util.listify( id )
+ message = "Purged %d groups: " % len( ids )
+ for group_id in ids:
+ group = get_group( trans, group_id )
+ if not group.deleted:
+ # We should never reach here, but just in case there is a bug somewhere...
+ message = "Group '%s' has not been deleted, so it cannot be purged." % group.name
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ # Delete UserGroupAssociations
+ for uga in group.users:
+ trans.sa_session.delete( uga )
+ # Delete GroupRoleAssociations
+ for gra in group.roles:
+ trans.sa_session.delete( gra )
+ trans.sa_session.flush()
+ message += " %s " % group.name
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='groups',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+
+ # Galaxy User Stuff
+ @web.expose
+ @web.require_admin
+ def create_new_user( self, trans, **kwd ):
+ return trans.response.send_redirect( web.url_for( controller='user',
+ action='create',
+ cntrller='admin' ) )
+ @web.expose
+ @web.require_admin
+ def reset_user_password( self, trans, **kwd ):
+ user_id = kwd.get( 'id', None )
+ if not user_id:
+ message = "No users received for resetting passwords."
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=message,
+ status='error' ) )
+ user_ids = util.listify( user_id )
+ if 'reset_user_password_button' in kwd:
+ message = ''
+ status = ''
+ for user_id in user_ids:
+ user = get_user( trans, user_id )
+ password = kwd.get( 'password', None )
+ confirm = kwd.get( 'confirm' , None )
+ if len( password ) < 6:
+ message = "Use a password of at least 6 characters."
+ status = 'error'
+ break
+ elif password != confirm:
+ message = "Passwords do not match."
+ status = 'error'
+ break
+ else:
+ user.set_password_cleartext( password )
+ trans.sa_session.add( user )
+ trans.sa_session.flush()
+ if not message and not status:
+ message = "Passwords reset for %d %s." % ( len( user_ids ), inflector.cond_plural( len( user_ids ), 'user' ) )
+ status = 'done'
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=util.sanitize_text( message ),
+ status=status ) )
+ users = [ get_user( trans, user_id ) for user_id in user_ids ]
+ if len( user_ids ) > 1:
+ user_id = ','.join( user_ids )
+ return trans.fill_template( '/admin/user/reset_password.mako',
+ id=user_id,
+ users=users,
+ password='',
+ confirm='' )
+ @web.expose
+ @web.require_admin
+ def mark_user_deleted( self, trans, **kwd ):
+ id = kwd.get( 'id', None )
+ if not id:
+ message = "No user ids received for deleting"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=message,
+ status='error' ) )
+ ids = util.listify( id )
+ message = "Deleted %d users: " % len( ids )
+ for user_id in ids:
+ user = get_user( trans, user_id )
+ user.deleted = True
+ trans.sa_session.add( user )
+ trans.sa_session.flush()
+ message += " %s " % user.email
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ @web.expose
+ @web.require_admin
+ def undelete_user( self, trans, **kwd ):
+ id = kwd.get( 'id', None )
+ if not id:
+ message = "No user ids received for undeleting"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=message,
+ status='error' ) )
+ ids = util.listify( id )
+ count = 0
+ undeleted_users = ""
+ for user_id in ids:
+ user = get_user( trans, user_id )
+ if not user.deleted:
+ message = "User '%s' has not been deleted, so it cannot be undeleted." % user.email
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ user.deleted = False
+ trans.sa_session.add( user )
+ trans.sa_session.flush()
+ count += 1
+ undeleted_users += " %s" % user.email
+ message = "Undeleted %d users: %s" % ( count, undeleted_users )
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ @web.expose
+ @web.require_admin
+ def purge_user( self, trans, **kwd ):
+ # This method should only be called for a User that has previously been deleted.
+ # We keep the User in the database ( marked as purged ), and stuff associated
+ # with the user's private role in case we want the ability to unpurge the user
+ # some time in the future.
+ # Purging a deleted User deletes all of the following:
+ # - History where user_id = User.id
+ # - HistoryDatasetAssociation where history_id = History.id
+ # - Dataset where HistoryDatasetAssociation.dataset_id = Dataset.id
+ # - UserGroupAssociation where user_id == User.id
+ # - UserRoleAssociation where user_id == User.id EXCEPT FOR THE PRIVATE ROLE
+ # - UserAddress where user_id == User.id
+ # Purging Histories and Datasets must be handled via the cleanup_datasets.py script
+ id = kwd.get( 'id', None )
+ if not id:
+ message = "No user ids received for purging"
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ ids = util.listify( id )
+ message = "Purged %d users: " % len( ids )
+ for user_id in ids:
+ user = get_user( trans, user_id )
+ if not user.deleted:
+ # We should never reach here, but just in case there is a bug somewhere...
+ message = "User '%s' has not been deleted, so it cannot be purged." % user.email
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ private_role = trans.app.security_agent.get_private_user_role( user )
+ # Delete History
+ for h in user.active_histories:
+ trans.sa_session.refresh( h )
+ for hda in h.active_datasets:
+ # Delete HistoryDatasetAssociation
+ d = trans.sa_session.query( trans.app.model.Dataset ).get( hda.dataset_id )
+ # Delete Dataset
+ if not d.deleted:
+ d.deleted = True
+ trans.sa_session.add( d )
+ hda.deleted = True
+ trans.sa_session.add( hda )
+ h.deleted = True
+ trans.sa_session.add( h )
+ # Delete UserGroupAssociations
+ for uga in user.groups:
+ trans.sa_session.delete( uga )
+ # Delete UserRoleAssociations EXCEPT FOR THE PRIVATE ROLE
+ for ura in user.roles:
+ if ura.role_id != private_role.id:
+ trans.sa_session.delete( ura )
+ # Delete UserAddresses
+ for address in user.addresses:
+ trans.sa_session.delete( address )
+ # Purge the user
+ user.purged = True
+ trans.sa_session.add( user )
+ trans.sa_session.flush()
+ message += "%s " % user.email
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ @web.expose
+ @web.require_admin
+ def users( self, trans, **kwd ):
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
+ if operation == "roles":
+ return self.user( trans, **kwd )
+ elif operation == "reset password":
+ return self.reset_user_password( trans, **kwd )
+ elif operation == "delete":
+ return self.mark_user_deleted( trans, **kwd )
+ elif operation == "undelete":
+ return self.undelete_user( trans, **kwd )
+ elif operation == "purge":
+ return self.purge_user( trans, **kwd )
+ elif operation == "create":
+ return self.create_new_user( trans, **kwd )
+ elif operation == "information":
+ user_id = kwd.get( 'id', None )
+ if not user_id:
+ kwd[ 'message' ] = util.sanitize_text( "Invalid user id (%s) received" % str( user_id ) )
+ kwd[ 'status' ] = 'error'
+ else:
+ return trans.response.send_redirect( web.url_for( controller='user',
+ action='manage_user_info',
+ cntrller='admin',
+ **kwd ) )
+ elif operation == "manage roles and groups":
+ return self.manage_roles_and_groups_for_user( trans, **kwd )
+ if trans.app.config.allow_user_deletion:
+ if self.delete_operation not in self.user_list_grid.operations:
+ self.user_list_grid.operations.append( self.delete_operation )
+ if self.undelete_operation not in self.user_list_grid.operations:
+ self.user_list_grid.operations.append( self.undelete_operation )
+ if self.purge_operation not in self.user_list_grid.operations:
+ self.user_list_grid.operations.append( self.purge_operation )
+ # Render the list view
+ return self.user_list_grid( trans, **kwd )
+ @web.expose
+ @web.require_admin
+ def name_autocomplete_data( self, trans, q=None, limit=None, timestamp=None ):
+ """Return autocomplete data for user emails"""
+ ac_data = ""
+ for user in trans.sa_session.query( User ).filter_by( deleted=False ).filter( func.lower( User.email ).like( q.lower() + "%" ) ):
+ ac_data = ac_data + user.email + "\n"
+ return ac_data
+ @web.expose
+ @web.require_admin
+ def manage_roles_and_groups_for_user( self, trans, **kwd ):
+ user_id = kwd.get( 'id', None )
+ message = ''
+ status = ''
+ if not user_id:
+ message += "Invalid user id (%s) received" % str( user_id )
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ user = get_user( trans, user_id )
+ private_role = trans.app.security_agent.get_private_user_role( user )
+ if kwd.get( 'user_roles_groups_edit_button', False ):
+ # Make sure the user is not dis-associating himself from his private role
+ out_roles = kwd.get( 'out_roles', [] )
+ if out_roles:
+ out_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( out_roles ) ]
+ if private_role in out_roles:
+ message += "You cannot eliminate a user's private role association. "
+ status = 'error'
+ in_roles = kwd.get( 'in_roles', [] )
+ if in_roles:
+ in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( in_roles ) ]
+ out_groups = kwd.get( 'out_groups', [] )
+ if out_groups:
+ out_groups = [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in util.listify( out_groups ) ]
+ in_groups = kwd.get( 'in_groups', [] )
+ if in_groups:
+ in_groups = [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in util.listify( in_groups ) ]
+ if in_roles:
+ trans.app.security_agent.set_entity_user_associations( users=[ user ], roles=in_roles, groups=in_groups )
+ trans.sa_session.refresh( user )
+ message += "User '%s' has been updated with %d associated roles and %d associated groups (private roles are not displayed)" % \
+ ( user.email, len( in_roles ), len( in_groups ) )
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='users',
+ message=util.sanitize_text( message ),
+ status='done' ) )
+ in_roles = []
+ out_roles = []
+ in_groups = []
+ out_groups = []
+ for role in trans.sa_session.query( trans.app.model.Role ).filter( trans.app.model.Role.table.c.deleted==False ) \
+ .order_by( trans.app.model.Role.table.c.name ):
+ if role in [ x.role for x in user.roles ]:
+ in_roles.append( ( role.id, role.name ) )
+ elif role.type != trans.app.model.Role.types.PRIVATE:
+ # There is a 1 to 1 mapping between a user and a PRIVATE role, so private roles should
+ # not be listed in the roles form fields, except for the currently selected user's private
+ # role, which should always be in in_roles. The check above is added as an additional
+ # precaution, since for a period of time we were including private roles in the form fields.
+ out_roles.append( ( role.id, role.name ) )
+ for group in trans.sa_session.query( trans.app.model.Group ).filter( trans.app.model.Group.table.c.deleted==False ) \
+ .order_by( trans.app.model.Group.table.c.name ):
+ if group in [ x.group for x in user.groups ]:
+ in_groups.append( ( group.id, group.name ) )
+ else:
+ out_groups.append( ( group.id, group.name ) )
+ message += "User '%s' is currently associated with %d roles and is a member of %d groups" % \
+ ( user.email, len( in_roles ), len( in_groups ) )
+ if not status:
+ status = 'done'
+ return trans.fill_template( '/admin/user/user.mako',
+ user=user,
+ in_roles=in_roles,
+ out_roles=out_roles,
+ in_groups=in_groups,
+ out_groups=out_groups,
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
+ def memdump( self, trans, ids = 'None', sorts = 'None', pages = 'None', new_id = None, new_sort = None, **kwd ):
+ if self.app.memdump is None:
+ return trans.show_error_message( "Memdump is not enabled (set <code>use_memdump = True</code> in universe_wsgi.ini)" )
+ heap = self.app.memdump.get()
+ p = util.Params( kwd )
+ msg = None
+ if p.dump:
+ heap = self.app.memdump.get( update = True )
+ msg = "Heap dump complete"
+ elif p.setref:
+ self.app.memdump.setref()
+ msg = "Reference point set (dump to see delta from this point)"
+ ids = ids.split( ',' )
+ sorts = sorts.split( ',' )
+ if new_id is not None:
+ ids.append( new_id )
+ sorts.append( 'None' )
+ elif new_sort is not None:
+ sorts[-1] = new_sort
+ breadcrumb = "<a href='%s' class='breadcrumb'>heap</a>" % web.url_for()
+ # new lists so we can assemble breadcrumb links
+ new_ids = []
+ new_sorts = []
+ for id, sort in zip( ids, sorts ):
+ new_ids.append( id )
+ if id != 'None':
+ breadcrumb += "<a href='%s' class='breadcrumb'>[%s]</a>" % ( web.url_for( ids=','.join( new_ids ), sorts=','.join( new_sorts ) ), id )
+ heap = heap[int(id)]
+ new_sorts.append( sort )
+ if sort != 'None':
+ breadcrumb += "<a href='%s' class='breadcrumb'>.by('%s')</a>" % ( web.url_for( ids=','.join( new_ids ), sorts=','.join( new_sorts ) ), sort )
+ heap = heap.by( sort )
+ ids = ','.join( new_ids )
+ sorts = ','.join( new_sorts )
+ if p.theone:
+ breadcrumb += ".theone"
+ heap = heap.theone
+ return trans.fill_template( '/admin/memdump.mako', heap = heap, ids = ids, sorts = sorts, breadcrumb = breadcrumb, msg = msg )
+
+ @web.expose
+ @web.require_admin
+ def jobs( self, trans, stop = [], stop_msg = None, cutoff = 180, job_lock = None, ajl_submit = None, **kwd ):
+ deleted = []
+ msg = None
+ status = None
+ if self.app.config.job_manager != self.app.config.server_name:
+ return trans.show_error_message( 'This Galaxy instance (%s) is not the job manager (%s). If using multiple servers, please directly access the job manager instance to manage jobs.' % (self.app.config.server_name, self.app.config.job_manager) )
+ job_ids = util.listify( stop )
+ if job_ids and stop_msg in [ None, '' ]:
+ msg = 'Please enter an error message to display to the user describing why the job was terminated'
+ status = 'error'
+ elif job_ids:
+ if stop_msg[-1] not in string.punctuation:
+ stop_msg += '.'
+ for job_id in job_ids:
+ trans.app.job_manager.job_stop_queue.put( job_id, error_msg="This job was stopped by an administrator: %s For more information or help" % stop_msg )
+ deleted.append( str( job_id ) )
+ if deleted:
+ msg = 'Queued job'
+ if len( deleted ) > 1:
+ msg += 's'
+ msg += ' for deletion: '
+ msg += ', '.join( deleted )
+ status = 'done'
+ if ajl_submit:
+ if job_lock == 'on':
+ trans.app.job_manager.job_queue.job_lock = True
+ else:
+ trans.app.job_manager.job_queue.job_lock = False
+ cutoff_time = datetime.utcnow() - timedelta( seconds=int( cutoff ) )
+ 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.NEW,
+ 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() )
+ last_updated = {}
+ for job in jobs:
+ delta = datetime.utcnow() - job.update_time
+ if delta > timedelta( minutes=60 ):
+ last_updated[job.id] = '%s hours' % int( delta.seconds / 60 / 60 )
+ else:
+ last_updated[job.id] = '%s minutes' % int( delta.seconds / 60 )
+ return trans.fill_template( '/admin/jobs.mako',
+ jobs = jobs,
+ last_updated = last_updated,
+ cutoff = cutoff,
+ msg = msg,
+ status = status,
+ job_lock = trans.app.job_manager.job_queue.job_lock )
+
+## ---- Utility methods -------------------------------------------------------
+
+def get_ids_of_tool_shed_repositories_being_installed( trans, as_string=False ):
+ installing_repository_ids = []
+ new_status = trans.model.ToolShedRepository.installation_status.NEW
+ cloning_status = trans.model.ToolShedRepository.installation_status.CLONING
+ setting_tool_versions_status = trans.model.ToolShedRepository.installation_status.SETTING_TOOL_VERSIONS
+ installing_dependencies_status = trans.model.ToolShedRepository.installation_status.INSTALLING_TOOL_DEPENDENCIES
+ loading_datatypes_status = trans.model.ToolShedRepository.installation_status.LOADING_PROPRIETARY_DATATYPES
+ for tool_shed_repository in trans.sa_session.query( trans.model.ToolShedRepository ) \
+ .filter( or_( trans.model.ToolShedRepository.status == new_status,
+ trans.model.ToolShedRepository.status == cloning_status,
+ trans.model.ToolShedRepository.status == setting_tool_versions_status,
+ trans.model.ToolShedRepository.status == installing_dependencies_status,
+ trans.model.ToolShedRepository.status == loading_datatypes_status ) ):
+ installing_repository_ids.append( trans.security.encode_id( tool_shed_repository.id ) )
+ if as_string:
+ return ','.join( installing_repository_ids )
+ return installing_repository_ids
+
+def get_user( trans, user_id ):
+ """Get a User from the database by id."""
+ user = trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( user_id ) )
+ if not user:
+ return trans.show_error_message( "User not found for id (%s)" % str( user_id ) )
+ return user
+def get_user_by_username( trans, username ):
+ """Get a user from the database by username"""
+ # TODO: Add exception handling here.
+ return trans.sa_session.query( trans.model.User ) \
+ .filter( trans.model.User.table.c.username == username ) \
+ .one()
+def get_role( trans, id ):
+ """Get a Role from the database by id."""
+ # Load user from database
+ id = trans.security.decode_id( id )
+ role = trans.sa_session.query( trans.model.Role ).get( id )
+ if not role:
+ return trans.show_error_message( "Role not found for id (%s)" % str( id ) )
+ return role
+def get_group( trans, id ):
+ """Get a Group from the database by id."""
+ # Load user from database
+ id = trans.security.decode_id( id )
+ group = trans.sa_session.query( trans.model.Group ).get( id )
+ if not group:
+ return trans.show_error_message( "Group not found for id (%s)" % str( id ) )
+ return group
+def get_quota( trans, id ):
+ """Get a Quota from the database by id."""
+ # Load user from database
+ id = trans.security.decode_id( id )
+ quota = trans.sa_session.query( trans.model.Quota ).get( id )
+ return quota
\ No newline at end of file
diff -r ee2feed164a9efc9201409b3e6191e2707ec3e18 -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py
+++ b/lib/galaxy/web/framework/__init__.py
@@ -607,7 +607,7 @@
Update the session cookie to match the current session.
"""
self.set_cookie( self.security.encode_guid( self.galaxy_session.session_key ), name=name, path=self.app.config.cookie_path )
- def handle_user_login( self, user, webapp ):
+ def handle_user_login( self, user ):
"""
Login a new user (possibly newly created)
- create a new session
@@ -621,7 +621,7 @@
prev_galaxy_session.is_valid = False
# Define a new current_session
self.galaxy_session = self.__create_new_session( prev_galaxy_session, user )
- if webapp == 'galaxy':
+ if self.webapp.name == 'galaxy':
cookie_name = 'galaxysession'
# Associated the current user's last accessed history (if exists) with their new session
history = None
diff -r ee2feed164a9efc9201409b3e6191e2707ec3e18 -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 lib/galaxy/web/framework/helpers/grids.py
--- a/lib/galaxy/web/framework/helpers/grids.py
+++ b/lib/galaxy/web/framework/helpers/grids.py
@@ -53,7 +53,8 @@
def __call__( self, trans, **kwargs ):
# Get basics.
- webapp = get_webapp( trans, **kwargs )
+ # FIXME: pretty sure this is only here to pass along, can likely be eliminated
+ webapp = trans.webapp.name
status = kwargs.get( 'status', None )
message = kwargs.get( 'message', None )
# Build a base filter and sort key that is the combination of the saved state and defaults.
diff -r ee2feed164a9efc9201409b3e6191e2707ec3e18 -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 lib/galaxy/webapps/community/controllers/admin.py
--- a/lib/galaxy/webapps/community/controllers/admin.py
+++ b/lib/galaxy/webapps/community/controllers/admin.py
@@ -1,4 +1,5 @@
from galaxy.web.base.controller import *
+from galaxy.web.base.controllers.admin import Admin
from galaxy.webapps.community import model
from galaxy.model.orm import *
from galaxy.web.framework.helpers import time_ago, iff, grids
diff -r ee2feed164a9efc9201409b3e6191e2707ec3e18 -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 lib/galaxy/webapps/galaxy/api/quotas.py
--- a/lib/galaxy/webapps/galaxy/api/quotas.py
+++ b/lib/galaxy/webapps/galaxy/api/quotas.py
@@ -2,7 +2,8 @@
API operations on Quota objects.
"""
import logging
-from galaxy.web.base.controller import BaseAPIController, Admin, UsesQuotaMixin, url_for
+from galaxy.web.base.controller import BaseAPIController, UsesQuotaMixin, url_for
+from galaxy.web.base.controllers.admin import Admin
from galaxy import web, util
from elementtree.ElementTree import XML
diff -r ee2feed164a9efc9201409b3e6191e2707ec3e18 -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 lib/galaxy/webapps/galaxy/controllers/admin.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin.py
@@ -1,4 +1,5 @@
from galaxy.web.base.controller import *
+from galaxy.web.base.controllers.admin import Admin
from galaxy import model
from galaxy.model.orm import *
from galaxy.web.framework.helpers import time_ago, iff, grids
diff -r ee2feed164a9efc9201409b3e6191e2707ec3e18 -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 lib/galaxy/webapps/galaxy/controllers/user.py
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -14,7 +14,7 @@
from galaxy.security.validate_user_input import validate_email, validate_publicname, validate_password, transform_publicname
from galaxy.util.json import from_json_string, to_json_string
from galaxy.web import url_for
-from galaxy.web.base.controller import BaseUIController, UsesFormDefinitionsMixin, get_webapp
+from galaxy.web.base.controller import BaseUIController, UsesFormDefinitionsMixin
from galaxy.web.form_builder import CheckboxField, build_select_field
from galaxy.web.framework.helpers import time_ago, grids
@@ -49,10 +49,10 @@
installed_len_files = None
@web.expose
- def index( self, trans, cntrller, webapp='galaxy', **kwd ):
- return trans.fill_template( '/user/index.mako', cntrller=cntrller, webapp=webapp )
+ def index( self, trans, cntrller, **kwd ):
+ return trans.fill_template( '/user/index.mako', cntrller=cntrller )
@web.expose
- def openid_auth( self, trans, webapp='galaxy', **kwd ):
+ def openid_auth( self, trans, **kwd ):
'''Handles user request to access an OpenID provider'''
if not trans.app.config.enable_openid:
return trans.show_error_message( 'OpenID authentication is not enabled in this instance of Galaxy' )
@@ -102,7 +102,7 @@
message=message,
status='error' ) )
@web.expose
- def openid_process( self, trans, webapp='galaxy', **kwd ):
+ def openid_process( self, trans, **kwd ):
'''Handle's response from OpenID Providers'''
if not trans.app.config.enable_openid:
return trans.show_error_message( 'OpenID authentication is not enabled in this instance of Galaxy' )
@@ -178,7 +178,7 @@
message=message,
status=status ) )
elif user_openid.user:
- trans.handle_user_login( user_openid.user, webapp )
+ trans.handle_user_login( user_openid.user )
trans.log_event( "User logged in via OpenID: %s" % display_identifier )
openid_provider_obj.post_authentication( trans, trans.app.openid_manager, info )
if not redirect:
@@ -222,7 +222,7 @@
message=message,
status=status ) )
@web.expose
- def openid_associate( self, trans, cntrller='user', webapp='galaxy', **kwd ):
+ def openid_associate( self, trans, cntrller='user', **kwd ):
'''Associates a user with an OpenID log in'''
if not trans.app.config.enable_openid:
return trans.show_error_message( 'OpenID authentication is not enabled in this instance of Galaxy' )
@@ -241,7 +241,7 @@
elif is_admin:
return trans.show_error_message( 'Associating OpenIDs with accounts cannot be done by administrators.' )
if kwd.get( 'login_button', False ):
- message, status, user, success = self.__validate_login( trans, webapp, **kwd )
+ message, status, user, success = self.__validate_login( trans, **kwd )
if success:
openid_objs = []
for openid in openids:
@@ -285,7 +285,7 @@
error = 'User registration is disabled. Please contact your Galaxy administrator for an account.'
else:
# Check email and password validity
- error = self.__validate( trans, params, email, password, confirm, username, webapp )
+ error = self.__validate( trans, params, email, password, confirm, username )
if not error:
# all the values are valid
message, status, user, success = self.__register( trans,
@@ -330,7 +330,7 @@
else:
message = error
status = 'error'
- if webapp == 'galaxy':
+ if trans.webapp.name == 'galaxy':
user_type_form_definition = self.__get_user_type_form_definition( trans, user=user, **kwd )
user_type_fd_id = params.get( 'user_type_fd_id', 'none' )
if user_type_fd_id == 'none' and user_type_form_definition is not None:
@@ -342,7 +342,6 @@
user_type_form_definition = None
widgets = []
return trans.fill_template( '/user/openid_associate.mako',
- webapp=webapp,
cntrller=cntrller,
email=email,
password='',
@@ -362,7 +361,7 @@
openids=openids )
@web.expose
@web.require_login( 'manage OpenIDs' )
- def openid_disassociate( self, trans, webapp='galaxy', **kwd ):
+ def openid_disassociate( self, trans, **kwd ):
'''Disassociates a user with an OpenID'''
if not trans.app.config.enable_openid:
return trans.show_error_message( 'OpenID authentication is not enabled in this instance of Galaxy' )
@@ -404,7 +403,7 @@
@web.expose
@web.require_login( 'manage OpenIDs' )
- def openid_manage( self, trans, webapp='galaxy', **kwd ):
+ def openid_manage( self, trans, **kwd ):
'''Manage OpenIDs for user'''
if not trans.app.config.enable_openid:
return trans.show_error_message( 'OpenID authentication is not enabled in this instance of Galaxy' )
@@ -421,7 +420,7 @@
return self.user_openid_grid( trans, **kwd )
@web.expose
- def login( self, trans, webapp='galaxy', redirect_url='', refresh_frames=[], **kwd ):
+ def login( self, trans, redirect_url='', refresh_frames=[], **kwd ):
'''Handle Galaxy Log in'''
redirect = kwd.get( 'redirect', trans.request.referer ).strip()
use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
@@ -430,16 +429,13 @@
header = ''
user = None
email = kwd.get( 'email', '' )
- #Sanitize webapp login here, once, since it can be reflected to the user in messages/etc.
- #Only text is valid.
- webapp = util.sanitize_text(webapp)
if kwd.get( 'login_button', False ):
- if webapp == 'galaxy' and not refresh_frames:
+ if trans.webapp.name == 'galaxy' and not refresh_frames:
if trans.app.config.require_login:
refresh_frames = [ 'masthead', 'history', 'tools' ]
else:
refresh_frames = [ 'masthead', 'history' ]
- message, status, user, success = self.__validate_login( trans, webapp, **kwd )
+ message, status, user, success = self.__validate_login( trans, **kwd )
if success and redirect and not redirect.startswith( trans.request.base + url_for( controller='user', action='logout' ) ):
redirect_url = redirect
elif success:
@@ -447,18 +443,17 @@
if not user and trans.app.config.require_login:
if trans.app.config.allow_user_creation:
create_account_str = " If you don't already have an account, <a href='%s'>you may create one</a>." % \
- web.url_for( action='create', cntrller='user', webapp=webapp )
- if webapp == 'galaxy':
+ web.url_for( action='create', cntrller='user' )
+ if trans.webapp.name == 'galaxy':
header = require_login_template % ( "Galaxy instance", create_account_str )
else:
header = require_login_template % ( "Galaxy tool shed", create_account_str )
else:
- if webapp == 'galaxy':
+ if trans.webapp.name == 'galaxy':
header = require_login_template % ( "Galaxy instance", "" )
else:
header = require_login_template % ( "Galaxy tool shed", "" )
return trans.fill_template( '/user/login.mako',
- webapp=webapp,
email=email,
header=header,
use_panels=use_panels,
@@ -469,7 +464,7 @@
status=status,
openid_providers=trans.app.openid_providers,
active_view="user" )
- def __validate_login( self, trans, webapp='galaxy', **kwd ):
+ def __validate_login( self, trans, **kwd ):
message = kwd.get( 'message', '' )
status = kwd.get( 'status', 'done' )
email = kwd.get( 'email', '' )
@@ -490,8 +485,8 @@
message = "Invalid password"
status = 'error'
else:
- trans.handle_user_login( user, webapp )
- if webapp == 'galaxy':
+ trans.handle_user_login( user )
+ if trans.webapp.name == 'galaxy':
trans.log_event( "User logged in" )
message = 'You are now logged in as %s.<br>You can <a target="_top" href="%s">go back to the page you were visiting</a> or <a target="_top" href="%s">go to the home page</a>.' % \
( user.email, redirect, url_for( '/' ) )
@@ -501,8 +496,8 @@
return ( message, status, user, success )
@web.expose
- def logout( self, trans, webapp='galaxy', logout_all=False ):
- if webapp == 'galaxy':
+ def logout( self, trans, logout_all=False ):
+ if trans.webapp.name == 'galaxy':
if trans.app.config.require_login:
refresh_frames = [ 'masthead', 'history', 'tools' ]
else:
@@ -515,7 +510,6 @@
message = 'You have been logged out.<br>You can log in again, <a target="_top" href="%s">go back to the page you were visiting</a> or <a target="_top" href="%s">go to the home page</a>.' % \
( trans.request.referer, url_for( '/' ) )
return trans.fill_template( '/user/logout.mako',
- webapp=webapp,
refresh_frames=refresh_frames,
message=message,
status='done',
@@ -526,7 +520,6 @@
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
- webapp = get_webapp( trans, **kwd )
use_panels = util.string_as_bool( kwd.get( 'use_panels', True ) )
email = util.restore_text( params.get( 'email', '' ) )
# Do not sanitize passwords, so take from kwd
@@ -543,7 +536,7 @@
status = 'error'
else:
if not refresh_frames:
- if webapp == 'galaxy':
+ if trans.webapp.name == 'galaxy':
if trans.app.config.require_login:
refresh_frames = [ 'masthead', 'history', 'tools' ]
else:
@@ -553,19 +546,19 @@
# Create the user, save all the user info and login to Galaxy
if params.get( 'create_user_button', False ):
# Check email and password validity
- message = self.__validate( trans, params, email, password, confirm, username, webapp )
+ message = self.__validate( trans, params, email, password, confirm, username )
if not message:
# All the values are valid
message, status, user, success = self.__register( trans,
cntrller,
subscribe_checked,
**kwd )
- if webapp == 'community':
+ if trans.webapp.name == 'community':
redirect_url = url_for( '/' )
if success and not is_admin:
# The handle_user_login() method has a call to the history_set_default_permissions() method
# (needed when logging in with a history), user needs to have default permissions set before logging in
- trans.handle_user_login( user, webapp )
+ trans.handle_user_login( user )
trans.log_event( "User created a new account" )
trans.log_event( "User logged in" )
if success and is_admin:
@@ -577,7 +570,7 @@
status=status ) )
else:
status = 'error'
- if webapp == 'galaxy':
+ if trans.webapp.name == 'galaxy':
user_type_form_definition = self.__get_user_type_form_definition( trans, user=None, **kwd )
user_type_fd_id = params.get( 'user_type_fd_id', 'none' )
if user_type_fd_id == 'none' and user_type_form_definition is not None:
@@ -596,7 +589,6 @@
user_type_fd_id_select_field=user_type_fd_id_select_field,
user_type_form_definition=user_type_form_definition,
widgets=widgets,
- webapp=webapp,
use_panels=use_panels,
redirect=redirect,
redirect_url=redirect_url,
@@ -608,7 +600,6 @@
email = util.restore_text( kwd.get( 'email', '' ) )
password = kwd.get( 'password', '' )
username = util.restore_text( kwd.get( 'username', '' ) )
- webapp = get_webapp( trans, **kwd )
status = kwd.get( 'status', 'done' )
is_admin = cntrller == 'admin' and trans.user_is_admin()
user = trans.app.model.User( email=email )
@@ -618,7 +609,7 @@
trans.sa_session.flush()
trans.app.security_agent.create_private_user_role( user )
error = ''
- if webapp == 'galaxy':
+ if trans.webapp.name == 'galaxy':
# We set default user permissions, before we log in and set the default history permissions
trans.app.security_agent.user_set_default_permissions( user,
default_access_private=trans.app.config.new_user_dataset_access_role_default_private )
@@ -654,7 +645,7 @@
if not error and not is_admin:
# The handle_user_login() method has a call to the history_set_default_permissions() method
# (needed when logging in with a history), user needs to have default permissions set before logging in
- trans.handle_user_login( user, webapp )
+ trans.handle_user_login( user )
trans.log_event( "User created a new account" )
trans.log_event( "User logged in" )
elif not error:
@@ -706,14 +697,13 @@
user = trans.user
if not user:
raise AssertionError, "The user id (%s) is not valid" % str( user_id )
- webapp = get_webapp( trans, **kwd )
email = util.restore_text( params.get( 'email', user.email ) )
username = util.restore_text( params.get( 'username', '' ) )
if not username:
username = user.username
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
- if webapp == 'galaxy':
+ if trans.webapp.name == 'galaxy':
user_type_form_definition = self.__get_user_type_form_definition( trans, user=user, **kwd )
user_type_fd_id = params.get( 'user_type_fd_id', 'none' )
if user_type_fd_id == 'none' and user_type_form_definition is not None:
@@ -742,7 +732,6 @@
widgets=widgets,
addresses=addresses,
show_filter=show_filter,
- webapp=webapp,
message=message,
status=status )
else:
@@ -751,7 +740,6 @@
user=user,
email=email,
username=username,
- webapp=webapp,
message=message,
status=status )
@@ -761,7 +749,6 @@
def edit_username( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
is_admin = cntrller == 'admin' and trans.user_is_admin()
- webapp = get_webapp( trans, **kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
user_id = params.get( 'user_id', None )
@@ -784,14 +771,12 @@
cntrller=cntrller,
user=user,
username=user.username,
- webapp=webapp,
message=message,
status=status )
@web.expose
def edit_info( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
is_admin = cntrller == 'admin' and trans.user_is_admin()
- webapp = get_webapp( trans, **kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
user_id = params.get( 'user_id', None )
@@ -885,7 +870,7 @@
trans.sa_session.add( user )
trans.sa_session.flush()
message = "The user information has been updated with the changes."
- if user and webapp == 'galaxy' and is_admin:
+ if user and trans.webapp.name == 'galaxy' and is_admin:
kwd[ 'user_id' ] = trans.security.encode_id( user.id )
kwd[ 'id' ] = user_id
if message:
@@ -897,7 +882,7 @@
cntrller=cntrller,
**kwd ) )
@web.expose
- def reset_password( self, trans, email=None, webapp='galaxy', **kwd ):
+ def reset_password( self, trans, email=None, **kwd ):
if trans.app.config.smtp_server is None:
return trans.show_error_message( "Mail is not configured for this Galaxy instance. Please contact an administrator." )
message = util.restore_text( kwd.get( 'message', '' ) )
@@ -941,12 +926,11 @@
elif email is None:
email = ""
return trans.fill_template( '/user/reset_password.mako',
- webapp=webapp,
message=message,
status=status )
- def __validate( self, trans, params, email, password, confirm, username, webapp ):
+ def __validate( self, trans, params, email, password, confirm, username ):
# If coming from the community webapp, we'll require a public user name
- if webapp == 'community' and not username:
+ if trans.webapp.name == 'community' and not username:
return "A public user name is required"
message = validate_email( trans, email )
if not message:
@@ -954,7 +938,7 @@
if not message and username:
message = validate_publicname( trans, username )
if not message:
- if webapp == 'galaxy':
+ if trans.webapp.name == 'galaxy':
if self.get_all_forms( trans,
filter=dict( deleted=False ),
form_type=trans.app.model.FormDefinition.types.USER_INFO ):
diff -r ee2feed164a9efc9201409b3e6191e2707ec3e18 -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 templates/user/dbkeys.mako
--- a/templates/user/dbkeys.mako
+++ b/templates/user/dbkeys.mako
@@ -1,11 +1,7 @@
<%!
def inherit(context):
if context.get('use_panels'):
- if context.get('webapp'):
- webapp = context.get('webapp')
- else:
- webapp = 'galaxy'
- return '/webapps/%s/base_panels.mako' % webapp
+ return '/webapps/%s/base_panels.mako' % t.webapp.name
else:
return '/base.mako'
%>
diff -r ee2feed164a9efc9201409b3e6191e2707ec3e18 -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 templates/user/index.mako
--- a/templates/user/index.mako
+++ b/templates/user/index.mako
@@ -9,27 +9,27 @@
<h2>${_('User preferences')}</h2><p>You are currently logged in as ${trans.user.email}.</p><ul>
- %if webapp == 'galaxy':
- <li><a href="${h.url_for( controller='user', action='manage_user_info', cntrller=cntrller, webapp=webapp )}">${_('Manage your information')}</a></li>
- <li><a href="${h.url_for( controller='user', action='set_default_permissions', cntrller=cntrller, webapp=webapp )}">${_('Change default permissions')}</a> for new histories</li>
- <li><a href="${h.url_for( controller='user', action='api_keys', cntrller=cntrller, webapp=webapp )}">${_('Manage your API keys')}</a></li>
+ %if t.webapp.name == 'galaxy':
+ <li><a href="${h.url_for( controller='user', action='manage_user_info', cntrller=cntrller )}">${_('Manage your information')}</a></li>
+ <li><a href="${h.url_for( controller='user', action='set_default_permissions', cntrller=cntrller )}">${_('Change default permissions')}</a> for new histories</li>
+ <li><a href="${h.url_for( controller='user', action='api_keys', cntrller=cntrller )}">${_('Manage your API keys')}</a></li>
%if trans.app.config.enable_openid:
- <li><a href="${h.url_for( controller='user', action='openid_manage', cntrller=cntrller, webapp=webapp )}">${_('Manage OpenIDs')}</a> linked to your account</li>
+ <li><a href="${h.url_for( controller='user', action='openid_manage', cntrller=cntrller )}">${_('Manage OpenIDs')}</a> linked to your account</li>
%endif
%if trans.app.config.use_remote_user:
%if trans.app.config.remote_user_logout_href:
<li><a href="${trans.app.config.remote_user_logout_href}" target="_top">${_('Logout')}</a></li>
%endif
%else:
- <li><a href="${h.url_for( controller='user', action='logout', webapp=webapp, logout_all=True )}" target="_top">${_('Logout')}</a> ${_('of all user sessions')}</li>
+ <li><a href="${h.url_for( controller='user', action='logout', logout_all=True )}" target="_top">${_('Logout')}</a> ${_('of all user sessions')}</li>
%endif
%else:
- <li><a href="${h.url_for( controller='user', action='manage_user_info', cntrller=cntrller, webapp=webapp )}">${_('Manage your information')}</a></li>
- <li><a href="${h.url_for( controller='repository', action='manage_email_alerts', cntrller=cntrller, webapp=webapp )}">${_('Manage your email alerts')}</a></li>
- <li><a href="${h.url_for( controller='user', action='logout', webapp=webapp, logout_all=True )}" target="_top">${_('Logout')}</a> ${_('of all user sessions')}</li>
+ <li><a href="${h.url_for( controller='user', action='manage_user_info', cntrller=cntrller )}">${_('Manage your information')}</a></li>
+ <li><a href="${h.url_for( controller='repository', action='manage_email_alerts', cntrller=cntrller )}">${_('Manage your email alerts')}</a></li>
+ <li><a href="${h.url_for( controller='user', action='logout', logout_all=True )}" target="_top">${_('Logout')}</a> ${_('of all user sessions')}</li>
%endif
</ul>
- %if webapp == 'galaxy':
+ %if t.webapp.name == 'galaxy':
<p>
You are using <strong>${trans.user.get_disk_usage( nice_size=True )}</strong> of disk space in this Galaxy instance.
%if trans.app.config.enable_quotas:
@@ -43,7 +43,7 @@
<p>${n_('You are currently not logged in.')}</p>
%endif
<ul>
- <li><a href="${h.url_for( action='login', webapp=webapp )}">${_('Login')}</li>
- <li><a href="${h.url_for( action='create', cntrller='user', webapp=webapp )}">${_('Register')}</a></li>
+ <li><a href="${h.url_for( action='login' )}">${_('Login')}</li>
+ <li><a href="${h.url_for( action='create', cntrller='user' )}">${_('Register')}</a></li></ul>
%endif
diff -r ee2feed164a9efc9201409b3e6191e2707ec3e18 -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 templates/user/info.mako
--- a/templates/user/info.mako
+++ b/templates/user/info.mako
@@ -7,13 +7,12 @@
%if not is_admin:
<ul class="manage-table-actions"><li>
- <a class="action-button" href="${h.url_for( controller='user', action='index', cntrller=cntrller, webapp=webapp )}">User preferences</a>
+ <a class="action-button" href="${h.url_for( controller='user', action='index', cntrller=cntrller )}">User preferences</a></li></ul>
%endif
<div class="toolForm"><form name="login_info" id="login_info" action="${h.url_for( controller='user', action='edit_info', cntrller=cntrller, user_id=trans.security.encode_id( user.id ) )}" method="post" >
- <input type="hidden" name="webapp" value="${webapp}" size="40"/><div class="toolFormTitle">Login Information</div><div class="form-row"><label>Email address:</label>
@@ -21,7 +20,7 @@
</div><div class="form-row"><label>Public name:</label>
- %if webapp == 'community':
+ %if t.webapp.name == 'community':
%if user.active_repositories:
<input type="hidden" name="username" value="${username}"/>
${username}
@@ -54,7 +53,6 @@
<p></p><div class="toolForm"><form name="change_password" id="change_password" action="${h.url_for( controller='user', action='edit_info', cntrller=cntrller, user_id=trans.security.encode_id( user.id ) )}" method="post" >
- <input type="hidden" name="webapp" value="${webapp}" size="40"/><div class="toolFormTitle">Change Password</div>
%if not is_admin:
<div class="form-row">
diff -r ee2feed164a9efc9201409b3e6191e2707ec3e18 -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 templates/user/login.mako
--- a/templates/user/login.mako
+++ b/templates/user/login.mako
@@ -1,11 +1,7 @@
<%!
def inherit(context):
if context.get('use_panels'):
- if context.get('webapp'):
- webapp = context.get('webapp')
- else:
- webapp = 'galaxy'
- return '/webapps/%s/base_panels.mako' % webapp
+ return '/webapps/%s/base_panels.mako' % context.get('t').webapp.name
else:
return '/base.mako'
%>
@@ -82,14 +78,13 @@
<div class="form-row"><label>Email address:</label><input type="text" name="email" value="${email | h}" size="40"/>
- <input type="hidden" name="webapp" value="${webapp | h}" size="40"/><input type="hidden" name="redirect" value="${redirect | h}" size="40"/></div><div class="form-row"><label>Password:</label><input type="password" name="password" value="" size="40"/><div class="toolParamHelp" style="clear: both;">
- <a href="${h.url_for( controller='user', action='reset_password', webapp=webapp, use_panels=use_panels )}">Forgot password? Reset here</a>
+ <a href="${h.url_for( controller='user', action='reset_password', use_panels=use_panels )}">Forgot password? Reset here</a></div></div><div class="form-row">
@@ -107,7 +102,6 @@
<div class="form-row"><label>OpenID URL:</label><input type="text" name="openid_url" size="60" style="background-image:url('${h.url_for( '/static/images/openid-16x16.gif' )}' ); background-repeat: no-repeat; padding-right: 20px; background-position: 99% 50%;"/>
- <input type="hidden" name="webapp" value="${webapp | h}" size="40"/><input type="hidden" name="redirect" value="${redirect | h}" size="40"/></div><div class="form-row">
diff -r ee2feed164a9efc9201409b3e6191e2707ec3e18 -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 templates/user/logout.mako
--- a/templates/user/logout.mako
+++ b/templates/user/logout.mako
@@ -1,10 +1,6 @@
<%!
def inherit(context):
- if context.get('webapp'):
- webapp = context.get('webapp')
- else:
- webapp = 'galaxy'
- return '/webapps/%s/base_panels.mako' % webapp
+ return '/webapps/%s/base_panels.mako' % context.get('t').webapp.name
%><%inherit file="${inherit(context)}"/><%namespace file="/message.mako" import="render_msg" />
diff -r ee2feed164a9efc9201409b3e6191e2707ec3e18 -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 templates/user/openid_associate.mako
--- a/templates/user/openid_associate.mako
+++ b/templates/user/openid_associate.mako
@@ -1,11 +1,7 @@
<%!
def inherit(context):
if context.get('use_panels'):
- if context.get('webapp'):
- webapp = context.get('webapp')
- else:
- webapp = 'galaxy'
- return '/webapps/%s/base_panels.mako' % webapp
+ return '/webapps/%s/base_panels.mako' % t.webapp.name
else:
return '/base.mako'
%>
diff -r ee2feed164a9efc9201409b3e6191e2707ec3e18 -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 templates/user/register.mako
--- a/templates/user/register.mako
+++ b/templates/user/register.mako
@@ -43,7 +43,6 @@
<div class="form-row"><label>Email address:</label><input type="text" name="email" value="${email | h}" size="40"/>
- <input type="hidden" name="webapp" value="${webapp | h}" size="40"/><input type="hidden" name="redirect" value="${redirect | h}" size="40"/></div><div class="form-row">
@@ -57,7 +56,7 @@
<div class="form-row"><label>Public name:</label><input type="text" name="username" size="40" value="${username |h}"/>
- %if webapp == 'galaxy':
+ %if t.webapp.name == 'galaxy':
<div class="toolParamHelp" style="clear: both;">
Your public name is an identifier that will be used to generate addresses for information
you share publicly. Public names must be at least four characters in length and contain only lower-case
diff -r ee2feed164a9efc9201409b3e6191e2707ec3e18 -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 templates/user/reset_password.mako
--- a/templates/user/reset_password.mako
+++ b/templates/user/reset_password.mako
@@ -11,7 +11,6 @@
<div class="form-row"><label>Email:</label><input type="text" name="email" value="" size="40"/>
- <input type="hidden" name="webapp" value="${webapp}" size="40"/></div><div style="clear: both"></div><div class="form-row">
diff -r ee2feed164a9efc9201409b3e6191e2707ec3e18 -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 templates/user/username.mako
--- a/templates/user/username.mako
+++ b/templates/user/username.mako
@@ -5,7 +5,6 @@
<h2>Manage Public Name</h2><div class="toolForm"><form name="username" id="username" action="${h.url_for( controller='user', action='edit_username', cntrller=cntrller, user_id=trans.security.encode_id( user.id ) )}" method="post" >
- <input type="hidden" name="webapp" value="${webapp}" size="40"/><div class="toolFormTitle">Login Information</div><div class="form-row"><label>Public name:</label>
diff -r ee2feed164a9efc9201409b3e6191e2707ec3e18 -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 templates/webapps/galaxy/admin/index.mako
--- a/templates/webapps/galaxy/admin/index.mako
+++ b/templates/webapps/galaxy/admin/index.mako
@@ -42,11 +42,11 @@
<div class="toolSectionTitle">Security</div><div class="toolSectionBody"><div class="toolSectionBg">
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='users', webapp=webapp )}" target="galaxy_main">Manage users</a></div>
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='groups', webapp=webapp )}" target="galaxy_main">Manage groups</a></div>
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='roles', webapp=webapp )}" target="galaxy_main">Manage roles</a></div>
+ <div class="toolTitle"><a href="${h.url_for( controller='admin', action='users' )}" target="galaxy_main">Manage users</a></div>
+ <div class="toolTitle"><a href="${h.url_for( controller='admin', action='groups' )}" target="galaxy_main">Manage groups</a></div>
+ <div class="toolTitle"><a href="${h.url_for( controller='admin', action='roles' )}" target="galaxy_main">Manage roles</a></div>
%if trans.app.config.allow_user_impersonation:
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='impersonate', webapp=webapp )}" target="galaxy_main">Impersonate a user</a></div>
+ <div class="toolTitle"><a href="${h.url_for( controller='admin', action='impersonate' )}" target="galaxy_main">Impersonate a user</a></div>
%endif
</div></div>
@@ -54,7 +54,7 @@
<div class="toolSectionTitle">Data</div><div class="toolSectionBody"><div class="toolSectionBg">
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='quotas', webapp=webapp )}" target="galaxy_main">Manage quotas</a></div>
+ <div class="toolTitle"><a href="${h.url_for( controller='admin', action='quotas' )}" target="galaxy_main">Manage quotas</a></div><div class="toolTitle"><a href="${h.url_for( controller='library_admin', action='browse_libraries' )}" target="galaxy_main">Manage data libraries</a></div>
%if trans.app.config.enable_beta_job_managers:
<div class="toolTitle"><a href="${h.url_for( controller='data_admin', action='manage_data' )}" target="galaxy_main">Manage local data</a></div>
@@ -111,6 +111,6 @@
</%def><%def name="center_panel()">
- <% center_url = h.url_for( controller='admin', action='center', webapp='galaxy', message=message, status=status ) %>
+ <% center_url = h.url_for( controller='admin', action='center', message=message, status=status ) %><iframe name="galaxy_main" id="galaxy_main" frameborder="0" style="position: absolute; width: 100%; height: 100%;" src="${center_url}"></iframe></%def>
diff -r ee2feed164a9efc9201409b3e6191e2707ec3e18 -r 5b977db5fc50b50caa5309838f37a77010f1e2b7 templates/webapps/galaxy/base_panels.mako
--- a/templates/webapps/galaxy/base_panels.mako
+++ b/templates/webapps/galaxy/base_panels.mako
@@ -136,7 +136,7 @@
# Menu for user who is not logged in.
menu_options = [ [ _("Login"), h.url_for( controller='/user', action='login' ), "galaxy_main" ] ]
if app.config.allow_user_creation:
- menu_options.append( [ _("Register"), h.url_for( controller='/user', action='create', cntrller='user', webapp='galaxy' ), "galaxy_main" ] )
+ menu_options.append( [ _("Register"), h.url_for( controller='/user', action='create', cntrller='user' ), "galaxy_main" ] )
extra_class = "loggedout-only"
visible = ( trans.user == None )
tab( "user", _("User"), None, visible=visible, menu_options=menu_options )
@@ -151,20 +151,20 @@
if app.config.remote_user_logout_href:
menu_options.append( [ _('Logout'), app.config.remote_user_logout_href, "_top" ] )
else:
- menu_options.append( [ _('Preferences'), h.url_for( controller='/user', action='index', cntrller='user', webapp='galaxy' ), "galaxy_main" ] )
+ menu_options.append( [ _('Preferences'), h.url_for( controller='/user', action='index', cntrller='user' ), "galaxy_main" ] )
menu_options.append( [ 'Custom Builds', h.url_for( controller='/user', action='dbkeys' ), "galaxy_main" ] )
if app.config.require_login:
- logout_url = h.url_for( controller='/root', action='index', m_c='user', m_a='logout', webapp='galaxy' )
+ logout_url = h.url_for( controller='/root', action='index', m_c='user', m_a='logout' )
else:
- logout_url = h.url_for( controller='/user', action='logout', webapp='galaxy' )
+ logout_url = h.url_for( controller='/user', action='logout' )
menu_options.append( [ 'Logout', logout_url, "_top" ] )
menu_options.append( None )
menu_options.append( [ _('Saved Histories'), h.url_for( controller='/history', action='list' ), "galaxy_main" ] )
menu_options.append( [ _('Saved Datasets'), h.url_for( controller='/dataset', action='list' ), "galaxy_main" ] )
menu_options.append( [ _('Saved Pages'), h.url_for( controller='/page', action='list' ), "_top" ] )
- menu_options.append( [ _('API Keys'), h.url_for( controller='/user', action='api_keys', cntrller='user', webapp='galaxy' ), "galaxy_main" ] )
+ menu_options.append( [ _('API Keys'), h.url_for( controller='/user', action='api_keys', cntrller='user' ), "galaxy_main" ] )
if app.config.use_remote_user:
- menu_options.append( [ _('Public Name'), h.url_for( controller='/user', action='edit_username', cntrller='user', webapp='galaxy' ), "galaxy_main" ] )
+ menu_options.append( [ _('Public Name'), h.url_for( controller='/user', action='edit_username', cntrller='user' ), "galaxy_main" ] )
extra_class = "loggedin-only"
visible = ( trans.user != 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.
1
0
commit/galaxy-central: natefoo: pgcleanup/set_user_disk_usage: set usage to 0, not null, when the user has no active data.
by Bitbucket 28 Sep '12
by Bitbucket 28 Sep '12
28 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/ee2feed164a9/
changeset: ee2feed164a9
user: natefoo
date: 2012-09-28 22:11:38
summary: pgcleanup/set_user_disk_usage: set usage to 0, not null, when the user has no active data.
affected #: 2 files
diff -r cfd26c91f6c3bb8b6365e7835263e11fc3013004 -r ee2feed164a9efc9201409b3e6191e2707ec3e18 scripts/cleanup_datasets/pgcleanup.py
--- a/scripts/cleanup_datasets/pgcleanup.py
+++ b/scripts/cleanup_datasets/pgcleanup.py
@@ -261,7 +261,7 @@
# TODO: h.purged = false should be unnecessary once all hdas in purged histories are purged.
sql = """
UPDATE galaxy_user
- SET disk_usage = (SELECT SUM(total_size)
+ SET disk_usage = (SELECT COALESCE(SUM(total_size), 0)
FROM ( SELECT d.total_size
FROM history_dataset_association hda
JOIN history h ON h.id = hda.history_id
diff -r cfd26c91f6c3bb8b6365e7835263e11fc3013004 -r ee2feed164a9efc9201409b3e6191e2707ec3e18 scripts/set_user_disk_usage.py
--- a/scripts/set_user_disk_usage.py
+++ b/scripts/set_user_disk_usage.py
@@ -30,6 +30,9 @@
import galaxy.config
from galaxy.objectstore import build_object_store_from_config
+ # lazy
+ globals()['nice_size'] = __import__( 'galaxy.util', globals(), locals(), ( 'nice_size', ) ).nice_size
+
config_parser = ConfigParser( dict( here = os.getcwd(),
database_connection = 'sqlite:///database/universe.sqlite?isolation_level=IMMEDIATE' ) )
config_parser.read( os.path.basename( options.config ) )
@@ -48,7 +51,7 @@
def pgcalc( sa_session, id ):
sql = """
UPDATE galaxy_user
- SET disk_usage = (SELECT SUM(total_size)
+ SET disk_usage = (SELECT COALESCE(SUM(total_size), 0)
FROM ( SELECT d.total_size
FROM history_dataset_association hda
JOIN history h ON h.id = hda.history_id
@@ -72,7 +75,7 @@
def quotacheck( sa_session, users, engine ):
sa_session.refresh( user )
current = user.get_disk_usage()
- print user.username, '<' + user.email + '> old usage:', str( current ) + ',',
+ print user.username, '<' + user.email + '>:',
if engine != 'postgres':
new = user.calculate_disk_usage()
sa_session.refresh( user )
@@ -83,10 +86,14 @@
else:
new = pgcalc( sa_session, user.id )
# yes, still a small race condition between here and the flush
- if new == current:
- print 'no change'
+ print 'old usage:', nice_size( current ), 'change:',
+ if new in ( current, None ):
+ print 'none'
else:
- print 'new usage:', new
+ op = '-'
+ if new > current:
+ op = '+'
+ print '%s%s' % ( op, nice_size( new ) )
if not options.dryrun and engine != 'postgres':
user.set_disk_usage( new )
sa_session.add( user )
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.
1
0
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/cfd26c91f6c3/
changeset: cfd26c91f6c3
user: inithello
date: 2012-09-28 21:35:44
summary: Migrate BWA to the tool shed.
affected #: 6 files
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r cfd26c91f6c3bb8b6365e7835263e11fc3013004 lib/galaxy/tool_shed/migrate/versions/0005_tools.py
--- /dev/null
+++ b/lib/galaxy/tool_shed/migrate/versions/0005_tools.py
@@ -0,0 +1,15 @@
+"""
+The tools "Map with BWA for Illumina" and "Map with BWA for SOLiD" have
+been eliminated from the distribution. The tools are now available
+in the repository named bwa_wrappers from the main Galaxy tool shed at
+http://toolshed.g2.bx.psu.edu, and will be installed into your local
+Galaxy instance at the location discussed above by running the following
+command.
+"""
+
+import sys
+
+def upgrade():
+ print __doc__
+def downgrade():
+ pass
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r cfd26c91f6c3bb8b6365e7835263e11fc3013004 scripts/migrate_tools/0005_tools.sh
--- /dev/null
+++ b/scripts/migrate_tools/0005_tools.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd `dirname $0`/../..
+python ./scripts/migrate_tools/migrate_tools.py 0005_tools.xml $@
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r cfd26c91f6c3bb8b6365e7835263e11fc3013004 scripts/migrate_tools/0005_tools.xml
--- /dev/null
+++ b/scripts/migrate_tools/0005_tools.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<toolshed name="toolshed.g2.bx.psu.edu">
+ <repository name="bwa_wrappers" description="Galaxy wrappers for the BWA short read aligner." changeset_revision="ffa8aaa14f7c">
+ <tool id="bwa_wrapper" version="1.2.3" file="bwa_wrapper.xml"/>
+ <tool id="bwa_color_wrapper" version="1.0.2" file="bwa_color_wrapper.xml"/>
+ </repository>
+</toolshed>
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r cfd26c91f6c3bb8b6365e7835263e11fc3013004 tool-data/bwa_index.loc.sample
--- a/tool-data/bwa_index.loc.sample
+++ /dev/null
@@ -1,38 +0,0 @@
-#This is a sample file distributed with Galaxy that enables tools
-#to use a directory of BWA indexed sequences data files. You will need
-#to create these data files and then create a bwa_index.loc file
-#similar to this one (store it in this directory) that points to
-#the directories in which those files are stored. The bwa_index.loc
-#file has this format (longer white space characters are TAB characters):
-#
-#<unique_build_id><dbkey><display_name><file_path>
-#
-#So, for example, if you had phiX indexed stored in
-#/depot/data2/galaxy/phiX/base/,
-#then the bwa_index.loc entry would look like this:
-#
-#phiX174 phiX phiX Pretty /depot/data2/galaxy/phiX/base/phiX.fa
-#
-#and your /depot/data2/galaxy/phiX/base/ directory
-#would contain phiX.fa.* files:
-#
-#-rw-r--r-- 1 james universe 830134 2005-09-13 10:12 phiX.fa.amb
-#-rw-r--r-- 1 james universe 527388 2005-09-13 10:12 phiX.fa.ann
-#-rw-r--r-- 1 james universe 269808 2005-09-13 10:12 phiX.fa.bwt
-#...etc...
-#
-#Your bwa_index.loc file should include an entry per line for each
-#index set you have stored. The "file" in the path does not actually
-#exist, but it is the prefix for the actual index files. For example:
-#
-#phiX174 phiX phiX174 /depot/data2/galaxy/phiX/base/phiX.fa
-#hg18canon hg18 hg18 Canonical /depot/data2/galaxy/hg18/base/hg18canon.fa
-#hg18full hg18 hg18 Full /depot/data2/galaxy/hg18/base/hg18full.fa
-#/orig/path/hg19.fa hg19 hg19 /depot/data2/galaxy/hg19/base/hg19.fa
-#...etc...
-#
-#Note that for backwards compatibility with workflows, the unique ID of
-#an entry must be the path that was in the original loc file, because that
-#is the value stored in the workflow for that parameter. That is why the
-#hg19 entry above looks odd. New genomes can be better-looking.
-#
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r cfd26c91f6c3bb8b6365e7835263e11fc3013004 tool-data/bwa_index_color.loc.sample
--- a/tool-data/bwa_index_color.loc.sample
+++ /dev/null
@@ -1,38 +0,0 @@
-#This is a sample file distributed with Galaxy that enables tools
-#to use a directory of BWA indexed sequences data files. You will need
-#to create these data files and then create a bwa_index_color.loc file
-#similar to this one (store it in this directory) that points to
-#the directories in which those files are stored. The bwa_index_color.loc
-#file has this format (longer white space characters are TAB characters):
-#
-#<unique_build_id><dbkey><display_name><file_path>
-#
-#So, for example, if you had phiX indexed stored in
-#/depot/data2/galaxy/phiX/color/,
-#then the bwa_index.loc entry would look like this:
-#
-#phiX174 phiX phiX Pretty /depot/data2/galaxy/phiX/color/phiX.fa
-#
-#and your /depot/data2/galaxy/phiX/color/ directory
-#would contain phiX.fa.* files:
-#
-#-rw-r--r-- 1 james universe 830134 2005-09-13 10:12 phiX.fa.amb
-#-rw-r--r-- 1 james universe 527388 2005-09-13 10:12 phiX.fa.ann
-#-rw-r--r-- 1 james universe 269808 2005-09-13 10:12 phiX.fa.bwt
-#...etc...
-#
-#Your bwa_index_color.loc file should include an entry per line for each
-#index set you have stored. The "file" in the path does not actually
-#exist, but it is the prefix for the actual index files. For example:
-#
-#phiX174 phiX phiX174 /depot/data2/galaxy/phiX/color/phiX.fa
-#hg18canon hg18 hg18 Canonical /depot/data2/galaxy/hg18/color/hg18canon.fa
-#hg18full hg18 hg18 Full /depot/data2/galaxy/hg18/color/hg18full.fa
-#/orig/path/hg19.fa hg19 hg19 /depot/data2/galaxy/hg19/color/hg19.fa
-#...etc...
-#
-#Note that for backwards compatibility with workflows, the unique ID of
-#an entry must be the path that was in the original loc file, because that
-#is the value stored in the workflow for that parameter. That is why the
-#hg19 entry above looks odd. New genomes can be better-looking.
-#
diff -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c -r cfd26c91f6c3bb8b6365e7835263e11fc3013004 tool_conf.xml.sample
--- a/tool_conf.xml.sample
+++ b/tool_conf.xml.sample
@@ -330,8 +330,6 @@
<tool file="sr_mapping/bowtie_wrapper.xml" /><tool file="sr_mapping/bowtie2_wrapper.xml" /><tool file="sr_mapping/bowtie_color_wrapper.xml" />
- <tool file="sr_mapping/bwa_wrapper.xml" />
- <tool file="sr_mapping/bwa_color_wrapper.xml" /><tool file="sr_mapping/bfast_wrapper.xml" /><tool file="metag_tools/megablast_wrapper.xml" /><tool file="metag_tools/megablast_xml_parser.xml" />
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.
1
0
28 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/203e3e159243/
changeset: 203e3e159243
user: james_taylor
date: 2012-09-28 21:33:08
summary: Fix for main app rename in toolshed
affected #: 1 file
diff -r a37d82881a493b1ebf5e43d692c93b69bea44527 -r 203e3e1592439dcb8c0eb890545ed38a66f08f2c lib/galaxy/webapps/community/controllers/user.py
--- a/lib/galaxy/webapps/community/controllers/user.py
+++ b/lib/galaxy/webapps/community/controllers/user.py
@@ -1,1 +1,1 @@
-from galaxy.webapps.main.controllers.user import *
\ No newline at end of file
+from galaxy.webapps.galaxy.controllers.user import *
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.
1
0
10 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/8dbde0261fda/
changeset: 8dbde0261fda
user: james_taylor
date: 2012-09-24 19:09:50
summary: Moving main webapp out of framework directory
affected #: 103 files
Diff too large to display.
https://bitbucket.org/galaxy/galaxy-central/changeset/14878d0d6389/
changeset: 14878d0d6389
user: james_taylor
date: 2012-09-24 19:27:30
summary: Extracting add ui/api controllers methods up to the framework level
affected #: 2 files
diff -r 8dbde0261fda8d5b955527c76043e9efc4a7aec6 -r 14878d0d63898409799c929460366e2ef46fd505 lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py
+++ b/lib/galaxy/web/framework/__init__.py
@@ -6,6 +6,12 @@
import os, sys, time, socket, random, string
import inspect
+
+from galaxy.web.base.controller import BaseUIController
+from galaxy.web.base.controller import BaseAPIController
+from galaxy.web.base.controller import ControllerUnavailable
+
+
pkg_resources.require( "Cheetah" )
from Cheetah.Template import Template
import base
@@ -212,6 +218,7 @@
return FormBuilder( *args, **kwargs )
class WebApplication( base.WebApplication ):
+
def __init__( self, galaxy_app, session_cookie='galaxysession' ):
base.WebApplication.__init__( self )
self.set_transaction_factory( lambda e: self.transaction_chooser( e, galaxy_app, session_cookie ) )
@@ -223,19 +230,70 @@
output_encoding = 'utf-8' )
# Security helper
self.security = galaxy_app.security
+
def handle_controller_exception( self, e, trans, **kwargs ):
+
if isinstance( e, MessageException ):
return trans.show_message( e.err_msg, e.type )
def make_body_iterable( self, trans, body ):
+
if isinstance( body, FormBuilder ):
body = trans.show_form( body )
return base.WebApplication.make_body_iterable( self, trans, body )
+
def transaction_chooser( self, environ, galaxy_app, session_cookie ):
if 'is_api_request' in environ:
return GalaxyWebAPITransaction( environ, galaxy_app, self, session_cookie )
else:
return GalaxyWebUITransaction( environ, galaxy_app, self, session_cookie )
+ def add_ui_controllers( self, app, package_name ):
+ """
+ Search for UI controllers in `package_name` and add
+ them to the webapp.
+ """
+ package = __import__( package_name )
+ controller_dir = package.__path__[0]
+ for fname in os.listdir( controller_dir ):
+ if not( fname.startswith( "_" ) ) and fname.endswith( ".py" ):
+ name = fname[:-3]
+ module_name = package_name + "." + name
+ try:
+ module = __import__( module_name )
+ except ControllerUnavailable, exc:
+ log.debug("%s could not be loaded: %s" % (module_name, str(exc)))
+ continue
+ for comp in module_name.split( "." )[1:]:
+ module = getattr( module, comp )
+ # Look for a controller inside the modules
+ for key in dir( module ):
+ T = getattr( module, key )
+ if inspect.isclass( T ) and T is not BaseUIController and issubclass( T, BaseUIController ):
+ self.add_ui_controller( name, T( app ) )
+
+ def add_api_controllers( self, app, package_name ):
+ """
+ Search for UI controllers in `package_name` and add
+ them to the webapp.
+ """
+ package = __import__( package_name )
+ controller_dir = package.__path__[0]
+ for fname in os.listdir( controller_dir ):
+ if not( fname.startswith( "_" ) ) and fname.endswith( ".py" ):
+ name = fname[:-3]
+ module_name = package_name + "." + name
+ try:
+ module = __import__( module_name )
+ except ControllerUnavailable, exc:
+ log.debug("%s could not be loaded: %s" % (module_name, str(exc)))
+ continue
+ for comp in module_name.split( "." )[1:]:
+ module = getattr( module, comp )
+ for key in dir( module ):
+ T = getattr( module, key )
+ if inspect.isclass( T ) and T is not BaseAPIController and issubclass( T, BaseAPIController ):
+ self.add_api_controller( name, T( app ) )
+
class GalaxyWebTransaction( base.DefaultWebTransaction ):
"""
Encapsulates web transaction specific state for the Galaxy application
diff -r 8dbde0261fda8d5b955527c76043e9efc4a7aec6 -r 14878d0d63898409799c929460366e2ef46fd505 lib/galaxy/webapps/main/buildapp.py
--- a/lib/galaxy/webapps/main/buildapp.py
+++ b/lib/galaxy/webapps/main/buildapp.py
@@ -22,53 +22,6 @@
import galaxy.datatypes.registry
import galaxy.web.framework
-def add_ui_controllers( webapp, app ):
- """
- Search for controllers in the 'galaxy.web.controllers' module and add
- them to the webapp.
- """
- from galaxy.web.base.controller import BaseUIController
- from galaxy.web.base.controller import ControllerUnavailable
- import galaxy.web.controllers
- controller_dir = galaxy.web.controllers.__path__[0]
- for fname in os.listdir( controller_dir ):
- if not( fname.startswith( "_" ) ) and fname.endswith( ".py" ):
- name = fname[:-3]
- module_name = "galaxy.web.controllers." + name
- try:
- module = __import__( module_name )
- except ControllerUnavailable, exc:
- log.debug("%s could not be loaded: %s" % (module_name, str(exc)))
- continue
- for comp in module_name.split( "." )[1:]:
- module = getattr( module, comp )
- # Look for a controller inside the modules
- for key in dir( module ):
- T = getattr( module, key )
- if isclass( T ) and T is not BaseUIController and issubclass( T, BaseUIController ):
- webapp.add_ui_controller( name, T( app ) )
-
-def add_api_controllers( webapp, app ):
- from galaxy.web.base.controller import BaseAPIController
- from galaxy.web.base.controller import ControllerUnavailable
- import galaxy.web.api
- controller_dir = galaxy.web.api.__path__[0]
- for fname in os.listdir( controller_dir ):
- if not( fname.startswith( "_" ) ) and fname.endswith( ".py" ):
- name = fname[:-3]
- module_name = "galaxy.web.api." + name
- try:
- module = __import__( module_name )
- except ControllerUnavailable, exc:
- log.debug("%s could not be loaded: %s" % (module_name, str(exc)))
- continue
- for comp in module_name.split( "." )[1:]:
- module = getattr( module, comp )
- for key in dir( module ):
- T = getattr( module, key )
- if isclass( T ) and T is not BaseAPIController and issubclass( T, BaseAPIController ):
- webapp.add_api_controller( name, T( app ) )
-
def app_factory( global_conf, **kwargs ):
"""
Return a wsgi application serving the root object
@@ -87,7 +40,7 @@
atexit.register( app.shutdown )
# Create the universe WSGI application
webapp = galaxy.web.framework.WebApplication( app, session_cookie='galaxysession' )
- add_ui_controllers( webapp, app )
+ webapp.add_ui_controllers( 'galaxy.webapps.main.controllers', app )
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
# These two routes handle our simple needs at the moment
@@ -105,7 +58,7 @@
webapp.add_route( '/u/:username/v/:slug', controller='visualization', action='display_by_username_and_slug' )
# Add the web API
- add_api_controllers( webapp, app )
+ webapp.add_api_controllers( 'galaxy.webapps.main.api', app )
webapp.api_mapper.resource( 'content',
'contents',
controller='library_contents',
https://bitbucket.org/galaxy/galaxy-central/changeset/20e1f9fb8da9/
changeset: 20e1f9fb8da9
user: james_taylor
date: 2012-09-24 20:34:25
summary: Cleaning up controller imports
affected #: 13 files
diff -r 14878d0d63898409799c929460366e2ef46fd505 -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd lib/galaxy/util/backports/__init__.py
--- /dev/null
+++ b/lib/galaxy/util/backports/__init__.py
@@ -0,0 +1,3 @@
+"""
+Modules for providing backward compatibility with future versions of Python
+"""
\ No newline at end of file
diff -r 14878d0d63898409799c929460366e2ef46fd505 -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd lib/galaxy/util/backports/importlib/__init__.py
--- /dev/null
+++ b/lib/galaxy/util/backports/importlib/__init__.py
@@ -0,0 +1,42 @@
+"""Backport of importlib.import_module from 3.x."""
+# While not critical (and in no way guaranteed!), it would be nice to keep this
+# code compatible with Python 2.3.
+import sys
+
+def _resolve_name(name, package, level):
+ """Return the absolute name of the module to be imported."""
+ if not hasattr(package, 'rindex'):
+ raise ValueError("'package' not set to a string")
+ dot = len(package)
+ for x in xrange(level, 1, -1):
+ try:
+ dot = package.rindex('.', 0, dot)
+ except ValueError:
+ raise ValueError("attempted relative import beyond top-level "
+ "package")
+ return "%s.%s" % (package[:dot], name)
+
+
+def import_module(name, package=None):
+ """Import a module.
+
+ The 'package' argument is required when performing a relative import. It
+ specifies the package to use as the anchor point from which to resolve the
+ relative import to an absolute import.
+
+ """
+ if name.startswith('.'):
+ if not package:
+ raise TypeError("relative imports require the 'package' argument")
+ level = 0
+ for character in name:
+ if character != '.':
+ break
+ level += 1
+ name = _resolve_name(name[level:], package, level)
+ __import__(name)
+ return sys.modules[name]
+
+ ## Note: this was copied from
+ ## http://svn.python.org/projects/python/trunk/Lib/importlib/__init__.py
+ ## on 24 September 2012
\ No newline at end of file
diff -r 14878d0d63898409799c929460366e2ef46fd505 -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd lib/galaxy/web/buildapp.py
--- /dev/null
+++ b/lib/galaxy/web/buildapp.py
@@ -0,0 +1,3 @@
+"""For backward compatibility only, pulls app_factor from galaxy.webapps.main"""
+
+from galaxy.webapps.main.buildapp import app_factory
diff -r 14878d0d63898409799c929460366e2ef46fd505 -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py
+++ b/lib/galaxy/web/framework/__init__.py
@@ -7,11 +7,6 @@
import os, sys, time, socket, random, string
import inspect
-from galaxy.web.base.controller import BaseUIController
-from galaxy.web.base.controller import BaseAPIController
-from galaxy.web.base.controller import ControllerUnavailable
-
-
pkg_resources.require( "Cheetah" )
from Cheetah.Template import Template
import base
@@ -20,6 +15,7 @@
from galaxy import util
from galaxy.exceptions import MessageException
from galaxy.util.json import to_json_string, from_json_string
+from galaxy.util.backports.importlib import import_module
pkg_resources.require( "simplejson" )
import simplejson
@@ -247,48 +243,50 @@
else:
return GalaxyWebUITransaction( environ, galaxy_app, self, session_cookie )
- def add_ui_controllers( self, app, package_name ):
+ def add_ui_controllers( self, package_name, app ):
"""
Search for UI controllers in `package_name` and add
them to the webapp.
"""
- package = __import__( package_name )
+ from galaxy.web.base.controller import BaseUIController
+ from galaxy.web.base.controller import ControllerUnavailable
+ package = import_module( package_name )
+ controller_dir = package.__path__[0]
+ print ">>>", controller_dir, package.__path__
+ for fname in os.listdir( controller_dir ):
+ if not( fname.startswith( "_" ) ) and fname.endswith( ".py" ):
+ name = fname[:-3]
+ module_name = package_name + "." + name
+ print package_name, name, module_name
+ try:
+ module = import_module( module_name )
+ except ControllerUnavailable, exc:
+ log.debug("%s could not be loaded: %s" % (module_name, str(exc)))
+ continue
+ # Look for a controller inside the modules
+ for key in dir( module ):
+ T = getattr( module, key )
+ if inspect.isclass( T ) and T is not BaseUIController and issubclass( T, BaseUIController ):
+ self.add_ui_controller( name, T( app ) )
+
+ def add_api_controllers( self, package_name, app ):
+ """
+ Search for UI controllers in `package_name` and add
+ them to the webapp.
+ """
+ from galaxy.web.base.controller import BaseAPIController
+ from galaxy.web.base.controller import ControllerUnavailable
+ package = import_module( package_name )
controller_dir = package.__path__[0]
for fname in os.listdir( controller_dir ):
if not( fname.startswith( "_" ) ) and fname.endswith( ".py" ):
name = fname[:-3]
module_name = package_name + "." + name
try:
- module = __import__( module_name )
+ module = import_module( module_name )
except ControllerUnavailable, exc:
log.debug("%s could not be loaded: %s" % (module_name, str(exc)))
continue
- for comp in module_name.split( "." )[1:]:
- module = getattr( module, comp )
- # Look for a controller inside the modules
- for key in dir( module ):
- T = getattr( module, key )
- if inspect.isclass( T ) and T is not BaseUIController and issubclass( T, BaseUIController ):
- self.add_ui_controller( name, T( app ) )
-
- def add_api_controllers( self, app, package_name ):
- """
- Search for UI controllers in `package_name` and add
- them to the webapp.
- """
- package = __import__( package_name )
- controller_dir = package.__path__[0]
- for fname in os.listdir( controller_dir ):
- if not( fname.startswith( "_" ) ) and fname.endswith( ".py" ):
- name = fname[:-3]
- module_name = package_name + "." + name
- try:
- module = __import__( module_name )
- except ControllerUnavailable, exc:
- log.debug("%s could not be loaded: %s" % (module_name, str(exc)))
- continue
- for comp in module_name.split( "." )[1:]:
- module = getattr( module, comp )
for key in dir( module ):
T = getattr( module, key )
if inspect.isclass( T ) and T is not BaseAPIController and issubclass( T, BaseAPIController ):
diff -r 14878d0d63898409799c929460366e2ef46fd505 -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd lib/galaxy/webapps/main/api/workflows.py
--- a/lib/galaxy/webapps/main/api/workflows.py
+++ b/lib/galaxy/webapps/main/api/workflows.py
@@ -1,3 +1,5 @@
+from __future__ import absolute_import
+
"""
API operations for Workflows
"""
@@ -11,7 +13,8 @@
from galaxy.workflow.modules import module_factory
from galaxy.jobs.actions.post import ActionBox
from galaxy.model.item_attrs import UsesAnnotations
-from galaxy.web.controllers.workflow import attach_ordered_steps
+
+from ..controllers.workflow import attach_ordered_steps
log = logging.getLogger(__name__)
diff -r 14878d0d63898409799c929460366e2ef46fd505 -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd lib/galaxy/webapps/main/buildapp.py
--- a/lib/galaxy/webapps/main/buildapp.py
+++ b/lib/galaxy/webapps/main/buildapp.py
@@ -6,8 +6,6 @@
import os, os.path
import sys, warnings
-from inspect import isclass
-
from paste.request import parse_formvars
from paste.util import import_string
from paste import httpexceptions
@@ -209,7 +207,7 @@
log.debug( "Enabling 'error' middleware" )
# Transaction logging (apache access.log style)
if asbool( conf.get( 'use_translogger', True ) ):
- from framework.middleware.translogger import TransLogger
+ from galaxy.web.framework.middleware.translogger import TransLogger
app = TransLogger( app )
log.debug( "Enabling 'trans logger' middleware" )
# Config middleware just stores the paste config along with the request,
diff -r 14878d0d63898409799c929460366e2ef46fd505 -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd lib/galaxy/webapps/main/controllers/admin_toolshed.py
--- a/lib/galaxy/webapps/main/controllers/admin_toolshed.py
+++ b/lib/galaxy/webapps/main/controllers/admin_toolshed.py
@@ -1,5 +1,5 @@
import urllib2, tempfile
-from galaxy.web.controllers.admin import *
+from admin import *
from galaxy.util.json import from_json_string, to_json_string
from galaxy.util.shed_util import *
from galaxy.tool_shed.encoding_util import *
diff -r 14878d0d63898409799c929460366e2ef46fd505 -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd lib/galaxy/webapps/main/controllers/external_service.py
--- a/lib/galaxy/webapps/main/controllers/external_service.py
+++ b/lib/galaxy/webapps/main/controllers/external_service.py
@@ -1,9 +1,11 @@
+from __future__ import absolute_import
+
from galaxy.web.base.controller import *
from galaxy.web.framework.helpers import time_ago, iff, grids
from galaxy.model.orm import *
from galaxy import model, util
from galaxy.web.form_builder import *
-from galaxy.web.controllers.requests_common import invalid_id_redirect
+from .requests_common import invalid_id_redirect
import logging, os
log = logging.getLogger( __name__ )
diff -r 14878d0d63898409799c929460366e2ef46fd505 -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd lib/galaxy/webapps/main/controllers/request_type.py
--- a/lib/galaxy/webapps/main/controllers/request_type.py
+++ b/lib/galaxy/webapps/main/controllers/request_type.py
@@ -1,9 +1,11 @@
+from __future__ import absolute_import
+
from galaxy.web.base.controller import *
from galaxy.web.framework.helpers import time_ago, iff, grids
from galaxy.model.orm import *
from galaxy import model, util
from galaxy.web.form_builder import *
-from galaxy.web.controllers.requests_common import invalid_id_redirect
+from .requests_common import invalid_id_redirect
import logging, os
log = logging.getLogger( __name__ )
diff -r 14878d0d63898409799c929460366e2ef46fd505 -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd lib/galaxy/webapps/main/controllers/requests.py
--- a/lib/galaxy/webapps/main/controllers/requests.py
+++ b/lib/galaxy/webapps/main/controllers/requests.py
@@ -1,8 +1,10 @@
+from __future__ import absolute_import
+
from galaxy.web.base.controller import *
from galaxy.web.framework.helpers import grids
from galaxy.model.orm import *
from galaxy.web.form_builder import *
-from galaxy.web.controllers.requests_common import RequestsGrid
+from .requests_common import RequestsGrid
import logging
log = logging.getLogger( __name__ )
diff -r 14878d0d63898409799c929460366e2ef46fd505 -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd lib/galaxy/webapps/main/controllers/requests_admin.py
--- a/lib/galaxy/webapps/main/controllers/requests_admin.py
+++ b/lib/galaxy/webapps/main/controllers/requests_admin.py
@@ -1,9 +1,11 @@
+from __future__ import absolute_import
+
from galaxy.web.base.controller import *
from galaxy.web.framework.helpers import time_ago, iff, grids
from galaxy.model.orm import *
from galaxy import model, util
from galaxy.web.form_builder import *
-from galaxy.web.controllers.requests_common import RequestsGrid, invalid_id_redirect
+from .requests_common import RequestsGrid, invalid_id_redirect
from amqplib import client_0_8 as amqp
import logging, os, pexpect, ConfigParser
diff -r 14878d0d63898409799c929460366e2ef46fd505 -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd lib/galaxy/webapps/main/controllers/visualization.py
--- a/lib/galaxy/webapps/main/controllers/visualization.py
+++ b/lib/galaxy/webapps/main/controllers/visualization.py
@@ -1,13 +1,16 @@
+from __future__ import absolute_import
+
from galaxy import model
from galaxy.model.item_attrs import *
from galaxy.web.base.controller import *
from galaxy.web.framework.helpers import time_ago, grids, iff
from galaxy.util.sanitize_html import sanitize_html
-from galaxy.web.controllers.library import LibraryListGrid
from galaxy.visualization.genomes import decode_dbkey
from galaxy.visualization.genome.visual_analytics import get_dataset_job
from galaxy.visualization.data_providers.basic import ColumnDataProvider
+from .library import LibraryListGrid
+
#
# -- Grids --
#
https://bitbucket.org/galaxy/galaxy-central/changeset/08dbb6b88aeb/
changeset: 08dbb6b88aeb
user: james_taylor
date: 2012-09-28 21:10:28
summary: Automated merge with ssh://bitbucket.org/james_taylor/galaxy-webapp-refactoring
affected #: 107 files
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1278,6 +1278,18 @@
was converted successfully.
"""
+ # FIXME: copied from controller.py
+ messages = Bunch(
+ PENDING = "pending",
+ NO_DATA = "no data",
+ NO_CHROMOSOME = "no chromosome",
+ NO_CONVERTER = "no converter",
+ NO_TOOL = "no tool",
+ DATA = "data",
+ ERROR = "error",
+ OK = "ok"
+ )
+
# Get converted dataset; this will start the conversion if necessary.
try:
converted_dataset = self.get_converted_dataset( trans, target_type )
@@ -1785,6 +1797,7 @@
# we'll return the next available info_association in the inheritable hierarchy.
# True is also returned if the info_association was inherited, and False if not.
# This enables us to eliminate displaying any contents of the inherited template.
+ # SM: Accessing self.info_association can cause a query to be emitted
if self.info_association:
return self.info_association[0], inherited
if restrict:
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 lib/galaxy/security/__init__.py
--- a/lib/galaxy/security/__init__.py
+++ b/lib/galaxy/security/__init__.py
@@ -225,13 +225,16 @@
# If role.type is neither private nor sharing, it's ok to display
return True
return role.type != self.model.Role.types.PRIVATE and role.type != self.model.Role.types.SHARING
+
def allow_action( self, roles, action, item ):
"""
Method for checking a permission for the current user ( based on roles ) to perform a
specific action on an item, which must be one of:
Dataset, Library, LibraryFolder, LibraryDataset, LibraryDatasetDatasetAssociation
"""
+ # SM: Note that calling get_item_actions will emit a query.
item_actions = self.get_item_actions( action, item )
+
if not item_actions:
return action.model == 'restrict'
ret_val = False
@@ -249,8 +252,189 @@
ret_val = True
break
return ret_val
- def can_access_dataset( self, roles, dataset ):
- return self.dataset_is_public( dataset ) or self.allow_action( roles, self.permitted_actions.DATASET_ACCESS, dataset )
+
+
+ def get_actions_for_items( self, trans, action, permission_items ):
+ # TODO: Rename this; it's a replacement for get_item_actions, but it
+ # doesn't represent what it's really confusing.
+ # TODO: Make this work for other classes besides lib_datasets.
+ # That should be as easy as checking the type and writing a query for each;
+ # we're avoiding using the SQLAlchemy backrefs because they can cause lots
+ # of queries to be generated.
+ #
+ # Originally, get_item_actions did:
+ # return [ permission for permission in item.actions if permission.action == action.action ]
+ # The "item" can be just about anything with permissions, and referencing
+ # item.actions causes the item's permissions to be retrieved.
+ # This method will retrieve all permissions for all "items" and only
+ # return the permissions associated with that given action.
+ # We initialize the permissions list to be empty; we will return an
+ # empty list by default.
+ #
+ # If the dataset id has no corresponding action in its permissions,
+ # then the returned permissions will not carry an entry for the dataset.
+ ret_permissions = {}
+ if ( len( permission_items ) > 0 ):
+ if ( isinstance( permission_items[0], trans.model.LibraryDataset ) ):
+ ids = [ item.library_dataset_id for item in permission_items ]
+ permissions = trans.sa_session.query( trans.model.LibraryDatasetPermissions ) \
+ .filter( and_( trans.model.LibraryDatasetPermissions.library_dataset_id.in_( ids ),
+ trans.model.LibraryDatasetPermissions.action == action ) ) \
+ .all()
+
+ # Massage the return data. We will return a list of permissions
+ # for each library dataset. So we initialize the return list to
+ # have an empty list for each dataset. Then each permission is
+ # appended to the right lib dataset.
+ # TODO: Consider eliminating the initialization and just return
+ # empty values for each library dataset id.
+ for item in permission_items:
+ ret_permissions[ item.library_dataset_id ] = []
+ for permission in permissions:
+ ret_permissions[ permission.library_dataset_id ].append( permission )
+
+ # Test that we get the same response from get_item_actions each item:
+ test_code = False
+ if test_code:
+ try:
+ log.debug( "get_actions_for_items: Test start" )
+ for item in permission_items:
+ base_result = self.get_item_actions( action, item )
+ new_result = ret_permissions[ item.library_dataset_id ]
+ # For now, just test against LibraryDatasetIds; other classes
+ # are not tested yet.
+ if len( base_result ) == len( new_result ):
+ common_result = set(base_result).intersection( new_result )
+ if len( common_result ) == len( base_result ):
+ log.debug( "Match on permissions for id %d" %
+ item.library_dataset_id )
+ # TODO: Fix this failure message:
+ else:
+ log.debug( "Error: dataset %d; originally: %s; now: %s"
+ % ( item.library_dataset_id,
+ base_result, new_result ) )
+ else:
+ log.debug( "Error: dataset %d: had %d entries, now %d entries"
+ % ( item.library_dataset_id, len( base_result ),
+ len( new_result ) ) )
+ log.debug( "get_actions_for_items: Test end" )
+ except Exception as e:
+ log.debug( "Exception in test code: %s" % e )
+
+ return ret_permissions
+
+
+ def allow_action_on_libitems( self, trans, user_roles, action, items ):
+ """
+ This should be the equivalent of allow_action defined on multiple items.
+ It is meant to specifically replace allow_action for multiple
+ LibraryDatasets, but it could be reproduced or modified for
+ allow_action's permitted classes - Dataset, Library, LibraryFolder, and
+ LDDAs.
+ """
+ all_items_actions = self.get_actions_for_items( trans, action, items )
+
+ ret_allow_action = {}
+ # Change item to lib_dataset or vice-versa.
+ for item in items:
+ if all_items_actions.has_key( item.id ):
+ item_actions = all_items_actions[ item.id ]
+
+ # For access, all of the dataset's
+ if self.permitted_actions.DATASET_ACCESS == action:
+ ret_allow_action[ item.id ] = True
+ for item_action in item_actions:
+ if item_action.role not in user_roles:
+ ret_allow_action[ item.id ] = False
+ break
+
+ # Else look for just one dataset role to be in the list of
+ # acceptable user roles:
+ else:
+ ret_allow_action[ item.id ] = False
+ for item_action in item_actions:
+ if item_action.role in user_roles:
+ ret_allow_action[ item.id ] = True
+ break
+
+ else:
+ if 'restrict' == action.model:
+ ret_allow_action[ item.id ] = True
+ else:
+ ret_allow_action[ item.id ] = False
+
+ # Test it: the result for each dataset should match the result for
+ # allow_action:
+ test_code = False
+ if test_code:
+ log.debug( "allow_action_for_items: test start" )
+ for item in items:
+ orig_value = self.allow_action( user_roles, action, item )
+ if orig_value == ret_allow_action[ item.id ]:
+ log.debug( "Item %d: success" % item.id )
+ else:
+ log.debug( "Item %d: fail: original: %s; new: %s"
+ % ( item.id, orig_value, ret_allow_action[ item.id ] ) )
+ log.debug( "allow_action_for_items: test end" )
+ return ret_allow_action
+
+
+ # DELETEME: SM: DO NOT TOUCH! This actually works.
+ def dataset_access_mapping( self, trans, user_roles, datasets ):
+ '''
+ For the given list of datasets, return a mapping of the datasets' ids
+ to whether they can be accessed by the user or not. The datasets input
+ is expected to be a simple list of Dataset objects.
+ '''
+ datasets_public_map = self.datasets_are_public( trans, datasets )
+ datasets_allow_action_map = self.allow_action_on_libitems( trans, user_roles, self.permitted_actions.DATASET_ACCESS, datasets )
+ can_access = {}
+ for dataset in datasets:
+ can_access[ dataset.id ] = datasets_public_map[ dataset.id ] or datasets_allow_action_map[ dataset.id ]
+ return can_access
+
+ def dataset_permission_map_for_access( self, trans, user_roles, libitems ):
+ '''
+ For a given list of library items (e.g., Datasets), return a map of the
+ datasets' ids to whether they can have permission to use that action
+ (e.g., "access" or "modify") on the dataset. The libitems input is
+ expected to be a simple list of library items, such as Datasets or
+ LibraryDatasets.
+ NB: This is currently only usable for Datasets; it was intended to
+ be used for any library item.
+ '''
+ # Map the library items to whether they are publicly accessible or not.
+ # Then determine what actions are allowed on the item (in case it's not
+ # public). Finally, the item is accessible if it's publicly available
+ # or the right permissions are enabled.
+ # TODO: This only works for Datasets; other code is using X_is_public,
+ # so this will have to be rewritten to support other items.
+ libitems_public_map = self.datasets_are_public( trans, libitems )
+ libitems_allow_action_map = self.allow_action_on_libitems(
+ trans, user_roles, self.permitted_actions.DATASET_ACCESS, libitems )
+ can_access = {}
+ for libitem in libitems:
+ can_access[ libitem.id ] = libitems_public_map[ libitem.id ] or libitems_allow_action_map[ libitem.id ]
+ return can_access
+
+ def item_permission_map_for_modify( self, trans, user_roles, libitems ):
+ return self.allow_action_on_libitems(
+ trans, user_roles, self.permitted_actions.LIBRARY_MODIFY, libitems )
+
+ def item_permission_map_for_manage( self, trans, user_roles, libitems ):
+ return self.allow_action_on_libitems(
+ trans, user_roles, self.permitted_actions.LIBRARY_MANAGE, libitems )
+
+ def item_permission_map_for_add( self, trans, user_roles, libitems ):
+ return self.allow_action_on_libitems(
+ trans, user_roles, self.permitted_actions.LIBRARY_ADD, libitems )
+
+ def can_access_dataset( self, user_roles, dataset ):
+ # SM: dataset_is_public will access dataset.actions, which is a
+ # backref that causes a query to be made to DatasetPermissions
+ retval = self.dataset_is_public( dataset ) or self.allow_action( user_roles, self.permitted_actions.DATASET_ACCESS, dataset )
+ return retval
+
def can_manage_dataset( self, roles, dataset ):
return self.allow_action( roles, self.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset )
def can_access_library( self, roles, library ):
@@ -317,9 +501,14 @@
return self.allow_action( roles, self.permitted_actions.LIBRARY_MODIFY, item )
def can_manage_library_item( self, roles, item ):
return self.allow_action( roles, self.permitted_actions.LIBRARY_MANAGE, item )
+
def get_item_actions( self, action, item ):
# item must be one of: Dataset, Library, LibraryFolder, LibraryDataset, LibraryDatasetDatasetAssociation
+ # SM: Accessing item.actions emits a query to Library_Dataset_Permissions
+ # if the item is a LibraryDataset:
+ # TODO: Pass in the item's actions - the item isn't needed
return [ permission for permission in item.actions if permission.action == action.action ]
+
def guess_derived_permissions_for_datasets( self, datasets=[] ):
"""Returns a dict of { action : [ role, role, ... ] } for the output dataset based upon provided datasets"""
perms = {}
@@ -667,10 +856,55 @@
dataset = library_dataset.library_dataset_dataset_association.dataset
if not dataset.purged and not self.dataset_is_public( dataset ):
self.make_dataset_public( dataset )
+
def dataset_is_public( self, dataset ):
# A dataset is considered public if there are no "access" actions associated with it. Any
# other actions ( 'manage permissions', 'edit metadata' ) are irrelevant.
+ # SM: Accessing dataset.actions will cause a query to be emitted.
return self.permitted_actions.DATASET_ACCESS.action not in [ a.action for a in dataset.actions ]
+
+ def datasets_are_public( self, trans, datasets ):
+ '''
+ Given a transaction object and a list of Datasets, return
+ a mapping from Dataset ids to whether the Dataset is public
+ or not. All Dataset ids should be returned in the mapping's keys.
+ '''
+ # We go the other way around from dataset_is_public: we start with
+ # all datasets being marked as public. If there is an access action
+ # associated with the dataset, then we mark it as nonpublic:
+ datasets_public = {}
+ dataset_ids = [ dataset.id for dataset in datasets ]
+ for dataset_id in dataset_ids:
+ datasets_public[ dataset_id ] = True
+
+ # Now get all datasets which have DATASET_ACCESS actions:
+ access_data_perms = trans.sa_session.query( trans.app.model.DatasetPermissions ) \
+ .filter( and_( trans.app.model.DatasetPermissions.dataset_id in dataset_ids,
+ trans.app.model.DatasetPermissions.action == self.permitted_actions.DATASET_ACCESS.action ) ) \
+ .all()
+
+ # Every dataset returned has "access" privileges associated with it,
+ # so it's not public.
+ for permission in access_data_perms:
+ datasets_public[ permission.dataset_id ] = False
+
+ # Test code: Check if the results match up with the original:
+ test_code = False
+ if test_code:
+ log.debug( "datasets_are_public test: check datasets_are_public matches dataset_is_public:" )
+ test_success = True
+ for dataset in datasets:
+ orig_is_public = self.dataset_is_public( dataset )
+ if orig_is_public == datasets_public[ dataset.id ]:
+ log.debug( "\tMatch for dataset %d" % dataset.id )
+ else:
+ success = False
+ log.error( "\tERROR: Did not match: single is_public: %s; multiple is_public: %s"
+ % ( single_is_public, datasets_public[ dataset.id ] ) )
+ log.debug( "datasets_are_public: test succeeded? %s" % test_success )
+ return datasets_public
+
+
def make_dataset_public( self, dataset ):
# A dataset is considered public if there are no "access" actions associated with it. Any
# other actions ( 'manage permissions', 'edit metadata' ) are irrelevant.
@@ -707,7 +941,7 @@
# Ensure that roles being associated with DATASET_ACCESS are a subset of the legitimate roles
# derived from the roles associated with the access permission on item if it's not public. This
# will keep ill-legitimate roles from being associated with the DATASET_ACCESS permission on the
- # dataset (i.e., in the case where item is a library, if Role1 is associated with LIBRARY_ACCESS,
+ # dataset (i.e., in the case where item is .a library, if Role1 is associated with LIBRARY_ACCESS,
# then only those users that have Role1 should be associated with DATASET_ACCESS.
legitimate_roles = self.get_legitimate_roles( trans, item, cntrller )
ill_legitimate_roles = []
@@ -944,12 +1178,21 @@
if self.can_add_library_item( roles, folder ):
return True, ''
action = self.permitted_actions.DATASET_ACCESS
+
+ # SM: TODO: This is for timing debug. Delete it later.
+ from datetime import datetime, timedelta
+ query_start = datetime.now()
lddas = self.sa_session.query( self.model.LibraryDatasetDatasetAssociation ) \
.join( "library_dataset" ) \
.filter( self.model.LibraryDataset.folder == folder ) \
.join( "dataset" ) \
.options( eagerload_all( "dataset.actions" ) ) \
.all()
+ query_end = datetime.now()
+ query_delta = query_end - query_start
+ #log.debug( "Check folder contents: join query time: %d.%.6d sec" %
+ # ( query_delta.seconds, query_delta.microseconds ) )
+
for ldda in lddas:
ldda_access_permissions = self.get_item_actions( action, ldda.dataset )
if not ldda_access_permissions:
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 lib/galaxy/tool_shed/install_manager.py
--- a/lib/galaxy/tool_shed/install_manager.py
+++ b/lib/galaxy/tool_shed/install_manager.py
@@ -133,6 +133,7 @@
for k, v in tool_panel_dict_for_tool_config.items():
tool_panel_dict_for_display[ k ] = v
metadata_dict, invalid_file_tups = generate_metadata_for_changeset_revision( app=self.app,
+ repository=tool_shed_repository,
repository_clone_url=repository_clone_url,
relative_install_dir=relative_install_dir,
repository_files_dir=None,
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -159,6 +159,8 @@
else:
panel_dict = panel_component
already_loaded = False
+ loaded_version_key = None
+ lineage_id = None
for lineage_id in tool.lineage_ids:
if lineage_id in self.tools_by_id:
loaded_version_key = 'tool_%s' % lineage_id
@@ -176,7 +178,13 @@
if not inserted:
# If the tool is not defined in integrated_tool_panel.xml, append it to the tool panel.
panel_dict[ key ] = tool
- log.debug( "Loaded tool id: %s, version: %s." % ( tool.id, tool.version ) )
+ log.debug( "Loaded tool id: %s, version: %s into tool panel." % ( tool.id, tool.version ) )
+ elif tool.lineage_ids.index( tool_id ) > tool.lineage_ids.index( lineage_id ):
+ key = 'tool_%s' % tool.id
+ index = panel_dict.keys().index( loaded_version_key )
+ del panel_dict[ loaded_version_key ]
+ panel_dict.insert( index, key, tool )
+ log.debug( "Loaded tool id: %s, version: %s into tool panel." % ( tool.id, tool.version ) )
def load_tool_panel( self ):
for key, val in self.integrated_tool_panel.items():
if key.startswith( 'tool_' ):
@@ -255,70 +263,68 @@
os.write( fd, '<?xml version="1.0"?>\n' )
os.write( fd, '<toolbox>\n' )
for key, item in self.integrated_tool_panel.items():
- if key.startswith( 'tool_' ):
- if item:
+ if item:
+ if key.startswith( 'tool_' ):
os.write( fd, ' <tool id="%s" />\n' % item.id )
- elif key.startswith( 'workflow_' ):
- if item:
+ elif key.startswith( 'workflow_' ):
os.write( fd, ' <workflow id="%s" />\n' % item.id )
- elif key.startswith( 'label_' ):
- label_id = item.id or ''
- label_text = item.text or ''
- label_version = item.version or ''
- os.write( fd, ' <label id="%s" text="%s" version="%s" />\n' % ( label_id, label_text, label_version ) )
- elif key.startswith( 'section_' ):
- section_id = item.id or ''
- section_name = item.name or ''
- section_version = item.version or ''
- os.write( fd, ' <section id="%s" name="%s" version="%s">\n' % ( section_id, section_name, section_version ) )
- for section_key, section_item in item.elems.items():
- if section_key.startswith( 'tool_' ):
- if section_item:
- os.write( fd, ' <tool id="%s" />\n' % section_item.id )
- elif section_key.startswith( 'workflow_' ):
- if section_item:
- os.write( fd, ' <workflow id="%s" />\n' % section_item.id )
- elif section_key.startswith( 'label_' ):
- if section_item:
- label_id = section_item.id or ''
- label_text = section_item.text or ''
- label_version = section_item.version or ''
- os.write( fd, ' <label id="%s" text="%s" version="%s" />\n' % ( label_id, label_text, label_version ) )
- os.write( fd, ' </section>\n' )
+ elif key.startswith( 'label_' ):
+ label_id = item.id or ''
+ label_text = item.text or ''
+ label_version = item.version or ''
+ os.write( fd, ' <label id="%s" text="%s" version="%s" />\n' % ( label_id, label_text, label_version ) )
+ elif key.startswith( 'section_' ):
+ section_id = item.id or ''
+ section_name = item.name or ''
+ section_version = item.version or ''
+ os.write( fd, ' <section id="%s" name="%s" version="%s">\n' % ( section_id, section_name, section_version ) )
+ for section_key, section_item in item.elems.items():
+ if section_key.startswith( 'tool_' ):
+ if section_item:
+ os.write( fd, ' <tool id="%s" />\n' % section_item.id )
+ elif section_key.startswith( 'workflow_' ):
+ if section_item:
+ os.write( fd, ' <workflow id="%s" />\n' % section_item.id )
+ elif section_key.startswith( 'label_' ):
+ if section_item:
+ label_id = section_item.id or ''
+ label_text = section_item.text or ''
+ label_version = section_item.version or ''
+ os.write( fd, ' <label id="%s" text="%s" version="%s" />\n' % ( label_id, label_text, label_version ) )
+ os.write( fd, ' </section>\n' )
os.write( fd, '</toolbox>\n' )
os.close( fd )
shutil.move( filename, os.path.abspath( self.integrated_tool_panel_config ) )
os.chmod( self.integrated_tool_panel_config, 0644 )
def get_tool( self, tool_id, tool_version=None, get_all_versions=False ):
"""Attempt to locate a tool in the tool box."""
- if tool_id in self.tools_by_id:
- tool = self.tools_by_id[ tool_id ]
- if tool_version and tool.version == tool_version:
- if get_all_versions:
- return [ tool ]
- else:
- return tool
- else:
- if get_all_versions:
- return [ tool ]
- else:
- return tool
+ if tool_id in self.tools_by_id and not get_all_versions:
+ #tool_id exactly matches an available tool by id (which is 'old' tool_id or guid)
+ return self.tools_by_id[ tool_id ]
+ #exact tool id match not found, or all versions requested, search for other options, e.g. migrated tools or different versions
+ rval = []
tv = self.__get_tool_version( tool_id )
if tv:
tool_version_ids = tv.get_version_ids( self.app )
- if get_all_versions:
- available_tool_versions = []
- for tool_version_id in tool_version_ids:
- if tool_version_id in self.tools_by_id:
- available_tool_versions.append( self.tools_by_id[ tool_version_id ] )
- return available_tool_versions
for tool_version_id in tool_version_ids:
if tool_version_id in self.tools_by_id:
- tool = self.tools_by_id[ tool_version_id ]
- if tool_version and tool.version == tool_version:
- return tool
- else:
- return tool
+ rval.append( self.tools_by_id[ tool_version_id ] )
+ if not rval:
+ #still no tool, do a deeper search and try to match by old ids
+ for tool in self.tools_by_id.itervalues():
+ if tool.old_id == tool_id:
+ rval.append( tool )
+ if rval:
+ if get_all_versions:
+ return rval
+ else:
+ if tool_version:
+ #return first tool with matching version
+ for tool in rval:
+ if tool.version == tool_version:
+ return tool
+ #No tool matches by version, simply return the first available tool found
+ return rval[0]
return None
def get_loaded_tools_by_lineage( self, tool_id ):
"""Get all loaded tools associated by lineage to the tool whose id is tool_id."""
@@ -381,7 +387,6 @@
tool.repository_owner = repository_owner
tool.installed_changeset_revision = installed_changeset_revision
tool.guid = guid
- tool.old_id = elem.find( "id" ).text
tool.version = elem.find( "version" ).text
# Make sure the tool has a tool_version.
if not self.__get_tool_version( tool.id ):
@@ -906,8 +911,9 @@
if not self.name:
raise Exception, "Missing tool 'name'"
# Get the UNIQUE id for the tool
+ self.old_id = root.get( "id" )
if guid is None:
- self.id = root.get( "id" )
+ self.id = self.old_id
else:
self.id = guid
if not self.id:
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 lib/galaxy/util/shed_util.py
--- a/lib/galaxy/util/shed_util.py
+++ b/lib/galaxy/util/shed_util.py
@@ -31,8 +31,8 @@
'&' : '&',
'\'' : ''' }
MAX_CONTENT_SIZE = 32768
+NOT_TOOL_CONFIGS = [ 'datatypes_conf.xml', 'tool_dependencies.xml' ]
VALID_CHARS = set( string.letters + string.digits + "'\"-=_.()/+*^,:?!#[]%\\$@;{}" )
-NOT_TOOL_CONFIGS = [ 'datatypes_conf.xml', 'tool_dependencies.xml' ]
class ShedCounter( object ):
def __init__( self, model ):
@@ -602,7 +602,7 @@
else:
tool_dependencies_dict[ 'set_environment' ] = [ requirements_dict ]
return tool_dependencies_dict
-def generate_metadata_for_changeset_revision( app, repository_clone_url, relative_install_dir=None, repository_files_dir=None,
+def generate_metadata_for_changeset_revision( app, repository, repository_clone_url, relative_install_dir=None, repository_files_dir=None,
resetting_all_metadata_on_repository=False, webapp='galaxy' ):
"""
Generate metadata for a repository using it's files on disk. To generate metadata for changeset revisions older than the repository tip,
@@ -610,6 +610,7 @@
disk files, so the value of repository_files_dir will not always be repository.repo_path (it could be an absolute path to a temporary directory
containing a clone). If it is an absolute path, the value of relative_install_dir must contain repository.repo_path.
"""
+ readme_file_names = get_readme_file_names( repository.name )
metadata_dict = {}
invalid_file_tups = []
invalid_tool_configs = []
@@ -653,14 +654,24 @@
tool_data_path=app.config.tool_data_path,
tool_data_table_config_path=app.config.tool_data_table_config_path,
persist=False )
- # Find all tool configs and exported workflows and add them to the repository's metadata.
for root, dirs, files in os.walk( files_dir ):
if root.find( '.hg' ) < 0 and root.find( 'hgrc' ) < 0:
if '.hg' in dirs:
dirs.remove( '.hg' )
for name in files:
- # Find all tool configs.
- if name not in NOT_TOOL_CONFIGS and name.endswith( '.xml' ):
+ # See if we have a READ_ME file.
+ if name.lower() in readme_file_names:
+ if resetting_all_metadata_on_repository:
+ full_path_to_readme = os.path.join( root, name )
+ stripped_path_to_readme = full_path_to_readme.replace( work_dir, '' )
+ if stripped_path_to_readme.startswith( '/' ):
+ stripped_path_to_readme = stripped_path_to_readme[ 1: ]
+ relative_path_to_readme = os.path.join( relative_install_dir, stripped_path_to_readme )
+ else:
+ relative_path_to_readme = os.path.join( root, name )
+ metadata_dict[ 'readme' ] = relative_path_to_readme
+ # See if we have a tool config.
+ elif name not in NOT_TOOL_CONFIGS and name.endswith( '.xml' ):
full_path = os.path.abspath( os.path.join( root, name ) )
if os.path.getsize( full_path ) > 0:
if not ( check_binary( full_path ) or check_image( full_path ) or check_gzip( full_path )[ 0 ]
@@ -699,7 +710,7 @@
else:
for tup in invalid_files_and_errors_tups:
invalid_file_tups.append( tup )
- # Find all exported workflows
+ # Find all exported workflows.
elif name.endswith( '.ga' ):
relative_path = os.path.join( root, name )
if os.path.getsize( os.path.abspath( relative_path ) ) > 0:
@@ -1195,6 +1206,25 @@
if contents:
contents.sort()
return contents
+def get_repository_metadata_by_changeset_revision( trans, id, changeset_revision ):
+ """Get metadata for a specified repository change set from the database"""
+ # Make sure there are no duplicate records, and return the single unique record for the changeset_revision. Duplicate records were somehow
+ # created in the past. The cause of this issue has been resolved, but we'll leave this method as is for a while longer to ensure all duplicate
+ # records are removed.
+ all_metadata_records = trans.sa_session.query( trans.model.RepositoryMetadata ) \
+ .filter( and_( trans.model.RepositoryMetadata.table.c.repository_id == trans.security.decode_id( id ),
+ trans.model.RepositoryMetadata.table.c.changeset_revision == changeset_revision ) ) \
+ .order_by( trans.model.RepositoryMetadata.table.c.update_time.desc() ) \
+ .all()
+ if len( all_metadata_records ) > 1:
+ # Delete all recrds older than the last one updated.
+ for repository_metadata in all_metadata_records[ 1: ]:
+ trans.sa_session.delete( repository_metadata )
+ trans.sa_session.flush()
+ return all_metadata_records[ 0 ]
+ elif all_metadata_records:
+ return all_metadata_records[ 0 ]
+ return None
def get_repository_owner( cleaned_repository_url ):
items = cleaned_repository_url.split( 'repos' )
repo_path = items[ 1 ]
@@ -1405,6 +1435,13 @@
return shed_url
# The tool shed from which the repository was originally installed must no longer be configured in tool_sheds_conf.xml.
return None
+def get_readme_file_names( repository_name ):
+ readme_files = [ 'readme', 'read_me', 'install' ]
+ valid_filenames = [ r for r in readme_files ]
+ for r in readme_files:
+ valid_filenames.append( '%s.txt' % r )
+ valid_filenames.append( '%s.txt' % repository_name )
+ return valid_filenames
def handle_missing_data_table_entry( app, relative_install_dir, tool_path, repository_tools_tups ):
"""
Inspect each tool to see if any have input parameters that are dynamically generated select lists that require entries in the
@@ -1469,7 +1506,7 @@
error, message = handle_sample_tool_data_table_conf_file( trans.app, tool_data_table_config )
tool, valid, message2 = load_tool_from_config( trans.app, tool_config_filepath )
message = concat_messages( message, message2 )
- return tool, valid, message
+ return tool, valid, message, sample_files
def handle_sample_files_and_load_tool_from_tmp_config( trans, repo, changeset_revision, tool_config_filename, work_dir ):
tool = None
message = ''
@@ -1490,7 +1527,7 @@
if manifest_ctx and ctx_file:
tool, message2 = load_tool_from_tmp_config( trans, repo, manifest_ctx, ctx_file, work_dir )
message = concat_messages( message, message2 )
- return tool, message
+ return tool, message, sample_files
def handle_sample_tool_data_table_conf_file( app, filename, persist=False ):
"""
Parse the incoming filename and add new entries to the in-memory app.tool_data_tables dictionary. If persist is True (should only occur)
@@ -1907,6 +1944,19 @@
elif c not in [ '\r' ]:
translated.append( '' )
return ''.join( translated )
+def translate_string( raw_text, to_html=True ):
+ if raw_text:
+ if to_html:
+ if len( raw_text ) <= MAX_CONTENT_SIZE:
+ translated_string = to_html_str( raw_text )
+ else:
+ large_str = '\nFile contents truncated because file size is larger than maximum viewing size of %s\n' % util.nice_size( MAX_CONTENT_SIZE )
+ translated_string = to_html_str( '%s%s' % ( raw_text[ 0:MAX_CONTENT_SIZE ], large_str ) )
+ else:
+ raise Exception( "String translation currently only supports text to HTML." )
+ else:
+ translated_string = ''
+ return translated_string
def update_repository( repo, ctx_rev=None ):
"""
Update the cloned repository to changeset_revision. It is critical that the installed repository is updated to the desired
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 lib/galaxy/visualization/data_providers/basic.py
--- a/lib/galaxy/visualization/data_providers/basic.py
+++ b/lib/galaxy/visualization/data_providers/basic.py
@@ -109,10 +109,10 @@
""" Cast value based on type. """
if type == 'int':
try: val = int( val )
- except: pass
+ except: return None
elif type == 'float':
try: val = float( val )
- except: pass
+ except: return None
return val
f = open( self.original_dataset.file_name )
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 lib/galaxy/visualization/data_providers/genome.py
--- a/lib/galaxy/visualization/data_providers/genome.py
+++ b/lib/galaxy/visualization/data_providers/genome.py
@@ -113,7 +113,7 @@
class GenomeDataProvider( BaseDataProvider ):
""" Base class for genome data providers. """
- data_type = None
+ dataset_type = None
"""
Mapping from column name to payload data; this mapping is used to create
@@ -179,12 +179,21 @@
"""
Returns data for complete genome.
"""
- dataset_summary = []
+ genome_data = []
for chrom_info in chroms_info[ 'chrom_info' ]:
- summary = self.get_data( chrom_info[ 'chrom' ], 0, chrom_info[ 'len' ], **kwargs )
- dataset_summary.append( summary )
+ chrom = chrom_info[ 'chrom' ]
+ chrom_len = chrom_info[ 'len' ]
+ chrom_data = self.get_data( chrom, 0, chrom_len, **kwargs )
+ if chrom_data:
+ chrom_data[ 'region' ] = "%s:%i-%i" % ( chrom, 0, chrom_len )
+ chrom_data[ 'dataset_type' ] = self.dataset_type
+ genome_data.append( chrom_data )
- return dataset_summary
+ return {
+ 'data': genome_data,
+ 'dataset_type': self.dataset_type
+ }
+
def get_filters( self ):
"""
@@ -316,7 +325,7 @@
class TabixDataProvider( FilterableMixin, GenomeDataProvider ):
- data_type = 'tabix'
+ dataset_type = 'tabix'
"""
Tabix index data provider for the Galaxy track browser.
@@ -358,7 +367,7 @@
#
class IntervalDataProvider( GenomeDataProvider ):
- data_type = 'interval_index'
+ dataset_type = 'interval_index'
"""
Processes interval data from native format to payload format.
@@ -444,7 +453,7 @@
Payload format: [ uid (offset), start, end, name, strand, thick_start, thick_end, blocks ]
"""
- data_type = 'interval_index'
+ dataset_type = 'interval_index'
def get_iterator( self, chrom, start, end ):
raise Exception( "Unimplemented Method" )
@@ -541,7 +550,7 @@
for large datasets.
"""
- data_type = 'interval_index'
+ dataset_type = 'interval_index'
def get_iterator( self, chrom=None, start=None, end=None ):
# Read first line in order to match chrom naming format.
@@ -581,7 +590,7 @@
col_name_data_attr_mapping = { 'Qual' : { 'index': 6 , 'name' : 'Qual' } }
- data_type = 'bai'
+ dataset_type = 'bai'
def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ):
"""
@@ -687,7 +696,7 @@
for large datasets.
"""
- data_type = 'tabix'
+ dataset_type = 'tabix'
def get_iterator( self, chrom, start, end ):
# Read first line in order to match chrom naming format.
@@ -721,7 +730,7 @@
Summary tree data provider for the Galaxy track browser.
"""
- data_type = 'summary_tree'
+ dataset_type = 'summary_tree'
CACHE = LRUCache( 20 ) # Store 20 recently accessed indices for performance
@@ -729,7 +738,7 @@
st = summary_tree_from_file( self.converted_dataset.file_name )
return st.chrom_blocks.keys()
- def get_data( self, chrom, start, end, level=None, resolution=None, detail_cutoff=None, draw_cutoff=None ):
+ def get_data( self, chrom, start, end, level=None, resolution=None, detail_cutoff=None, draw_cutoff=None, **kwargs ):
"""
Returns summary tree data for a given genomic region.
"""
@@ -765,7 +774,14 @@
if results == "detail" or results == "draw":
return results
else:
- return results, stats[ level ][ "max" ], stats[ level ]["avg" ], stats[ level ][ "delta" ]
+ return {
+ 'dataset_type': self.dataset_type,
+ 'data': results,
+ 'max': stats[ level ][ "max" ],
+ 'avg': stats[ level ][ "avg" ],
+ 'delta': stats[ level ][ "delta" ],
+ 'level': level
+ }
def has_data( self, chrom ):
"""
@@ -788,7 +804,7 @@
is reported in 1-based, closed format, i.e. SAM/BAM format.
"""
- data_type = 'bai'
+ dataset_type = 'bai'
def get_filters( self ):
"""
@@ -970,7 +986,7 @@
class SamDataProvider( BamDataProvider ):
- data_type = 'bai'
+ dataset_type = 'bai'
def __init__( self, converted_dataset=None, original_dataset=None, dependencies=None ):
""" Create SamDataProvider. """
@@ -987,7 +1003,7 @@
BBI data provider for the Galaxy track browser.
"""
- data_type = 'bigwig'
+ dataset_type = 'bigwig'
def valid_chroms( self ):
# No way to return this info as of now
@@ -1000,6 +1016,9 @@
return all_dat is not None
def get_data( self, chrom, start, end, start_val=0, max_vals=None, num_samples=1000, **kwargs ):
+ start = int( start )
+ end = int( end )
+
# Bigwig can be a standalone bigwig file, in which case we use
# original_dataset, or coming from wig->bigwig conversion in
# which we use converted_dataset
@@ -1096,7 +1115,10 @@
# Cleanup and return.
f.close()
- return { 'data': result }
+ return {
+ 'data': result,
+ 'dataset_type': self.dataset_type
+ }
class BigBedDataProvider( BBIDataProvider ):
def _get_dataset( self ):
@@ -1122,7 +1144,7 @@
"""
col_name_data_attr_mapping = { 4 : { 'index': 4 , 'name' : 'Score' } }
- data_type = 'interval_index'
+ dataset_type = 'interval_index'
def write_data_to_file( self, regions, filename ):
source = open( self.original_dataset.file_name )
@@ -1201,7 +1223,7 @@
for large datasets.
"""
- data_type = 'interval_index'
+ dataset_type = 'interval_index'
def get_iterator( self, chrom, start, end ):
"""
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 lib/galaxy/webapps/community/controllers/common.py
--- a/lib/galaxy/webapps/community/controllers/common.py
+++ b/lib/galaxy/webapps/community/controllers/common.py
@@ -5,11 +5,11 @@
from galaxy.tools import *
from galaxy.util.json import from_json_string, to_json_string
from galaxy.util.hash_util import *
-from galaxy.util.shed_util import check_tool_input_params, clone_repository, copy_sample_file, generate_metadata_for_changeset_revision
+from galaxy.util.shed_util import check_tool_input_params, clone_repository, concat_messages, copy_sample_file, generate_metadata_for_changeset_revision
from galaxy.util.shed_util import get_changectx_for_changeset, get_config_from_disk, get_configured_ui, get_file_context_from_ctx, get_named_tmpfile_from_ctx
-from galaxy.util.shed_util import handle_sample_files_and_load_tool_from_disk, handle_sample_files_and_load_tool_from_tmp_config
-from galaxy.util.shed_util import handle_sample_tool_data_table_conf_file, INITIAL_CHANGELOG_HASH, load_tool_from_config, reset_tool_data_tables
-from galaxy.util.shed_util import reversed_upper_bounded_changelog, strip_path
+from galaxy.util.shed_util import get_repository_metadata_by_changeset_revision, handle_sample_files_and_load_tool_from_disk
+from galaxy.util.shed_util import handle_sample_files_and_load_tool_from_tmp_config, handle_sample_tool_data_table_conf_file, INITIAL_CHANGELOG_HASH
+from galaxy.util.shed_util import load_tool_from_config, reset_tool_data_tables, reversed_upper_bounded_changelog, strip_path
from galaxy.web.base.controller import *
from galaxy.webapps.community import model
from galaxy.model.orm import *
@@ -473,25 +473,6 @@
.filter( and_( trans.model.Repository.table.c.name == name,
trans.model.Repository.table.c.user_id == user.id ) ) \
.first()
-def get_repository_metadata_by_changeset_revision( trans, id, changeset_revision ):
- """Get metadata for a specified repository change set from the database"""
- # Make sure there are no duplicate records, and return the single unique record for the changeset_revision. Duplicate records were somehow
- # created in the past. The cause of this issue has been resolved, but we'll leave this method as is for a while longer to ensure all duplicate
- # records are removed.
- all_metadata_records = trans.sa_session.query( trans.model.RepositoryMetadata ) \
- .filter( and_( trans.model.RepositoryMetadata.table.c.repository_id == trans.security.decode_id( id ),
- trans.model.RepositoryMetadata.table.c.changeset_revision == changeset_revision ) ) \
- .order_by( trans.model.RepositoryMetadata.table.c.update_time.desc() ) \
- .all()
- if len( all_metadata_records ) > 1:
- # Delete all recrds older than the last one updated.
- for repository_metadata in all_metadata_records[ 1: ]:
- trans.sa_session.delete( repository_metadata )
- trans.sa_session.flush()
- return all_metadata_records[ 0 ]
- elif all_metadata_records:
- return all_metadata_records[ 0 ]
- return None
def get_repository_metadata_by_id( trans, id ):
"""Get repository metadata from the database"""
return trans.sa_session.query( trans.model.RepositoryMetadata ).get( trans.security.decode_id( id ) )
@@ -620,7 +601,7 @@
can_use_disk_file = can_use_tool_config_disk_file( trans, repository, repo, tool_config_filepath, changeset_revision )
if can_use_disk_file:
trans.app.config.tool_data_path = work_dir
- tool, valid, message = handle_sample_files_and_load_tool_from_disk( trans, repo_files_dir, tool_config_filepath, work_dir )
+ tool, valid, message, sample_files = handle_sample_files_and_load_tool_from_disk( trans, repo_files_dir, tool_config_filepath, work_dir )
if tool is not None:
invalid_files_and_errors_tups = check_tool_input_params( trans.app,
repo_files_dir,
@@ -637,7 +618,7 @@
message = concat_messages( message, message2 )
status = 'error'
else:
- tool, message = handle_sample_files_and_load_tool_from_tmp_config( trans, repo, changeset_revision, tool_config_filename, work_dir )
+ tool, message, sample_files = handle_sample_files_and_load_tool_from_tmp_config( trans, repo, changeset_revision, tool_config_filename, work_dir )
try:
shutil.rmtree( work_dir )
except:
@@ -762,6 +743,7 @@
if cloned_ok:
log.debug( "Generating metadata for changset revision: %s", str( ctx.rev() ) )
current_metadata_dict, invalid_file_tups = generate_metadata_for_changeset_revision( app=trans.app,
+ repository=repository,
repository_clone_url=repository_clone_url,
relative_install_dir=repo_dir,
repository_files_dir=work_dir,
@@ -836,6 +818,7 @@
repo_dir = repository.repo_path
repo = hg.repository( get_configured_ui(), repo_dir )
metadata_dict, invalid_file_tups = generate_metadata_for_changeset_revision( app=trans.app,
+ repository=repository,
repository_clone_url=repository_clone_url,
relative_install_dir=repo_dir,
repository_files_dir=None,
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 lib/galaxy/webapps/community/controllers/repository.py
--- a/lib/galaxy/webapps/community/controllers/repository.py
+++ b/lib/galaxy/webapps/community/controllers/repository.py
@@ -10,9 +10,10 @@
from galaxy.util.json import from_json_string, to_json_string
from galaxy.model.orm import *
from galaxy.util.shed_util import create_repo_info_dict, get_changectx_for_changeset, get_configured_ui, get_repository_file_contents
-from galaxy.util.shed_util import handle_sample_files_and_load_tool_from_disk, handle_sample_files_and_load_tool_from_tmp_config, INITIAL_CHANGELOG_HASH
-from galaxy.util.shed_util import load_tool_from_config, NOT_TOOL_CONFIGS, open_repository_files_folder, reversed_lower_upper_bounded_changelog
-from galaxy.util.shed_util import reversed_upper_bounded_changelog, strip_path, to_html_escaped, update_repository, url_join
+from galaxy.util.shed_util import get_repository_metadata_by_changeset_revision, handle_sample_files_and_load_tool_from_disk
+from galaxy.util.shed_util import handle_sample_files_and_load_tool_from_tmp_config, INITIAL_CHANGELOG_HASH, load_tool_from_config, NOT_TOOL_CONFIGS
+from galaxy.util.shed_util import open_repository_files_folder, reversed_lower_upper_bounded_changelog, reversed_upper_bounded_changelog, strip_path
+from galaxy.util.shed_util import to_html_escaped, translate_string, update_repository, url_join
from galaxy.tool_shed.encoding_util import *
from common import *
@@ -23,7 +24,6 @@
log = logging.getLogger( __name__ )
VALID_REPOSITORYNAME_RE = re.compile( "^[a-z0-9\_]+$" )
-README_FILES = [ 'readme', 'read_me', 'install' ]
class CategoryListGrid( grids.Grid ):
class NameColumn( grids.TextColumn ):
@@ -613,8 +613,10 @@
# Update repository files for browsing.
update_repository( repo )
is_malicious = changeset_is_malicious( trans, id, repository.tip )
+ metadata = self.get_metadata( trans, id, repository.tip )
return trans.fill_template( '/webapps/community/repository/browse_repository.mako',
repository=repository,
+ metadata=metadata,
commit_message=commit_message,
is_malicious=is_malicious,
webapp=webapp,
@@ -828,9 +830,11 @@
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
repository = get_repository( trans, id )
+ metadata = self.get_metadata( trans, id, repository.tip )
if trans.user and trans.user.email:
return trans.fill_template( "/webapps/community/repository/contact_owner.mako",
repository=repository,
+ metadata=metadata,
message=message,
status=status )
else:
@@ -939,9 +943,11 @@
repository, tool, message = load_tool_from_changeset_revision( trans, repository_id, changeset_revision, tool_config )
tool_state = self.__new_state( trans )
is_malicious = changeset_is_malicious( trans, repository_id, repository.tip )
+ metadata = self.get_metadata( trans, repository_id, changeset_revision )
try:
return trans.fill_template( "/webapps/community/repository/tool_form.mako",
repository=repository,
+ metadata=metadata,
changeset_revision=changeset_revision,
tool=tool,
tool_state=tool_state,
@@ -1181,6 +1187,11 @@
trans.response.headers['Pragma'] = 'no-cache'
trans.response.headers['Expires'] = '0'
return get_repository_file_contents( file_path )
+ def get_metadata( self, trans, repository_id, changeset_revision ):
+ repository_metadata = get_repository_metadata_by_changeset_revision( trans, repository_id, changeset_revision )
+ if repository_metadata and repository_metadata.metadata:
+ return repository_metadata.metadata
+ return None
@web.json
def get_repository_information( self, trans, repository_ids, changeset_revisions, **kwd ):
"""
@@ -1208,23 +1219,18 @@
return dict( includes_tools=includes_tools, includes_tool_dependencies=includes_tool_dependencies, repo_info_dicts=repo_info_dicts )
@web.expose
def get_readme( self, trans, **kwd ):
- """If the received changeset_revision includes a file named readme (case ignored), return it's contents."""
+ """If the received changeset_revision includes a readme file, return it's contents."""
repository_name = kwd[ 'name' ]
repository_owner = kwd[ 'owner' ]
changeset_revision = kwd[ 'changeset_revision' ]
- valid_filenames = [ r for r in README_FILES ]
- for r in README_FILES:
- valid_filenames.append( '%s.txt' % r )
- valid_filenames.append( '%s.txt' % repository_name )
repository = get_repository_by_name_and_owner( trans, repository_name, repository_owner )
- repo_dir = repository.repo_path
- for root, dirs, files in os.walk( repo_dir ):
- for name in files:
- if name.lower() in valid_filenames:
- f = open( os.path.join( root, name ), 'r' )
- text = f.read()
- f.close()
- return str( text )
+ repository_metadata = get_repository_metadata_by_changeset_revision( trans, trans.security.encode_id( repository.id ), changeset_revision )
+ metadata = repository_metadata.metadata
+ if metadata and 'readme' in metadata:
+ f = open( metadata[ 'readme' ], 'r' )
+ text = f.read()
+ f.close()
+ return str( text )
return ''
@web.expose
def get_tool_dependencies( self, trans, **kwd ):
@@ -1829,8 +1835,10 @@
display_reviews = util.string_as_bool( params.get( 'display_reviews', False ) )
rra = self.get_user_item_rating( trans.sa_session, trans.user, repository, webapp_model=trans.model )
is_malicious = changeset_is_malicious( trans, id, repository.tip )
+ metadata = self.get_metadata( trans, id, repository.tip )
return trans.fill_template( '/webapps/community/repository/rate_repository.mako',
repository=repository,
+ metadata=metadata,
avg_rating=avg_rating,
display_reviews=display_reviews,
num_ratings=num_ratings,
@@ -2156,8 +2164,10 @@
# Make sure we'll view latest changeset first.
changesets.insert( 0, change_dict )
is_malicious = changeset_is_malicious( trans, id, repository.tip )
+ metadata = self.get_metadata( trans, id, repository.tip )
return trans.fill_template( '/webapps/community/repository/view_changelog.mako',
repository=repository,
+ metadata=metadata,
changesets=changesets,
is_malicious=is_malicious,
message=message,
@@ -2185,8 +2195,10 @@
for diff in patch.diff( repo, node1=ctx_parent.node(), node2=ctx.node() ):
diffs.append( to_html_escaped( diff ) )
is_malicious = changeset_is_malicious( trans, id, repository.tip )
+ metadata = self.get_metadata( trans, id, ctx_str )
return trans.fill_template( '/webapps/community/repository/view_changeset.mako',
repository=repository,
+ metadata=metadata,
ctx=ctx,
anchors=anchors,
modified=modified,
@@ -2201,6 +2213,33 @@
message=message,
status=status )
@web.expose
+ def view_readme( self, trans, id, changeset_revision, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ cntrller = params.get( 'cntrller', 'repository' )
+ webapp = params.get( 'webapp', 'community' )
+ repository = get_repository( trans, id )
+ repository_metadata = get_repository_metadata_by_changeset_revision( trans, trans.security.encode_id( repository.id ), changeset_revision )
+ metadata = repository_metadata.metadata
+ if metadata and 'readme' in metadata:
+ f = open( metadata[ 'readme' ], 'r' )
+ raw_text = f.read()
+ f.close()
+ readme_text = translate_string( raw_text, to_html=True )
+ else:
+ readme_text = ''
+ is_malicious = changeset_is_malicious( trans, id, changeset_revision )
+ return trans.fill_template( '/webapps/community/common/view_readme.mako',
+ cntrller=cntrller,
+ repository=repository,
+ changeset_revision=changeset_revision,
+ readme_text=readme_text,
+ is_malicious=is_malicious,
+ webapp=webapp,
+ message=message,
+ status=status )
+ @web.expose
def view_repository( self, trans, id, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
@@ -2301,16 +2340,25 @@
can_use_disk_file = can_use_tool_config_disk_file( trans, repository, repo, full_path_to_tool_config, changeset_revision )
if can_use_disk_file:
trans.app.config.tool_data_path = work_dir
- tool, valid, message = handle_sample_files_and_load_tool_from_disk( trans, repo_files_dir, full_path_to_tool_config, work_dir )
+ tool, valid, message, sample_files = handle_sample_files_and_load_tool_from_disk( trans,
+ repo_files_dir,
+ full_path_to_tool_config,
+ work_dir )
if message:
status = 'error'
else:
- tool, message = handle_sample_files_and_load_tool_from_tmp_config( trans, repo, changeset_revision, tool_config_filename, work_dir )
+ tool, message, sample_files = handle_sample_files_and_load_tool_from_tmp_config( trans,
+ repo,
+ changeset_revision,
+ tool_config_filename,
+ work_dir )
if message:
status = 'error'
break
if guid:
tool_lineage = self.get_versions_of_tool( trans, repository, repository_metadata, guid )
+ else:
+ metadata = None
is_malicious = changeset_is_malicious( trans, repository_id, repository.tip )
changeset_revision_select_field = build_changeset_revision_select_field( trans,
repository,
@@ -2320,6 +2368,7 @@
trans.app.config.tool_data_path = original_tool_data_path
return trans.fill_template( "/webapps/community/repository/view_tool_metadata.mako",
repository=repository,
+ metadata=metadata,
tool=tool,
tool_metadata_dict=tool_metadata_dict,
tool_lineage=tool_lineage,
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 lib/galaxy/webapps/community/controllers/workflow.py
--- a/lib/galaxy/webapps/community/controllers/workflow.py
+++ b/lib/galaxy/webapps/community/controllers/workflow.py
@@ -151,6 +151,7 @@
changeset_revision=repository_metadata.changeset_revision,
repository_metadata_id=repository_metadata_id,
workflow_name=workflow_name,
+ metadata=repository_metadata,
webapp=webapp,
message=message,
status=status )
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 lib/galaxy/webapps/main/api/datasets.py
--- a/lib/galaxy/webapps/main/api/datasets.py
+++ b/lib/galaxy/webapps/main/api/datasets.py
@@ -139,12 +139,11 @@
if mode == "Coverage":
# Get summary using minimal cutoffs.
indexer = data_provider_registry.get_data_provider( trans, original_dataset=dataset, source='index' )
- summary = indexer.get_data( chrom, low, high, resolution=kwargs[ 'resolution' ], detail_cutoff=0, draw_cutoff=0 )
+ summary = indexer.get_data( chrom, low, high, detail_cutoff=0, draw_cutoff=0, **kwargs )
if summary == "detail":
# Use maximum level of detail--2--to get summary data no matter the resolution.
summary = indexer.get_data( chrom, low, high, resolution=kwargs[ 'resolution' ], level=2, detail_cutoff=0, draw_cutoff=0 )
- frequencies, max_v, avg_v, delta = summary
- return { 'dataset_type': indexer.data_type, 'data': frequencies, 'max': max_v, 'avg': avg_v, 'delta': delta }
+ return summary
if 'index' in data_sources and data_sources['index']['name'] == "summary_tree" and mode == "Auto":
# Only check for summary_tree if it's Auto mode (which is the default)
@@ -153,14 +152,13 @@
indexer = data_provider_registry.get_data_provider( trans, original_dataset=dataset, source='index' )
summary = indexer.get_data( chrom, low, high, resolution=kwargs[ 'resolution' ] )
if summary is None:
- return { 'dataset_type': indexer.data_type, 'data': None }
+ return { 'dataset_type': indexer.dataset_type, 'data': None }
if summary == "draw":
kwargs["no_detail"] = True # meh
extra_info = "no_detail"
elif summary != "detail":
- frequencies, max_v, avg_v, delta = summary
- return { 'dataset_type': indexer.data_type, 'data': frequencies, 'max': max_v, 'avg': avg_v, 'delta': delta }
+ return summary
# Get data provider.
data_provider = data_provider_registry.get_data_provider( trans, original_dataset=dataset, source='data' )
@@ -171,7 +169,7 @@
# Get and return data from data_provider.
result = data_provider.get_data( chrom, int( low ), int( high ), int( start_val ), int( max_vals ), **kwargs )
- result.update( { 'dataset_type': data_provider.data_type, 'extra_info': extra_info } )
+ result.update( { 'dataset_type': data_provider.dataset_type, 'extra_info': extra_info } )
return result
def _raw_data( self, trans, dataset, **kwargs ):
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 lib/galaxy/webapps/main/controllers/admin_toolshed.py
--- a/lib/galaxy/webapps/main/controllers/admin_toolshed.py
+++ b/lib/galaxy/webapps/main/controllers/admin_toolshed.py
@@ -342,8 +342,10 @@
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
repository = get_repository( trans, kwd[ 'id' ] )
+ has_readme = repository.metadata and 'readme' in repository.metadata
return trans.fill_template( '/admin/tool_shed_repository/browse_repository.mako',
repository=repository,
+ has_readme=has_readme,
message=message,
status=status )
@web.expose
@@ -388,8 +390,11 @@
message += "Choose <b>Uninstall this tool dependency</b> from the <b>Repository Actions</b> menu, correct problems "
message += "if necessary, and try installing the dependency again."
status = "error"
+ tool_shed_repository = tool_dependency.tool_shed_repository
+ has_readme = tool_shed_repository.metadata and 'readme' in tool_shed_repository.metadata
return trans.fill_template( '/admin/tool_shed_repository/browse_tool_dependency.mako',
- repository=tool_dependency.tool_shed_repository,
+ repository=tool_shed_repository,
+ has_readme=has_readme,
tool_dependency=tool_dependency,
in_error_state=in_error_state,
can_uninstall=can_uninstall,
@@ -487,9 +492,11 @@
message=message,
status=status ) )
remove_from_disk_check_box = CheckboxField( 'remove_from_disk', checked=remove_from_disk_checked )
+ has_readme = tool_shed_repository.metadata and 'readme' in tool_shed_repository.metadata
return trans.fill_template( '/admin/tool_shed_repository/deactivate_or_uninstall_repository.mako',
repository=tool_shed_repository,
remove_from_disk_check_box=remove_from_disk_check_box,
+ has_readme=has_readme,
message=message,
status=status )
@web.expose
@@ -716,6 +723,7 @@
when an admin is installing a new repository or reinstalling an uninstalled repository.
"""
metadata_dict, invalid_file_tups = generate_metadata_for_changeset_revision( app=trans.app,
+ repository=tool_shed_repository,
repository_clone_url=repository_clone_url,
relative_install_dir=relative_install_dir,
repository_files_dir=None,
@@ -837,8 +845,10 @@
trans.sa_session.add( repository )
trans.sa_session.flush()
message = "Repository metadata has been reset."
+ has_readme = repository.metadata and 'readme' in repository.metadata
return trans.fill_template( '/admin/tool_shed_repository/manage_repository.mako',
repository=repository,
+ has_readme=has_readme,
in_error_state=in_error_state,
can_install=can_install,
description=description,
@@ -914,14 +924,14 @@
tool_shed_repository = tool_dependency.tool_shed_repository
self.tool_dependency_grid.title = "Tool shed repository '%s' tool dependencies" % tool_shed_repository.name
self.tool_dependency_grid.global_actions = \
- [ grids.GridAction( label='Browse repository',
+ [ grids.GridAction( label='Manage repository',
+ url_args=dict( controller='admin_toolshed',
+ action='manage_repository',
+ id=trans.security.encode_id( tool_shed_repository.id ) ) ),
+ grids.GridAction( label='Browse repository',
url_args=dict( controller='admin_toolshed',
action='browse_repository',
id=trans.security.encode_id( tool_shed_repository.id ) ) ),
- grids.GridAction( label='Manage repository',
- url_args=dict( controller='admin_toolshed',
- action='manage_repository',
- id=trans.security.encode_id( tool_shed_repository.id ) ) ),
grids.GridAction( label='Get repository updates',
url_args=dict( controller='admin_toolshed',
action='check_for_updates',
@@ -934,6 +944,12 @@
url_args=dict( controller='admin_toolshed',
action='deactivate_or_uninstall_repository',
id=trans.security.encode_id( tool_shed_repository.id ) ) ) ]
+ if tool_shed_repository.metadata and 'readme' in tool_shed_repository.metadata:
+ view_readme_action = grids.GridAction( label='View README',
+ url_args=dict( controller='admin_toolshed',
+ action='view_readme',
+ id=trans.security.encode_id( tool_shed_repository.id ) ) )
+ self.tool_dependency_grid.global_actions.insert( 1, view_readme_action )
if 'operation' in kwd:
operation = kwd[ 'operation' ].lower()
if not tool_dependency_ids:
@@ -1010,9 +1026,8 @@
message += '<b><toolbox></b> tag that includes a <b>tool_path</b> attribute value which is a directory relative to the Galaxy installation '
message += 'directory in order to automatically install tools from a Galaxy tool shed (e.g., the file name <b>shed_tool_conf.xml</b> whose '
message += '<b><toolbox></b> tag is <b><toolbox tool_path="../shed_tools"></b>).<p/>See the '
- message += '<a href="http://wiki.g2.bx.psu.edu/Tool%20Shed#Automatic_installation_of_Galaxy_tool…" '
- message += 'target="_blank">Automatic installation of Galaxy tool shed repository tools into a local Galaxy instance</a> section of the '
- message += '<a href="http://wiki.g2.bx.psu.edu/Tool%20Shed" target="_blank">Galaxy tool shed wiki</a> for all of the details.'
+ message += '<a href="http://wiki.g2.bx.psu.edu/InstallingRepositoriesToGalaxy" target="_blank">Installation of Galaxy tool shed repository tools '
+ message += 'into a local Galaxy instance</a> section of the Galaxy tool shed wiki for all of the details.'
return trans.show_error_message( message )
message = kwd.get( 'message', '' )
status = kwd.get( 'status', 'done' )
@@ -1182,13 +1197,7 @@
response = urllib2.urlopen( url )
raw_text = response.read()
response.close()
- readme_text = ''
- for i, line in enumerate( raw_text ):
- readme_text = '%s%s' % ( readme_text, to_html_str( line ) )
- if len( readme_text ) > MAX_CONTENT_SIZE:
- large_str = '\nFile contents truncated because file size is larger than maximum viewing size of %s\n' % util.nice_size( MAX_CONTENT_SIZE )
- readme_text = '%s%s' % ( readme_text, to_html_str( large_str ) )
- break
+ readme_text = translate_string( raw_text, to_html=True )
else:
readme_text = ''
if trans.app.config.tool_dependency_dir is None:
@@ -1419,8 +1428,19 @@
status = 'warning'
includes_tool_dependencies = 'tool_dependencies' in metadata
install_tool_dependencies_check_box = CheckboxField( 'install_tool_dependencies', checked=True )
+ if metadata and 'readme' in metadata:
+ url = url_join( tool_shed_url,
+ 'repository/get_readme?name=%s&owner=%s&changeset_revision=%s&webapp=galaxy' % \
+ ( repository.name, repository.owner, repository.installed_changeset_revision ) )
+ response = urllib2.urlopen( url )
+ raw_text = response.read()
+ response.close()
+ readme_text = translate_string( raw_text, to_html=True )
+ else:
+ readme_text = ''
return trans.fill_template( '/admin/tool_shed_repository/reselect_tool_panel_section.mako',
repository=repository,
+ readme_text=readme_text,
no_changes_check_box=no_changes_check_box,
original_section_name=original_section_name,
install_tool_dependencies_check_box=install_tool_dependencies_check_box,
@@ -1528,6 +1548,8 @@
for tool_dependency_id in tool_dependency_ids:
tool_dependency = get_tool_dependency( trans, tool_dependency_id )
tool_dependencies.append( tool_dependency )
+ tool_shed_repository = tool_dependencies[ 0 ].tool_shed_repository
+ has_readme = tool_shed_repository.metadata and 'readme' in tool_shed_repository.metadata
if kwd.get( 'uninstall_tool_dependencies_button', False ):
errors = False
# Filter tool dependencies to only those that are installed.
@@ -1546,7 +1568,6 @@
status = 'error'
else:
message = "These tool dependencies have been uninstalled: %s" % ','.join( td.name for td in tool_dependencies_for_uninstallation )
- tool_shed_repository = tool_dependencies[ 0 ].tool_shed_repository
td_ids = [ trans.security.encode_id( td.id ) for td in tool_shed_repository.tool_dependencies ]
return trans.response.send_redirect( web.url_for( controller='admin_toolshed',
action='manage_tool_dependencies',
@@ -1554,6 +1575,9 @@
status=status,
message=message ) )
return trans.fill_template( '/admin/tool_shed_repository/uninstall_tool_dependencies.mako',
+ repository=tool_shed_repository,
+ has_readme=has_readme,
+ tool_dependency_ids=tool_dependency_ids,
tool_dependencies=tool_dependencies,
message=message,
status=status )
@@ -1617,27 +1641,55 @@
status=status ) )
@web.expose
@web.require_admin
+ def view_readme( self, trans, id, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ cntrller = params.get( 'cntrller', 'admin_toolshed' )
+ status = params.get( 'status', 'done' )
+ repository = get_repository( trans, id )
+ metadata = repository.metadata
+ if metadata and 'readme' in metadata:
+ f = open( metadata[ 'readme' ], 'r' )
+ raw_text = f.read()
+ f.close()
+ readme_text = translate_string( raw_text, to_html=True )
+ else:
+ readme_text = ''
+ is_malicious = False
+ return trans.fill_template( '/webapps/community/common/view_readme.mako',
+ cntrller=cntrller,
+ repository=repository,
+ changeset_revision=repository.changeset_revision,
+ readme_text=readme_text,
+ is_malicious=is_malicious,
+ webapp='galaxy',
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_admin
def view_tool_metadata( self, trans, repository_id, tool_id, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
webapp = get_webapp( trans, **kwd )
repository = get_repository( trans, repository_id )
- metadata = {}
+ repository_metadata = repository.metadata
+ tool_metadata = {}
tool_lineage = []
tool = None
- if 'tools' in repository.metadata:
- for tool_metadata_dict in repository.metadata[ 'tools' ]:
+ if 'tools' in repository_metadata:
+ for tool_metadata_dict in repository_metadata[ 'tools' ]:
if tool_metadata_dict[ 'id' ] == tool_id:
- metadata = tool_metadata_dict
- tool = trans.app.toolbox.load_tool( os.path.abspath( metadata[ 'tool_config' ] ), guid=metadata[ 'guid' ] )
+ tool_metadata = tool_metadata_dict
+ tool = trans.app.toolbox.load_tool( os.path.abspath( tool_metadata[ 'tool_config' ] ), guid=metadata[ 'guid' ] )
if tool:
tool_lineage = self.get_versions_of_tool( trans.app, tool.id )
break
return trans.fill_template( "/admin/tool_shed_repository/view_tool_metadata.mako",
repository=repository,
+ repository_metadata=repository_metadata,
tool=tool,
- metadata=metadata,
+ tool_metadata=tool_metadata,
tool_lineage=tool_lineage,
message=message,
status=status )
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 lib/galaxy/webapps/main/controllers/library_common.py
--- a/lib/galaxy/webapps/main/controllers/library_common.py
+++ b/lib/galaxy/webapps/main/controllers/library_common.py
@@ -92,6 +92,7 @@
#"force_history_refresh": force_history_refresh
}
return rval
+
@web.expose
def browse_library( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
@@ -129,6 +130,7 @@
status = "info"
comptypes = get_comptypes( trans )
try:
+ # SM: TODO: Add configuration variable asap.
return trans.fill_template( '/library/common/browse_library.mako',
cntrller=cntrller,
use_panels=use_panels,
@@ -144,6 +146,7 @@
message = 'Error attempting to display contents of library (%s): %s.' % ( str( library.name ), str( e ) )
status = 'error'
default_action = params.get( 'default_action', None )
+
return trans.response.send_redirect( web.url_for( use_panels=use_panels,
controller=cntrller,
action='browse_libraries',
@@ -2534,16 +2537,57 @@
.options( eagerload_all( "actions" ) ) \
.order_by( trans.app.model.LibraryFolder.table.c.name ) \
.all()
+
+def map_library_datasets_to_lddas( trans, lib_datasets ):
+ '''
+ Given a list of LibraryDatasets, return a map from the LibraryDatasets
+ to their LDDAs. If an LDDA does not exist for a LibraryDataset, then
+ there will be no entry in the return hash.
+ '''
+ # Get a list of the LibraryDatasets' ids so that we can pass it along to
+ # a query to retrieve the LDDAs. This eliminates querying for each
+ # LibraryDataset.
+ lib_dataset_ids = [ x.library_dataset_dataset_association_id for x in lib_datasets ]
+ lddas = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ) \
+ .filter( trans.app.model.LibraryDatasetDatasetAssociation.id.in_( lib_dataset_ids ) ) \
+ .all()
+
+ # Map the LibraryDataset to the returned LDDAs:
+ ret_lddas = {}
+ for ldda in lddas:
+ ret_lddas[ldda.library_dataset_id] = ldda
+ return ret_lddas
+
+def datasets_for_lddas( trans, lddas ):
+ '''
+ Given a list of LDDAs, return a list of Datasets for them.
+ '''
+ dataset_ids = [ x.dataset_id for x in lddas ]
+ datasets = trans.sa_session.query( trans.app.model.Dataset ) \
+ .filter( trans.app.model.Dataset.id.in_( dataset_ids ) ) \
+ .all()
+ return datasets
+
def active_folders_and_library_datasets( trans, folder ):
+ # SM: TODO: Eliminate timing code
+ from datetime import datetime, timedelta
+ query_start = datetime.now()
folders = active_folders( trans, folder )
library_datasets = trans.sa_session.query( trans.model.LibraryDataset ) \
.filter( and_( trans.model.LibraryDataset.table.c.deleted == False,
trans.model.LibraryDataset.table.c.folder_id == folder.id ) ) \
.order_by( trans.model.LibraryDataset.table.c._name ) \
.all()
+ query_end = datetime.now()
+ query_delta = query_end - query_start
+ #log.debug( "active_folders_and_library_datasets: %d.%.6d" %
+ # ( query_delta.seconds, query_delta.microseconds ) )
return folders, library_datasets
+
def activatable_folders_and_library_datasets( trans, folder ):
folders = activatable_folders( trans, folder )
+ from datetime import datetime, timedelta
+ query_start = datetime.now()
library_datasets = trans.sa_session.query( trans.model.LibraryDataset ) \
.filter( trans.model.LibraryDataset.table.c.folder_id == folder.id ) \
.join( ( trans.model.LibraryDatasetDatasetAssociation.table,
@@ -2553,6 +2597,10 @@
.filter( trans.model.Dataset.table.c.deleted == False ) \
.order_by( trans.model.LibraryDataset.table.c._name ) \
.all()
+ query_end = datetime.now()
+ query_delta = query_end - query_start
+ log.debug( "activatable_folders_and_library_datasets: %d.%.6d" %
+ ( query_delta.seconds, query_delta.microseconds ) )
return folders, library_datasets
def branch_deleted( folder ):
# Return True if a folder belongs to a branch that has been deleted
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 lib/galaxy/webapps/main/controllers/tool_runner.py
--- a/lib/galaxy/webapps/main/controllers/tool_runner.py
+++ b/lib/galaxy/webapps/main/controllers/tool_runner.py
@@ -43,6 +43,7 @@
toolbox = self.get_toolbox()
tool_version_select_field = None
tools = []
+ tool = None
# Backwards compatibility for datasource tools that have default tool_id configured, but which are now using only GALAXY_URL.
tool_ids = util.listify( tool_id )
for tool_id in tool_ids:
@@ -50,27 +51,21 @@
tools = toolbox.get_loaded_tools_by_lineage( tool_id )
else:
tools = toolbox.get_tool( tool_id, tool_version=tool_version, get_all_versions=True )
- if len( tools ) > 1:
- tool_version_select_field = self.build_tool_version_select_field( tools, tool_id, set_selected )
- for tool in tools:
- if tool.id == tool_id:
- break
- else:
- tool = tools[ 0 ]
- else:
- tool = tools[ 0 ]
- break
+ if tools:
+ tool = toolbox.get_tool( tool_id, tool_version=tool_version, get_all_versions=False )
+ if len( tools ) > 1:
+ tool_version_select_field = self.build_tool_version_select_field( tools, tool.id, set_selected )
+ break
return tool_version_select_field, tools, tool
@web.expose
def index(self, trans, tool_id=None, from_noframe=None, **kwd):
# No tool id passed, redirect to main page
if tool_id is None:
return trans.response.send_redirect( url_for( "/static/welcome.html" ) )
- set_selected = 'refresh' in kwd
tool_version_select_field, tools, tool = self.__get_tool_components( tool_id,
tool_version=None,
- get_loaded_tools_by_lineage=True,
- set_selected=set_selected )
+ get_loaded_tools_by_lineage=False,
+ set_selected=True )
# No tool matching the tool id, display an error (shouldn't happen)
if not tool:
log.error( "index called with tool id '%s' but no such tool exists", tool_id )
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 lib/galaxy/webapps/main/controllers/user.py
--- a/lib/galaxy/webapps/main/controllers/user.py
+++ b/lib/galaxy/webapps/main/controllers/user.py
@@ -1,16 +1,22 @@
"""
Contains the user interface in the Universe class
"""
+
+import glob
+import logging
+import os
+import socket
+import string
+import random
+from galaxy import web
+from galaxy import util, model
+from galaxy.model.orm import and_
+from galaxy.security.validate_user_input import validate_email, validate_publicname, validate_password, transform_publicname
+from galaxy.util.json import from_json_string, to_json_string
+from galaxy.web import url_for
+from galaxy.web.base.controller import BaseUIController, UsesFormDefinitionsMixin, get_webapp
+from galaxy.web.form_builder import CheckboxField, build_select_field
from galaxy.web.framework.helpers import time_ago, grids
-from galaxy.web.base.controller import *
-from galaxy.model.orm import *
-from galaxy import util, model
-import logging, os, string, re, socket, glob
-from random import choice
-from galaxy.web.form_builder import *
-from galaxy.util.json import from_json_string, to_json_string
-from galaxy.web.framework.helpers import iff
-from galaxy.security.validate_user_input import validate_email, validate_publicname, validate_password, transform_publicname
log = logging.getLogger( __name__ )
@@ -395,6 +401,7 @@
use_panels=use_panels,
message=message,
status=status ) )
+
@web.expose
@web.require_login( 'manage OpenIDs' )
def openid_manage( self, trans, webapp='galaxy', **kwd ):
@@ -409,10 +416,10 @@
action='openid_disassociate',
use_panels=use_panels,
id=kwd['id'] ) )
-
kwd['redirect'] = kwd.get( 'redirect', url_for( controller='user', action='openid_manage', use_panels=True ) ).strip()
kwd['openid_providers'] = trans.app.openid_providers
return self.user_openid_grid( trans, **kwd )
+
@web.expose
def login( self, trans, webapp='galaxy', redirect_url='', refresh_frames=[], **kwd ):
'''Handle Galaxy Log in'''
@@ -423,6 +430,9 @@
header = ''
user = None
email = kwd.get( 'email', '' )
+ #Sanitize webapp login here, once, since it can be reflected to the user in messages/etc.
+ #Only text is valid.
+ webapp = util.sanitize_text(webapp)
if kwd.get( 'login_button', False ):
if webapp == 'galaxy' and not refresh_frames:
if trans.app.config.require_login:
@@ -489,6 +499,7 @@
message += ' <a target="_top" href="%s">Click here</a> to continue to the home page.' % web.url_for( '/static/welcome.html' )
success = True
return ( message, status, user, success )
+
@web.expose
def logout( self, trans, webapp='galaxy', logout_all=False ):
if webapp == 'galaxy':
@@ -509,6 +520,7 @@
message=message,
status='done',
active_view="user" )
+
@web.expose
def create( self, trans, cntrller='user', redirect_url='', refresh_frames=[], **kwd ):
params = util.Params( kwd )
@@ -579,8 +591,6 @@
return trans.fill_template( '/user/register.mako',
cntrller=cntrller,
email=email,
- password=password,
- confirm=confirm,
username=transform_publicname( trans, username ),
subscribe_checked=subscribe_checked,
user_type_fd_id_select_field=user_type_fd_id_select_field,
@@ -593,6 +603,7 @@
refresh_frames=refresh_frames,
message=message,
status=status )
+
def __register( self, trans, cntrller, subscribe_checked, **kwd ):
email = util.restore_text( kwd.get( 'email', '' ) )
password = kwd.get( 'password', '' )
@@ -683,6 +694,7 @@
else:
widgets = user_type_form_definition.get_widgets( None, contents={}, **kwd )
return widgets
+
@web.expose
def manage_user_info( self, trans, cntrller, **kwd ):
'''Manage a user's login, password, public username, type, addresses, etc.'''
@@ -696,11 +708,6 @@
raise AssertionError, "The user id (%s) is not valid" % str( user_id )
webapp = get_webapp( trans, **kwd )
email = util.restore_text( params.get( 'email', user.email ) )
- # Do not sanitize passwords, so take from kwd
- # instead of params ( which were sanitized )
- current = kwd.get( 'current', '' )
- password = kwd.get( 'password', '' )
- confirm = kwd.get( 'confirm', '' )
username = util.restore_text( params.get( 'username', '' ) )
if not username:
username = user.username
@@ -710,7 +717,7 @@
user_type_form_definition = self.__get_user_type_form_definition( trans, user=user, **kwd )
user_type_fd_id = params.get( 'user_type_fd_id', 'none' )
if user_type_fd_id == 'none' and user_type_form_definition is not None:
- user_type_fd_id = trans.security.encode_id( user_type_form_definition.id )
+ user_type_fd_id = trans.security.encode_id( user_type_form_definition.id )
user_type_fd_id_select_field = self.__build_user_type_fd_id_select_field( trans, selected_value=user_type_fd_id )
widgets = self.__get_widgets( trans, user_type_form_definition, user=user, **kwd )
# user's addresses
@@ -728,14 +735,11 @@
cntrller=cntrller,
user=user,
email=email,
- current=current,
- password=password,
- confirm=confirm,
username=username,
user_type_fd_id_select_field=user_type_fd_id_select_field,
user_info_forms=user_info_forms,
user_type_form_definition=user_type_form_definition,
- widgets=widgets,
+ widgets=widgets,
addresses=addresses,
show_filter=show_filter,
webapp=webapp,
@@ -746,13 +750,11 @@
cntrller=cntrller,
user=user,
email=email,
- current=current,
- password=password,
- confirm=confirm,
username=username,
webapp=webapp,
message=message,
status=status )
+
# For REMOTE_USER, we need the ability to just edit the username
@web.expose
@web.require_login( "to manage the public name" )
@@ -911,7 +913,7 @@
chars = string.letters + string.digits
new_pass = ""
for i in range(15):
- new_pass = new_pass + choice(chars)
+ new_pass = new_pass + random.choice(chars)
host = trans.request.host.split(':')[0]
if host == 'localhost':
host = socket.getfqdn()
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 lib/galaxy/webapps/main/controllers/visualization.py
--- a/lib/galaxy/webapps/main/controllers/visualization.py
+++ b/lib/galaxy/webapps/main/controllers/visualization.py
@@ -763,7 +763,7 @@
original_dataset=dataset,
source='index' )
# HACK: pass in additional params, which are only used for summary tree data, not BBI data.
- track[ 'genome_wide_data' ] = { 'data': data_provider.get_genome_data( chroms_info, level=4, detail_cutoff=0, draw_cutoff=0 ) }
+ track[ 'preloaded_data' ] = data_provider.get_genome_data( chroms_info, level=4, detail_cutoff=0, draw_cutoff=0 )
return trans.fill_template( 'visualization/circster.mako', viz_config=viz_config, genome=genome )
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 lib/galaxy/workflow/modules.py
--- a/lib/galaxy/workflow/modules.py
+++ b/lib/galaxy/workflow/modules.py
@@ -199,13 +199,9 @@
# TODO: If workflows are ever enhanced to use tool version
# in addition to tool id, enhance the selection process here
# to retrieve the correct version of the tool.
- tool_version = Class.__get_tool_version( trans, tool_id )
- if tool_version:
- tool_version_ids = tool_version.get_version_ids( trans.app )
- for tool_version_id in tool_version_ids:
- if tool_version_id in trans.app.toolbox.tools_by_id:
- tool_id = tool_version_id
- break
+ tool = trans.app.toolbox.get_tool( tool_id )
+ if tool:
+ tool_id = tool.id
if ( trans.app.toolbox and tool_id in trans.app.toolbox.tools_by_id ):
module = Class( trans, tool_id )
module.state = DefaultToolState()
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/june_2007_style/base.less
--- a/static/june_2007_style/base.less
+++ b/static/june_2007_style/base.less
@@ -763,8 +763,16 @@
#search-clear-btn {
position: absolute;
- right: 4px;
- top: 8px;
+ right: 5px;
+ top: 9px;
+ display: block;
+ font-size: 1.4em;
+ text-decoration: none;
+ color: #888;
+ .ficon();
+ &:before {
+ content: "\f057";
+ }
}
// Messages
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/june_2007_style/blue/base.css
--- a/static/june_2007_style/blue/base.css
+++ b/static/june_2007_style/blue/base.css
@@ -715,7 +715,7 @@
.search-query{display:inline-block;padding:4px;font-size:12px;line-height:16px;color:#555555;border:1px solid #999999;padding-left:14px !important;padding-right:14px !important;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px;max-width:auto;}
.search-query:focus{border-color:rgba(24, 132, 218, 0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);outline:0;outline:thin dotted \9;}
.search-spinner{position:absolute;display:none;right:5px;top:9px;}
-#search-clear-btn{position:absolute;right:4px;top:8px;}
+#search-clear-btn{position:absolute;right:5px;top:9px;display:block;font-size:1.4em;text-decoration:none;color:#888;font-family:FontAwesome;font-weight:normal;font-style:normal;display:inline-block;}#search-clear-btn:before{content:"\f057";}
.errormessagelarge,.warningmessagelarge,.donemessagelarge,.infomessagelarge{padding:8px 35px 8px 14px;margin-bottom:16px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#ffffcc;border:1px solid #ffdd33;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;color:#666600;min-height:36px;padding-left:52px;background-image:url(error_large.png);background-repeat:no-repeat;background-position:10px 10px;}
.errormessagelarge{background-color:#ffcccc;border-color:#ff3355;color:#660000;padding-left:52px;}
.warningmessagelarge{background-image:url(warn_large.png);border-color:#aaaa66;background-color:#ffffcc;}
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/galaxy.base.js
--- a/static/scripts/galaxy.base.js
+++ b/static/scripts/galaxy.base.js
@@ -65,15 +65,19 @@
});
};
+/**
+ * Sets up popupmenu rendering and binds options functions to the appropriate links
+ */
function make_popupmenu(button_element, initial_options) {
-
/* Use the $.data feature to store options with the link element.
This allows options to be changed at a later time
*/
var element_menu_exists = (button_element.data("menu_options"));
button_element.data("menu_options", initial_options);
+
// If element already has menu, nothing else to do since HTML and actions are already set.
if (element_menu_exists) { return; }
+
button_element.bind("click.show_popup", function(e) {
// Close existing visible menus
$(".popmenu-wrapper").remove();
@@ -93,16 +97,17 @@
menu_element.append( $("<li></li>").addClass( "head" ).append( $("<a href='#'></a>").html(k) ) );
}
});
- var wrapper = $( "<div class='popmenu-wrapper' style='position: absolute;left: 0; top: -1000;'></div>" ).append( menu_element ).appendTo( "body" );
+ var wrapper = $( "<div class='popmenu-wrapper' style='position: absolute;left: 0; top: -1000;'></div>" )
+ .append( menu_element ).appendTo( "body" );
var x = e.pageX - wrapper.width() / 2 ;
x = Math.min( x, $(document).scrollLeft() + $(window).width() - $(wrapper).width() - 5 );
x = Math.max( x, $(document).scrollLeft() + 5 );
- wrapper.css( {
+ wrapper.css({
top: e.pageY,
left: x
- } );
+ });
}, 10);
setTimeout( function() {
@@ -127,26 +132,53 @@
}
-function make_popup_menus() {
- jQuery( "div[popupmenu]" ).each( function() {
+/**
+ * Convert two seperate (often adjacent) divs into galaxy popupmenu
+ * - div 1 contains a number of anchors which become the menu options
+ * - div 1 should have a 'popupmenu' attribute
+ * - this popupmenu attribute contains the id of div 2
+ * - div 2 becomes the 'face' of the popupmenu
+ *
+ * NOTE: make_popup_menus finds and operates on all divs with a popupmenu attr (no need to point it at something)
+ * but (since that selector searches the dom on the page), you can send a parent in
+ * NOTE: make_popup_menus, and make_popupmenu are horrible names
+ */
+function make_popup_menus( parent ) {
+ // find all popupmenu menu divs (divs that contains anchors to be converted to menu options)
+ // either in the parent or the document if no parent passed
+ parent = parent || document;
+ $( parent ).find( "div[popupmenu]" ).each( function() {
var options = {};
var menu = $(this);
+
+ // find each anchor in the menu, convert them into an options map: { a.text : click_function }
menu.find( "a" ).each( function() {
var link = $(this),
- link_dom = link.get(0);
- var confirmtext = link_dom.getAttribute( "confirm" ),
+ // why do we need the DOM (mixed with jq)?
+ link_dom = link.get(0),
+ confirmtext = link_dom.getAttribute( "confirm" ),
href = link_dom.getAttribute( "href" ),
target = link_dom.getAttribute( "target" );
+
+ // no href - no function (gen. a label)
if (!href) {
options[ link.text() ] = null;
+
} else {
options[ link.text() ] = function() {
+
+ // if theres confirm text, send the dialog
if ( !confirmtext || confirm( confirmtext ) ) {
var f;
+ // relocate the center panel
if ( target == "_parent" ) {
window.parent.location = href;
+
+ // relocate the entire window
} else if ( target == "_top" ) {
window.top.location = href;
+
+ //??...wot?
} else if ( target == "demo" ) {
// Http request target is a window named
// demolocal on the local box
@@ -154,6 +186,8 @@
f = window.open( href,target );
f.creator = self;
}
+
+ // relocate this panel
} else {
window.location = href;
}
@@ -161,6 +195,7 @@
};
}
});
+ // locate the element with the id corresponding to the menu's popupmenu attr
var box = $( "#" + menu.attr( 'popupmenu' ) );
// For menus with clickable link text, make clicking on the link go through instead
@@ -170,6 +205,7 @@
return true;
});
+ // attach the click events and menu box building to the box element
make_popupmenu(box, options);
box.addClass("popup");
menu.remove();
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/libs/underscore.js
--- a/static/scripts/libs/underscore.js
+++ b/static/scripts/libs/underscore.js
@@ -1,10 +1,7 @@
-// Underscore.js 1.3.1
+// Underscore.js 1.4.0
+// http://underscorejs.org
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
-// Underscore is freely distributable under the MIT license.
-// Portions of Underscore are inspired or borrowed from Prototype,
-// Oliver Steele's Functional, and John Resig's Micro-Templating.
-// For all details and documentation:
-// http://documentcloud.github.com/underscore
+// Underscore may be freely distributed under the MIT license.
(function() {
@@ -24,7 +21,9 @@
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
// Create quick reference variables for speed access to core prototypes.
- var slice = ArrayProto.slice,
+ var push = ArrayProto.push,
+ slice = ArrayProto.slice,
+ concat = ArrayProto.concat,
unshift = ArrayProto.unshift,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
@@ -46,7 +45,11 @@
nativeBind = FuncProto.bind;
// Create a safe reference to the Underscore object for use below.
- var _ = function(obj) { return new wrapper(obj); };
+ var _ = function(obj) {
+ if (obj instanceof _) return obj;
+ if (!(this instanceof _)) return new _(obj);
+ this._wrapped = obj;
+ };
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
@@ -62,7 +65,7 @@
}
// Current version.
- _.VERSION = '1.3.1';
+ _.VERSION = '1.4.0';
// Collection Functions
// --------------------
@@ -71,12 +74,11 @@
// Handles objects with the built-in `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _.each = _.forEach = function(obj, iterator, context) {
- if (obj == null) return;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (var i = 0, l = obj.length; i < l; i++) {
- if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
+ if (iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
for (var key in obj) {
@@ -91,12 +93,10 @@
// Delegates to **ECMAScript 5**'s native `map` if available.
_.map = _.collect = function(obj, iterator, context) {
var results = [];
- if (obj == null) return results;
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
each(obj, function(value, index, list) {
results[results.length] = iterator.call(context, value, index, list);
});
- if (obj.length === +obj.length) results.length = obj.length;
return results;
};
@@ -104,7 +104,6 @@
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
- if (obj == null) obj = [];
if (nativeReduce && obj.reduce === nativeReduce) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
@@ -125,14 +124,26 @@
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
- if (obj == null) obj = [];
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
if (context) iterator = _.bind(iterator, context);
- return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
+ return arguments.length > 2 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
}
- var reversed = _.toArray(obj).reverse();
- if (context && !initial) iterator = _.bind(iterator, context);
- return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
+ var length = obj.length;
+ if (length !== +length) {
+ var keys = _.keys(obj);
+ length = keys.length;
+ }
+ each(obj, function(value, index, list) {
+ index = keys ? keys[--length] : --length;
+ if (!initial) {
+ memo = obj[index];
+ initial = true;
+ } else {
+ memo = iterator.call(context, memo, obj[index], index, list);
+ }
+ });
+ if (!initial) throw new TypeError('Reduce of empty array with no initial value');
+ return memo;
};
// Return the first value which passes a truth test. Aliased as `detect`.
@@ -152,7 +163,6 @@
// Aliased as `select`.
_.filter = _.select = function(obj, iterator, context) {
var results = [];
- if (obj == null) return results;
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
each(obj, function(value, index, list) {
if (iterator.call(context, value, index, list)) results[results.length] = value;
@@ -163,7 +173,6 @@
// Return all the elements for which a truth test fails.
_.reject = function(obj, iterator, context) {
var results = [];
- if (obj == null) return results;
each(obj, function(value, index, list) {
if (!iterator.call(context, value, index, list)) results[results.length] = value;
});
@@ -174,13 +183,13 @@
// Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`.
_.every = _.all = function(obj, iterator, context) {
+ iterator || (iterator = _.identity);
var result = true;
- if (obj == null) return result;
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
each(obj, function(value, index, list) {
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
});
- return result;
+ return !!result;
};
// Determine if at least one element in the object matches a truth test.
@@ -189,7 +198,6 @@
var any = _.some = _.any = function(obj, iterator, context) {
iterator || (iterator = _.identity);
var result = false;
- if (obj == null) return result;
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
each(obj, function(value, index, list) {
if (result || (result = iterator.call(context, value, index, list))) return breaker;
@@ -197,11 +205,10 @@
return !!result;
};
- // Determine if a given value is included in the array or object using `===`.
- // Aliased as `contains`.
- _.include = _.contains = function(obj, target) {
+ // Determine if the array or object contains a given value (using `===`).
+ // Aliased as `include`.
+ _.contains = _.include = function(obj, target) {
var found = false;
- if (obj == null) return found;
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
found = any(obj, function(value) {
return value === target;
@@ -213,7 +220,7 @@
_.invoke = function(obj, method) {
var args = slice.call(arguments, 2);
return _.map(obj, function(value) {
- return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
+ return (_.isFunction(method) ? method : value[method]).apply(value, args);
});
};
@@ -222,9 +229,25 @@
return _.map(obj, function(value){ return value[key]; });
};
+ // Convenience version of a common use case of `filter`: selecting only objects
+ // with specific `key:value` pairs.
+ _.where = function(obj, attrs) {
+ if (_.isEmpty(attrs)) return [];
+ return _.filter(obj, function(value) {
+ for (var key in attrs) {
+ if (attrs[key] !== value[key]) return false;
+ }
+ return true;
+ });
+ };
+
// Return the maximum element or (element-based computation).
+ // Can't optimize arrays of integers longer than 65,535 elements.
+ // See: https://bugs.webkit.org/show_bug.cgi?id=80797
_.max = function(obj, iterator, context) {
- if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
+ if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
+ return Math.max.apply(Math, obj);
+ }
if (!iterator && _.isEmpty(obj)) return -Infinity;
var result = {computed : -Infinity};
each(obj, function(value, index, list) {
@@ -236,7 +259,9 @@
// Return the minimum element (or element-based computation).
_.min = function(obj, iterator, context) {
- if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
+ if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
+ return Math.min.apply(Math, obj);
+ }
if (!iterator && _.isEmpty(obj)) return Infinity;
var result = {computed : Infinity};
each(obj, function(value, index, list) {
@@ -248,81 +273,107 @@
// Shuffle an array.
_.shuffle = function(obj) {
- var shuffled = [], rand;
- each(obj, function(value, index, list) {
- if (index == 0) {
- shuffled[0] = value;
- } else {
- rand = Math.floor(Math.random() * (index + 1));
- shuffled[index] = shuffled[rand];
- shuffled[rand] = value;
- }
+ var rand;
+ var index = 0;
+ var shuffled = [];
+ each(obj, function(value) {
+ rand = _.random(index++);
+ shuffled[index - 1] = shuffled[rand];
+ shuffled[rand] = value;
});
return shuffled;
};
+ // An internal function to generate lookup iterators.
+ var lookupIterator = function(value) {
+ return _.isFunction(value) ? value : function(obj){ return obj[value]; };
+ };
+
// Sort the object's values by a criterion produced by an iterator.
- _.sortBy = function(obj, iterator, context) {
+ _.sortBy = function(obj, value, context) {
+ var iterator = lookupIterator(value);
return _.pluck(_.map(obj, function(value, index, list) {
return {
value : value,
+ index : index,
criteria : iterator.call(context, value, index, list)
};
}).sort(function(left, right) {
- var a = left.criteria, b = right.criteria;
- return a < b ? -1 : a > b ? 1 : 0;
+ var a = left.criteria;
+ var b = right.criteria;
+ if (a !== b) {
+ if (a > b || a === void 0) return 1;
+ if (a < b || b === void 0) return -1;
+ }
+ return left.index < right.index ? -1 : 1;
}), 'value');
};
+ // An internal function used for aggregate "group by" operations.
+ var group = function(obj, value, context, behavior) {
+ var result = {};
+ var iterator = lookupIterator(value);
+ each(obj, function(value, index) {
+ var key = iterator.call(context, value, index, obj);
+ behavior(result, key, value);
+ });
+ return result;
+ };
+
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
- _.groupBy = function(obj, val) {
- var result = {};
- var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
- each(obj, function(value, index) {
- var key = iterator(value, index);
- (result[key] || (result[key] = [])).push(value);
+ _.groupBy = function(obj, value, context) {
+ return group(obj, value, context, function(result, key, value) {
+ (_.has(result, key) ? result[key] : (result[key] = [])).push(value);
});
- return result;
};
- // Use a comparator function to figure out at what index an object should
- // be inserted so as to maintain order. Uses binary search.
- _.sortedIndex = function(array, obj, iterator) {
- iterator || (iterator = _.identity);
+ // Counts instances of an object that group by a certain criterion. Pass
+ // either a string attribute to count by, or a function that returns the
+ // criterion.
+ _.countBy = function(obj, value, context) {
+ return group(obj, value, context, function(result, key, value) {
+ if (!_.has(result, key)) result[key] = 0;
+ result[key]++;
+ });
+ };
+
+ // Use a comparator function to figure out the smallest index at which
+ // an object should be inserted so as to maintain order. Uses binary search.
+ _.sortedIndex = function(array, obj, iterator, context) {
+ iterator = iterator == null ? _.identity : lookupIterator(iterator);
+ var value = iterator.call(context, obj);
var low = 0, high = array.length;
while (low < high) {
- var mid = (low + high) >> 1;
- iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
+ var mid = (low + high) >>> 1;
+ iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
}
return low;
};
// Safely convert anything iterable into a real, live array.
- _.toArray = function(iterable) {
- if (!iterable) return [];
- if (iterable.toArray) return iterable.toArray();
- if (_.isArray(iterable)) return slice.call(iterable);
- if (_.isArguments(iterable)) return slice.call(iterable);
- return _.values(iterable);
+ _.toArray = function(obj) {
+ if (!obj) return [];
+ if (obj.length === +obj.length) return slice.call(obj);
+ return _.values(obj);
};
// Return the number of elements in an object.
_.size = function(obj) {
- return _.toArray(obj).length;
+ return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
};
// Array Functions
// ---------------
// Get the first element of an array. Passing **n** will return the first N
- // values in the array. Aliased as `head`. The **guard** check allows it to work
- // with `_.map`.
- _.first = _.head = function(array, n, guard) {
+ // values in the array. Aliased as `head` and `take`. The **guard** check
+ // allows it to work with `_.map`.
+ _.first = _.head = _.take = function(array, n, guard) {
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
};
- // Returns everything but the last entry of the array. Especcialy useful on
+ // Returns everything but the last entry of the array. Especially useful on
// the arguments object. Passing **n** will return all the values in
// the array, excluding the last N. The **guard** check allows it to work with
// `_.map`.
@@ -340,12 +391,12 @@
}
};
- // Returns everything but the first entry of the array. Aliased as `tail`.
- // Especially useful on the arguments object. Passing an **index** will return
- // the rest of the values in the array from that index onward. The **guard**
+ // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
+ // Especially useful on the arguments object. Passing an **n** will return
+ // the rest N values in the array. The **guard**
// check allows it to work with `_.map`.
- _.rest = _.tail = function(array, index, guard) {
- return slice.call(array, (index == null) || guard ? 1 : index);
+ _.rest = _.tail = _.drop = function(array, n, guard) {
+ return slice.call(array, (n == null) || guard ? 1 : n);
};
// Trim out all falsy values from an array.
@@ -353,13 +404,21 @@
return _.filter(array, function(value){ return !!value; });
};
+ // Internal implementation of a recursive `flatten` function.
+ var flatten = function(input, shallow, output) {
+ each(input, function(value) {
+ if (_.isArray(value)) {
+ shallow ? push.apply(output, value) : flatten(value, shallow, output);
+ } else {
+ output.push(value);
+ }
+ });
+ return output;
+ };
+
// Return a completely flattened version of an array.
_.flatten = function(array, shallow) {
- return _.reduce(array, function(memo, value) {
- if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
- memo[memo.length] = value;
- return memo;
- }, []);
+ return flatten(array, shallow, []);
};
// Return a version of the array that does not contain the specified value(s).
@@ -370,28 +429,28 @@
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
- _.uniq = _.unique = function(array, isSorted, iterator) {
- var initial = iterator ? _.map(array, iterator) : array;
- var result = [];
- _.reduce(initial, function(memo, el, i) {
- if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
- memo[memo.length] = el;
- result[result.length] = array[i];
+ _.uniq = _.unique = function(array, isSorted, iterator, context) {
+ var initial = iterator ? _.map(array, iterator, context) : array;
+ var results = [];
+ var seen = [];
+ each(initial, function(value, index) {
+ if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
+ seen.push(value);
+ results.push(array[index]);
}
- return memo;
- }, []);
- return result;
+ });
+ return results;
};
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = function() {
- return _.uniq(_.flatten(arguments, true));
+ return _.uniq(concat.apply(ArrayProto, arguments));
};
// Produce an array that contains every item shared between all the
- // passed-in arrays. (Aliased as "intersect" for back-compat.)
- _.intersection = _.intersect = function(array) {
+ // passed-in arrays.
+ _.intersection = function(array) {
var rest = slice.call(arguments, 1);
return _.filter(_.uniq(array), function(item) {
return _.every(rest, function(other) {
@@ -403,8 +462,8 @@
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_.difference = function(array) {
- var rest = _.flatten(slice.call(arguments, 1));
- return _.filter(array, function(value){ return !_.include(rest, value); });
+ var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
+ return _.filter(array, function(value){ return !_.contains(rest, value); });
};
// Zip together multiple lists into a single array -- elements that share
@@ -413,10 +472,27 @@
var args = slice.call(arguments);
var length = _.max(_.pluck(args, 'length'));
var results = new Array(length);
- for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
+ for (var i = 0; i < length; i++) {
+ results[i] = _.pluck(args, "" + i);
+ }
return results;
};
+ // Converts lists into objects. Pass either a single array of `[key, value]`
+ // pairs, or two parallel arrays of the same length -- one of keys, and one of
+ // the corresponding values.
+ _.object = function(list, values) {
+ var result = {};
+ for (var i = 0, l = list.length; i < l; i++) {
+ if (values) {
+ result[list[i]] = values[i];
+ } else {
+ result[list[i][0]] = list[i][1];
+ }
+ }
+ return result;
+ };
+
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
// we need this function. Return the position of the first occurrence of an
// item in an array, or -1 if the item is not included in the array.
@@ -424,23 +500,25 @@
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_.indexOf = function(array, item, isSorted) {
- if (array == null) return -1;
- var i, l;
+ var i = 0, l = array.length;
if (isSorted) {
- i = _.sortedIndex(array, item);
- return array[i] === item ? i : -1;
+ if (typeof isSorted == 'number') {
+ i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);
+ } else {
+ i = _.sortedIndex(array, item);
+ return array[i] === item ? i : -1;
+ }
}
- if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
- for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
+ if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
+ for (; i < l; i++) if (array[i] === item) return i;
return -1;
};
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
- _.lastIndexOf = function(array, item) {
- if (array == null) return -1;
- if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
- var i = array.length;
- while (i--) if (i in array && array[i] === item) return i;
+ _.lastIndexOf = function(array, item, fromIndex) {
+ if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item, fromIndex);
+ var i = (fromIndex != null ? fromIndex : array.length);
+ while (i--) if (array[i] === item) return i;
return -1;
};
@@ -514,7 +592,7 @@
// it with the arguments supplied.
_.delay = function(func, wait) {
var args = slice.call(arguments, 2);
- return setTimeout(function(){ return func.apply(func, args); }, wait);
+ return setTimeout(function(){ return func.apply(null, args); }, wait);
};
// Defers a function, scheduling it to run after the current call stack has
@@ -526,39 +604,46 @@
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time.
_.throttle = function(func, wait) {
- var context, args, timeout, throttling, more;
+ var context, args, timeout, throttling, more, result;
var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
return function() {
context = this; args = arguments;
var later = function() {
timeout = null;
- if (more) func.apply(context, args);
+ if (more) {
+ result = func.apply(context, args);
+ }
whenDone();
};
if (!timeout) timeout = setTimeout(later, wait);
if (throttling) {
more = true;
} else {
- func.apply(context, args);
+ throttling = true;
+ result = func.apply(context, args);
}
whenDone();
- throttling = true;
+ return result;
};
};
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
- // N milliseconds.
- _.debounce = function(func, wait) {
- var timeout;
+ // N milliseconds. If `immediate` is passed, trigger the function on the
+ // leading edge, instead of the trailing.
+ _.debounce = function(func, wait, immediate) {
+ var timeout, result;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
- func.apply(context, args);
+ if (!immediate) result = func.apply(context, args);
};
+ var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
+ if (callNow) result = func.apply(context, args);
+ return result;
};
};
@@ -569,7 +654,9 @@
return function() {
if (ran) return memo;
ran = true;
- return memo = func.apply(this, arguments);
+ memo = func.apply(this, arguments);
+ func = null;
+ return memo;
};
};
@@ -578,7 +665,8 @@
// conditionally execute the original function.
_.wrap = function(func, wrapper) {
return function() {
- var args = [func].concat(slice.call(arguments, 0));
+ var args = [func];
+ push.apply(args, arguments);
return wrapper.apply(this, args);
};
};
@@ -600,7 +688,9 @@
_.after = function(times, func) {
if (times <= 0) return func();
return function() {
- if (--times < 1) { return func.apply(this, arguments); }
+ if (--times < 1) {
+ return func.apply(this, arguments);
+ }
};
};
@@ -618,7 +708,23 @@
// Retrieve the values of an object's properties.
_.values = function(obj) {
- return _.map(obj, _.identity);
+ var values = [];
+ for (var key in obj) if (_.has(obj, key)) values.push(obj[key]);
+ return values;
+ };
+
+ // Convert an object into a list of `[key, value]` pairs.
+ _.pairs = function(obj) {
+ var pairs = [];
+ for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]);
+ return pairs;
+ };
+
+ // Invert the keys and values of an object. The values must be serializable.
+ _.invert = function(obj) {
+ var result = {};
+ for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key;
+ return result;
};
// Return a sorted list of the function names available on the object.
@@ -641,6 +747,26 @@
return obj;
};
+ // Return a copy of the object only containing the whitelisted properties.
+ _.pick = function(obj) {
+ var copy = {};
+ var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
+ each(keys, function(key) {
+ if (key in obj) copy[key] = obj[key];
+ });
+ return copy;
+ };
+
+ // Return a copy of the object without the blacklisted properties.
+ _.omit = function(obj) {
+ var copy = {};
+ var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
+ for (var key in obj) {
+ if (!_.contains(keys, key)) copy[key] = obj[key];
+ }
+ return copy;
+ };
+
// Fill in a given object with default properties.
_.defaults = function(obj) {
each(slice.call(arguments, 1), function(source) {
@@ -665,19 +791,16 @@
return obj;
};
- // Internal recursive comparison function.
- function eq(a, b, stack) {
+ // Internal recursive comparison function for `isEqual`.
+ var eq = function(a, b, aStack, bStack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
if (a === b) return a !== 0 || 1 / a == 1 / b;
// A strict comparison is necessary because `null == undefined`.
if (a == null || b == null) return a === b;
// Unwrap any wrapped objects.
- if (a._chain) a = a._wrapped;
- if (b._chain) b = b._wrapped;
- // Invoke a custom `isEqual` method if one is provided.
- if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
- if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
+ if (a instanceof _) a = a._wrapped;
+ if (b instanceof _) b = b._wrapped;
// Compare `[[Class]]` names.
var className = toString.call(a);
if (className != toString.call(b)) return false;
@@ -707,14 +830,15 @@
if (typeof a != 'object' || typeof b != 'object') return false;
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
- var length = stack.length;
+ var length = aStack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
- if (stack[length] == a) return true;
+ if (aStack[length] == a) return bStack[length] == b;
}
// Add the first object to the stack of traversed objects.
- stack.push(a);
+ aStack.push(a);
+ bStack.push(b);
var size = 0, result = true;
// Recursively compare objects and arrays.
if (className == '[object Array]') {
@@ -724,20 +848,24 @@
if (result) {
// Deep compare the contents, ignoring non-numeric properties.
while (size--) {
- // Ensure commutative equality for sparse arrays.
- if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
+ if (!(result = eq(a[size], b[size], aStack, bStack))) break;
}
}
} else {
- // Objects with different constructors are not equivalent.
- if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
+ // Objects with different constructors are not equivalent, but `Object`s
+ // from different frames are.
+ var aCtor = a.constructor, bCtor = b.constructor;
+ if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
+ _.isFunction(bCtor) && (bCtor instanceof bCtor))) {
+ return false;
+ }
// Deep compare objects.
for (var key in a) {
if (_.has(a, key)) {
// Count the expected number of properties.
size++;
// Deep compare each member.
- if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
+ if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
}
}
// Ensure that both objects contain the same number of properties.
@@ -749,18 +877,20 @@
}
}
// Remove the first object from the stack of traversed objects.
- stack.pop();
+ aStack.pop();
+ bStack.pop();
return result;
- }
+ };
// Perform a deep comparison to check if two objects are equal.
_.isEqual = function(a, b) {
- return eq(a, b, []);
+ return eq(a, b, [], []);
};
// Is a given array, string, or object empty?
// An "empty" object has no enumerable own-properties.
_.isEmpty = function(obj) {
+ if (obj == null) return true;
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
for (var key in obj) if (_.has(obj, key)) return false;
return true;
@@ -768,7 +898,7 @@
// Is a given value a DOM element?
_.isElement = function(obj) {
- return !!(obj && obj.nodeType == 1);
+ return !!(obj && obj.nodeType === 1);
};
// Is a given value an array?
@@ -782,35 +912,36 @@
return obj === Object(obj);
};
- // Is a given variable an arguments object?
- _.isArguments = function(obj) {
- return toString.call(obj) == '[object Arguments]';
- };
+ // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
+ each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
+ _['is' + name] = function(obj) {
+ return toString.call(obj) == '[object ' + name + ']';
+ };
+ });
+
+ // Define a fallback version of the method in browsers (ahem, IE), where
+ // there isn't any inspectable "Arguments" type.
if (!_.isArguments(arguments)) {
_.isArguments = function(obj) {
return !!(obj && _.has(obj, 'callee'));
};
}
- // Is a given value a function?
- _.isFunction = function(obj) {
- return toString.call(obj) == '[object Function]';
+ // Optimize `isFunction` if appropriate.
+ if (typeof (/./) !== 'function') {
+ _.isFunction = function(obj) {
+ return typeof obj === 'function';
+ };
+ }
+
+ // Is a given object a finite number?
+ _.isFinite = function(obj) {
+ return _.isNumber(obj) && isFinite(obj);
};
- // Is a given value a string?
- _.isString = function(obj) {
- return toString.call(obj) == '[object String]';
- };
-
- // Is a given value a number?
- _.isNumber = function(obj) {
- return toString.call(obj) == '[object Number]';
- };
-
- // Is the given value `NaN`?
+ // Is the given value `NaN`? (NaN is the only number which does not equal itself).
_.isNaN = function(obj) {
- // `NaN` is the only value for which `===` is not reflexive.
- return obj !== obj;
+ return _.isNumber(obj) && obj != +obj;
};
// Is a given value a boolean?
@@ -818,16 +949,6 @@
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
};
- // Is a given value a date?
- _.isDate = function(obj) {
- return toString.call(obj) == '[object Date]';
- };
-
- // Is the given value a regular expression?
- _.isRegExp = function(obj) {
- return toString.call(obj) == '[object RegExp]';
- };
-
// Is a given value equal to null?
_.isNull = function(obj) {
return obj === null;
@@ -838,7 +959,8 @@
return obj === void 0;
};
- // Has own property?
+ // Shortcut function for checking if an object has a given property directly
+ // on itself (in other words, not on a prototype).
_.has = function(obj, key) {
return hasOwnProperty.call(obj, key);
};
@@ -859,20 +981,65 @@
};
// Run a function **n** times.
- _.times = function (n, iterator, context) {
+ _.times = function(n, iterator, context) {
for (var i = 0; i < n; i++) iterator.call(context, i);
};
- // Escape a string for HTML interpolation.
- _.escape = function(string) {
- return (''+string).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g,'/');
+ // Return a random integer between min and max (inclusive).
+ _.random = function(min, max) {
+ if (max == null) {
+ max = min;
+ min = 0;
+ }
+ return min + (0 | Math.random() * (max - min + 1));
};
- // Add your own custom functions to the Underscore object, ensuring that
- // they're correctly added to the OOP wrapper as well.
+ // List of HTML entities for escaping.
+ var entityMap = {
+ escape: {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": ''',
+ '/': '/'
+ }
+ };
+ entityMap.unescape = _.invert(entityMap.escape);
+
+ // Regexes containing the keys and values listed immediately above.
+ var entityRegexes = {
+ escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
+ unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
+ };
+
+ // Functions for escaping and unescaping strings to/from HTML interpolation.
+ _.each(['escape', 'unescape'], function(method) {
+ _[method] = function(string) {
+ if (string == null) return '';
+ return ('' + string).replace(entityRegexes[method], function(match) {
+ return entityMap[method][match];
+ });
+ };
+ });
+
+ // If the value of the named property is a function then invoke it;
+ // otherwise, return it.
+ _.result = function(object, property) {
+ if (object == null) return null;
+ var value = object[property];
+ return _.isFunction(value) ? value.call(object) : value;
+ };
+
+ // Add your own custom functions to the Underscore object.
_.mixin = function(obj) {
each(_.functions(obj), function(name){
- addToWrapper(name, _[name] = obj[name]);
+ var func = _[name] = obj[name];
+ _.prototype[name] = function() {
+ var args = [this._wrapped];
+ push.apply(args, arguments);
+ return result.call(this, func.apply(_, args));
+ };
});
};
@@ -895,41 +1062,72 @@
// When customizing `templateSettings`, if you don't want to define an
// interpolation, evaluation or escaping regex, we need one that is
// guaranteed not to match.
- var noMatch = /.^/;
+ var noMatch = /(.)^/;
- // Within an interpolation, evaluation, or escaping, remove HTML escaping
- // that had been previously added.
- var unescape = function(code) {
- return code.replace(/\\\\/g, '\\').replace(/\\'/g, "'");
+ // Certain characters need to be escaped so that they can be put into a
+ // string literal.
+ var escapes = {
+ "'": "'",
+ '\\': '\\',
+ '\r': 'r',
+ '\n': 'n',
+ '\t': 't',
+ '\u2028': 'u2028',
+ '\u2029': 'u2029'
};
+ var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
+
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
- _.template = function(str, data) {
- var c = _.templateSettings;
- var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
- 'with(obj||{}){__p.push(\'' +
- str.replace(/\\/g, '\\\\')
- .replace(/'/g, "\\'")
- .replace(c.escape || noMatch, function(match, code) {
- return "',_.escape(" + unescape(code) + "),'";
- })
- .replace(c.interpolate || noMatch, function(match, code) {
- return "'," + unescape(code) + ",'";
- })
- .replace(c.evaluate || noMatch, function(match, code) {
- return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('";
- })
- .replace(/\r/g, '\\r')
- .replace(/\n/g, '\\n')
- .replace(/\t/g, '\\t')
- + "');}return __p.join('');";
- var func = new Function('obj', '_', tmpl);
- if (data) return func(data, _);
- return function(data) {
- return func.call(this, data, _);
+ _.template = function(text, data, settings) {
+ settings = _.defaults({}, settings, _.templateSettings);
+
+ // Combine delimiters into one regular expression via alternation.
+ var matcher = new RegExp([
+ (settings.escape || noMatch).source,
+ (settings.interpolate || noMatch).source,
+ (settings.evaluate || noMatch).source
+ ].join('|') + '|$', 'g');
+
+ // Compile the template source, escaping string literals appropriately.
+ var index = 0;
+ var source = "__p+='";
+ text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
+ source += text.slice(index, offset)
+ .replace(escaper, function(match) { return '\\' + escapes[match]; });
+ source +=
+ escape ? "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'" :
+ interpolate ? "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'" :
+ evaluate ? "';\n" + evaluate + "\n__p+='" : '';
+ index = offset + match.length;
+ });
+ source += "';\n";
+
+ // If a variable is not specified, place data values in local scope.
+ if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
+
+ source = "var __t,__p='',__j=Array.prototype.join," +
+ "print=function(){__p+=__j.call(arguments,'');};\n" +
+ source + "return __p;\n";
+
+ try {
+ var render = new Function(settings.variable || 'obj', '_', source);
+ } catch (e) {
+ e.source = source;
+ throw e;
+ }
+
+ if (data) return render(data, _);
+ var template = function(data) {
+ return render.call(this, data, _);
};
+
+ // Provide the compiled function source as a convenience for precompilation.
+ template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
+
+ return template;
};
// Add a "chain" function, which will delegate to the wrapper.
@@ -937,29 +1135,15 @@
return _(obj).chain();
};
- // The OOP Wrapper
+ // OOP
// ---------------
-
// If Underscore is called as a function, it returns a wrapped object that
// can be used OO-style. This wrapper holds altered versions of all the
// underscore functions. Wrapped objects may be chained.
- var wrapper = function(obj) { this._wrapped = obj; };
-
- // Expose `wrapper.prototype` as `_.prototype`
- _.prototype = wrapper.prototype;
// Helper function to continue chaining intermediate results.
- var result = function(obj, chain) {
- return chain ? _(obj).chain() : obj;
- };
-
- // A method to easily add functions to the OOP wrapper.
- var addToWrapper = function(name, func) {
- wrapper.prototype[name] = function() {
- var args = slice.call(arguments);
- unshift.call(args, this._wrapped);
- return result(func.apply(_, args), this._chain);
- };
+ var result = function(obj) {
+ return this._chain ? _(obj).chain() : obj;
};
// Add all of the Underscore functions to the wrapper object.
@@ -968,32 +1152,35 @@
// Add all mutator Array functions to the wrapper.
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = ArrayProto[name];
- wrapper.prototype[name] = function() {
- var wrapped = this._wrapped;
- method.apply(wrapped, arguments);
- var length = wrapped.length;
- if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
- return result(wrapped, this._chain);
+ _.prototype[name] = function() {
+ var obj = this._wrapped;
+ method.apply(obj, arguments);
+ if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
+ return result.call(this, obj);
};
});
// Add all accessor Array functions to the wrapper.
each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name];
- wrapper.prototype[name] = function() {
- return result(method.apply(this._wrapped, arguments), this._chain);
+ _.prototype[name] = function() {
+ return result.call(this, method.apply(this._wrapped, arguments));
};
});
- // Start chaining a wrapped Underscore object.
- wrapper.prototype.chain = function() {
- this._chain = true;
- return this;
- };
+ _.extend(_.prototype, {
- // Extracts the result from a wrapped and chained object.
- wrapper.prototype.value = function() {
- return this._wrapped;
- };
+ // Start chaining a wrapped Underscore object.
+ chain: function() {
+ this._chain = true;
+ return this;
+ },
+
+ // Extracts the result from a wrapped and chained object.
+ value: function() {
+ return this._wrapped;
+ }
+
+ });
}).call(this);
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/mvc/history.js
--- a/static/scripts/mvc/history.js
+++ b/static/scripts/mvc/history.js
@@ -1,4 +1,8 @@
-/*
+//define([
+// "../mvc/base-mvc"
+//
+//], function(){
+/* =============================================================================
Backbone.js implementation of history panel
TODO:
@@ -10,6 +14,8 @@
_render_displayApps
_render_downloadButton
widget building (popupmenu, etc.)
+ history.mako js: updater, etc.
+ have info bodies prev. opened, redisplay on refresh
don't draw body until it's first unhide event
all history.mako js -> this
@@ -30,26 +36,7 @@
move inline styles into base.less
add classes, ids on empty divs
watch the magic strings
-*/
-
-//==============================================================================
-
-//==============================================================================
-//TODO: move to Galaxy obj./namespace, decorate for current page (as GalaxyPaths)
-/*
-var Localizable = {
- localizedStrings : {},
- setLocalizedString : function( str, localizedString ){
- this.localizedStrings[ str ] = localizedString;
- },
- localize : function( str ){
- if( str in this.localizedStrings ){ return this.localizedStrings[ str ]; }
- return str;
- }
-};
-var LocalizableView = LoggingView.extend( Localizable );
-*/
-//TODO: wire up to views
+============================================================================= */
//==============================================================================
// jq plugin?
@@ -204,12 +191,8 @@
// set up canned behavior on children (bootstrap, popupmenus, editable_text, etc.)
itemWrapper.find( '.tooltip' ).tooltip({ placement : 'bottom' });
- //TODO: broken
- var popupmenus = itemWrapper.find( '[popupmenu]' );
- popupmenus.each( function( i, menu ){
- menu = $( menu );
- make_popupmenu( menu );
- });
+ // we can potentially skip this step and call popupmenu directly on the download button
+ make_popup_menus( itemWrapper );
//TODO: better transition/method than this...
this.$el.children().remove();
@@ -343,54 +326,25 @@
// ................................................................................ primary actions
_render_primaryActionButtons : function( buttonRenderingFuncs ){
- var primaryActionButtons = $( '<div/>' ),
+ var primaryActionButtons = $( '<div/>' ).attr( 'id', 'primary-actions-' + this.model.get( 'id' ) ),
view = this;
_.each( buttonRenderingFuncs, function( fn ){
- primaryActionButtons.append( fn.call( view ) );
+ var render_return = fn.call( view );
+ primaryActionButtons.append( render_return );
});
return primaryActionButtons;
},
_render_downloadButton : function(){
- // return either: a single download icon-button (if there are no meta files)
- // or a popupmenu with links to download assoc. meta files (if there are meta files)
-
// don't show anything if the data's been purged
if( this.model.get( 'purged' ) ){ return null; }
- var downloadLink = linkHTMLTemplate({
- title : 'Download',
- href : this.model.get( 'download_url' ),
- classes : [ 'icon-button', 'tooltip', 'disk' ]
- });
+ // return either: a single download icon-button (if there are no meta files)
+ // or a popupmenu with links to download assoc. meta files (if there are meta files)
+ var downloadLinkHTML = HistoryItemView.templates.downloadLinks( this.model.toJSON() );
+ this.log( '_render_downloadButton, downloadLinkHTML:', downloadLinkHTML );
- // if no metafiles, return only the main download link
- var download_meta_urls = this.model.get( 'download_meta_urls' );
- if( !download_meta_urls ){
- return downloadLink;
- }
-
- // build the popupmenu for downloading main, meta files
- var popupmenu = $( '<div popupmenu="dataset-' + this.model.get( 'id' ) + '-popup"></div>' );
- popupmenu.append( linkHTMLTemplate({
- text : 'Download Dataset',
- title : 'Download',
- href : this.model.get( 'download_url' ),
- classes : [ 'icon-button', 'tooltip', 'disk' ]
- }));
- popupmenu.append( '<a>Additional Files</a>' );
- for( file_type in download_meta_urls ){
- popupmenu.append( linkHTMLTemplate({
- text : 'Download ' + file_type,
- href : download_meta_urls[ file_type ],
- classes : [ 'action-button' ]
- }));
- }
- var menuButton = $( ( '<div style="float:left;" class="menubutton split popup"'
- + ' id="dataset-${dataset_id}-popup"></div>' ) );
- menuButton.append( downloadLink );
- popupmenu.append( menuButton );
- return popupmenu;
+ return $( downloadLinkHTML );
},
//NOTE: button renderers have the side effect of caching their IconButtonViews to this view
@@ -451,8 +405,12 @@
// ................................................................................ secondary actions
_render_secondaryActionButtons : function( buttonRenderingFuncs ){
// move to the right (same level as primary)
- var secondaryActionButtons = $( '<div style="float: right;"></div>' ),
+ var secondaryActionButtons = $( '<div/>' ),
view = this;
+ secondaryActionButtons
+ .attr( 'style', 'float: right;' )
+ .attr( 'id', 'secondary-actions-' + this.model.get( 'id' ) );
+
_.each( buttonRenderingFuncs, function( fn ){
secondaryActionButtons.append( fn.call( view ) );
});
@@ -501,54 +459,23 @@
},
_render_displayApps : function(){
- if( !this.model.get( 'display_apps' ) ){ return null; }
- var displayApps = this.model.get( 'displayApps' ),
- displayAppsDiv = $( '<div/>' ),
- displayAppSpan = $( '<span/>' );
-
- this.log( this + 'displayApps:', displayApps );
- ////TODO: grrr...somethings not in the right scope here
- //for( app_name in displayApps ){
- // //TODO: to template
- // var display_app = displayApps[ app_name ],
- // display_app_HTML = app_name + ' ';
- // for( location_name in display_app ){
- // display_app_HTML += linkHTMLTemplate({
- // text : location_name,
- // href : display_app[ location_name ].url,
- // target : display_app[ location_name ].target
- // }) + ' ';
- // }
- // display_app_span.append( display_app_HTML );
- //}
- //displayAppsDiv.append( display_app_span );
+ // render links to external genome display applications (igb, gbrowse, etc.)
+ if( !this.model.hasData() ){ return null; }
- //displayAppsDiv.append( '<br />' );
-
- //var display_appsDiv = $( '<div/>' );
- //if( this.model.get( 'display_apps' ) ){
- //
- // var display_apps = this.model.get( 'display_apps' ),
- // display_app_span = $( '<span/>' );
- //
- // //TODO: grrr...somethings not in the right scope here
- // for( app_name in display_apps ){
- // //TODO: to template
- // var display_app = display_apps[ app_name ],
- // display_app_HTML = app_name + ' ';
- // for( location_name in display_app ){
- // display_app_HTML += linkHTMLTemplate({
- // text : location_name,
- // href : display_app[ location_name ].url,
- // target : display_app[ location_name ].target
- // }) + ' ';
- // }
- // display_app_span.append( display_app_HTML );
- // }
- // display_appsDiv.append( display_app_span );
- //}
- ////display_appsDiv.append( '<br />' );
- //parent.append( display_appsDiv );
+ var displayAppsDiv = $( '<div/>' ).addClass( 'display-apps' );
+ if( !_.isEmpty( this.model.get( 'display_types' ) ) ){
+ this.log( this + 'display_types:', this.model.get( 'display_types' ) );
+ //TODO:?? does this ever get used?
+ displayAppsDiv.append(
+ HistoryItemView.templates.displayApps({ displayApps : this.model.toJSON().display_types })
+ );
+ }
+ if( !_.isEmpty( this.model.get( 'display_apps' ) ) ){
+ this.log( this + 'display_apps:', this.model.get( 'display_apps' ) );
+ displayAppsDiv.append(
+ HistoryItemView.templates.displayApps({ displayApps : this.model.toJSON().display_apps })
+ );
+ }
return displayAppsDiv;
},
@@ -815,9 +742,11 @@
messages : 'template-history-warning-messages',
titleLink : 'template-history-titleLink',
hdaSummary : 'template-history-hdaSummary',
+ downloadLinks : 'template-history-downloadLinks',
failedMetadata : 'template-history-failedMetaData',
tagArea : 'template-history-tagArea',
- annotationArea : 'template-history-annotationArea'
+ annotationArea : 'template-history-annotationArea',
+ displayApps : 'template-history-displayApps'
}
});
@@ -967,163 +896,10 @@
//==============================================================================
-function createMockHistoryData(){
- mockHistory = {};
- mockHistory.data = {
-
- template : {
- id : 'a799d38679e985db',
- name : 'template',
- data_type : 'fastq',
- file_size : 226297533,
- genome_build : '?',
- metadata_data_lines : 0,
- metadata_dbkey : '?',
- metadata_sequences : 0,
- misc_blurb : '215.8 MB',
- misc_info : 'uploaded fastq file (misc_info)',
- model_class : 'HistoryDatasetAssociation',
- download_url : '',
- state : 'ok',
- visible : true,
- deleted : false,
- purged : false,
-
- hid : 0,
- //TODO: move to history
- for_editing : true,
- //for_editing : false,
-
- //?? not needed
- //can_edit : true,
- //can_edit : false,
-
- accessible : true,
-
- //TODO: move into model functions (build there (and cache?))
- //!! be careful with adding these accrd. to permissions
- //!! IOW, don't send them via template/API if the user doesn't have perms to use
- //!! (even if they don't show up)
- undelete_url : '',
- purge_url : '',
- unhide_url : '',
-
- display_url : 'example.com/display',
- edit_url : 'example.com/edit',
- delete_url : 'example.com/delete',
-
- show_params_url : 'example.com/show_params',
- rerun_url : 'example.com/rerun',
-
- retag_url : 'example.com/retag',
- annotate_url : 'example.com/annotate',
-
- peek : [
- '<table cellspacing="0" cellpadding="3"><tr><th>1.QNAME</th><th>2.FLAG</th><th>3.RNAME</th><th>4.POS</th><th>5.MAPQ</th><th>6.CIGAR</th><th>7.MRNM</th><th>8.MPOS</th><th>9.ISIZE</th><th>10.SEQ</th><th>11.QUAL</th><th>12.OPT</th></tr>',
- '<tr><td colspan="100%">@SQ SN:gi|87159884|ref|NC_007793.1| LN:2872769</td></tr>',
- '<tr><td colspan="100%">@PG ID:bwa PN:bwa VN:0.5.9-r16</td></tr>',
- '<tr><td colspan="100%">HWUSI-EAS664L:15:64HOJAAXX:1:1:13280:968 73 gi|87159884|ref|NC_007793.1| 2720169 37 101M = 2720169 0 NAATATGACATTATTTTCAAAACAGCTGAAAATTTAGACGTACCGATTTATCTACATCCCGCGCCAGTTAACAGTGACATTTATCAATCATACTATAAAGG !!!!!!!!!!$!!!$!!!!!$!!!!!!$!$!$$$!!$!!$!!!!!!!!!!!$!</td></tr>',
- '<tr><td colspan="100%">!!!$!$!$$!!$$!!$!!!!!!!!!!!!!!!!!!!!!!!!!!$!!$!! XT:A:U NM:i:1 SM:i:37 AM:i:0 X0:i:1 X1:i:0 XM:i:1 XO:i:0 XG:i:0 MD:Z:0A100</td></tr>',
- '<tr><td colspan="100%">HWUSI-EAS664L:15:64HOJAAXX:1:1:13280:968 133 gi|87159884|ref|NC_007793.1| 2720169 0 * = 2720169 0 NAAACTGTGGCTTCGTTNNNNNNNNNNNNNNNGTGANNNNNNNNNNNNNNNNNNNGNNNNNNNNNNNNNNNNNNNNCNAANNNNNNNNNNNNNNNNNNNNN !!!!!!!!!!!!$!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</td></tr>',
- '<tr><td colspan="100%">!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</td></tr>',
- '</table>'
- ].join( '' )
- }
-
- };
- _.extend( mockHistory.data, {
-
- notAccessible :
- _.extend( _.clone( mockHistory.data.template ),
- { accessible : false }),
-
- //deleted, purged, visible
- deleted :
- _.extend( _.clone( mockHistory.data.template ),
- { deleted : true,
- delete_url : '',
- purge_url : 'example.com/purge',
- undelete_url : 'example.com/undelete' }),
- purgedNotDeleted :
- _.extend( _.clone( mockHistory.data.template ),
- { purged : true,
- delete_url : '' }),
- notvisible :
- _.extend( _.clone( mockHistory.data.template ),
- { visible : false,
- unhide_url : 'example.com/unhide' }),
-
- hasDisplayApps :
- _.extend( _.clone( mockHistory.data.template ),
- { display_apps : {
- 'display in IGB' : {
- Web: "/display_application/63cd3858d057a6d1/igb_bam/Web",
- Local: "/display_application/63cd3858d057a6d1/igb_bam/Local"
- }
- }
- }
- ),
- canTrackster :
- _.extend( _.clone( mockHistory.data.template ),
- { trackster_urls : {
- 'data-url' : "example.com/trackster-data",
- 'action-url' : "example.com/trackster-action",
- 'new-url' : "example.com/trackster-new"
- }
- }
- ),
- zeroSize :
- _.extend( _.clone( mockHistory.data.template ),
- { file_size : 0 }),
-
- hasMetafiles :
- _.extend( _.clone( mockHistory.data.template ), {
- download_meta_urls : {
- 'bam_index' : "example.com/bam-index"
- }
- }),
-
- //states
- upload :
- _.extend( _.clone( mockHistory.data.template ),
- { state : HistoryItem.STATES.UPLOAD }),
- queued :
- _.extend( _.clone( mockHistory.data.template ),
- { state : HistoryItem.STATES.QUEUED }),
- running :
- _.extend( _.clone( mockHistory.data.template ),
- { state : HistoryItem.STATES.RUNNING }),
- empty :
- _.extend( _.clone( mockHistory.data.template ),
- { state : HistoryItem.STATES.EMPTY }),
- error :
- _.extend( _.clone( mockHistory.data.template ),
- { state : HistoryItem.STATES.ERROR,
- report_error_url: 'example.com/report_err' }),
- discarded :
- _.extend( _.clone( mockHistory.data.template ),
- { state : HistoryItem.STATES.DISCARDED }),
- setting_metadata :
- _.extend( _.clone( mockHistory.data.template ),
- { state : HistoryItem.STATES.SETTING_METADATA }),
- failed_metadata :
- _.extend( _.clone( mockHistory.data.template ),
- { state : HistoryItem.STATES.FAILED_METADATA })
-/*
-*/
- });
-
- $( document ).ready( function(){
- //mockHistory.views.deleted.logger = console;
- mockHistory.items = {};
- mockHistory.views = {};
- for( key in mockHistory.data ){
- mockHistory.items[ key ] = new HistoryItem( mockHistory.data[ key ] );
- mockHistory.items[ key ].set( 'name', key );
- mockHistory.views[ key ] = new HistoryItemView({ model : mockHistory.items[ key ] });
- //console.debug( 'view: ', mockHistory.views[ key ] );
- $( 'body' ).append( mockHistory.views[ key ].render() );
- }
- });
-}
-
+//return {
+// HistoryItem : HistoryItem,
+// HitoryItemView : HistoryItemView,
+// HistoryCollection : HistoryCollection,
+// History : History,
+// HistoryView : HistoryView
+//};});
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/packed/galaxy.base.js
--- a/static/scripts/packed/galaxy.base.js
+++ b/static/scripts/packed/galaxy.base.js
@@ -1,1 +1,1 @@
-(function(){var b=0;var c=["ms","moz","webkit","o"];for(var a=0;a<c.length&&!window.requestAnimationFrame;++a){window.requestAnimationFrame=window[c[a]+"RequestAnimationFrame"];window.cancelRequestAnimationFrame=window[c[a]+"CancelRequestAnimationFrame"]}if(!window.requestAnimationFrame){window.requestAnimationFrame=function(h,e){var d=new Date().getTime();var f=Math.max(0,16-(d-b));var g=window.setTimeout(function(){h(d+f)},f);b=d+f;return g}}if(!window.cancelAnimationFrame){window.cancelAnimationFrame=function(d){clearTimeout(d)}}}());if(!Array.indexOf){Array.prototype.indexOf=function(c){for(var b=0,a=this.length;b<a;b++){if(this[b]==c){return b}}return -1}}function obj_length(c){if(c.length!==undefined){return c.length}var b=0;for(var a in c){b++}return b}$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};function make_popupmenu(b,c){var a=(b.data("menu_options"));b.data("menu_options",c);if(a){return}b.bind("click.show_popup",function(d){$(".popmenu-wrapper").remove();setTimeout(function(){var g=$("<ul class='dropdown-menu' id='"+b.attr("id")+"-menu'></ul>");var f=b.data("menu_options");if(obj_length(f)<=0){$("<li>No Options.</li>").appendTo(g)}$.each(f,function(j,i){if(i){g.append($("<li></li>").append($("<a href='#'></a>").html(j).click(i)))}else{g.append($("<li></li>").addClass("head").append($("<a href='#'></a>").html(j)))}});var h=$("<div class='popmenu-wrapper' style='position: absolute;left: 0; top: -1000;'></div>").append(g).appendTo("body");var e=d.pageX-h.width()/2;e=Math.min(e,$(document).scrollLeft()+$(window).width()-$(h).width()-5);e=Math.max(e,$(document).scrollLeft()+5);h.css({top:d.pageY,left:e})},10);setTimeout(function(){var f=function(h){$(h).bind("click.close_popup",function(){$(".popmenu-wrapper").remove();h.unbind("click.close_popup")})};f($(window.document));f($(window.top.document));for(var e=window.top.frames.length;e--;){var g=$(window.top.frames[e].document);f(g)}},50);return false})}function make_popup_menus(){jQuery("div[popupmenu]").each(function(){var a={};var c=$(this);c.find("a").each(function(){var f=$(this),h=f.get(0);var d=h.getAttribute("confirm"),e=h.getAttribute("href"),g=h.getAttribute("target");if(!e){a[f.text()]=null}else{a[f.text()]=function(){if(!d||confirm(d)){var i;if(g=="_parent"){window.parent.location=e}else{if(g=="_top"){window.top.location=e}else{if(g=="demo"){if(i===undefined||i.closed){i=window.open(e,g);i.creator=self}}else{window.location=e}}}}}}});var b=$("#"+c.attr("popupmenu"));b.find("a").bind("click",function(d){d.stopPropagation();return true});make_popupmenu(b,a);b.addClass("popup");c.remove()})}function naturalSort(j,h){var p=/(-?[0-9\.]+)/g,k=j.toString().toLowerCase()||"",g=h.toString().toLowerCase()||"",l=String.fromCharCode(0),n=k.replace(p,l+"$1"+l).split(l),e=g.replace(p,l+"$1"+l).split(l),d=(new Date(k)).getTime(),o=d?(new Date(g)).getTime():null;if(o){if(d<o){return -1}else{if(d>o){return 1}}}var m,f;for(var i=0,c=Math.max(n.length,e.length);i<c;i++){m=parseFloat(n[i])||n[i];f=parseFloat(e[i])||e[i];if(m<f){return -1}else{if(m>f){return 1}}}return 0}function replace_big_select_inputs(a,c,b){if(!jQuery().autocomplete){return}if(a===undefined){a=20}if(c===undefined){c=3000}var b=b||$("select");b.each(function(){var e=$(this);var h=e.find("option").length;if((h<a)||(h>c)){return}if(e.attr("multiple")==="multiple"){return}if(e.hasClass("no-autocomplete")){return}var n=e.attr("value");var d=$("<input type='text' class='text-and-autocomplete-select'></input>");d.attr("size",40);d.attr("name",e.attr("name"));d.attr("id",e.attr("id"));d.click(function(){var o=$(this).val();$(this).val("Loading...");$(this).showAllInCache();$(this).val(o);$(this).select()});var f=[];var j={};e.children("option").each(function(){var p=$(this).text();var o=$(this).attr("value");f.push(p);j[p]=o;j[o]=o;if(o==n){d.attr("value",p)}});if(n===""||n==="?"){d.attr("value","Click to Search or Select")}if(e.attr("name")=="dbkey"){f=f.sort(naturalSort)}var g={selectFirst:false,autoFill:false,mustMatch:false,matchContains:true,max:c,minChars:0,hideForLessThanMinChars:false};d.autocomplete(f,g);e.replaceWith(d);var l=function(){var p=d.attr("value");var o=j[p];if(o!==null&&o!==undefined){d.attr("value",o)}else{if(n!==""){d.attr("value",n)}else{d.attr("value","?")}}};d.parents("form").submit(function(){l()});$(document).bind("convert_to_values",function(){l()});if(e.attr("refresh_on_change")=="true"){var i=e.attr("refresh_on_change_values"),m=e.attr("last_selected_value");if(i!==undefined){i=i.split(",")}var k=function(){var o=j[d.attr("value")];if(m!==o&&o!==null&&o!==undefined){if(i!==undefined&&$.inArray(o,i)===-1&&$.inArray(m,i)===-1){return}d.attr("value",o);$(window).trigger("refresh_on_change");d.parents("form").submit()}};d.bind("result",k);d.keyup(function(o){if(o.keyCode===13){k()}});d.keydown(function(o){if(o.keyCode===13){return false}})}})}$.fn.make_text_editable=function(g){var d=("num_cols" in g?g.num_cols:30),c=("num_rows" in g?g.num_rows:4),e=("use_textarea" in g?g.use_textarea:false),b=("on_finish" in g?g.on_finish:null),f=("help_text" in g?g.help_text:null);var a=$(this);a.addClass("editable-text").click(function(l){if($(this).children(":input").length>0){return}a.removeClass("editable-text");var i=function(m){a.find(":input").remove();if(m!==""){a.text(m)}else{a.html("<br>")}a.addClass("editable-text");if(b){b(m)}};var h=a.text(),k,j;if(e){k=$("<textarea/>").attr({rows:c,cols:d}).text($.trim(h)).keyup(function(m){if(m.keyCode===27){i(h)}});j=$("<button/>").text("Done").click(function(){i(k.val());return false})}else{k=$("<input type='text'/>").attr({value:$.trim(h),size:d}).blur(function(){i(h)}).keyup(function(m){if(m.keyCode===27){$(this).trigger("blur")}else{if(m.keyCode===13){i($(this).val())}}})}a.text("");a.append(k);if(j){a.append(j)}k.focus();k.select();l.stopPropagation()});if(f){a.attr("title",f).tooltip()}return a};function async_save_text(d,f,e,a,c,h,i,g,b){if(c===undefined){c=30}if(i===undefined){i=4}$("#"+d).live("click",function(){if($("#renaming-active").length>0){return}var l=$("#"+f),k=l.text(),j;if(h){j=$("<textarea></textarea>").attr({rows:i,cols:c}).text($.trim(k))}else{j=$("<input type='text'></input>").attr({value:$.trim(k),size:c})}j.attr("id","renaming-active");j.blur(function(){$(this).remove();l.show();if(b){b(j)}});j.keyup(function(n){if(n.keyCode===27){$(this).trigger("blur")}else{if(n.keyCode===13){var m={};m[a]=$(this).val();$(this).trigger("blur");$.ajax({url:e,data:m,error:function(){alert("Text editing for elt "+f+" failed")},success:function(o){if(o!==""){l.text(o)}else{l.html("<em>None</em>")}if(b){b(j)}}})}}});if(g){g(j)}l.hide();j.insertAfter(l);j.focus();j.select();return})}function init_history_items(d,a,c){var b=function(){try{var e=$.jStorage.get("history_expand_state");if(e){for(var g in e){$("#"+g+" div.historyItemBody").show()}}}catch(f){$.jStorage.deleteKey("history_expand_state")}if($.browser.mozilla){$("div.historyItemBody").each(function(){if(!$(this).is(":visible")){$(this).find("pre.peek").css("overflow","hidden")}})}d.each(function(){var j=this.id,h=$(this).children("div.historyItemBody"),i=h.find("pre.peek");$(this).find(".historyItemTitleBar > .historyItemTitle").wrap("<a href='javascript:void(0);'></a>").click(function(){var k;if(h.is(":visible")){if($.browser.mozilla){i.css("overflow","hidden")}h.slideUp("fast");if(!c){k=$.jStorage.get("history_expand_state");if(k){delete k[j];$.jStorage.set("history_expand_state",k)}}}else{h.slideDown("fast",function(){if($.browser.mozilla){i.css("overflow","auto")}});if(!c){k=$.jStorage.get("history_expand_state");if(!k){k={}}k[j]=true;$.jStorage.set("history_expand_state",k)}}return false})});$("#top-links > a.toggle").click(function(){var h=$.jStorage.get("history_expand_state");if(!h){h={}}$("div.historyItemBody:visible").each(function(){if($.browser.mozilla){$(this).find("pre.peek").css("overflow","hidden")}$(this).slideUp("fast");if(h){delete h[$(this).parent().attr("id")]}});$.jStorage.set("history_expand_state",h)}).show()};b()}function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}function reset_tool_search(a){var c=$("#galaxy_tools").contents();if(c.length===0){c=$(document)}$(this).removeClass("search_active");c.find(".toolTitle").removeClass("search_match");c.find(".toolSectionBody").hide();c.find(".toolTitle").show();c.find(".toolPanelLabel").show();c.find(".toolSectionWrapper").each(function(){if($(this).attr("id")!="recently_used_wrapper"){$(this).show()}else{if($(this).hasClass("user_pref_visible")){$(this).show()}}});c.find("#search-no-results").hide();c.find("#search-spinner").hide();if(a){var b=c.find("#tool-search-query");b.val("search tools")}}var GalaxyAsync=function(a){this.url_dict={};this.log_action=(a===undefined?false:a)};GalaxyAsync.prototype.set_func_url=function(a,b){this.url_dict[a]=b};GalaxyAsync.prototype.set_user_pref=function(a,b){var c=this.url_dict[arguments.callee];if(c===undefined){return false}$.ajax({url:c,data:{pref_name:a,pref_value:b},error:function(){return false},success:function(){return true}})};GalaxyAsync.prototype.log_user_action=function(c,b,d){if(!this.log_action){return}var a=this.url_dict[arguments.callee];if(a===undefined){return false}$.ajax({url:a,data:{action:c,context:b,params:d},error:function(){return false},success:function(){return true}})};$(document).ready(function(){$("select[refresh_on_change='true']").change(function(){var a=$(this),e=a.val(),d=false,c=a.attr("refresh_on_change_values");if(c){c=c.split(",");var b=a.attr("last_selected_value");if($.inArray(e,c)===-1&&$.inArray(b,c)===-1){return}}$(window).trigger("refresh_on_change");$(document).trigger("convert_to_values");a.get(0).form.submit()});$(":checkbox[refresh_on_change='true']").click(function(){var a=$(this),e=a.val(),d=false,c=a.attr("refresh_on_change_values");if(c){c=c.split(",");var b=a.attr("last_selected_value");if($.inArray(e,c)===-1&&$.inArray(b,c)===-1){return}}$(window).trigger("refresh_on_change");a.get(0).form.submit()});$("a[confirm]").click(function(){return confirm($(this).attr("confirm"))});if($.fn.tooltip){$(".tooltip").tooltip({placement:"top"})}make_popup_menus();replace_big_select_inputs(20,1500);$("a").click(function(){var b=$(this);var c=(parent.frames&&parent.frames.galaxy_main);if((b.attr("target")=="galaxy_main")&&(!c)){var a=b.attr("href");if(a.indexOf("?")==-1){a+="?"}else{a+="&"}a+="use_panels=True";b.attr("href",a);b.attr("target","_self")}return b})});
\ No newline at end of file
+(function(){var b=0;var c=["ms","moz","webkit","o"];for(var a=0;a<c.length&&!window.requestAnimationFrame;++a){window.requestAnimationFrame=window[c[a]+"RequestAnimationFrame"];window.cancelRequestAnimationFrame=window[c[a]+"CancelRequestAnimationFrame"]}if(!window.requestAnimationFrame){window.requestAnimationFrame=function(h,e){var d=new Date().getTime();var f=Math.max(0,16-(d-b));var g=window.setTimeout(function(){h(d+f)},f);b=d+f;return g}}if(!window.cancelAnimationFrame){window.cancelAnimationFrame=function(d){clearTimeout(d)}}}());if(!Array.indexOf){Array.prototype.indexOf=function(c){for(var b=0,a=this.length;b<a;b++){if(this[b]==c){return b}}return -1}}function obj_length(c){if(c.length!==undefined){return c.length}var b=0;for(var a in c){b++}return b}$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};function make_popupmenu(b,c){var a=(b.data("menu_options"));b.data("menu_options",c);if(a){return}b.bind("click.show_popup",function(d){$(".popmenu-wrapper").remove();setTimeout(function(){var g=$("<ul class='dropdown-menu' id='"+b.attr("id")+"-menu'></ul>");var f=b.data("menu_options");if(obj_length(f)<=0){$("<li>No Options.</li>").appendTo(g)}$.each(f,function(j,i){if(i){g.append($("<li></li>").append($("<a href='#'></a>").html(j).click(i)))}else{g.append($("<li></li>").addClass("head").append($("<a href='#'></a>").html(j)))}});var h=$("<div class='popmenu-wrapper' style='position: absolute;left: 0; top: -1000;'></div>").append(g).appendTo("body");var e=d.pageX-h.width()/2;e=Math.min(e,$(document).scrollLeft()+$(window).width()-$(h).width()-5);e=Math.max(e,$(document).scrollLeft()+5);h.css({top:d.pageY,left:e})},10);setTimeout(function(){var f=function(h){$(h).bind("click.close_popup",function(){$(".popmenu-wrapper").remove();h.unbind("click.close_popup")})};f($(window.document));f($(window.top.document));for(var e=window.top.frames.length;e--;){var g=$(window.top.frames[e].document);f(g)}},50);return false})}function make_popup_menus(a){a=a||document;$(a).find("div[popupmenu]").each(function(){var b={};var d=$(this);d.find("a").each(function(){var g=$(this),i=g.get(0),e=i.getAttribute("confirm"),f=i.getAttribute("href"),h=i.getAttribute("target");if(!f){b[g.text()]=null}else{b[g.text()]=function(){if(!e||confirm(e)){var j;if(h=="_parent"){window.parent.location=f}else{if(h=="_top"){window.top.location=f}else{if(h=="demo"){if(j===undefined||j.closed){j=window.open(f,h);j.creator=self}}else{window.location=f}}}}}}});var c=$("#"+d.attr("popupmenu"));c.find("a").bind("click",function(f){f.stopPropagation();return true});make_popupmenu(c,b);c.addClass("popup");d.remove()})}function naturalSort(j,h){var p=/(-?[0-9\.]+)/g,k=j.toString().toLowerCase()||"",g=h.toString().toLowerCase()||"",l=String.fromCharCode(0),n=k.replace(p,l+"$1"+l).split(l),e=g.replace(p,l+"$1"+l).split(l),d=(new Date(k)).getTime(),o=d?(new Date(g)).getTime():null;if(o){if(d<o){return -1}else{if(d>o){return 1}}}var m,f;for(var i=0,c=Math.max(n.length,e.length);i<c;i++){m=parseFloat(n[i])||n[i];f=parseFloat(e[i])||e[i];if(m<f){return -1}else{if(m>f){return 1}}}return 0}function replace_big_select_inputs(a,c,b){if(!jQuery().autocomplete){return}if(a===undefined){a=20}if(c===undefined){c=3000}var b=b||$("select");b.each(function(){var e=$(this);var h=e.find("option").length;if((h<a)||(h>c)){return}if(e.attr("multiple")==="multiple"){return}if(e.hasClass("no-autocomplete")){return}var n=e.attr("value");var d=$("<input type='text' class='text-and-autocomplete-select'></input>");d.attr("size",40);d.attr("name",e.attr("name"));d.attr("id",e.attr("id"));d.click(function(){var o=$(this).val();$(this).val("Loading...");$(this).showAllInCache();$(this).val(o);$(this).select()});var f=[];var j={};e.children("option").each(function(){var p=$(this).text();var o=$(this).attr("value");f.push(p);j[p]=o;j[o]=o;if(o==n){d.attr("value",p)}});if(n===""||n==="?"){d.attr("value","Click to Search or Select")}if(e.attr("name")=="dbkey"){f=f.sort(naturalSort)}var g={selectFirst:false,autoFill:false,mustMatch:false,matchContains:true,max:c,minChars:0,hideForLessThanMinChars:false};d.autocomplete(f,g);e.replaceWith(d);var l=function(){var p=d.attr("value");var o=j[p];if(o!==null&&o!==undefined){d.attr("value",o)}else{if(n!==""){d.attr("value",n)}else{d.attr("value","?")}}};d.parents("form").submit(function(){l()});$(document).bind("convert_to_values",function(){l()});if(e.attr("refresh_on_change")=="true"){var i=e.attr("refresh_on_change_values"),m=e.attr("last_selected_value");if(i!==undefined){i=i.split(",")}var k=function(){var o=j[d.attr("value")];if(m!==o&&o!==null&&o!==undefined){if(i!==undefined&&$.inArray(o,i)===-1&&$.inArray(m,i)===-1){return}d.attr("value",o);$(window).trigger("refresh_on_change");d.parents("form").submit()}};d.bind("result",k);d.keyup(function(o){if(o.keyCode===13){k()}});d.keydown(function(o){if(o.keyCode===13){return false}})}})}$.fn.make_text_editable=function(g){var d=("num_cols" in g?g.num_cols:30),c=("num_rows" in g?g.num_rows:4),e=("use_textarea" in g?g.use_textarea:false),b=("on_finish" in g?g.on_finish:null),f=("help_text" in g?g.help_text:null);var a=$(this);a.addClass("editable-text").click(function(l){if($(this).children(":input").length>0){return}a.removeClass("editable-text");var i=function(m){a.find(":input").remove();if(m!==""){a.text(m)}else{a.html("<br>")}a.addClass("editable-text");if(b){b(m)}};var h=a.text(),k,j;if(e){k=$("<textarea/>").attr({rows:c,cols:d}).text($.trim(h)).keyup(function(m){if(m.keyCode===27){i(h)}});j=$("<button/>").text("Done").click(function(){i(k.val());return false})}else{k=$("<input type='text'/>").attr({value:$.trim(h),size:d}).blur(function(){i(h)}).keyup(function(m){if(m.keyCode===27){$(this).trigger("blur")}else{if(m.keyCode===13){i($(this).val())}}})}a.text("");a.append(k);if(j){a.append(j)}k.focus();k.select();l.stopPropagation()});if(f){a.attr("title",f).tooltip()}return a};function async_save_text(d,f,e,a,c,h,i,g,b){if(c===undefined){c=30}if(i===undefined){i=4}$("#"+d).live("click",function(){if($("#renaming-active").length>0){return}var l=$("#"+f),k=l.text(),j;if(h){j=$("<textarea></textarea>").attr({rows:i,cols:c}).text($.trim(k))}else{j=$("<input type='text'></input>").attr({value:$.trim(k),size:c})}j.attr("id","renaming-active");j.blur(function(){$(this).remove();l.show();if(b){b(j)}});j.keyup(function(n){if(n.keyCode===27){$(this).trigger("blur")}else{if(n.keyCode===13){var m={};m[a]=$(this).val();$(this).trigger("blur");$.ajax({url:e,data:m,error:function(){alert("Text editing for elt "+f+" failed")},success:function(o){if(o!==""){l.text(o)}else{l.html("<em>None</em>")}if(b){b(j)}}})}}});if(g){g(j)}l.hide();j.insertAfter(l);j.focus();j.select();return})}function init_history_items(d,a,c){var b=function(){try{var e=$.jStorage.get("history_expand_state");if(e){for(var g in e){$("#"+g+" div.historyItemBody").show()}}}catch(f){$.jStorage.deleteKey("history_expand_state")}if($.browser.mozilla){$("div.historyItemBody").each(function(){if(!$(this).is(":visible")){$(this).find("pre.peek").css("overflow","hidden")}})}d.each(function(){var j=this.id,h=$(this).children("div.historyItemBody"),i=h.find("pre.peek");$(this).find(".historyItemTitleBar > .historyItemTitle").wrap("<a href='javascript:void(0);'></a>").click(function(){var k;if(h.is(":visible")){if($.browser.mozilla){i.css("overflow","hidden")}h.slideUp("fast");if(!c){k=$.jStorage.get("history_expand_state");if(k){delete k[j];$.jStorage.set("history_expand_state",k)}}}else{h.slideDown("fast",function(){if($.browser.mozilla){i.css("overflow","auto")}});if(!c){k=$.jStorage.get("history_expand_state");if(!k){k={}}k[j]=true;$.jStorage.set("history_expand_state",k)}}return false})});$("#top-links > a.toggle").click(function(){var h=$.jStorage.get("history_expand_state");if(!h){h={}}$("div.historyItemBody:visible").each(function(){if($.browser.mozilla){$(this).find("pre.peek").css("overflow","hidden")}$(this).slideUp("fast");if(h){delete h[$(this).parent().attr("id")]}});$.jStorage.set("history_expand_state",h)}).show()};b()}function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}function reset_tool_search(a){var c=$("#galaxy_tools").contents();if(c.length===0){c=$(document)}$(this).removeClass("search_active");c.find(".toolTitle").removeClass("search_match");c.find(".toolSectionBody").hide();c.find(".toolTitle").show();c.find(".toolPanelLabel").show();c.find(".toolSectionWrapper").each(function(){if($(this).attr("id")!="recently_used_wrapper"){$(this).show()}else{if($(this).hasClass("user_pref_visible")){$(this).show()}}});c.find("#search-no-results").hide();c.find("#search-spinner").hide();if(a){var b=c.find("#tool-search-query");b.val("search tools")}}var GalaxyAsync=function(a){this.url_dict={};this.log_action=(a===undefined?false:a)};GalaxyAsync.prototype.set_func_url=function(a,b){this.url_dict[a]=b};GalaxyAsync.prototype.set_user_pref=function(a,b){var c=this.url_dict[arguments.callee];if(c===undefined){return false}$.ajax({url:c,data:{pref_name:a,pref_value:b},error:function(){return false},success:function(){return true}})};GalaxyAsync.prototype.log_user_action=function(c,b,d){if(!this.log_action){return}var a=this.url_dict[arguments.callee];if(a===undefined){return false}$.ajax({url:a,data:{action:c,context:b,params:d},error:function(){return false},success:function(){return true}})};$(document).ready(function(){$("select[refresh_on_change='true']").change(function(){var a=$(this),e=a.val(),d=false,c=a.attr("refresh_on_change_values");if(c){c=c.split(",");var b=a.attr("last_selected_value");if($.inArray(e,c)===-1&&$.inArray(b,c)===-1){return}}$(window).trigger("refresh_on_change");$(document).trigger("convert_to_values");a.get(0).form.submit()});$(":checkbox[refresh_on_change='true']").click(function(){var a=$(this),e=a.val(),d=false,c=a.attr("refresh_on_change_values");if(c){c=c.split(",");var b=a.attr("last_selected_value");if($.inArray(e,c)===-1&&$.inArray(b,c)===-1){return}}$(window).trigger("refresh_on_change");a.get(0).form.submit()});$("a[confirm]").click(function(){return confirm($(this).attr("confirm"))});if($.fn.tooltip){$(".tooltip").tooltip({placement:"top"})}make_popup_menus();replace_big_select_inputs(20,1500);$("a").click(function(){var b=$(this);var c=(parent.frames&&parent.frames.galaxy_main);if((b.attr("target")=="galaxy_main")&&(!c)){var a=b.attr("href");if(a.indexOf("?")==-1){a+="?"}else{a+="&"}a+="use_panels=True";b.attr("href",a);b.attr("target","_self")}return b})});
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/packed/libs/underscore.js
--- a/static/scripts/packed/libs/underscore.js
+++ b/static/scripts/packed/libs/underscore.js
@@ -1,1 +1,1 @@
-(function(){var x=this;var t=x._;var b={};var j=Array.prototype,D=Object.prototype,G=Function.prototype;var v=j.slice,z=j.unshift,y=D.toString,p=D.hasOwnProperty;var n=j.forEach,i=j.map,B=j.reduce,e=j.reduceRight,m=j.filter,a=j.every,A=j.some,w=j.indexOf,f=j.lastIndexOf,c=Array.isArray,C=Object.keys,k=G.bind;var F=function(I){return new g(I)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports){exports=module.exports=F}exports._=F}else{x._=F}F.VERSION="1.3.1";var d=F.each=F.forEach=function(N,M,L){if(N==null){return}if(n&&N.forEach===n){N.forEach(M,L)}else{if(N.length===+N.length){for(var K=0,I=N.length;K<I;K++){if(K in N&&M.call(L,N[K],K,N)===b){return}}}else{for(var J in N){if(F.has(N,J)){if(M.call(L,N[J],J,N)===b){return}}}}}};F.map=F.collect=function(L,K,J){var I=[];if(L==null){return I}if(i&&L.map===i){return L.map(K,J)}d(L,function(O,M,N){I[I.length]=K.call(J,O,M,N)});if(L.length===+L.length){I.length=L.length}return I};F.reduce=F.foldl=F.inject=function(M,L,I,K){var J=arguments.length>2;if(M==null){M=[]}if(B&&M.reduce===B){if(K){L=F.bind(L,K)}return J?M.reduce(L,I):M.reduce(L)}d(M,function(P,N,O){if(!J){I=P;J=true}else{I=L.call(K,I,P,N,O)}});if(!J){throw new TypeError("Reduce of empty array with no initial value")}return I};F.reduceRight=F.foldr=function(M,L,I,K){var J=arguments.length>2;if(M==null){M=[]}if(e&&M.reduceRight===e){if(K){L=F.bind(L,K)}return J?M.reduceRight(L,I):M.reduceRight(L)}var N=F.toArray(M).reverse();if(K&&!J){L=F.bind(L,K)}return J?F.reduce(N,L,I,K):F.reduce(N,L)};F.find=F.detect=function(L,K,J){var I;r(L,function(O,M,N){if(K.call(J,O,M,N)){I=O;return true}});return I};F.filter=F.select=function(L,K,J){var I=[];if(L==null){return I}if(m&&L.filter===m){return L.filter(K,J)}d(L,function(O,M,N){if(K.call(J,O,M,N)){I[I.length]=O}});return I};F.reject=function(L,K,J){var I=[];if(L==null){return I}d(L,function(O,M,N){if(!K.call(J,O,M,N)){I[I.length]=O}});return I};F.every=F.all=function(L,K,J){var I=true;if(L==null){return I}if(a&&L.every===a){return L.every(K,J)}d(L,function(O,M,N){if(!(I=I&&K.call(J,O,M,N))){return b}});return I};var r=F.some=F.any=function(L,K,J){K||(K=F.identity);var I=false;if(L==null){return I}if(A&&L.some===A){return L.some(K,J)}d(L,function(O,M,N){if(I||(I=K.call(J,O,M,N))){return b}});return !!I};F.include=F.contains=function(K,J){var I=false;if(K==null){return I}if(w&&K.indexOf===w){return K.indexOf(J)!=-1}I=r(K,function(L){return L===J});return I};F.invoke=function(J,K){var I=v.call(arguments,2);return F.map(J,function(L){return(F.isFunction(K)?K||L:L[K]).apply(L,I)})};F.pluck=function(J,I){return F.map(J,function(K){return K[I]})};F.max=function(L,K,J){if(!K&&F.isArray(L)){return Math.max.apply(Math,L)}if(!K&&F.isEmpty(L)){return -Infinity}var I={computed:-Infinity};d(L,function(P,M,O){var N=K?K.call(J,P,M,O):P;N>=I.computed&&(I={value:P,computed:N})});return I.value};F.min=function(L,K,J){if(!K&&F.isArray(L)){return Math.min.apply(Math,L)}if(!K&&F.isEmpty(L)){return Infinity}var I={computed:Infinity};d(L,function(P,M,O){var N=K?K.call(J,P,M,O):P;N<I.computed&&(I={value:P,computed:N})});return I.value};F.shuffle=function(K){var I=[],J;d(K,function(N,L,M){if(L==0){I[0]=N}else{J=Math.floor(Math.random()*(L+1));I[L]=I[J];I[J]=N}});return I};F.sortBy=function(K,J,I){return F.pluck(F.map(K,function(N,L,M){return{value:N,criteria:J.call(I,N,L,M)}}).sort(function(O,N){var M=O.criteria,L=N.criteria;return M<L?-1:M>L?1:0}),"value")};F.groupBy=function(K,L){var I={};var J=F.isFunction(L)?L:function(M){return M[L]};d(K,function(O,M){var N=J(O,M);(I[N]||(I[N]=[])).push(O)});return I};F.sortedIndex=function(N,M,K){K||(K=F.identity);var I=0,L=N.length;while(I<L){var J=(I+L)>>1;K(N[J])<K(M)?I=J+1:L=J}return I};F.toArray=function(I){if(!I){return[]}if(I.toArray){return I.toArray()}if(F.isArray(I)){return v.call(I)}if(F.isArguments(I)){return v.call(I)}return F.values(I)};F.size=function(I){return F.toArray(I).length};F.first=F.head=function(K,J,I){return(J!=null)&&!I?v.call(K,0,J):K[0]};F.initial=function(K,J,I){return v.call(K,0,K.length-((J==null)||I?1:J))};F.last=function(K,J,I){if((J!=null)&&!I){return v.call(K,Math.max(K.length-J,0))}else{return K[K.length-1]}};F.rest=F.tail=function(K,I,J){return v.call(K,(I==null)||J?1:I)};F.compact=function(I){return F.filter(I,function(J){return !!J})};F.flatten=function(J,I){return F.reduce(J,function(K,L){if(F.isArray(L)){return K.concat(I?L:F.flatten(L))}K[K.length]=L;return K},[])};F.without=function(I){return F.difference(I,v.call(arguments,1))};F.uniq=F.unique=function(M,L,K){var J=K?F.map(M,K):M;var I=[];F.reduce(J,function(N,P,O){if(0==O||(L===true?F.last(N)!=P:!F.include(N,P))){N[N.length]=P;I[I.length]=M[O]}return N},[]);return I};F.union=function(){return F.uniq(F.flatten(arguments,true))};F.intersection=F.intersect=function(J){var I=v.call(arguments,1);return F.filter(F.uniq(J),function(K){return F.every(I,function(L){return F.indexOf(L,K)>=0})})};F.difference=function(J){var I=F.flatten(v.call(arguments,1));return F.filter(J,function(K){return !F.include(I,K)})};F.zip=function(){var I=v.call(arguments);var L=F.max(F.pluck(I,"length"));var K=new Array(L);for(var J=0;J<L;J++){K[J]=F.pluck(I,""+J)}return K};F.indexOf=function(M,K,L){if(M==null){return -1}var J,I;if(L){J=F.sortedIndex(M,K);return M[J]===K?J:-1}if(w&&M.indexOf===w){return M.indexOf(K)}for(J=0,I=M.length;J<I;J++){if(J in M&&M[J]===K){return J}}return -1};F.lastIndexOf=function(K,J){if(K==null){return -1}if(f&&K.lastIndexOf===f){return K.lastIndexOf(J)}var I=K.length;while(I--){if(I in K&&K[I]===J){return I}}return -1};F.range=function(N,L,M){if(arguments.length<=1){L=N||0;N=0}M=arguments[2]||1;var J=Math.max(Math.ceil((L-N)/M),0);var I=0;var K=new Array(J);while(I<J){K[I++]=N;N+=M}return K};var h=function(){};F.bind=function H(L,J){var K,I;if(L.bind===k&&k){return k.apply(L,v.call(arguments,1))}if(!F.isFunction(L)){throw new TypeError}I=v.call(arguments,2);return K=function(){if(!(this instanceof K)){return L.apply(J,I.concat(v.call(arguments)))}h.prototype=L.prototype;var N=new h;var M=L.apply(N,I.concat(v.call(arguments)));if(Object(M)===M){return M}return N}};F.bindAll=function(J){var I=v.call(arguments,1);if(I.length==0){I=F.functions(J)}d(I,function(K){J[K]=F.bind(J[K],J)});return J};F.memoize=function(K,J){var I={};J||(J=F.identity);return function(){var L=J.apply(this,arguments);return F.has(I,L)?I[L]:(I[L]=K.apply(this,arguments))}};F.delay=function(J,K){var I=v.call(arguments,2);return setTimeout(function(){return J.apply(J,I)},K)};F.defer=function(I){return F.delay.apply(F,[I,1].concat(v.call(arguments,1)))};F.throttle=function(N,P){var L,I,O,M,K;var J=F.debounce(function(){K=M=false},P);return function(){L=this;I=arguments;var Q=function(){O=null;if(K){N.apply(L,I)}J()};if(!O){O=setTimeout(Q,P)}if(M){K=true}else{N.apply(L,I)}J();M=true}};F.debounce=function(I,K){var J;return function(){var N=this,M=arguments;var L=function(){J=null;I.apply(N,M)};clearTimeout(J);J=setTimeout(L,K)}};F.once=function(K){var I=false,J;return function(){if(I){return J}I=true;return J=K.apply(this,arguments)}};F.wrap=function(I,J){return function(){var K=[I].concat(v.call(arguments,0));return J.apply(this,K)}};F.compose=function(){var I=arguments;return function(){var J=arguments;for(var K=I.length-1;K>=0;K--){J=[I[K].apply(this,J)]}return J[0]}};F.after=function(J,I){if(J<=0){return I()}return function(){if(--J<1){return I.apply(this,arguments)}}};F.keys=C||function(K){if(K!==Object(K)){throw new TypeError("Invalid object")}var J=[];for(var I in K){if(F.has(K,I)){J[J.length]=I}}return J};F.values=function(I){return F.map(I,F.identity)};F.functions=F.methods=function(K){var J=[];for(var I in K){if(F.isFunction(K[I])){J.push(I)}}return J.sort()};F.extend=function(I){d(v.call(arguments,1),function(J){for(var K in J){I[K]=J[K]}});return I};F.defaults=function(I){d(v.call(arguments,1),function(J){for(var K in J){if(I[K]==null){I[K]=J[K]}}});return I};F.clone=function(I){if(!F.isObject(I)){return I}return F.isArray(I)?I.slice():F.extend({},I)};F.tap=function(J,I){I(J);return J};function E(L,K,J){if(L===K){return L!==0||1/L==1/K}if(L==null||K==null){return L===K}if(L._chain){L=L._wrapped}if(K._chain){K=K._wrapped}if(L.isEqual&&F.isFunction(L.isEqual)){return L.isEqual(K)}if(K.isEqual&&F.isFunction(K.isEqual)){return K.isEqual(L)}var O=y.call(L);if(O!=y.call(K)){return false}switch(O){case"[object String]":return L==String(K);case"[object Number]":return L!=+L?K!=+K:(L==0?1/L==1/K:L==+K);case"[object Date]":case"[object Boolean]":return +L==+K;case"[object RegExp]":return L.source==K.source&&L.global==K.global&&L.multiline==K.multiline&&L.ignoreCase==K.ignoreCase}if(typeof L!="object"||typeof K!="object"){return false}var P=J.length;while(P--){if(J[P]==L){return true}}J.push(L);var N=0,I=true;if(O=="[object Array]"){N=L.length;I=N==K.length;if(I){while(N--){if(!(I=N in L==N in K&&E(L[N],K[N],J))){break}}}}else{if("constructor" in L!="constructor" in K||L.constructor!=K.constructor){return false}for(var M in L){if(F.has(L,M)){N++;if(!(I=F.has(K,M)&&E(L[M],K[M],J))){break}}}if(I){for(M in K){if(F.has(K,M)&&!(N--)){break}}I=!N}}J.pop();return I}F.isEqual=function(J,I){return E(J,I,[])};F.isEmpty=function(J){if(F.isArray(J)||F.isString(J)){return J.length===0}for(var I in J){if(F.has(J,I)){return false}}return true};F.isElement=function(I){return !!(I&&I.nodeType==1)};F.isArray=c||function(I){return y.call(I)=="[object Array]"};F.isObject=function(I){return I===Object(I)};F.isArguments=function(I){return y.call(I)=="[object Arguments]"};if(!F.isArguments(arguments)){F.isArguments=function(I){return !!(I&&F.has(I,"callee"))}}F.isFunction=function(I){return y.call(I)=="[object Function]"};F.isString=function(I){return y.call(I)=="[object String]"};F.isNumber=function(I){return y.call(I)=="[object Number]"};F.isNaN=function(I){return I!==I};F.isBoolean=function(I){return I===true||I===false||y.call(I)=="[object Boolean]"};F.isDate=function(I){return y.call(I)=="[object Date]"};F.isRegExp=function(I){return y.call(I)=="[object RegExp]"};F.isNull=function(I){return I===null};F.isUndefined=function(I){return I===void 0};F.has=function(J,I){return p.call(J,I)};F.noConflict=function(){x._=t;return this};F.identity=function(I){return I};F.times=function(L,K,J){for(var I=0;I<L;I++){K.call(J,I)}};F.escape=function(I){return(""+I).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};F.mixin=function(I){d(F.functions(I),function(J){s(J,F[J]=I[J])})};var l=0;F.uniqueId=function(I){var J=l++;return I?I+J:J};F.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var u=/.^/;var q=function(I){return I.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};F.template=function(L,K){var M=F.templateSettings;var I="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+L.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(M.escape||u,function(N,O){return"',_.escape("+q(O)+"),'"}).replace(M.interpolate||u,function(N,O){return"',"+q(O)+",'"}).replace(M.evaluate||u,function(N,O){return"');"+q(O).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');";var J=new Function("obj","_",I);if(K){return J(K,F)}return function(N){return J.call(this,N,F)}};F.chain=function(I){return F(I).chain()};var g=function(I){this._wrapped=I};F.prototype=g.prototype;var o=function(J,I){return I?F(J).chain():J};var s=function(I,J){g.prototype[I]=function(){var K=v.call(arguments);z.call(K,this._wrapped);return o(J.apply(F,K),this._chain)}};F.mixin(F);d(["pop","push","reverse","shift","sort","splice","unshift"],function(I){var J=j[I];g.prototype[I]=function(){var K=this._wrapped;J.apply(K,arguments);var L=K.length;if((I=="shift"||I=="splice")&&L===0){delete K[0]}return o(K,this._chain)}});d(["concat","join","slice"],function(I){var J=j[I];g.prototype[I]=function(){return o(J.apply(this._wrapped,arguments),this._chain)}});g.prototype.chain=function(){this._chain=true;return this};g.prototype.value=function(){return this._wrapped}}).call(this);
\ No newline at end of file
+(function(){var w=this;var k=w._;var E={};var D=Array.prototype,g=Object.prototype,r=Function.prototype;var I=D.push,o=D.slice,y=D.concat,B=D.unshift,d=g.toString,j=g.hasOwnProperty;var M=D.forEach,q=D.map,F=D.reduce,c=D.reduceRight,b=D.filter,C=D.every,p=D.some,n=D.indexOf,l=D.lastIndexOf,u=Array.isArray,f=Object.keys,G=r.bind;var N=function(O){if(O instanceof N){return O}if(!(this instanceof N)){return new N(O)}this._wrapped=O};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports){exports=module.exports=N}exports._=N}else{w._=N}N.VERSION="1.4.0";var J=N.each=N.forEach=function(T,S,R){if(M&&T.forEach===M){T.forEach(S,R)}else{if(T.length===+T.length){for(var Q=0,O=T.length;Q<O;Q++){if(S.call(R,T[Q],Q,T)===E){return}}}else{for(var P in T){if(N.has(T,P)){if(S.call(R,T[P],P,T)===E){return}}}}}};N.map=N.collect=function(R,Q,P){var O=[];if(q&&R.map===q){return R.map(Q,P)}J(R,function(U,S,T){O[O.length]=Q.call(P,U,S,T)});return O};N.reduce=N.foldl=N.inject=function(S,R,O,Q){var P=arguments.length>2;if(F&&S.reduce===F){if(Q){R=N.bind(R,Q)}return P?S.reduce(R,O):S.reduce(R)}J(S,function(V,T,U){if(!P){O=V;P=true}else{O=R.call(Q,O,V,T,U)}});if(!P){throw new TypeError("Reduce of empty array with no initial value")}return O};N.reduceRight=N.foldr=function(U,R,O,Q){var P=arguments.length>2;if(c&&U.reduceRight===c){if(Q){R=N.bind(R,Q)}return arguments.length>2?U.reduceRight(R,O):U.reduceRight(R)}var T=U.length;if(T!==+T){var S=N.keys(U);T=S.length}J(U,function(X,V,W){V=S?S[--T]:--T;if(!P){O=U[V];P=true}else{O=R.call(Q,O,U[V],V,W)}});if(!P){throw new TypeError("Reduce of empty array with no initial value")}return O};N.find=N.detect=function(R,Q,P){var O;A(R,function(U,S,T){if(Q.call(P,U,S,T)){O=U;return true}});return O};N.filter=N.select=function(R,Q,P){var O=[];if(b&&R.filter===b){return R.filter(Q,P)}J(R,function(U,S,T){if(Q.call(P,U,S,T)){O[O.length]=U}});return O};N.reject=function(R,Q,P){var O=[];J(R,function(U,S,T){if(!Q.call(P,U,S,T)){O[O.length]=U}});return O};N.every=N.all=function(R,Q,P){Q||(Q=N.identity);var O=true;if(C&&R.every===C){return R.every(Q,P)}J(R,function(U,S,T){if(!(O=O&&Q.call(P,U,S,T))){return E}});return !!O};var A=N.some=N.any=function(R,Q,P){Q||(Q=N.identity);var O=false;if(p&&R.some===p){return R.some(Q,P)}J(R,function(U,S,T){if(O||(O=Q.call(P,U,S,T))){return E}});return !!O};N.contains=N.include=function(Q,P){var O=false;if(n&&Q.indexOf===n){return Q.indexOf(P)!=-1}O=A(Q,function(R){return R===P});return O};N.invoke=function(P,Q){var O=o.call(arguments,2);return N.map(P,function(R){return(N.isFunction(Q)?Q:R[Q]).apply(R,O)})};N.pluck=function(P,O){return N.map(P,function(Q){return Q[O]})};N.where=function(P,O){if(N.isEmpty(O)){return[]}return N.filter(P,function(R){for(var Q in O){if(O[Q]!==R[Q]){return false}}return true})};N.max=function(R,Q,P){if(!Q&&N.isArray(R)&&R[0]===+R[0]&&R.length<65535){return Math.max.apply(Math,R)}if(!Q&&N.isEmpty(R)){return -Infinity}var O={computed:-Infinity};J(R,function(V,S,U){var T=Q?Q.call(P,V,S,U):V;T>=O.computed&&(O={value:V,computed:T})});return O.value};N.min=function(R,Q,P){if(!Q&&N.isArray(R)&&R[0]===+R[0]&&R.length<65535){return Math.min.apply(Math,R)}if(!Q&&N.isEmpty(R)){return Infinity}var O={computed:Infinity};J(R,function(V,S,U){var T=Q?Q.call(P,V,S,U):V;T<O.computed&&(O={value:V,computed:T})});return O.value};N.shuffle=function(R){var Q;var P=0;var O=[];J(R,function(S){Q=N.random(P++);O[P-1]=O[Q];O[Q]=S});return O};var a=function(O){return N.isFunction(O)?O:function(P){return P[O]}};N.sortBy=function(R,Q,O){var P=a(Q);return N.pluck(N.map(R,function(U,S,T){return{value:U,index:S,criteria:P.call(O,U,S,T)}}).sort(function(V,U){var T=V.criteria;var S=U.criteria;if(T!==S){if(T>S||T===void 0){return 1}if(T<S||S===void 0){return -1}}return V.index<U.index?-1:1}),"value")};var t=function(T,S,P,R){var O={};var Q=a(S);J(T,function(W,U){var V=Q.call(P,W,U,T);R(O,V,W)});return O};N.groupBy=function(Q,P,O){return t(Q,P,O,function(R,S,T){(N.has(R,S)?R[S]:(R[S]=[])).push(T)})};N.countBy=function(Q,P,O){return t(Q,P,O,function(R,S,T){if(!N.has(R,S)){R[S]=0}R[S]++})};N.sortedIndex=function(V,U,R,Q){R=R==null?N.identity:a(R);var T=R.call(Q,U);var O=0,S=V.length;while(O<S){var P=(O+S)>>>1;R.call(Q,V[P])<T?O=P+1:S=P}return O};N.toArray=function(O){if(!O){return[]}if(O.length===+O.length){return o.call(O)}return N.values(O)};N.size=function(O){return(O.length===+O.length)?O.length:N.keys(O).length};N.first=N.head=N.take=function(Q,P,O){return(P!=null)&&!O?o.call(Q,0,P):Q[0]};N.initial=function(Q,P,O){return o.call(Q,0,Q.length-((P==null)||O?1:P))};N.last=function(Q,P,O){if((P!=null)&&!O){return o.call(Q,Math.max(Q.length-P,0))}else{return Q[Q.length-1]}};N.rest=N.tail=N.drop=function(Q,P,O){return o.call(Q,(P==null)||O?1:P)};N.compact=function(O){return N.filter(O,function(P){return !!P})};var x=function(P,Q,O){J(P,function(R){if(N.isArray(R)){Q?I.apply(O,R):x(R,Q,O)}else{O.push(R)}});return O};N.flatten=function(P,O){return x(P,O,[])};N.without=function(O){return N.difference(O,o.call(arguments,1))};N.uniq=N.unique=function(U,T,S,R){var P=S?N.map(U,S,R):U;var Q=[];var O=[];J(P,function(W,V){if(T?(!V||O[O.length-1]!==W):!N.contains(O,W)){O.push(W);Q.push(U[V])}});return Q};N.union=function(){return N.uniq(y.apply(D,arguments))};N.intersection=function(P){var O=o.call(arguments,1);return N.filter(N.uniq(P),function(Q){return N.every(O,function(R){return N.indexOf(R,Q)>=0})})};N.difference=function(P){var O=y.apply(D,o.call(arguments,1));return N.filter(P,function(Q){return !N.contains(O,Q)})};N.zip=function(){var O=o.call(arguments);var R=N.max(N.pluck(O,"length"));var Q=new Array(R);for(var P=0;P<R;P++){Q[P]=N.pluck(O,""+P)}return Q};N.object=function(S,Q){var O={};for(var R=0,P=S.length;R<P;R++){if(Q){O[S[R]]=Q[R]}else{O[S[R][0]]=S[R][1]}}return O};N.indexOf=function(S,Q,R){var P=0,O=S.length;if(R){if(typeof R=="number"){P=(R<0?Math.max(0,O+R):R)}else{P=N.sortedIndex(S,Q);return S[P]===Q?P:-1}}if(n&&S.indexOf===n){return S.indexOf(Q,R)}for(;P<O;P++){if(S[P]===Q){return P}}return -1};N.lastIndexOf=function(R,Q,P){if(l&&R.lastIndexOf===l){return R.lastIndexOf(Q,P)}var O=(P!=null?P:R.length);while(O--){if(R[O]===Q){return O}}return -1};N.range=function(T,R,S){if(arguments.length<=1){R=T||0;T=0}S=arguments[2]||1;var P=Math.max(Math.ceil((R-T)/S),0);var O=0;var Q=new Array(P);while(O<P){Q[O++]=T;T+=S}return Q};var H=function(){};N.bind=function e(R,P){var Q,O;if(R.bind===G&&G){return G.apply(R,o.call(arguments,1))}if(!N.isFunction(R)){throw new TypeError}O=o.call(arguments,2);return Q=function(){if(!(this instanceof Q)){return R.apply(P,O.concat(o.call(arguments)))}H.prototype=R.prototype;var T=new H;var S=R.apply(T,O.concat(o.call(arguments)));if(Object(S)===S){return S}return T}};N.bindAll=function(P){var O=o.call(arguments,1);if(O.length==0){O=N.functions(P)}J(O,function(Q){P[Q]=N.bind(P[Q],P)});return P};N.memoize=function(Q,P){var O={};P||(P=N.identity);return function(){var R=P.apply(this,arguments);return N.has(O,R)?O[R]:(O[R]=Q.apply(this,arguments))}};N.delay=function(P,Q){var O=o.call(arguments,2);return setTimeout(function(){return P.apply(null,O)},Q)};N.defer=function(O){return N.delay.apply(N,[O,1].concat(o.call(arguments,1)))};N.throttle=function(Q,R){var P,T,U,V,S,W;var O=N.debounce(function(){S=V=false},R);return function(){P=this;T=arguments;var X=function(){U=null;if(S){W=Q.apply(P,T)}O()};if(!U){U=setTimeout(X,R)}if(V){S=true}else{V=true;W=Q.apply(P,T)}O();return W}};N.debounce=function(Q,S,P){var R,O;return function(){var W=this,V=arguments;var U=function(){R=null;if(!P){O=Q.apply(W,V)}};var T=P&&!R;clearTimeout(R);R=setTimeout(U,S);if(T){O=Q.apply(W,V)}return O}};N.once=function(Q){var O=false,P;return function(){if(O){return P}O=true;P=Q.apply(this,arguments);Q=null;return P}};N.wrap=function(O,P){return function(){var Q=[O];I.apply(Q,arguments);return P.apply(this,Q)}};N.compose=function(){var O=arguments;return function(){var P=arguments;for(var Q=O.length-1;Q>=0;Q--){P=[O[Q].apply(this,P)]}return P[0]}};N.after=function(P,O){if(P<=0){return O()}return function(){if(--P<1){return O.apply(this,arguments)}}};N.keys=f||function(Q){if(Q!==Object(Q)){throw new TypeError("Invalid object")}var P=[];for(var O in Q){if(N.has(Q,O)){P[P.length]=O}}return P};N.values=function(Q){var O=[];for(var P in Q){if(N.has(Q,P)){O.push(Q[P])}}return O};N.pairs=function(Q){var P=[];for(var O in Q){if(N.has(Q,O)){P.push([O,Q[O]])}}return P};N.invert=function(Q){var O={};for(var P in Q){if(N.has(Q,P)){O[Q[P]]=P}}return O};N.functions=N.methods=function(Q){var P=[];for(var O in Q){if(N.isFunction(Q[O])){P.push(O)}}return P.sort()};N.extend=function(O){J(o.call(arguments,1),function(P){for(var Q in P){O[Q]=P[Q]}});return O};N.pick=function(P){var Q={};var O=y.apply(D,o.call(arguments,1));J(O,function(R){if(R in P){Q[R]=P[R]}});return Q};N.omit=function(Q){var R={};var P=y.apply(D,o.call(arguments,1));for(var O in Q){if(!N.contains(P,O)){R[O]=Q[O]}}return R};N.defaults=function(O){J(o.call(arguments,1),function(P){for(var Q in P){if(O[Q]==null){O[Q]=P[Q]}}});return O};N.clone=function(O){if(!N.isObject(O)){return O}return N.isArray(O)?O.slice():N.extend({},O)};N.tap=function(P,O){O(P);return P};var K=function(V,U,P,Q){if(V===U){return V!==0||1/V==1/U}if(V==null||U==null){return V===U}if(V instanceof N){V=V._wrapped}if(U instanceof N){U=U._wrapped}var S=d.call(V);if(S!=d.call(U)){return false}switch(S){case"[object String]":return V==String(U);case"[object Number]":return V!=+V?U!=+U:(V==0?1/V==1/U:V==+U);case"[object Date]":case"[object Boolean]":return +V==+U;case"[object RegExp]":return V.source==U.source&&V.global==U.global&&V.multiline==U.multiline&&V.ignoreCase==U.ignoreCase}if(typeof V!="object"||typeof U!="object"){return false}var O=P.length;while(O--){if(P[O]==V){return Q[O]==U}}P.push(V);Q.push(U);var X=0,Y=true;if(S=="[object Array]"){X=V.length;Y=X==U.length;if(Y){while(X--){if(!(Y=K(V[X],U[X],P,Q))){break}}}}else{var T=V.constructor,R=U.constructor;if(T!==R&&!(N.isFunction(T)&&(T instanceof T)&&N.isFunction(R)&&(R instanceof R))){return false}for(var W in V){if(N.has(V,W)){X++;if(!(Y=N.has(U,W)&&K(V[W],U[W],P,Q))){break}}}if(Y){for(W in U){if(N.has(U,W)&&!(X--)){break}}Y=!X}}P.pop();Q.pop();return Y};N.isEqual=function(P,O){return K(P,O,[],[])};N.isEmpty=function(P){if(P==null){return true}if(N.isArray(P)||N.isString(P)){return P.length===0}for(var O in P){if(N.has(P,O)){return false}}return true};N.isElement=function(O){return !!(O&&O.nodeType===1)};N.isArray=u||function(O){return d.call(O)=="[object Array]"};N.isObject=function(O){return O===Object(O)};J(["Arguments","Function","String","Number","Date","RegExp"],function(O){N["is"+O]=function(P){return d.call(P)=="[object "+O+"]"}});if(!N.isArguments(arguments)){N.isArguments=function(O){return !!(O&&N.has(O,"callee"))}}if(typeof(/./)!=="function"){N.isFunction=function(O){return typeof O==="function"}}N.isFinite=function(O){return N.isNumber(O)&&isFinite(O)};N.isNaN=function(O){return N.isNumber(O)&&O!=+O};N.isBoolean=function(O){return O===true||O===false||d.call(O)=="[object Boolean]"};N.isNull=function(O){return O===null};N.isUndefined=function(O){return O===void 0};N.has=function(P,O){return j.call(P,O)};N.noConflict=function(){w._=k;return this};N.identity=function(O){return O};N.times=function(R,Q,P){for(var O=0;O<R;O++){Q.call(P,O)}};N.random=function(P,O){if(O==null){O=P;P=0}return P+(0|Math.random()*(O-P+1))};var m={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};m.unescape=N.invert(m.escape);var L={escape:new RegExp("["+N.keys(m.escape).join("")+"]","g"),unescape:new RegExp("("+N.keys(m.unescape).join("|")+")","g")};N.each(["escape","unescape"],function(O){N[O]=function(P){if(P==null){return""}return(""+P).replace(L[O],function(Q){return m[O][Q]})}});N.result=function(O,Q){if(O==null){return null}var P=O[Q];return N.isFunction(P)?P.call(O):P};N.mixin=function(O){J(N.functions(O),function(P){var Q=N[P]=O[P];N.prototype[P]=function(){var R=[this._wrapped];I.apply(R,arguments);return s.call(this,Q.apply(N,R))}})};var z=0;N.uniqueId=function(O){var P=z++;return O?O+P:P};N.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var v=/(.)^/;var h={"'":"'","\\":"\\","\r":"r","\n":"n","\t":"t","\u2028":"u2028","\u2029":"u2029"};var i=/\\|'|\r|\n|\t|\u2028|\u2029/g;N.template=function(W,R,Q){Q=N.defaults({},Q,N.templateSettings);var S=new RegExp([(Q.escape||v).source,(Q.interpolate||v).source,(Q.evaluate||v).source].join("|")+"|$","g");var T=0;var O="__p+='";W.replace(S,function(Y,Z,X,ab,aa){O+=W.slice(T,aa).replace(i,function(ac){return"\\"+h[ac]});O+=Z?"'+\n((__t=("+Z+"))==null?'':_.escape(__t))+\n'":X?"'+\n((__t=("+X+"))==null?'':__t)+\n'":ab?"';\n"+ab+"\n__p+='":"";T=aa+Y.length});O+="';\n";if(!Q.variable){O="with(obj||{}){\n"+O+"}\n"}O="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+O+"return __p;\n";try{var P=new Function(Q.variable||"obj","_",O)}catch(U){U.source=O;throw U}if(R){return P(R,N)}var V=function(X){return P.call(this,X,N)};V.source="function("+(Q.variable||"obj")+"){\n"+O+"}";return V};N.chain=function(O){return N(O).chain()};var s=function(O){return this._chain?N(O).chain():O};N.mixin(N);J(["pop","push","reverse","shift","sort","splice","unshift"],function(O){var P=D[O];N.prototype[O]=function(){var Q=this._wrapped;P.apply(Q,arguments);if((O=="shift"||O=="splice")&&Q.length===0){delete Q[0]}return s.call(this,Q)}});J(["concat","join","slice"],function(O){var P=D[O];N.prototype[O]=function(){return s.call(this,P.apply(this._wrapped,arguments))}});N.extend(N.prototype,{chain:function(){this._chain=true;return this},value:function(){return this._wrapped}})}).call(this);
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/packed/mvc/data.js
--- a/static/scripts/packed/mvc/data.js
+++ b/static/scripts/packed/mvc/data.js
@@ -1,1 +1,1 @@
-var Dataset=Backbone.RelationalModel.extend({defaults:{id:"",type:"",name:"",hda_ldda:"hda"},urlRoot:galaxy_paths.get("datasets_url")});var DatasetCollection=Backbone.Collection.extend({model:Dataset});
\ No newline at end of file
+define(["libs/backbone/backbone-relational"],function(){var a=Backbone.RelationalModel.extend({defaults:{id:"",type:"",name:"",hda_ldda:"hda"},urlRoot:galaxy_paths.get("datasets_url")});var b=Backbone.Collection.extend({model:a});return{Dataset:a,DatasetCollection:b}});
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/packed/mvc/history.js
--- a/static/scripts/packed/mvc/history.js
+++ b/static/scripts/packed/mvc/history.js
@@ -1,1 +1,1 @@
-function linkHTMLTemplate(b,a){if(!b){return"<a></a>"}a=a||"a";var c=["<"+a];for(key in b){var d=b[key];if(d===""){continue}switch(key){case"text":continue;case"classes":key="class";d=(b.classes.join)?(b.classes.join(" ")):(b.classes);default:c.push([" ",key,'="',d,'"'].join(""))}}c.push(">");if("text" in b){c.push(b.text)}c.push("</"+a+">");return c.join("")}var HistoryItem=BaseModel.extend(LoggableMixin).extend({defaults:{id:null,name:"",data_type:null,file_size:0,genome_build:null,metadata_data_lines:0,metadata_dbkey:null,metadata_sequences:0,misc_blurb:"",misc_info:"",model_class:"",state:"",deleted:false,purged:false,visible:true,for_editing:true,bodyIsShown:false},initialize:function(){this.log(this+".initialize",this.attributes);this.log("\tparent history_id: "+this.get("history_id"));if(!this.get("accessible")){this.set("state",HistoryItem.STATES.NOT_VIEWABLE)}},isEditable:function(){return(!(this.get("deleted")||this.get("purged")))},hasData:function(){return(this.get("file_size")>0)},toString:function(){var a=this.get("id")||"";if(this.get("name")){a+=':"'+this.get("name")+'"'}return"HistoryItem("+a+")"}});HistoryItem.STATES={NOT_VIEWABLE:"not_viewable",NEW:"new",UPLOAD:"upload",QUEUED:"queued",RUNNING:"running",OK:"ok",EMPTY:"empty",ERROR:"error",DISCARDED:"discarded",SETTING_METADATA:"setting_metadata",FAILED_METADATA:"failed_metadata"};var HistoryItemView=BaseView.extend(LoggableMixin).extend({tagName:"div",className:"historyItemContainer",initialize:function(){this.log(this+".initialize:",this,this.model)},render:function(){var d=this.model.get("id"),c=this.model.get("state");this.clearReferences();this.$el.attr("id","historyItemContainer-"+d);var a=$("<div/>").attr("id","historyItem-"+d).addClass("historyItemWrapper").addClass("historyItem").addClass("historyItem-"+c);a.append(this._render_warnings());a.append(this._render_titleBar());this.body=$(this._render_body());a.append(this.body);a.find(".tooltip").tooltip({placement:"bottom"});var b=a.find("[popupmenu]");b.each(function(e,f){f=$(f);make_popupmenu(f)});this.$el.children().remove();return this.$el.append(a)},clearReferences:function(){this.displayButton=null;this.editButton=null;this.deleteButton=null;this.errButton=null},_render_warnings:function(){return $(jQuery.trim(HistoryItemView.templates.messages(this.model.toJSON())))},_render_titleBar:function(){var a=$('<div class="historyItemTitleBar" style="overflow: hidden"></div>');a.append(this._render_titleButtons());a.append('<span class="state-icon"></span>');a.append(this._render_titleLink());return a},_render_titleButtons:function(){var a=$('<div class="historyItemButtons"></div>');a.append(this._render_displayButton());a.append(this._render_editButton());a.append(this._render_deleteButton());return a},_render_displayButton:function(){if(this.model.get("state")===HistoryItem.STATES.UPLOAD){return null}displayBtnData=(this.model.get("purged"))?({title:"Cannot display datasets removed from disk",enabled:false,icon_class:"display"}):({title:"Display data in browser",href:this.model.get("display_url"),target:(this.model.get("for_editing"))?("galaxy_main"):(null),icon_class:"display"});this.displayButton=new IconButtonView({model:new IconButton(displayBtnData)});return this.displayButton.render().$el},_render_editButton:function(){if((this.model.get("state")===HistoryItem.STATES.UPLOAD)||(!this.model.get("for_editing"))){return null}var c=this.model.get("purged"),a=this.model.get("deleted"),b={title:"Edit attributes",href:this.model.get("edit_url"),target:"galaxy_main",icon_class:"edit"};if(a||c){b.enabled=false}if(a){b.title="Undelete dataset to edit attributes"}else{if(c){b.title="Cannot edit attributes of datasets removed from disk"}}this.editButton=new IconButtonView({model:new IconButton(b)});return this.editButton.render().$el},_render_deleteButton:function(){if(!this.model.get("for_editing")){return null}var a={title:"Delete",href:this.model.get("delete_url"),target:"galaxy_main",id:"historyItemDeleter-"+this.model.get("id"),icon_class:"delete"};if((this.model.get("deleted")||this.model.get("purged"))&&(!this.model.get("delete_url"))){a={title:"Dataset is already deleted",icon_class:"delete",enabled:false}}this.deleteButton=new IconButtonView({model:new IconButton(a)});return this.deleteButton.render().$el},_render_titleLink:function(){return $(jQuery.trim(HistoryItemView.templates.titleLink(this.model.toJSON())))},_render_hdaSummary:function(){var a=this.model.toJSON();if(this.model.get("metadata_dbkey")==="?"&&this.model.isEditable()){_.extend(a,{dbkey_unknown_and_editable:true})}return HistoryItemView.templates.hdaSummary(a)},_render_primaryActionButtons:function(c){var b=$("<div/>"),a=this;_.each(c,function(d){b.append(d.call(a))});return b},_render_downloadButton:function(){if(this.model.get("purged")){return null}var a=linkHTMLTemplate({title:"Download",href:this.model.get("download_url"),classes:["icon-button","tooltip","disk"]});var d=this.model.get("download_meta_urls");if(!d){return a}var c=$('<div popupmenu="dataset-'+this.model.get("id")+'-popup"></div>');c.append(linkHTMLTemplate({text:"Download Dataset",title:"Download",href:this.model.get("download_url"),classes:["icon-button","tooltip","disk"]}));c.append("<a>Additional Files</a>");for(file_type in d){c.append(linkHTMLTemplate({text:"Download "+file_type,href:d[file_type],classes:["action-button"]}))}var b=$(('<div style="float:left;" class="menubutton split popup" id="dataset-${dataset_id}-popup"></div>'));b.append(a);c.append(b);return c},_render_errButton:function(){if((this.model.get("state")!==HistoryItem.STATES.ERROR)||(!this.model.get("for_editing"))){return null}this.errButton=new IconButtonView({model:new IconButton({title:"View or report this error",href:this.model.get("report_error_url"),target:"galaxy_main",icon_class:"bug"})});return this.errButton.render().$el},_render_showParamsButton:function(){this.showParamsButton=new IconButtonView({model:new IconButton({title:"View details",href:this.model.get("show_params_url"),target:"galaxy_main",icon_class:"information"})});return this.showParamsButton.render().$el},_render_rerunButton:function(){if(!this.model.get("for_editing")){return null}this.rerunButton=new IconButtonView({model:new IconButton({title:"Run this job again",href:this.model.get("rerun_url"),target:"galaxy_main",icon_class:"arrow-circle"})});return this.rerunButton.render().$el},_render_tracksterButton:function(){var a=this.model.get("trackster_urls");if(!(this.model.hasData())||!(this.model.get("for_editing"))||!(a)){return null}this.tracksterButton=new IconButtonView({model:new IconButton({title:"View in Trackster",icon_class:"chart_curve"})});this.errButton.render();this.errButton.$el.addClass("trackster-add").attr({"data-url":a["data-url"],"action-url":a["action-url"],"new-url":a["new-url"]});return this.errButton.$el},_render_secondaryActionButtons:function(b){var c=$('<div style="float: right;"></div>'),a=this;_.each(b,function(d){c.append(d.call(a))});return c},_render_tagButton:function(){if(!(this.model.hasData())||!(this.model.get("for_editing"))||(!this.model.get("retag_url"))){return null}this.tagButton=new IconButtonView({model:new IconButton({title:"Edit dataset tags",target:"galaxy_main",href:this.model.get("retag_url"),icon_class:"tags"})});return this.tagButton.render().$el},_render_annotateButton:function(){if(!(this.model.hasData())||!(this.model.get("for_editing"))||(!this.model.get("annotate_url"))){return null}this.annotateButton=new IconButtonView({model:new IconButton({title:"Edit dataset annotation",target:"galaxy_main",href:this.model.get("annotate_url"),icon_class:"annotate"})});return this.annotateButton.render().$el},_render_tagArea:function(){if(this.model.get("retag_url")){return null}return $(HistoryItemView.templates.tagArea(this.model.toJSON()))},_render_annotationArea:function(){if(!this.model.get("annotate_url")){return null}return $(HistoryItemView.templates.annotationArea(this.model.toJSON()))},_render_displayApps:function(){if(!this.model.get("display_apps")){return null}var a=this.model.get("displayApps"),c=$("<div/>"),b=$("<span/>");this.log(this+"displayApps:",a);return c},_render_peek:function(){if(!this.model.get("peek")){return null}return $("<div/>").append($("<pre/>").attr("id","peek"+this.model.get("id")).addClass("peek").append(this.model.get("peek")))},_render_body_not_viewable:function(a){a.append($("<div>You do not have permission to view dataset.</div>"))},_render_body_uploading:function(a){a.append($("<div>Dataset is uploading</div>"))},_render_body_queued:function(a){a.append($("<div>Job is waiting to run.</div>"));a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_running:function(a){a.append("<div>Job is currently running.</div>");a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_error:function(a){if(!this.model.get("purged")){a.append($("<div>"+this.model.get("misc_blurb")+"</div>"))}a.append(("An error occurred running this job: <i>"+$.trim(this.model.get("misc_info"))+"</i>"));a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_errButton,this._render_showParamsButton,this._render_rerunButton]))},_render_body_discarded:function(a){a.append("<div>The job creating this dataset was cancelled before completion.</div>");a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_setting_metadata:function(a){a.append($("<div>Metadata is being auto-detected.</div>"))},_render_body_empty:function(a){a.append($("<div>No data: <i>"+this.model.get("misc_blurb")+"</i></div>"));a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_failed_metadata:function(a){a.append($(HistoryItemView.templates.failedMetadata(this.model.toJSON())));this._render_body_ok(a)},_render_body_ok:function(a){a.append(this._render_hdaSummary());a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_errButton,this._render_showParamsButton,this._render_rerunButton]));a.append(this._render_secondaryActionButtons([this._render_tagButton,this._render_annotateButton]));a.append('<div class="clear"/>');a.append(this._render_tagArea());a.append(this._render_annotationArea());a.append(this._render_displayApps());a.append(this._render_peek())},_render_body:function(){var b=this.model.get("state");var a=$("<div/>").attr("id","info-"+this.model.get("id")).addClass("historyItemBody").attr("style","display: block");switch(b){case HistoryItem.STATES.NOT_VIEWABLE:this._render_body_not_viewable(a);break;case HistoryItem.STATES.UPLOAD:this._render_body_uploading(a);break;case HistoryItem.STATES.QUEUED:this._render_body_queued(a);break;case HistoryItem.STATES.RUNNING:this._render_body_running(a);break;case HistoryItem.STATES.ERROR:this._render_body_error(a);break;case HistoryItem.STATES.DISCARDED:this._render_body_discarded(a);break;case HistoryItem.STATES.SETTING_METADATA:this._render_body_setting_metadata(a);break;case HistoryItem.STATES.EMPTY:this._render_body_empty(a);break;case HistoryItem.STATES.FAILED_METADATA:this._render_body_failed_metadata(a);break;case HistoryItem.STATES.OK:this._render_body_ok(a);break;default:a.append($('<div>Error: unknown dataset state "'+b+'".</div>'))}a.append('<div style="clear: both"></div>');if(this.model.get("bodyIsShown")===false){a.hide()}return a},events:{"click .historyItemTitle":"toggleBodyVisibility","click a.icon-button.tags":"loadAndDisplayTags","click a.icon-button.annotate":"loadAndDisplayAnnotation"},loadAndDisplayTags:function(b){this.log(this+".loadAndDisplayTags",b);var c=this.$el.find(".tag-area"),a=c.find(".tag-elt");if(c.is(":hidden")){if(!a.html()){$.ajax({url:this.model.get("ajax_get_tag_url"),error:function(){alert("Tagging failed")},success:function(d){a.html(d);a.find(".tooltip").tooltip();c.slideDown("fast")}})}else{c.slideDown("fast")}}else{c.slideUp("fast")}return false},loadAndDisplayAnnotation:function(b){this.log(this+".loadAndDisplayAnnotation",b);var d=this.$el.find(".annotation-area"),c=d.find(".annotation-elt"),a=this.model.get("ajax_set_annotation_url");if(d.is(":hidden")){if(!c.html()){$.ajax({url:this.model.get("ajax_get_annotation_url"),error:function(){alert("Annotations failed")},success:function(e){if(e===""){e="<em>Describe or add notes to dataset</em>"}c.html(e);d.find(".tooltip").tooltip();async_save_text(c.attr("id"),c.attr("id"),a,"new_annotation",18,true,4);d.slideDown("fast")}})}else{d.slideDown("fast")}}else{d.slideUp("fast")}return false},toggleBodyVisibility:function(){this.log(this+".toggleBodyVisibility");this.$el.find(".historyItemBody").toggle()},toString:function(){var a=(this.model)?(this.model+""):("");return"HistoryItemView("+a+")"}});HistoryItemView.templates=CompiledTemplateLoader.getTemplates({"common-templates.html":{warningMsg:"template-warningmessagesmall"},"history-templates.html":{messages:"template-history-warning-messages",titleLink:"template-history-titleLink",hdaSummary:"template-history-hdaSummary",failedMetadata:"template-history-failedMetaData",tagArea:"template-history-tagArea",annotationArea:"template-history-annotationArea"}});var HistoryCollection=Backbone.Collection.extend({model:HistoryItem,toString:function(){return("HistoryCollection()")}});var History=BaseModel.extend(LoggableMixin).extend({defaults:{id:"",name:"",state:"",state_details:{discarded:0,empty:0,error:0,failed_metadata:0,ok:0,queued:0,running:0,setting_metadata:0,upload:0}},initialize:function(b,a){this.log(this+".initialize",b,a);this.items=new HistoryCollection()},loadDatasetsAsHistoryItems:function(c){var a=this,b=this.get("id"),d=this.get("state_details");_.each(c,function(f,e){a.log("loading dataset: ",f,e);var h=new HistoryItem(_.extend(f,{history_id:b}));a.log("as History:",h);a.items.add(h);var g=f.state;d[g]+=1});this.set("state_details",d);this._stateFromStateDetails();return this},_stateFromStateDetails:function(){this.set("state","");var a=this.get("state_details");if((a.error>0)||(a.failed_metadata>0)){this.set("state",HistoryItem.STATES.ERROR)}else{if((a.running>0)||(a.setting_metadata>0)){this.set("state",HistoryItem.STATES.RUNNING)}else{if(a.queued>0){this.set("state",HistoryItem.STATES.QUEUED)}else{if(a.ok===this.items.length){this.set("state",HistoryItem.STATES.OK)}else{throw ("_stateFromStateDetails: unable to determine history state from state details: "+this.state_details)}}}}return this},toString:function(){var a=(this.get("name"))?(","+this.get("name")):("");return"History("+this.get("id")+a+")"}});var HistoryView=BaseView.extend(LoggableMixin).extend({el:"body.historyPage",initialize:function(){this.log(this+".initialize");this.itemViews=[];var a=this;this.model.items.each(function(c){var b=new HistoryItemView({model:c});a.itemViews.push(b)})},render:function(){this.log(this+".render");var a=$("<div/>");_.each(this.itemViews,function(b){a.prepend(b.render())});this.$el.append(a.children());a.remove()},toString:function(){var a=this.model.get("name")||"";return"HistoryView("+a+")"}});function createMockHistoryData(){mockHistory={};mockHistory.data={template:{id:"a799d38679e985db",name:"template",data_type:"fastq",file_size:226297533,genome_build:"?",metadata_data_lines:0,metadata_dbkey:"?",metadata_sequences:0,misc_blurb:"215.8 MB",misc_info:"uploaded fastq file (misc_info)",model_class:"HistoryDatasetAssociation",download_url:"",state:"ok",visible:true,deleted:false,purged:false,hid:0,for_editing:true,accessible:true,undelete_url:"",purge_url:"",unhide_url:"",display_url:"example.com/display",edit_url:"example.com/edit",delete_url:"example.com/delete",show_params_url:"example.com/show_params",rerun_url:"example.com/rerun",retag_url:"example.com/retag",annotate_url:"example.com/annotate",peek:['<table cellspacing="0" cellpadding="3"><tr><th>1.QNAME</th><th>2.FLAG</th><th>3.RNAME</th><th>4.POS</th><th>5.MAPQ</th><th>6.CIGAR</th><th>7.MRNM</th><th>8.MPOS</th><th>9.ISIZE</th><th>10.SEQ</th><th>11.QUAL</th><th>12.OPT</th></tr>','<tr><td colspan="100%">@SQ SN:gi|87159884|ref|NC_007793.1| LN:2872769</td></tr>','<tr><td colspan="100%">@PG ID:bwa PN:bwa VN:0.5.9-r16</td></tr>','<tr><td colspan="100%">HWUSI-EAS664L:15:64HOJAAXX:1:1:13280:968 73 gi|87159884|ref|NC_007793.1| 2720169 37 101M = 2720169 0 NAATATGACATTATTTTCAAAACAGCTGAAAATTTAGACGTACCGATTTATCTACATCCCGCGCCAGTTAACAGTGACATTTATCAATCATACTATAAAGG !!!!!!!!!!$!!!$!!!!!$!!!!!!$!$!$$$!!$!!$!!!!!!!!!!!$!</td></tr>','<tr><td colspan="100%">!!!$!$!$$!!$$!!$!!!!!!!!!!!!!!!!!!!!!!!!!!$!!$!! XT:A:U NM:i:1 SM:i:37 AM:i:0 X0:i:1 X1:i:0 XM:i:1 XO:i:0 XG:i:0 MD:Z:0A100</td></tr>','<tr><td colspan="100%">HWUSI-EAS664L:15:64HOJAAXX:1:1:13280:968 133 gi|87159884|ref|NC_007793.1| 2720169 0 * = 2720169 0 NAAACTGTGGCTTCGTTNNNNNNNNNNNNNNNGTGANNNNNNNNNNNNNNNNNNNGNNNNNNNNNNNNNNNNNNNNCNAANNNNNNNNNNNNNNNNNNNNN !!!!!!!!!!!!$!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</td></tr>','<tr><td colspan="100%">!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</td></tr>',"</table>"].join("")}};_.extend(mockHistory.data,{notAccessible:_.extend(_.clone(mockHistory.data.template),{accessible:false}),deleted:_.extend(_.clone(mockHistory.data.template),{deleted:true,delete_url:"",purge_url:"example.com/purge",undelete_url:"example.com/undelete"}),purgedNotDeleted:_.extend(_.clone(mockHistory.data.template),{purged:true,delete_url:""}),notvisible:_.extend(_.clone(mockHistory.data.template),{visible:false,unhide_url:"example.com/unhide"}),hasDisplayApps:_.extend(_.clone(mockHistory.data.template),{display_apps:{"display in IGB":{Web:"/display_application/63cd3858d057a6d1/igb_bam/Web",Local:"/display_application/63cd3858d057a6d1/igb_bam/Local"}}}),canTrackster:_.extend(_.clone(mockHistory.data.template),{trackster_urls:{"data-url":"example.com/trackster-data","action-url":"example.com/trackster-action","new-url":"example.com/trackster-new"}}),zeroSize:_.extend(_.clone(mockHistory.data.template),{file_size:0}),hasMetafiles:_.extend(_.clone(mockHistory.data.template),{download_meta_urls:{bam_index:"example.com/bam-index"}}),upload:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.UPLOAD}),queued:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.QUEUED}),running:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.RUNNING}),empty:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.EMPTY}),error:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.ERROR,report_error_url:"example.com/report_err"}),discarded:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.DISCARDED}),setting_metadata:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.SETTING_METADATA}),failed_metadata:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.FAILED_METADATA})});$(document).ready(function(){mockHistory.items={};mockHistory.views={};for(key in mockHistory.data){mockHistory.items[key]=new HistoryItem(mockHistory.data[key]);mockHistory.items[key].set("name",key);mockHistory.views[key]=new HistoryItemView({model:mockHistory.items[key]});$("body").append(mockHistory.views[key].render())}})};
\ No newline at end of file
+function linkHTMLTemplate(b,a){if(!b){return"<a></a>"}a=a||"a";var c=["<"+a];for(key in b){var d=b[key];if(d===""){continue}switch(key){case"text":continue;case"classes":key="class";d=(b.classes.join)?(b.classes.join(" ")):(b.classes);default:c.push([" ",key,'="',d,'"'].join(""))}}c.push(">");if("text" in b){c.push(b.text)}c.push("</"+a+">");return c.join("")}var HistoryItem=BaseModel.extend(LoggableMixin).extend({defaults:{id:null,name:"",data_type:null,file_size:0,genome_build:null,metadata_data_lines:0,metadata_dbkey:null,metadata_sequences:0,misc_blurb:"",misc_info:"",model_class:"",state:"",deleted:false,purged:false,visible:true,for_editing:true,bodyIsShown:false},initialize:function(){this.log(this+".initialize",this.attributes);this.log("\tparent history_id: "+this.get("history_id"));if(!this.get("accessible")){this.set("state",HistoryItem.STATES.NOT_VIEWABLE)}},isEditable:function(){return(!(this.get("deleted")||this.get("purged")))},hasData:function(){return(this.get("file_size")>0)},toString:function(){var a=this.get("id")||"";if(this.get("name")){a+=':"'+this.get("name")+'"'}return"HistoryItem("+a+")"}});HistoryItem.STATES={NOT_VIEWABLE:"not_viewable",NEW:"new",UPLOAD:"upload",QUEUED:"queued",RUNNING:"running",OK:"ok",EMPTY:"empty",ERROR:"error",DISCARDED:"discarded",SETTING_METADATA:"setting_metadata",FAILED_METADATA:"failed_metadata"};var HistoryItemView=BaseView.extend(LoggableMixin).extend({tagName:"div",className:"historyItemContainer",initialize:function(){this.log(this+".initialize:",this,this.model)},render:function(){var c=this.model.get("id"),b=this.model.get("state");this.clearReferences();this.$el.attr("id","historyItemContainer-"+c);var a=$("<div/>").attr("id","historyItem-"+c).addClass("historyItemWrapper").addClass("historyItem").addClass("historyItem-"+b);a.append(this._render_warnings());a.append(this._render_titleBar());this.body=$(this._render_body());a.append(this.body);a.find(".tooltip").tooltip({placement:"bottom"});make_popup_menus(a);this.$el.children().remove();return this.$el.append(a)},clearReferences:function(){this.displayButton=null;this.editButton=null;this.deleteButton=null;this.errButton=null},_render_warnings:function(){return $(jQuery.trim(HistoryItemView.templates.messages(this.model.toJSON())))},_render_titleBar:function(){var a=$('<div class="historyItemTitleBar" style="overflow: hidden"></div>');a.append(this._render_titleButtons());a.append('<span class="state-icon"></span>');a.append(this._render_titleLink());return a},_render_titleButtons:function(){var a=$('<div class="historyItemButtons"></div>');a.append(this._render_displayButton());a.append(this._render_editButton());a.append(this._render_deleteButton());return a},_render_displayButton:function(){if(this.model.get("state")===HistoryItem.STATES.UPLOAD){return null}displayBtnData=(this.model.get("purged"))?({title:"Cannot display datasets removed from disk",enabled:false,icon_class:"display"}):({title:"Display data in browser",href:this.model.get("display_url"),target:(this.model.get("for_editing"))?("galaxy_main"):(null),icon_class:"display"});this.displayButton=new IconButtonView({model:new IconButton(displayBtnData)});return this.displayButton.render().$el},_render_editButton:function(){if((this.model.get("state")===HistoryItem.STATES.UPLOAD)||(!this.model.get("for_editing"))){return null}var c=this.model.get("purged"),a=this.model.get("deleted"),b={title:"Edit attributes",href:this.model.get("edit_url"),target:"galaxy_main",icon_class:"edit"};if(a||c){b.enabled=false}if(a){b.title="Undelete dataset to edit attributes"}else{if(c){b.title="Cannot edit attributes of datasets removed from disk"}}this.editButton=new IconButtonView({model:new IconButton(b)});return this.editButton.render().$el},_render_deleteButton:function(){if(!this.model.get("for_editing")){return null}var a={title:"Delete",href:this.model.get("delete_url"),target:"galaxy_main",id:"historyItemDeleter-"+this.model.get("id"),icon_class:"delete"};if((this.model.get("deleted")||this.model.get("purged"))&&(!this.model.get("delete_url"))){a={title:"Dataset is already deleted",icon_class:"delete",enabled:false}}this.deleteButton=new IconButtonView({model:new IconButton(a)});return this.deleteButton.render().$el},_render_titleLink:function(){return $(jQuery.trim(HistoryItemView.templates.titleLink(this.model.toJSON())))},_render_hdaSummary:function(){var a=this.model.toJSON();if(this.model.get("metadata_dbkey")==="?"&&this.model.isEditable()){_.extend(a,{dbkey_unknown_and_editable:true})}return HistoryItemView.templates.hdaSummary(a)},_render_primaryActionButtons:function(c){var b=$("<div/>").attr("id","primary-actions-"+this.model.get("id")),a=this;_.each(c,function(d){var e=d.call(a);b.append(e)});return b},_render_downloadButton:function(){if(this.model.get("purged")){return null}var a=HistoryItemView.templates.downloadLinks(this.model.toJSON());this.log("_render_downloadButton, downloadLinkHTML:",a);return $(a)},_render_errButton:function(){if((this.model.get("state")!==HistoryItem.STATES.ERROR)||(!this.model.get("for_editing"))){return null}this.errButton=new IconButtonView({model:new IconButton({title:"View or report this error",href:this.model.get("report_error_url"),target:"galaxy_main",icon_class:"bug"})});return this.errButton.render().$el},_render_showParamsButton:function(){this.showParamsButton=new IconButtonView({model:new IconButton({title:"View details",href:this.model.get("show_params_url"),target:"galaxy_main",icon_class:"information"})});return this.showParamsButton.render().$el},_render_rerunButton:function(){if(!this.model.get("for_editing")){return null}this.rerunButton=new IconButtonView({model:new IconButton({title:"Run this job again",href:this.model.get("rerun_url"),target:"galaxy_main",icon_class:"arrow-circle"})});return this.rerunButton.render().$el},_render_tracksterButton:function(){var a=this.model.get("trackster_urls");if(!(this.model.hasData())||!(this.model.get("for_editing"))||!(a)){return null}this.tracksterButton=new IconButtonView({model:new IconButton({title:"View in Trackster",icon_class:"chart_curve"})});this.errButton.render();this.errButton.$el.addClass("trackster-add").attr({"data-url":a["data-url"],"action-url":a["action-url"],"new-url":a["new-url"]});return this.errButton.$el},_render_secondaryActionButtons:function(b){var c=$("<div/>"),a=this;c.attr("style","float: right;").attr("id","secondary-actions-"+this.model.get("id"));_.each(b,function(d){c.append(d.call(a))});return c},_render_tagButton:function(){if(!(this.model.hasData())||!(this.model.get("for_editing"))||(!this.model.get("retag_url"))){return null}this.tagButton=new IconButtonView({model:new IconButton({title:"Edit dataset tags",target:"galaxy_main",href:this.model.get("retag_url"),icon_class:"tags"})});return this.tagButton.render().$el},_render_annotateButton:function(){if(!(this.model.hasData())||!(this.model.get("for_editing"))||(!this.model.get("annotate_url"))){return null}this.annotateButton=new IconButtonView({model:new IconButton({title:"Edit dataset annotation",target:"galaxy_main",href:this.model.get("annotate_url"),icon_class:"annotate"})});return this.annotateButton.render().$el},_render_tagArea:function(){if(this.model.get("retag_url")){return null}return $(HistoryItemView.templates.tagArea(this.model.toJSON()))},_render_annotationArea:function(){if(!this.model.get("annotate_url")){return null}return $(HistoryItemView.templates.annotationArea(this.model.toJSON()))},_render_displayApps:function(){if(!this.model.hasData()){return null}var a=$("<div/>").addClass("display-apps");if(!_.isEmpty(this.model.get("display_types"))){this.log(this+"display_types:",this.model.get("display_types"));a.append(HistoryItemView.templates.displayApps({displayApps:this.model.toJSON().display_types}))}if(!_.isEmpty(this.model.get("display_apps"))){this.log(this+"display_apps:",this.model.get("display_apps"));a.append(HistoryItemView.templates.displayApps({displayApps:this.model.toJSON().display_apps}))}return a},_render_peek:function(){if(!this.model.get("peek")){return null}return $("<div/>").append($("<pre/>").attr("id","peek"+this.model.get("id")).addClass("peek").append(this.model.get("peek")))},_render_body_not_viewable:function(a){a.append($("<div>You do not have permission to view dataset.</div>"))},_render_body_uploading:function(a){a.append($("<div>Dataset is uploading</div>"))},_render_body_queued:function(a){a.append($("<div>Job is waiting to run.</div>"));a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_running:function(a){a.append("<div>Job is currently running.</div>");a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_error:function(a){if(!this.model.get("purged")){a.append($("<div>"+this.model.get("misc_blurb")+"</div>"))}a.append(("An error occurred running this job: <i>"+$.trim(this.model.get("misc_info"))+"</i>"));a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_errButton,this._render_showParamsButton,this._render_rerunButton]))},_render_body_discarded:function(a){a.append("<div>The job creating this dataset was cancelled before completion.</div>");a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_setting_metadata:function(a){a.append($("<div>Metadata is being auto-detected.</div>"))},_render_body_empty:function(a){a.append($("<div>No data: <i>"+this.model.get("misc_blurb")+"</i></div>"));a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_failed_metadata:function(a){a.append($(HistoryItemView.templates.failedMetadata(this.model.toJSON())));this._render_body_ok(a)},_render_body_ok:function(a){a.append(this._render_hdaSummary());a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_errButton,this._render_showParamsButton,this._render_rerunButton]));a.append(this._render_secondaryActionButtons([this._render_tagButton,this._render_annotateButton]));a.append('<div class="clear"/>');a.append(this._render_tagArea());a.append(this._render_annotationArea());a.append(this._render_displayApps());a.append(this._render_peek())},_render_body:function(){var b=this.model.get("state");var a=$("<div/>").attr("id","info-"+this.model.get("id")).addClass("historyItemBody").attr("style","display: block");switch(b){case HistoryItem.STATES.NOT_VIEWABLE:this._render_body_not_viewable(a);break;case HistoryItem.STATES.UPLOAD:this._render_body_uploading(a);break;case HistoryItem.STATES.QUEUED:this._render_body_queued(a);break;case HistoryItem.STATES.RUNNING:this._render_body_running(a);break;case HistoryItem.STATES.ERROR:this._render_body_error(a);break;case HistoryItem.STATES.DISCARDED:this._render_body_discarded(a);break;case HistoryItem.STATES.SETTING_METADATA:this._render_body_setting_metadata(a);break;case HistoryItem.STATES.EMPTY:this._render_body_empty(a);break;case HistoryItem.STATES.FAILED_METADATA:this._render_body_failed_metadata(a);break;case HistoryItem.STATES.OK:this._render_body_ok(a);break;default:a.append($('<div>Error: unknown dataset state "'+b+'".</div>'))}a.append('<div style="clear: both"></div>');if(this.model.get("bodyIsShown")===false){a.hide()}return a},events:{"click .historyItemTitle":"toggleBodyVisibility","click a.icon-button.tags":"loadAndDisplayTags","click a.icon-button.annotate":"loadAndDisplayAnnotation"},loadAndDisplayTags:function(b){this.log(this+".loadAndDisplayTags",b);var c=this.$el.find(".tag-area"),a=c.find(".tag-elt");if(c.is(":hidden")){if(!a.html()){$.ajax({url:this.model.get("ajax_get_tag_url"),error:function(){alert("Tagging failed")},success:function(d){a.html(d);a.find(".tooltip").tooltip();c.slideDown("fast")}})}else{c.slideDown("fast")}}else{c.slideUp("fast")}return false},loadAndDisplayAnnotation:function(b){this.log(this+".loadAndDisplayAnnotation",b);var d=this.$el.find(".annotation-area"),c=d.find(".annotation-elt"),a=this.model.get("ajax_set_annotation_url");if(d.is(":hidden")){if(!c.html()){$.ajax({url:this.model.get("ajax_get_annotation_url"),error:function(){alert("Annotations failed")},success:function(e){if(e===""){e="<em>Describe or add notes to dataset</em>"}c.html(e);d.find(".tooltip").tooltip();async_save_text(c.attr("id"),c.attr("id"),a,"new_annotation",18,true,4);d.slideDown("fast")}})}else{d.slideDown("fast")}}else{d.slideUp("fast")}return false},toggleBodyVisibility:function(){this.log(this+".toggleBodyVisibility");this.$el.find(".historyItemBody").toggle()},toString:function(){var a=(this.model)?(this.model+""):("");return"HistoryItemView("+a+")"}});HistoryItemView.templates=CompiledTemplateLoader.getTemplates({"common-templates.html":{warningMsg:"template-warningmessagesmall"},"history-templates.html":{messages:"template-history-warning-messages",titleLink:"template-history-titleLink",hdaSummary:"template-history-hdaSummary",downloadLinks:"template-history-downloadLinks",failedMetadata:"template-history-failedMetaData",tagArea:"template-history-tagArea",annotationArea:"template-history-annotationArea",displayApps:"template-history-displayApps"}});var HistoryCollection=Backbone.Collection.extend({model:HistoryItem,toString:function(){return("HistoryCollection()")}});var History=BaseModel.extend(LoggableMixin).extend({defaults:{id:"",name:"",state:"",state_details:{discarded:0,empty:0,error:0,failed_metadata:0,ok:0,queued:0,running:0,setting_metadata:0,upload:0}},initialize:function(b,a){this.log(this+".initialize",b,a);this.items=new HistoryCollection()},loadDatasetsAsHistoryItems:function(c){var a=this,b=this.get("id"),d=this.get("state_details");_.each(c,function(f,e){a.log("loading dataset: ",f,e);var h=new HistoryItem(_.extend(f,{history_id:b}));a.log("as History:",h);a.items.add(h);var g=f.state;d[g]+=1});this.set("state_details",d);this._stateFromStateDetails();return this},_stateFromStateDetails:function(){this.set("state","");var a=this.get("state_details");if((a.error>0)||(a.failed_metadata>0)){this.set("state",HistoryItem.STATES.ERROR)}else{if((a.running>0)||(a.setting_metadata>0)){this.set("state",HistoryItem.STATES.RUNNING)}else{if(a.queued>0){this.set("state",HistoryItem.STATES.QUEUED)}else{if(a.ok===this.items.length){this.set("state",HistoryItem.STATES.OK)}else{throw ("_stateFromStateDetails: unable to determine history state from state details: "+this.state_details)}}}}return this},toString:function(){var a=(this.get("name"))?(","+this.get("name")):("");return"History("+this.get("id")+a+")"}});var HistoryView=BaseView.extend(LoggableMixin).extend({el:"body.historyPage",initialize:function(){this.log(this+".initialize");this.itemViews=[];var a=this;this.model.items.each(function(c){var b=new HistoryItemView({model:c});a.itemViews.push(b)})},render:function(){this.log(this+".render");var a=$("<div/>");_.each(this.itemViews,function(b){a.prepend(b.render())});this.$el.append(a.children());a.remove()},toString:function(){var a=this.model.get("name")||"";return"HistoryView("+a+")"}});
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/packed/mvc/tools.js
--- a/static/scripts/packed/mvc/tools.js
+++ b/static/scripts/packed/mvc/tools.js
@@ -1,1 +1,1 @@
-var ServerStateDeferred=Backbone.Model.extend({defaults:{ajax_settings:{},interval:1000,success_fn:function(a){return true}},go:function(){var d=$.Deferred(),c=this,f=c.get("ajax_settings"),e=c.get("success_fn"),b=c.get("interval"),a=function(){$.ajax(f).success(function(g){if(e(g)){d.resolve(g)}else{setTimeout(a,b)}})};a();return d}});var BaseModel=Backbone.RelationalModel.extend({defaults:{name:null,hidden:false},show:function(){this.set("hidden",false)},hide:function(){this.set("hidden",true)},is_visible:function(){return !this.attributes.hidden}});var Tool=BaseModel.extend({defaults:{description:null,target:null,inputs:[]},relations:[{type:Backbone.HasMany,key:"inputs",relatedModel:"ToolInput",reverseRelation:{key:"tool",includeInJSON:false}}],urlRoot:galaxy_paths.get("tool_url"),copy:function(b){var c=new Tool(this.toJSON());if(b){var a=new Backbone.Collection();c.get("inputs").each(function(d){if(d.get_samples()){a.push(d)}});c.set("inputs",a)}return c},apply_search_results:function(a){(_.indexOf(a,this.attributes.id)!==-1?this.show():this.hide());return this.is_visible()},set_input_value:function(a,b){this.get("inputs").find(function(c){return c.get("name")===a}).set("value",b)},set_input_values:function(b){var a=this;_.each(_.keys(b),function(c){a.set_input_value(c,b[c])})},run:function(){return this._run()},rerun:function(b,a){return this._run({action:"rerun",target_dataset_id:b.id,regions:a})},get_inputs_dict:function(){var a={};this.get("inputs").each(function(b){a[b.get("name")]=b.get("value")});return a},_run:function(c){var d=_.extend({tool_id:this.id,inputs:this.get_inputs_dict()},c);var b=$.Deferred(),a=new ServerStateDeferred({ajax_settings:{url:this.urlRoot,data:JSON.stringify(d),dataType:"json",contentType:"application/json",type:"POST"},interval:2000,success_fn:function(e){return e!=="pending"}});$.when(a.go()).then(function(e){b.resolve(new DatasetCollection().reset(e))});return b}});var ToolInput=Backbone.RelationalModel.extend({defaults:{name:null,label:null,type:null,value:null,num_samples:5},initialize:function(){this.attributes.html=unescape(this.attributes.html)},copy:function(){return new ToolInput(this.toJSON())},get_samples:function(){var b=this.get("type"),a=null;if(b==="number"){a=d3.scale.linear().domain([this.get("min"),this.get("max")]).ticks(this.get("num_samples"))}else{if(b==="select"){a=_.map(this.get("options"),function(c){return c[0]})}}return a}});var ToolCollection=Backbone.Collection.extend({model:Tool});var ToolPanelLabel=BaseModel.extend({});var ToolPanelSection=BaseModel.extend({defaults:{elems:[],open:false},clear_search_results:function(){_.each(this.attributes.elems,function(a){a.show()});this.show();this.set("open",false)},apply_search_results:function(b){var c=true,a;_.each(this.attributes.elems,function(d){if(d instanceof ToolPanelLabel){a=d;a.hide()}else{if(d instanceof Tool){if(d.apply_search_results(b)){c=false;if(a){a.show()}}}}});if(c){this.hide()}else{this.show();this.set("open",true)}}});var ToolSearch=BaseModel.extend({defaults:{search_hint_string:"search tools",min_chars_for_search:3,spinner_url:"",clear_btn_url:"",search_url:"",visible:true,query:"",results:null,clear_key:27},initialize:function(){this.on("change:query",this.do_search)},do_search:function(){var c=this.attributes.query;if(c.length<this.attributes.min_chars_for_search){this.set("results",null);return}var b=c+"*";if(this.timer){clearTimeout(this.timer)}$("#search-clear-btn").hide();$("#search-spinner").show();var a=this;this.timer=setTimeout(function(){$.get(a.attributes.search_url,{query:b},function(d){a.set("results",d);$("#search-spinner").hide();$("#search-clear-btn").show()},"json")},200)},clear_search:function(){this.set("query","");this.set("results",null)}});var ToolPanel=Backbone.Collection.extend({url:"/tools",tools:new ToolCollection(),parse:function(a){var b=function(e){var d=e.type;if(d==="tool"){return new Tool(e)}else{if(d==="section"){var c=_.map(e.elems,b);e.elems=c;return new ToolPanelSection(e)}else{if(d==="label"){return new ToolPanelLabel(e)}}}};return _.map(a,b)},initialize:function(a){this.tool_search=a.tool_search;this.tool_search.on("change:results",this.apply_search_results,this);this.on("reset",this.populate_tools,this)},populate_tools:function(){var a=this;a.tools=new ToolCollection();this.each(function(b){if(b instanceof ToolPanelSection){_.each(b.attributes.elems,function(c){if(c instanceof Tool){a.tools.push(c)}})}else{if(b instanceof Tool){a.tools.push(b)}}})},clear_search_results:function(){this.each(function(a){if(a instanceof ToolPanelSection){a.clear_search_results()}else{a.show()}})},apply_search_results:function(){var b=this.tool_search.attributes.results;if(b===null){this.clear_search_results();return}var a=null;this.each(function(c){if(c instanceof ToolPanelLabel){a=c;a.hide()}else{if(c instanceof Tool){if(c.apply_search_results(b)){if(a){a.show()}}}else{a=null;c.apply_search_results(b)}}})}});var BaseView=Backbone.View.extend({initialize:function(){this.model.on("change:hidden",this.update_visible,this);this.update_visible()},update_visible:function(){(this.model.attributes.hidden?this.$el.hide():this.$el.show())}});var ToolLinkView=BaseView.extend({tagName:"div",template:Handlebars.templates.tool_link,render:function(){this.$el.append(this.template(this.model.toJSON()));return this}});var ToolPanelLabelView=BaseView.extend({tagName:"div",className:"toolPanelLabel",render:function(){this.$el.append($("<span/>").text(this.model.attributes.name));return this}});var ToolPanelSectionView=BaseView.extend({tagName:"div",className:"toolSectionWrapper",template:Handlebars.templates.panel_section,initialize:function(){BaseView.prototype.initialize.call(this);this.model.on("change:open",this.update_open,this)},render:function(){this.$el.append(this.template(this.model.toJSON()));var a=this.$el.find(".toolSectionBody");_.each(this.model.attributes.elems,function(b){if(b instanceof Tool){var c=new ToolLinkView({model:b,className:"toolTitle"});c.render();a.append(c.$el)}else{if(b instanceof ToolPanelLabel){var d=new ToolPanelLabelView({model:b});d.render();a.append(d.$el)}else{}}});return this},events:{"click .toolSectionTitle > a":"toggle"},toggle:function(){this.model.set("open",!this.model.attributes.open)},update_open:function(){(this.model.attributes.open?this.$el.children(".toolSectionBody").slideDown("fast"):this.$el.children(".toolSectionBody").slideUp("fast"))}});var ToolSearchView=Backbone.View.extend({tagName:"div",id:"tool-search",className:"bar",template:Handlebars.templates.tool_search,events:{click:"focus_and_select","keyup :input":"query_changed","click #search-clear-btn":"clear"},render:function(){this.$el.append(this.template(this.model.toJSON()));if(!this.model.is_visible()){this.$el.hide()}return this},focus_and_select:function(){this.$el.find(":input").focus().select()},clear:function(){this.model.clear_search();this.$el.find(":input").val(this.model.attributes.search_hint_string);this.focus_and_select();return false},query_changed:function(a){if((this.model.attributes.clear_key)&&(this.model.attributes.clear_key===a.which)){this.clear();return false}this.model.set("query",this.$el.find(":input").val())}});var ToolPanelView=Backbone.View.extend({tagName:"div",className:"toolMenu",initialize:function(){this.collection.tool_search.on("change:results",this.handle_search_results,this)},render:function(){var a=this;var b=new ToolSearchView({model:this.collection.tool_search});b.render();a.$el.append(b.$el);this.collection.each(function(d){if(d instanceof ToolPanelSection){var c=new ToolPanelSectionView({model:d});c.render();a.$el.append(c.$el)}else{if(d instanceof Tool){var e=new ToolLinkView({model:d,className:"toolTitleNoSection"});e.render();a.$el.append(e.$el)}else{if(d instanceof ToolPanelLabel){var f=new ToolPanelLabelView({model:d});f.render();a.$el.append(f.$el)}}}});a.$el.find("a.tool-link").click(function(f){var d=$(this).attr("class").split(/\s+/)[0],c=a.collection.tools.get(d);a.trigger("tool_link_click",f,c)});return this},handle_search_results:function(){var a=this.collection.tool_search.attributes.results;if(a&&a.length===0){$("#search-no-results").show()}else{$("#search-no-results").hide()}}});var ToolFormView=Backbone.View.extend({className:"toolForm",template:Handlebars.templates.tool_form,render:function(){this.$el.children().remove();this.$el.append(this.template(this.model.toJSON()))}});var IntegratedToolMenuAndView=Backbone.View.extend({className:"toolMenuAndView",initialize:function(){this.tool_panel_view=new ToolPanelView({collection:this.collection});this.tool_form_view=new ToolFormView()},render:function(){this.tool_panel_view.render();this.tool_panel_view.$el.css("float","left");this.$el.append(this.tool_panel_view.$el);this.tool_form_view.$el.hide();this.$el.append(this.tool_form_view.$el);var a=this;this.tool_panel_view.on("tool_link_click",function(c,b){c.preventDefault();a.show_tool(b)})},show_tool:function(b){var a=this;b.fetch().done(function(){a.tool_form_view.model=b;a.tool_form_view.render();a.tool_form_view.$el.show();$("#left").width("650px")})}});
\ No newline at end of file
+define(["libs/underscore","viz/trackster/util","mvc/data","libs/backbone/backbone-relational"],function(q,a,r){var f=Backbone.RelationalModel.extend({defaults:{name:null,hidden:false},show:function(){this.set("hidden",false)},hide:function(){this.set("hidden",true)},is_visible:function(){return !this.attributes.hidden}});var k=Backbone.RelationalModel.extend({defaults:{name:null,label:null,type:null,value:null,num_samples:5},initialize:function(){this.attributes.html=unescape(this.attributes.html)},copy:function(){return new k(this.toJSON())},get_samples:function(){var u=this.get("type"),t=null;if(u==="number"){t=d3.scale.linear().domain([this.get("min"),this.get("max")]).ticks(this.get("num_samples"))}else{if(u==="select"){t=q.map(this.get("options"),function(v){return v[0]})}}return t}});var e=f.extend({defaults:{description:null,target:null,inputs:[]},relations:[{type:Backbone.HasMany,key:"inputs",relatedModel:k,reverseRelation:{key:"tool",includeInJSON:false}}],urlRoot:galaxy_paths.get("tool_url"),copy:function(u){var v=new e(this.toJSON());if(u){var t=new Backbone.Collection();v.get("inputs").each(function(w){if(w.get_samples()){t.push(w)}});v.set("inputs",t)}return v},apply_search_results:function(t){(q.indexOf(t,this.attributes.id)!==-1?this.show():this.hide());return this.is_visible()},set_input_value:function(t,u){this.get("inputs").find(function(v){return v.get("name")===t}).set("value",u)},set_input_values:function(u){var t=this;q.each(q.keys(u),function(v){t.set_input_value(v,u[v])})},run:function(){return this._run()},rerun:function(u,t){return this._run({action:"rerun",target_dataset_id:u.id,regions:t})},get_inputs_dict:function(){var t={};this.get("inputs").each(function(u){t[u.get("name")]=u.get("value")});return t},_run:function(v){var w=q.extend({tool_id:this.id,inputs:this.get_inputs_dict()},v);var u=$.Deferred(),t=new a.ServerStateDeferred({ajax_settings:{url:this.urlRoot,data:JSON.stringify(w),dataType:"json",contentType:"application/json",type:"POST"},interval:2000,success_fn:function(x){return x!=="pending"}});$.when(t.go()).then(function(x){u.resolve(new r.DatasetCollection().reset(x))});return u}});var i=Backbone.Collection.extend({model:e});var m=f.extend({});var p=f.extend({defaults:{elems:[],open:false},clear_search_results:function(){q.each(this.attributes.elems,function(t){t.show()});this.show();this.set("open",false)},apply_search_results:function(u){var v=true,t;q.each(this.attributes.elems,function(w){if(w instanceof m){t=w;t.hide()}else{if(w instanceof e){if(w.apply_search_results(u)){v=false;if(t){t.show()}}}}});if(v){this.hide()}else{this.show();this.set("open",true)}}});var b=f.extend({defaults:{search_hint_string:"search tools",min_chars_for_search:3,spinner_url:"",clear_btn_url:"",search_url:"",visible:true,query:"",results:null,clear_key:27},initialize:function(){this.on("change:query",this.do_search)},do_search:function(){var v=this.attributes.query;if(v.length<this.attributes.min_chars_for_search){this.set("results",null);return}var u=v+"*";if(this.timer){clearTimeout(this.timer)}$("#search-clear-btn").hide();$("#search-spinner").show();var t=this;this.timer=setTimeout(function(){$.get(t.attributes.search_url,{query:u},function(w){t.set("results",w);$("#search-spinner").hide();$("#search-clear-btn").show()},"json")},200)},clear_search:function(){this.set("query","");this.set("results",null)}});var j=Backbone.Collection.extend({url:"/tools",tools:new i(),parse:function(t){var u=function(x){var w=x.type;if(w==="tool"){return new e(x)}else{if(w==="section"){var v=q.map(x.elems,u);x.elems=v;return new p(x)}else{if(w==="label"){return new m(x)}}}};return q.map(t,u)},initialize:function(t){this.tool_search=t.tool_search;this.tool_search.on("change:results",this.apply_search_results,this);this.on("reset",this.populate_tools,this)},populate_tools:function(){var t=this;t.tools=new i();this.each(function(u){if(u instanceof p){q.each(u.attributes.elems,function(v){if(v instanceof e){t.tools.push(v)}})}else{if(u instanceof e){t.tools.push(u)}}})},clear_search_results:function(){this.each(function(t){if(t instanceof p){t.clear_search_results()}else{t.show()}})},apply_search_results:function(){var u=this.tool_search.attributes.results;if(u===null){this.clear_search_results();return}var t=null;this.each(function(v){if(v instanceof m){t=v;t.hide()}else{if(v instanceof e){if(v.apply_search_results(u)){if(t){t.show()}}}else{t=null;v.apply_search_results(u)}}})}});var n=Backbone.View.extend({initialize:function(){this.model.on("change:hidden",this.update_visible,this);this.update_visible()},update_visible:function(){(this.model.attributes.hidden?this.$el.hide():this.$el.show())}});var h=n.extend({tagName:"div",template:Handlebars.templates.tool_link,render:function(){this.$el.append(this.template(this.model.toJSON()));return this}});var c=n.extend({tagName:"div",className:"toolPanelLabel",render:function(){this.$el.append($("<span/>").text(this.model.attributes.name));return this}});var g=n.extend({tagName:"div",className:"toolSectionWrapper",template:Handlebars.templates.panel_section,initialize:function(){n.prototype.initialize.call(this);this.model.on("change:open",this.update_open,this)},render:function(){this.$el.append(this.template(this.model.toJSON()));var t=this.$el.find(".toolSectionBody");q.each(this.model.attributes.elems,function(u){if(u instanceof e){var v=new h({model:u,className:"toolTitle"});v.render();t.append(v.$el)}else{if(u instanceof m){var w=new c({model:u});w.render();t.append(w.$el)}else{}}});return this},events:{"click .toolSectionTitle > a":"toggle"},toggle:function(){this.model.set("open",!this.model.attributes.open)},update_open:function(){(this.model.attributes.open?this.$el.children(".toolSectionBody").slideDown("fast"):this.$el.children(".toolSectionBody").slideUp("fast"))}});var l=Backbone.View.extend({tagName:"div",id:"tool-search",className:"bar",template:Handlebars.templates.tool_search,events:{click:"focus_and_select","keyup :input":"query_changed","click #search-clear-btn":"clear"},render:function(){this.$el.append(this.template(this.model.toJSON()));if(!this.model.is_visible()){this.$el.hide()}return this},focus_and_select:function(){this.$el.find(":input").focus().select()},clear:function(){this.model.clear_search();this.$el.find(":input").val(this.model.attributes.search_hint_string);this.focus_and_select();return false},query_changed:function(t){if((this.model.attributes.clear_key)&&(this.model.attributes.clear_key===t.which)){this.clear();return false}this.model.set("query",this.$el.find(":input").val())}});var s=Backbone.View.extend({tagName:"div",className:"toolMenu",initialize:function(){this.collection.tool_search.on("change:results",this.handle_search_results,this)},render:function(){var t=this;var u=new l({model:this.collection.tool_search});u.render();t.$el.append(u.$el);this.collection.each(function(w){if(w instanceof p){var v=new g({model:w});v.render();t.$el.append(v.$el)}else{if(w instanceof e){var x=new h({model:w,className:"toolTitleNoSection"});x.render();t.$el.append(x.$el)}else{if(w instanceof m){var y=new c({model:w});y.render();t.$el.append(y.$el)}}}});t.$el.find("a.tool-link").click(function(x){var w=$(this).attr("class").split(/\s+/)[0],v=t.collection.tools.get(w);t.trigger("tool_link_click",x,v)});return this},handle_search_results:function(){var t=this.collection.tool_search.attributes.results;if(t&&t.length===0){$("#search-no-results").show()}else{$("#search-no-results").hide()}}});var o=Backbone.View.extend({className:"toolForm",template:Handlebars.templates.tool_form,render:function(){this.$el.children().remove();this.$el.append(this.template(this.model.toJSON()))}});var d=Backbone.View.extend({className:"toolMenuAndView",initialize:function(){this.tool_panel_view=new s({collection:this.collection});this.tool_form_view=new o()},render:function(){this.tool_panel_view.render();this.tool_panel_view.$el.css("float","left");this.$el.append(this.tool_panel_view.$el);this.tool_form_view.$el.hide();this.$el.append(this.tool_form_view.$el);var t=this;this.tool_panel_view.on("tool_link_click",function(v,u){v.preventDefault();t.show_tool(u)})},show_tool:function(u){var t=this;u.fetch().done(function(){t.tool_form_view.model=u;t.tool_form_view.render();t.tool_form_view.$el.show();$("#left").width("650px")})}});return{Tool:e,ToolSearch:b,ToolPanel:j,ToolPanelView:s,ToolFormView:o}});
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/packed/templates/compiled/helpers-common-templates.js
--- a/static/scripts/packed/templates/compiled/helpers-common-templates.js
+++ b/static/scripts/packed/templates/compiled/helpers-common-templates.js
@@ -1,1 +1,1 @@
-Handlebars.registerPartial("clearFloatDiv",function(a){return'<div class="clear"></div>'});Handlebars.registerPartial("iconButton",function(c,b){var a="";a+=(c.enabled)?("<a"):("<span");if(c.title){a+=' title="'+c.title+'"'}a+=' class="icon-button';if(c.isMenuButton){a+=" menu-button"}if(c.title){a+=" tooltip"}a+=" "+c.icon_class;if(!c.enabled){a+="_disabled"}a+='"';if(c.id){a+=' id="'+c.id+'"'}a+=' href="'+((c.href)?(c.href):("javascript:void(0);"))+'"';if(c.target){a+=' target="'+c.target+'"'}if(!c.visible){a+=' style="display: none;"'}a+=">"+((c.enabled)?("</a>"):("</span>"));return a});Handlebars.registerHelper("warningmessagesmall",function(a){return'<div class="warningmessagesmall"><strong>'+a.fn(this)+"</strong></div>"});
\ No newline at end of file
+Handlebars.registerPartial("clearFloatDiv",function(a){return'<div class="clear"></div>'});Handlebars.registerHelper("warningmessagesmall",function(a){return'<div class="warningmessagesmall"><strong>'+a.fn(this)+"</strong></div>"});Handlebars.registerPartial("iconButton",function(c,b){var a="";a+=(c.enabled)?("<a"):("<span");if(c.title){a+=' title="'+c.title+'"'}a+=' class="icon-button';if(c.isMenuButton){a+=" menu-button"}if(c.title){a+=" tooltip"}a+=" "+c.icon_class;if(!c.enabled){a+="_disabled"}a+='"';if(c.id){a+=' id="'+c.id+'"'}a+=' href="'+((c.href)?(c.href):("javascript:void(0);"))+'"';if(c.target){a+=' target="'+c.target+'"'}if(!c.visible){a+=' style="display: none;"'}a+=">"+((c.enabled)?("</a>"):("</span>"));return a});
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/packed/templates/compiled/template-history-displayApps.js
--- /dev/null
+++ b/static/scripts/packed/templates/compiled/template-history-displayApps.js
@@ -0,0 +1,1 @@
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-displayApps"]=b(function(g,l,f,k,j){f=f||g.helpers;var c,h="function",i=this.escapeExpression,m=this;function e(r,q){var o="",p,n;o+="\n ";n=f.label;if(n){p=n.call(r,{hash:{}})}else{p=r.label;p=typeof p===h?p():p}o+=i(p)+"\n ";p=r.links;p=f.each.call(r,p,{hash:{},inverse:m.noop,fn:m.program(2,d,q)});if(p||p===0){o+=p}o+="\n <br />\n";return o}function d(r,q){var o="",p,n;o+='\n <a target="';n=f.target;if(n){p=n.call(r,{hash:{}})}else{p=r.target;p=typeof p===h?p():p}o+=i(p)+'" href="';n=f.href;if(n){p=n.call(r,{hash:{}})}else{p=r.href;p=typeof p===h?p():p}o+=i(p)+'">';n=f.text;if(n){p=n.call(r,{hash:{}})}else{p=r.text;p=typeof p===h?p():p}o+=i(p)+"</a>\n ";return o}c=l.displayApps;c=f.each.call(l,c,{hash:{},inverse:m.noop,fn:m.program(1,e,j)});if(c||c===0){return c}else{return""}})})();
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/packed/templates/compiled/template-history-downloadLinks.js
--- /dev/null
+++ b/static/scripts/packed/templates/compiled/template-history-downloadLinks.js
@@ -0,0 +1,1 @@
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-downloadLinks"]=b(function(g,l,f,k,j){f=f||g.helpers;var c,h="function",i=this.escapeExpression,m=this;function e(s,r){var p="",q,o;p+='\n<div popupmenu="dataset-';o=f.id;if(o){q=o.call(s,{hash:{}})}else{q=s.id;q=typeof q===h?q():q}p+=i(q)+'-popup">\n <a class="action-button" href="';o=f.download_url;if(o){q=o.call(s,{hash:{}})}else{q=s.download_url;q=typeof q===h?q():q}p+=i(q)+'">Download Dataset</a>\n <a>Additional Files</a>\n ';q=s.meta_files;q=f.each.call(s,q,{hash:{},inverse:m.noop,fn:m.program(2,d,r)});if(q||q===0){p+=q}p+='\n</div>\n<div style="float:left;" class="menubutton split popup" id="dataset-';o=f.id;if(o){q=o.call(s,{hash:{}})}else{q=s.id;q=typeof q===h?q():q}p+=i(q)+'-popup">\n <a href="';o=f.download_url;if(o){q=o.call(s,{hash:{}})}else{q=s.download_url;q=typeof q===h?q():q}p+=i(q)+'" title="Download" class="icon-button disk tooltip"></a>\n</div>\n';return p}function d(s,r){var p="",q,o;p+='\n <a class="action-button" href="';o=f.meta_download_url;if(o){q=o.call(s,{hash:{}})}else{q=s.meta_download_url;q=typeof q===h?q():q}p+=i(q)+'">Download ';o=f.meta_file_type;if(o){q=o.call(s,{hash:{}})}else{q=s.meta_file_type;q=typeof q===h?q():q}p+=i(q)+"</a>\n ";return p}function n(p,o){return'\n<a href="" title="Download" class="icon-button disk tooltip"></a>\n'}c=l.meta_files;c=f["if"].call(l,c,{hash:{},inverse:m.program(4,n,j),fn:m.program(1,e,j)});if(c||c===0){return c}else{return""}})})();
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/packed/viz/circster.js
--- a/static/scripts/packed/viz/circster.js
+++ b/static/scripts/packed/viz/circster.js
@@ -1,1 +1,1 @@
-define(["libs/d3","viz/visualization"],function(e,f){var d=function(){this.initialize&&this.initialize.apply(this,arguments)};d.extend=Backbone.Model.extend;var c=Backbone.View.extend({className:"circster",initialize:function(h){this.total_gap=h.total_gap;this.genome=h.genome;this.dataset_arc_height=h.dataset_arc_height;this.track_gap=5},render:function(){var j=this,l=this.dataset_arc_height,m=j.$el.width(),h=j.$el.height(),k=(Math.min(m,h)/2-this.model.get("tracks").length*(this.dataset_arc_height+this.track_gap));var i=e.select(j.$el[0]).append("svg").attr("width",m).attr("height",h).attr("pointer-events","all").append("svg:g").call(e.behavior.zoom().on("zoom",function(){i.attr("transform","translate("+e.event.translate+") scale("+e.event.scale+")")})).attr("transform","translate("+m/2+","+h/2+")").append("svg:g");this.model.get("tracks").each(function(n,p){var q=n.get("genome_wide_data"),s=k+p*(l+j.track_gap),o=(q instanceof f.GenomeWideBigWigData?g:a);var r=new o({track:n,radius_start:s,radius_end:s+l,genome:j.genome,total_gap:j.total_gap});r.render(i)})}});var b=d.extend({initialize:function(h){this.options=h},render:function(m){var h=this.chroms_layout(),k=this.genome_data_layout();var n=this.options.radius_start,j=this.options.radius_end,p=m.append("g").attr("id","inner-arc"),o=e.svg.arc().innerRadius(n).outerRadius(j),i=p.selectAll("#inner-arc>path").data(h).enter().append("path").attr("d",o).style("stroke","#ccc").style("fill","#ccc").append("title").text(function(r){return r.data.chrom});var q=this.options.track.get("prefs"),l=q.block_color;_.each(k,function(r){if(!r){return}var u=m.append("g"),t=e.svg.arc().innerRadius(n),s=u.selectAll("path").data(r).enter();s.append("path").attr("d",t).style("stroke",l)})},chroms_layout:function(){var i=this.options.genome.get_chroms_info(),k=e.layout.pie().value(function(m){return m.len}).sort(null),l=k(i),h=this.options.total_gap/i.length,j=_.map(l,function(o,n){var m=o.endAngle-h;o.endAngle=(m>o.startAngle?m:o.startAngle);return o});return j},chrom_data_layout:function(k,j,i,l,h){},genome_data_layout:function(){var i=this,h=this.chroms_layout(),m=this.options.track.get("genome_wide_data"),l=this.options.radius_start,j=this.options.radius_end,k=_.zip(h,m.get("data")),n=_.map(k,function(p){var q=p[0],o=p[1];return i.chrom_data_layout(q,o,l,j,m.get("min"),m.get("max"))});return n}});var a=b.extend({chrom_data_layout:function(r,i,o,n,k,p){if(!i||typeof i==="string"){return null}var l=i[0],q=i[3],j=e.scale.linear().domain([k,p]).range([o,n]),m=e.layout.pie().value(function(s){return q}).startAngle(r.startAngle).endAngle(r.endAngle),h=m(l);_.each(l,function(s,t){h[t].outerRadius=j(s[1])});return h}});var g=b.extend({chrom_data_layout:function(q,i,o,n,k,p){var l=i.data;if(l.length===0){return}var j=e.scale.linear().domain([k,p]).range([o,n]),m=e.layout.pie().value(function(s,r){if(r+1===l.length){return 0}return l[r+1][0]-l[r][0]}).startAngle(q.startAngle).endAngle(q.endAngle),h=m(l);_.each(l,function(r,s){h[s].outerRadius=j(r[1])});return h}});return{CircsterView:c}});
\ No newline at end of file
+define(["libs/underscore","libs/d3","viz/visualization"],function(i,k,j){var e=function(){this.initialize&&this.initialize.apply(this,arguments)};e.extend=Backbone.Model.extend;var l=Backbone.Model.extend({is_visible:function(p,m){var n=p.getBoundingClientRect(),o=$("svg")[0].getBoundingClientRect();if(n.right<0||n.left>o.right||n.bottom<0||n.top>o.bottom){return false}return true}});var f=Backbone.Model.extend({defaults:{prefs:{color:"#ccc"}}});var b=Backbone.View.extend({className:"circster",initialize:function(m){this.total_gap=m.total_gap;this.genome=m.genome;this.dataset_arc_height=m.dataset_arc_height;this.track_gap=5;this.label_arc_height=20},render:function(){var u=this,r=this.dataset_arc_height,m=u.$el.width(),t=u.$el.height(),n=Math.min(m,t)/2-this.model.get("tracks").length*(this.dataset_arc_height+this.track_gap)-(this.label_arc_height+this.track_gap),s=this.model.get("tracks");var p=k.select(u.$el[0]).append("svg").attr("width",m).attr("height",t).attr("pointer-events","all").append("svg:g").call(k.behavior.zoom().on("zoom",function(){p.attr("transform","translate("+k.event.translate+") scale("+k.event.scale+")");var v=new l(),w={};s.each(function(x){w[x.id]=[]});k.selectAll("path.chrom-data").filter(function(y,x){return v.is_visible(this,p)}).each(function(z,x){var y=$.data(this,"chrom_data");w[y.track.id].push(y.chrom)})})).attr("transform","translate("+m/2+","+t/2+")").append("svg:g");s.each(function(v,x){var z=n+x*(r+u.track_gap),w=(v.get("track_type")==="LineTrack"?g:h);var y=new w({track:v,track_index:x,radius_start:z,radius_end:z+r,genome:u.genome,total_gap:u.total_gap});y.render(p)});var q=n+s.length*(r+u.track_gap)+u.track_gap;var o=new a({track:new f(),track_index:s.length,radius_start:q,radius_end:q,genome:u.genome,total_gap:u.total_gap});o.render(p)}});var c=e.extend({initialize:function(m){this.options=m;this.options.bg_stroke="ccc";this.options.bg_fill="ccc"},render:function(r){var o=r.append("g").attr("id","parent-"+this.options.track_index);var m=this._chroms_layout(),s=this.options.radius_start,p=this.options.radius_end,t=k.svg.arc().innerRadius(s).outerRadius(p),n=o.selectAll("g").data(m).enter().append("svg:g");n.append("path").attr("d",t).style("stroke",this.options.bg_stroke).style("fill",this.options.bg_fill).append("title").text(function(v){return v.data.chrom});this.render_data(o);var u=this.options.track.get("prefs"),q=u.block_color;if(!q){q=u.color}o.selectAll("path.chrom-data").style("stroke",q).style("fill",q)},_chroms_layout:function(){var n=this.options.genome.get_chroms_info(),p=k.layout.pie().value(function(r){return r.len}).sort(null),q=p(n),m=this.options.total_gap/n.length,o=i.map(q,function(t,s){var r=t.endAngle-m;t.endAngle=(r>t.startAngle?r:t.startAngle);return t});return o},render_chrom_data:function(o,p,q,n,r,m){},render_data:function(q){var v=this,u=this._chroms_layout(),o=this.options.track,n=this.options.radius_start,s=this.options.radius_end,t=o.get_genome_wide_data(this.options.genome),r=i.zip(u,t),m=this.get_bounds(t),p=i.map(r,function(w){var x=w[0],y=w[1];return v.render_chrom_data(q,x,y,n,s,m.min,m.max)});return p}});var a=c.extend({initialize:function(m){this.options=m;this.options.bg_stroke="fff";this.options.bg_fill="fff"},render_data:function(n){var m=n.selectAll("g");m.selectAll("path").attr("id",function(o){return"label-"+o.data.chrom});m.append("svg:text").filter(function(o){return o.endAngle-o.startAngle>0.08}).attr("text-anchor","middle").append("svg:textPath").attr("xlink:href",function(o){return"#label-"+o.data.chrom}).attr("startOffset","25%").text(function(o){return o.data.chrom})}});var d=c.extend({render_quantitative_data:function(q,w,p,t,s,o,u){var r=k.scale.linear().domain([o,u]).range([t,s]);var n=k.scale.linear().domain([0,p.length]).range([w.startAngle,w.endAngle]);var y=k.svg.line.radial().interpolate("linear").radius(function(z){return r(z[1])}).angle(function(A,z){return n(z)});var m=k.svg.area.radial().interpolate(y.interpolate()).innerRadius(r(0)).outerRadius(y.radius()).angle(y.angle());var v=q.datum(p),x=v.append("path").attr("class","chrom-data").attr("d",m);$.data(x[0][0],"chrom_data",{track:this.options.track,chrom:w.data.chrom})},get_bounds:function(){}});var h=d.extend({render_chrom_data:function(o,r,p,n,s,q,m){if(!p||typeof p==="string"){return null}return this.render_quantitative_data(o,r,p.data,n,s,q,m)},get_bounds:function(n){var m=i.map(n,function(o){if(!o||typeof o==="string"){return 0}return o.max});return{min:0,max:(m&&typeof m!=="string"?i.max(m):0)}}});var g=d.extend({render_chrom_data:function(o,r,p,n,t,q,m){var s=p.data;if(s.length===0){return}return this.render_quantitative_data(o,r,s,n,t,q,m)},get_bounds:function(n){var m=i.flatten(i.map(n,function(o){if(o){return i.map(o.data,function(q){return q[1]})}else{return 0}}));return{min:i.min(m),max:i.max(m)}}});return{CircsterView:b}});
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/packed/viz/scatterplot.js
--- /dev/null
+++ b/static/scripts/packed/viz/scatterplot.js
@@ -0,0 +1,1 @@
+define(["../libs/underscore","../libs/d3","../mvc/base-mvc"],function(){function b(e){var h=this,g=10,f=12,d=8,c=5;this.log=function(){if(this.debugging&&console&&console.debug){var i=Array.prototype.slice.call(arguments);i.unshift(this.toString());console.debug.apply(null,i)}};this.log("new TwoVarScatterplot:",e);this.defaults={id:"TwoVarScatterplot",containerSelector:"body",maxDataPoints:30000,bubbleRadius:4,entryAnimDuration:500,xNumTicks:10,yNumTicks:10,xAxisLabelBumpY:40,yAxisLabelBumpX:-35,width:500,height:500,marginTop:50,marginRight:50,marginBottom:50,marginLeft:50,xMin:null,xMax:null,yMin:null,yMax:null,xLabel:"X",yLabel:"Y"};this.config=_.extend({},this.defaults,e);this.updateConfig=function(i){_.extend(this.config,i)};this.toString=function(){return this.config.id};this.translateStr=function(i,j){return"translate("+i+","+j+")"};this.rotateStr=function(j,i,k){return"rotate("+j+","+i+","+k+")"};this.svg=d3.select(this.config.containerSelector).append("svg:svg").attr("class","chart").style("display","none");this.content=this.svg.append("svg:g").attr("class","content");this.xAxis=this.content.append("g").attr("class","axis").attr("id","x-axis");this.xAxisLabel=this.xAxis.append("text").attr("class","axis-label").attr("id","x-axis-label");this.yAxis=this.content.append("g").attr("class","axis").attr("id","y-axis");this.yAxisLabel=this.yAxis.append("text").attr("class","axis-label").attr("id","y-axis-label");this.log("built svg:",d3.selectAll("svg"));this.adjustChartDimensions=function(){this.svg.attr("width",this.config.width+(this.config.marginRight+this.config.marginLeft)).attr("height",this.config.height+(this.config.marginTop+this.config.marginBottom)).style("display","block");this.content=this.svg.select("g.content").attr("transform",this.translateStr(this.config.marginLeft,this.config.marginTop))};this.preprocessData=function(i){return i.slice(0,this.config.maxDataPoints)};this.setUpDomains=function(i,j){this.xMin=this.config.xMin||d3.min(i);this.xMax=this.config.xMax||d3.max(i);this.yMin=this.config.yMin||d3.min(j);this.yMax=this.config.yMax||d3.max(j)};this.setUpScales=function(){this.xScale=d3.scale.linear().domain([this.xMin,this.xMax]).range([0,this.config.width]),this.yScale=d3.scale.linear().domain([this.yMin,this.yMax]).range([this.config.height,0])};this.setUpXAxis=function(){this.xAxisFn=d3.svg.axis().scale(this.xScale).ticks(this.config.xNumTicks).orient("bottom");this.xAxis.attr("transform",this.translateStr(0,this.config.height)).call(this.xAxisFn);this.log("xAxis:",this.xAxis);this.xLongestLabel=d3.max(_.map([this.xMin,this.xMax],function(i){return(String(i)).length}));this.log("xLongestLabel:",this.xLongestLabel);if(this.xLongestLabel>=c){this.xAxis.selectAll("g").filter(":nth-child(odd)").style("display","none")}this.xAxisLabel.attr("x",this.config.width/2).attr("y",this.config.xAxisLabelBumpY).attr("text-anchor","middle").text(this.config.xLabel);this.log("xAxisLabel:",this.xAxisLabel)};this.setUpYAxis=function(){this.yAxisFn=d3.svg.axis().scale(this.yScale).ticks(this.config.yNumTicks).orient("left");this.yAxis.call(this.yAxisFn);this.log("yAxis:",this.yAxis);this.yLongestLabel=d3.max(_.map([this.yMin,this.yMax],function(j){return(String(j)).length}));this.log("yLongestLabel:",this.yLongestLabel);var i=this.yLongestLabel*g+(d);if(this.config.yAxisLabelBumpX>-(i)){this.config.yAxisLabelBumpX=-(i)}if(this.config.marginLeft<i){this.config.marginLeft=i+f;this.adjustChartDimensions()}this.log("this.config.yAxisLableBumpx, this.config.marginLeft:",this.config.yAxisLabelBumpX,this.config.marginLeft);this.yAxisLabel.attr("x",this.config.yAxisLabelBumpX).attr("y",this.config.height/2).attr("text-anchor","middle").attr("transform",this.rotateStr(-90,this.config.yAxisLabelBumpX,this.config.height/2)).text(this.config.yLabel);this.log("yAxisLabel:",this.yAxisLabel)};this.renderGrid=function(){this.vGridLines=this.content.selectAll("line.v-grid-line").data(this.xScale.ticks(this.xAxisFn.ticks()[0]));this.vGridLines.enter().append("svg:line").classed("grid-line v-grid-line",true);this.vGridLines.attr("x1",this.xScale).attr("y1",0).attr("x2",this.xScale).attr("y2",this.config.height);this.vGridLines.exit().remove();this.log("vGridLines:",this.vGridLines);this.hGridLines=this.content.selectAll("line.h-grid-line").data(this.yScale.ticks(this.yAxisFn.ticks()[0]));this.hGridLines.enter().append("svg:line").classed("grid-line h-grid-line",true);this.hGridLines.attr("x1",0).attr("y1",this.yScale).attr("x2",this.config.width).attr("y2",this.yScale);this.hGridLines.exit().remove();this.log("hGridLines:",this.hGridLines)};this.glyphEnterState=function(i){};this.glyphFinalState=function(i){};this.glyphExitState=function(i){};this.renderDatapoints=function(i,l){var k=function(n,m){return h.xScale(i[m])};var j=function(n,m){return h.yScale(l[m])};this.datapoints=this.content.selectAll(".glyph").data(i);this.datapoints.enter().append("svg:circle").attr("class","glyph").attr("cx",k).attr("cy",0).attr("r",0);this.datapoints.transition().duration(this.config.entryAnimDuration).attr("cx",k).attr("cy",j).attr("r",this.config.bubbleRadius);this.datapoints.exit().transition().duration(this.config.entryAnimDuration).attr("cy",this.config.height).attr("r",0).style("fill-opacity",0).remove();this.log(this.datapoints,"glyphs rendered")};this.render=function(i,j){this.log("renderScatterplot",i.length,j.length,this.config);i=this.preprocessData(i);j=this.preprocessData(j);this.log("xCol len",i.length,"yCol len",j.length);this.setUpDomains(i,j);this.log("xMin, xMax, yMin, yMax:",this.xMin,this.xMax,this.yMin,this.yMax);this.setUpScales();this.adjustChartDimensions();this.setUpXAxis();this.setUpYAxis();this.renderGrid();this.renderDatapoints(i,j)}}var a=BaseView.extend(LoggableMixin).extend({tagName:"form",className:"scatterplot-settings-form",events:{"click #render-button":"renderScatterplot"},initialize:function(c){if(!c||!c.dataset){throw ("ScatterplotView requires a dataset")}else{this.dataset=c.dataset}this.apiDatasetsURL=c.apiDatasetsURL;this.chartConfig=c.chartConfig||{};this.log("this.chartConfig:",this.chartConfig);this.plot=new b(this.chartConfig)},render:function(){var c=this,e="",d="";this.dataset.metadata_column_types=this.dataset.metadata_column_types.split(", ");_.each(this.dataset.metadata_column_types,function(h,g){if(h==="int"||h==="float"){var f="column "+g;if(c.dataset.metadata_column_names){f=c.dataset.metadata_column_names[g]}d+='<option value="'+g+'">'+f+"</column>"}});e+='<div id="x-column-input">';e+='<label for="">Data column for X: </label><select name="x-column">'+d+"</select>";e+="</div>";e+='<div id="y-column-input">';e+='<label for="">Data column for Y: </label><select name="y-column">'+d+"</select>";e+="</div>";e+='<input id="render-button" type="button" value="Draw" />';e+='<div class="clear"></div>';this.$el.append(e);this.$el.find("#render-button");return this},renderScatterplot:function(){var d=this,e=this.apiDatasetsURL+"/"+this.dataset.id+"?data_type=raw_data&",i=this.$el.find('[name="x-column"]'),j=i.val(),g=i.children('[value="'+j+'"]').text(),h=this.$el.find('[name="y-column"]'),f=h.val(),c=h.children('[value="'+f+'"]').text();this.log(g,c);this.chartConfig.xLabel=g;this.chartConfig.yLabel=c;d.plot.updateConfig(this.chartConfig);e+=jQuery.param({columns:"["+[j,f]+"]"});this.log("url:",e);jQuery.ajax({url:e,dataType:"json",success:function(k){d.endpoint=k.endpoint;d.plot.render(_.map(k.data,function(l){return l[0]}),_.map(k.data,function(l){return l[1]}))},error:function(m,k,l){alert("ERROR:"+k+"\n"+l)}})}});return{ScatterplotView:a}});
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/packed/viz/sweepster.js
--- a/static/scripts/packed/viz/sweepster.js
+++ b/static/scripts/packed/viz/sweepster.js
@@ -1,1 +1,1 @@
-define(["libs/d3","viz/trackster/util","viz/visualization","viz/trackster/tracks"],function(m,e,l,f){var k=Backbone.Model.extend({defaults:{inputs:null,values:null}});var n=Backbone.RelationalModel.extend({defaults:{tool:null,tree_data:null},initialize:function(p){var o=this;this.get("tool").get("inputs").each(function(q){if(!q.get_samples()){return}q.on("change:min change:max change:num_samples",function(r){if(r.get("in_ptree")){o.set_tree_data()}},o);q.on("change:in_ptree",function(r){if(r.get("in_ptree")){o.add_param(r)}else{o.remove_param(r)}o.set_tree_data()},o)});if(p.config){_.each(p.config,function(r){var q=o.get("tool").get("inputs").find(function(s){return s.get("name")===r.name});o.add_param(q);q.set(r)})}},add_param:function(o){if(o.get("ptree_index")){return}o.set("in_ptree",true);o.set("ptree_index",this.get_tree_params().length)},remove_param:function(o){o.set("in_ptree",false);o.set("ptree_index",null);_(this.get_tree_params()).each(function(p,q){p.set("ptree_index",q+1)})},set_tree_data:function(){var p=_.map(this.get_tree_params(),function(r){return{param:r,samples:r.get_samples()}});var o=0,q=function(u,r){var w=u[r],v=w.param,t=v.get("label"),s=w.samples;if(u.length-1===r){return _.map(s,function(x){return{id:o++,name:x,param:v,value:x}})}return _.map(s,function(x){return{id:o++,name:x,param:v,value:x,children:q(u,r+1)}})};this.set("tree_data",{name:"Root",id:o++,children:(p.length!==0?q(p,0):null)})},get_tree_params:function(){return _(this.get("tool").get("inputs").where({in_ptree:true})).sortBy(function(o){return o.get("ptree_index")})},get_num_leaves:function(){return this.get_tree_params().reduce(function(o,p){return o*p.get_samples().length},1)},get_node_settings:function(s){var q=this.get("tool").get_inputs_dict();var t=s.parent;if(t){while(t.depth!==0){q[t.param.get("name")]=t.value;t=t.parent}}var o=this,p=function(v,u){if(v.param){u[v.param.get("name")]=v.value}if(!v.children){return new k({inputs:o.get("tool").get("inputs"),values:u})}else{return _.flatten(_.map(v.children,function(w){return p(w,_.clone(u))}))}},r=p(s,q);if(!_.isArray(r)){r=[r]}return r},get_connected_nodes:function(q){var r=function(s){if(!s.children){return s}else{return _.flatten([s,_.map(s.children,function(t){return r(t)})])}};var p=[],o=q.parent;while(o){p.push(o);o=o.parent}return _.flatten([p,r(q)])},get_leaf:function(p){var q=this.get("tree_data"),o=function(r){return _.find(r,function(s){return p[s.param.get("name")]===s.value})};while(q.children){q=o(q.children)}return q},toJSON:function(){return this.get_tree_params().map(function(o){return{name:o.get("name"),min:o.get("min"),max:o.get("max"),num_samples:o.get("num_samples")}})}});var c=Backbone.RelationalModel.extend({defaults:{track:null,mode:"Pack",settings:null,regions:null},relations:[{type:Backbone.HasMany,key:"regions",relatedModel:l.GenomeRegion}],initialize:function(o){if(o.track){var p=_.extend({data_url:galaxy_paths.get("raw_data_url"),converted_datasets_state_url:galaxy_paths.get("dataset_state_url")},o.track);this.set("track",f.object_from_template(p,{},null))}},same_settings:function(o){var p=this.get("settings"),q=o.get("settings");for(var r in p){if(!q[r]||p[r]!==q[r]){return false}}return true},toJSON:function(){return{track:this.get("track").to_dict(),settings:this.get("settings"),regions:this.get("regions")}}});var a=Backbone.Collection.extend({model:c});var g=l.Visualization.extend({defaults:_.extend({},l.Visualization.prototype.defaults,{dataset:null,tool:null,parameter_tree:null,regions:null,tracks:null,default_mode:"Pack"}),relations:[{type:Backbone.HasOne,key:"dataset",relatedModel:Dataset},{type:Backbone.HasOne,key:"tool",relatedModel:Tool},{type:Backbone.HasMany,key:"regions",relatedModel:l.GenomeRegion},{type:Backbone.HasMany,key:"tracks",relatedModel:c}],initialize:function(o){var p=this.get("tool").copy(true);this.set("tool_with_samplable_inputs",p);this.set("parameter_tree",new n({tool:p,config:o.tree_config}))},add_track:function(o){this.get("tracks").add(o)},toJSON:function(){return{id:this.get("id"),title:"Parameter exploration for dataset '"+this.get("dataset").get("name")+"'",type:"sweepster",dataset_id:this.get("dataset").id,tool_id:this.get("tool").id,regions:this.get("regions").toJSON(),tree_config:this.get("parameter_tree").toJSON(),tracks:this.get("tracks").toJSON()}}});var j=Backbone.View.extend({tagName:"tr",TILE_LEN:250,initialize:function(o){this.canvas_manager=o.canvas_manager;this.render();this.model.on("change:track change:mode",this.draw_tiles,this)},render:function(){var t=this.model.get("settings"),p=t.get("values"),r=$("<td/>").addClass("settings").appendTo(this.$el),q=$("<div/>").addClass("track-info").hide().appendTo(r);q.append($("<div/>").css("font-weight","bold").text("Track Settings"));t.get("inputs").each(function(v){q.append(v.get("label")+": "+p[v.get("name")]+"<br/>")});var o=this,u=$("<button/>").appendTo(q).text("Run on complete dataset").click(function(){q.toggle();o.trigger("run_on_dataset",t)});var s=create_icon_buttons_menu([{title:"Settings",icon_class:"gear track-settings",on_click:function(){q.toggle()}},{title:"Remove",icon_class:"cross-circle",on_click:function(){o.$el.remove();$(".bs-tooltip").remove()}}]);r.prepend(s.$el);this.model.get("regions").each(function(){o.$el.append($("<td/>").addClass("tile").html($("<img/>").attr("src",galaxy_paths.get("image_path")+"/loading_large_white_bg.gif")))});if(this.model.get("track")){this.draw_tiles()}},draw_tiles:function(){var p=this,o=this.model.get("track"),r=this.model.get("regions"),q=this.$el.find("td.tile");if(!o){return}$.when(o.data_manager.data_is_ready()).then(function(s){r.each(function(v,u){var t=v.length()/p.TILE_LEN,x=1/t,w=p.model.get("mode");$.when(o.data_manager.get_data(v,w,t,{})).then(function(z){var y=p.canvas_manager.new_canvas();y.width=p.TILE_LEN;y.height=o.get_canvas_height(z,w,x,y.width);o.draw_tile(z,y.getContext("2d"),w,t,v,x);$(q[u]).empty().append(y)})})})}});var b=Backbone.View.extend({number_input_template:'<div class="form-row-input sweep"><input class="min" type="text" size="6" value="<%= min %>"> - <input class="max" type="text" size="6" value="<%= max %>"> samples: <input class="num_samples" type="text" size="1" value="<%= num_samples %>"></div>',select_input_template:'<div class="form-row-input sweep"><%= options %></div>',initialize:function(o){this.$el=o.tool_row;this.render()},render:function(){var p=this.model,t=p.get("type"),v=this.$el.find(".form-row-input"),r=null;v.find(":input").change(function(){p.set("value",$(this).val())});if(t==="number"){r=$(_.template(this.number_input_template,this.model.toJSON()))}else{if(t==="select"){var q=_.map(this.$el.find("select option"),function(w){return $(w).val()}),s=q.join(", ");r=$(_.template(this.select_input_template,{options:s}))}}r.insertAfter(v);var o=this,u=create_icon_buttons_menu([{title:"Add parameter to tree",icon_class:"plus-button",on_click:function(){p.set("in_ptree",true);v.hide();r.show();$(this).hide();o.$el.find(".icon-button.toggle").show()}},{title:"Remove parameter from tree",icon_class:"toggle",on_click:function(){p.set("in_ptree",false);r.hide();v.show();$(this).hide();o.$el.find(".icon-button.plus-button").show()}}],{});this.$el.prepend(u.$el);if(p.get("in_ptree")){v.hide();o.$el.find(".icon-button.plus-button").hide()}else{o.$el.find(".icon-button.toggle").hide();r.hide()}_.each(["min","max","num_samples"],function(w){r.find("."+w).change(function(){p.set(w,parseFloat($(this).val()))})})}});var i=Backbone.View.extend({className:"tree-design",initialize:function(o){this.render()},render:function(){var q=new ToolFormView({model:this.model.get("tool")});q.render();this.$el.append(q.$el);var p=this,o=p.model.get("tool").get("inputs");this.$el.find(".form-row").not(".form-actions").each(function(r){var s=new b({model:o.at(r),tool_row:$(this)})})}});var h=Backbone.View.extend({className:"tool-parameter-tree",initialize:function(o){this.model.on("change:tree_data",this.render,this)},render:function(){this.$el.children().remove();var w=this.model.get_tree_params();if(!w.length){return}this.width=100*(2+w.length);this.height=15*this.model.get_num_leaves();var v=this;var u=m.layout.cluster().size([this.height,this.width-160]);var q=m.svg.diagonal().projection(function(x){return[x.y,x.x]});var o=u.nodes(this.model.get("tree_data"));var r=_.uniq(_.pluck(o,"y"));_.each(w,function(A,z){var y=r[z+1],B=$("#center").position().left;v.$el.append($("<div>").addClass("label").text(A.get("label")).css("left",y+B))});var p=m.select(this.$el[0]).append("svg").attr("width",this.width).attr("height",this.height+30).append("g").attr("transform","translate(40, 20)");var t=p.selectAll("path.link").data(u.links(o)).enter().append("path").attr("class","link").attr("d",q);var s=p.selectAll("g.node").data(o).enter().append("g").attr("class","node").attr("transform",function(x){return"translate("+x.y+","+x.x+")"}).on("mouseover",function(y){var x=_.pluck(v.model.get_connected_nodes(y),"id");s.filter(function(z){return _.find(x,function(A){return A===z.id})!==undefined}).style("fill","#f00")}).on("mouseout",function(){s.style("fill","#000")});s.append("circle").attr("r",9);s.append("text").attr("dx",function(x){return x.children?-12:12}).attr("dy",3).attr("text-anchor",function(x){return x.children?"end":"start"}).text(function(x){return x.name})}});var d=Backbone.View.extend({className:"Sweepster",helpText:"<div><h4>Getting Started</h4><ol><li>Create a parameter tree by using the icons next to the tool's parameter names to add or remove parameters.<li>Adjust the tree by using parameter inputs to select min, max, and number of samples<li>Run the tool with different settings by clicking on tree nodes</ol></div>",initialize:function(p){this.canvas_manager=new l.CanvasManager(this.$el.parents("body"));this.tool_param_tree_view=new h({model:this.model.get("parameter_tree")});this.track_collection_container=$("<table/>").addClass("tracks");this.model.get("parameter_tree").on("change:tree_data",this.handle_node_clicks,this);var o=this;this.model.get("tracks").each(function(q){q.get("track").view=o});this.block_color=e.get_random_color();this.reverse_strand_color=e.get_random_color([this.block_color,"#ffffff"])},render:function(){var u=new i({model:this.model.get("parameter_tree")});$("#left").append(u.$el);var x=this,r=x.model.get("regions"),v=$("<tr/>").appendTo(this.track_collection_container);r.each(function(y){v.append($("<th>").text(y.toString()))});v.children().first().attr("colspan",2);var s=$("<div>").addClass("tiles");$("#right").append(s.append(this.track_collection_container));x.model.get("tracks").each(function(y){x.add_track(y)});var w=$(this.helpText).addClass("help"),t=create_icon_buttons_menu([{title:"Close",icon_class:"cross-circle",on_click:function(){$(".bs-tooltip").remove();w.remove()}}]);w.prepend(t.$el.css("float","right"));$("#center").append(w);this.tool_param_tree_view.render();$("#center").append(this.tool_param_tree_view.$el);this.handle_node_clicks();var q=create_icon_buttons_menu([{icon_class:"chevron-expand",title:"Set display mode"},{icon_class:"cross-circle",title:"Close",on_click:function(){window.location="${h.url_for( controller='visualization', action='list' )}"}}],{tooltip_config:{placement:"bottom"}});var p=["Squish","Pack"],o={};_.each(p,function(y){o[y]=function(){x.model.set("default_mode",y);x.model.get("tracks").each(function(z){z.set("mode",y)})}});make_popupmenu(q.$el.find(".chevron-expand"),o);q.$el.attr("style","float: right");$("#right .unified-panel-header-inner").append(q.$el)},run_tool_on_dataset:function(p){var o=this.model.get("tool"),r=o.get("name"),q=this.model.get("dataset");o.set_input_values(p.get("values"));$.when(o.rerun(q)).then(function(s){});show_modal("Running "+r+" on complete dataset",r+" is running on dataset '"+q.get("name")+"'. Outputs are in the dataset's history.",{Ok:function(){hide_modal()}})},add_track:function(r){var p=this,q=this.model.get("parameter_tree");p.model.add_track(r);var o=new j({model:r,canvas_manager:p.canvas_manager});o.on("run_on_dataset",p.run_tool_on_dataset,p);p.track_collection_container.append(o.$el);o.$el.hover(function(){var t=q.get_leaf(r.get("settings").get("values"));var s=_.pluck(q.get_connected_nodes(t),"id");m.select(p.tool_param_tree_view.$el[0]).selectAll("g.node").filter(function(u){return _.find(s,function(v){return v===u.id})!==undefined}).style("fill","#f00")},function(){m.select(p.tool_param_tree_view.$el[0]).selectAll("g.node").style("fill","#000")});return r},handle_node_clicks:function(){var o=this,p=this.model.get("parameter_tree"),r=this.model.get("regions"),q=m.select(this.tool_param_tree_view.$el[0]).selectAll("g.node");q.on("click",function(x,u){var t=o.model.get("tool"),w=o.model.get("dataset"),v=p.get_node_settings(x),s=$.Deferred();if(v.length>=10){show_modal("Whoa there cowboy!","You clicked on a node to try "+o.model.get("tool").get("name")+" with "+v.length+" different combinations of settings. You can only run 10 jobs at a time.",{Ok:function(){hide_modal();s.resolve(false)}})}else{s.resolve(true)}$.when(s).then(function(y){if(!y){return}var z=_.map(v,function(A){var B=new c({settings:A,regions:r,mode:o.model.get("default_mode")});o.add_track(B);return B});_.each(z,function(B,A){setTimeout(function(){t.set_input_values(B.get("settings").get("values"));$.when(t.rerun(w,r)).then(function(D){var E=_.extend({data_url:galaxy_paths.get("raw_data_url"),converted_datasets_state_url:galaxy_paths.get("dataset_state_url")},D.first().get("track_config")),C=f.object_from_template(E,o,null);C.data_manager.set("data_type","raw_data");C.prefs.block_color=o.block_color;C.prefs.reverse_strand_color=o.reverse_strand_color;B.set("track",C)})},A*10000)})})})}});return{SweepsterVisualization:g,SweepsterVisualizationView:d}});
\ No newline at end of file
+define(["libs/d3","viz/trackster/util","viz/visualization","viz/trackster/tracks","mvc/tools","mvc/data"],function(o,f,n,h,g,d){var m=Backbone.Model.extend({defaults:{inputs:null,values:null}});var p=Backbone.RelationalModel.extend({defaults:{tool:null,tree_data:null},initialize:function(r){var q=this;this.get("tool").get("inputs").each(function(s){if(!s.get_samples()){return}s.on("change:min change:max change:num_samples",function(t){if(t.get("in_ptree")){q.set_tree_data()}},q);s.on("change:in_ptree",function(t){if(t.get("in_ptree")){q.add_param(t)}else{q.remove_param(t)}q.set_tree_data()},q)});if(r.config){_.each(r.config,function(t){var s=q.get("tool").get("inputs").find(function(u){return u.get("name")===t.name});q.add_param(s);s.set(t)})}},add_param:function(q){if(q.get("ptree_index")){return}q.set("in_ptree",true);q.set("ptree_index",this.get_tree_params().length)},remove_param:function(q){q.set("in_ptree",false);q.set("ptree_index",null);_(this.get_tree_params()).each(function(r,s){r.set("ptree_index",s+1)})},set_tree_data:function(){var r=_.map(this.get_tree_params(),function(t){return{param:t,samples:t.get_samples()}});var q=0,s=function(w,t){var y=w[t],x=y.param,v=x.get("label"),u=y.samples;if(w.length-1===t){return _.map(u,function(z){return{id:q++,name:z,param:x,value:z}})}return _.map(u,function(z){return{id:q++,name:z,param:x,value:z,children:s(w,t+1)}})};this.set("tree_data",{name:"Root",id:q++,children:(r.length!==0?s(r,0):null)})},get_tree_params:function(){return _(this.get("tool").get("inputs").where({in_ptree:true})).sortBy(function(q){return q.get("ptree_index")})},get_num_leaves:function(){return this.get_tree_params().reduce(function(q,r){return q*r.get_samples().length},1)},get_node_settings:function(u){var s=this.get("tool").get_inputs_dict();var v=u.parent;if(v){while(v.depth!==0){s[v.param.get("name")]=v.value;v=v.parent}}var q=this,r=function(x,w){if(x.param){w[x.param.get("name")]=x.value}if(!x.children){return new m({inputs:q.get("tool").get("inputs"),values:w})}else{return _.flatten(_.map(x.children,function(y){return r(y,_.clone(w))}))}},t=r(u,s);if(!_.isArray(t)){t=[t]}return t},get_connected_nodes:function(s){var t=function(u){if(!u.children){return u}else{return _.flatten([u,_.map(u.children,function(v){return t(v)})])}};var r=[],q=s.parent;while(q){r.push(q);q=q.parent}return _.flatten([r,t(s)])},get_leaf:function(r){var s=this.get("tree_data"),q=function(t){return _.find(t,function(u){return r[u.param.get("name")]===u.value})};while(s.children){s=q(s.children)}return s},toJSON:function(){return this.get_tree_params().map(function(q){return{name:q.get("name"),min:q.get("min"),max:q.get("max"),num_samples:q.get("num_samples")}})}});var c=Backbone.RelationalModel.extend({defaults:{track:null,mode:"Pack",settings:null,regions:null},relations:[{type:Backbone.HasMany,key:"regions",relatedModel:n.GenomeRegion}],initialize:function(q){if(q.track){var r=_.extend({data_url:galaxy_paths.get("raw_data_url"),converted_datasets_state_url:galaxy_paths.get("dataset_state_url")},q.track);this.set("track",h.object_from_template(r,{},null))}},same_settings:function(q){var r=this.get("settings"),s=q.get("settings");for(var t in r){if(!s[t]||r[t]!==s[t]){return false}}return true},toJSON:function(){return{track:this.get("track").to_dict(),settings:this.get("settings"),regions:this.get("regions")}}});var a=Backbone.Collection.extend({model:c});var i=n.Visualization.extend({defaults:_.extend({},n.Visualization.prototype.defaults,{dataset:null,tool:null,parameter_tree:null,regions:null,tracks:null,default_mode:"Pack"}),relations:[{type:Backbone.HasOne,key:"dataset",relatedModel:d.Dataset},{type:Backbone.HasOne,key:"tool",relatedModel:g.Tool},{type:Backbone.HasMany,key:"regions",relatedModel:n.GenomeRegion},{type:Backbone.HasMany,key:"tracks",relatedModel:c}],initialize:function(q){var r=this.get("tool").copy(true);this.set("tool_with_samplable_inputs",r);this.set("parameter_tree",new p({tool:r,config:q.tree_config}))},add_track:function(q){this.get("tracks").add(q)},toJSON:function(){return{id:this.get("id"),title:"Parameter exploration for dataset '"+this.get("dataset").get("name")+"'",type:"sweepster",dataset_id:this.get("dataset").id,tool_id:this.get("tool").id,regions:this.get("regions").toJSON(),tree_config:this.get("parameter_tree").toJSON(),tracks:this.get("tracks").toJSON()}}});var l=Backbone.View.extend({tagName:"tr",TILE_LEN:250,initialize:function(q){this.canvas_manager=q.canvas_manager;this.render();this.model.on("change:track change:mode",this.draw_tiles,this)},render:function(){var v=this.model.get("settings"),r=v.get("values"),t=$("<td/>").addClass("settings").appendTo(this.$el),s=$("<div/>").addClass("track-info").hide().appendTo(t);s.append($("<div/>").css("font-weight","bold").text("Track Settings"));v.get("inputs").each(function(x){s.append(x.get("label")+": "+r[x.get("name")]+"<br/>")});var q=this,w=$("<button/>").appendTo(s).text("Run on complete dataset").click(function(){s.toggle();q.trigger("run_on_dataset",v)});var u=create_icon_buttons_menu([{title:"Settings",icon_class:"gear track-settings",on_click:function(){s.toggle()}},{title:"Remove",icon_class:"cross-circle",on_click:function(){q.$el.remove();$(".bs-tooltip").remove()}}]);t.prepend(u.$el);this.model.get("regions").each(function(){q.$el.append($("<td/>").addClass("tile").html($("<img/>").attr("src",galaxy_paths.get("image_path")+"/loading_large_white_bg.gif")))});if(this.model.get("track")){this.draw_tiles()}},draw_tiles:function(){var r=this,q=this.model.get("track"),t=this.model.get("regions"),s=this.$el.find("td.tile");if(!q){return}$.when(q.data_manager.data_is_ready()).then(function(u){t.each(function(x,w){var v=x.length()/r.TILE_LEN,z=1/v,y=r.model.get("mode");$.when(q.data_manager.get_data(x,y,v,{})).then(function(B){var A=r.canvas_manager.new_canvas();A.width=r.TILE_LEN;A.height=q.get_canvas_height(B,y,z,A.width);q.draw_tile(B,A.getContext("2d"),y,v,x,z);$(s[w]).empty().append(A)})})})}});var b=Backbone.View.extend({number_input_template:'<div class="form-row-input sweep"><input class="min" type="text" size="6" value="<%= min %>"> - <input class="max" type="text" size="6" value="<%= max %>"> samples: <input class="num_samples" type="text" size="1" value="<%= num_samples %>"></div>',select_input_template:'<div class="form-row-input sweep"><%= options %></div>',initialize:function(q){this.$el=q.tool_row;this.render()},render:function(){var r=this.model,v=r.get("type"),x=this.$el.find(".form-row-input"),t=null;x.find(":input").change(function(){r.set("value",$(this).val())});if(v==="number"){t=$(_.template(this.number_input_template,this.model.toJSON()))}else{if(v==="select"){var s=_.map(this.$el.find("select option"),function(y){return $(y).val()}),u=s.join(", ");t=$(_.template(this.select_input_template,{options:u}))}}t.insertAfter(x);var q=this,w=create_icon_buttons_menu([{title:"Add parameter to tree",icon_class:"plus-button",on_click:function(){r.set("in_ptree",true);x.hide();t.show();$(this).hide();q.$el.find(".icon-button.toggle").show()}},{title:"Remove parameter from tree",icon_class:"toggle",on_click:function(){r.set("in_ptree",false);t.hide();x.show();$(this).hide();q.$el.find(".icon-button.plus-button").show()}}],{});this.$el.prepend(w.$el);if(r.get("in_ptree")){x.hide();q.$el.find(".icon-button.plus-button").hide()}else{q.$el.find(".icon-button.toggle").hide();t.hide()}_.each(["min","max","num_samples"],function(y){t.find("."+y).change(function(){r.set(y,parseFloat($(this).val()))})})}});var k=Backbone.View.extend({className:"tree-design",initialize:function(q){this.render()},render:function(){var s=new g.ToolFormView({model:this.model.get("tool")});s.render();this.$el.append(s.$el);var r=this,q=r.model.get("tool").get("inputs");this.$el.find(".form-row").not(".form-actions").each(function(t){var u=new b({model:q.at(t),tool_row:$(this)})})}});var j=Backbone.View.extend({className:"tool-parameter-tree",initialize:function(q){this.model.on("change:tree_data",this.render,this)},render:function(){this.$el.children().remove();var y=this.model.get_tree_params();if(!y.length){return}this.width=100*(2+y.length);this.height=15*this.model.get_num_leaves();var x=this;var w=o.layout.cluster().size([this.height,this.width-160]);var s=o.svg.diagonal().projection(function(z){return[z.y,z.x]});var q=w.nodes(this.model.get("tree_data"));var t=_.uniq(_.pluck(q,"y"));_.each(y,function(B,A){var z=t[A+1],C=$("#center").position().left;x.$el.append($("<div>").addClass("label").text(B.get("label")).css("left",z+C))});var r=o.select(this.$el[0]).append("svg").attr("width",this.width).attr("height",this.height+30).append("g").attr("transform","translate(40, 20)");var v=r.selectAll("path.link").data(w.links(q)).enter().append("path").attr("class","link").attr("d",s);var u=r.selectAll("g.node").data(q).enter().append("g").attr("class","node").attr("transform",function(z){return"translate("+z.y+","+z.x+")"}).on("mouseover",function(A){var z=_.pluck(x.model.get_connected_nodes(A),"id");u.filter(function(B){return _.find(z,function(C){return C===B.id})!==undefined}).style("fill","#f00")}).on("mouseout",function(){u.style("fill","#000")});u.append("circle").attr("r",9);u.append("text").attr("dx",function(z){return z.children?-12:12}).attr("dy",3).attr("text-anchor",function(z){return z.children?"end":"start"}).text(function(z){return z.name})}});var e=Backbone.View.extend({className:"Sweepster",helpText:"<div><h4>Getting Started</h4><ol><li>Create a parameter tree by using the icons next to the tool's parameter names to add or remove parameters.<li>Adjust the tree by using parameter inputs to select min, max, and number of samples<li>Run the tool with different settings by clicking on tree nodes</ol></div>",initialize:function(r){this.canvas_manager=new n.CanvasManager(this.$el.parents("body"));this.tool_param_tree_view=new j({model:this.model.get("parameter_tree")});this.track_collection_container=$("<table/>").addClass("tracks");this.model.get("parameter_tree").on("change:tree_data",this.handle_node_clicks,this);var q=this;this.model.get("tracks").each(function(s){s.get("track").view=q});this.block_color=f.get_random_color();this.reverse_strand_color=f.get_random_color([this.block_color,"#ffffff"])},render:function(){var w=new k({model:this.model.get("parameter_tree")});$("#left").append(w.$el);var z=this,t=z.model.get("regions"),x=$("<tr/>").appendTo(this.track_collection_container);t.each(function(A){x.append($("<th>").text(A.toString()))});x.children().first().attr("colspan",2);var u=$("<div>").addClass("tiles");$("#right").append(u.append(this.track_collection_container));z.model.get("tracks").each(function(A){z.add_track(A)});var y=$(this.helpText).addClass("help"),v=create_icon_buttons_menu([{title:"Close",icon_class:"cross-circle",on_click:function(){$(".bs-tooltip").remove();y.remove()}}]);y.prepend(v.$el.css("float","right"));$("#center").append(y);this.tool_param_tree_view.render();$("#center").append(this.tool_param_tree_view.$el);this.handle_node_clicks();var s=create_icon_buttons_menu([{icon_class:"chevron-expand",title:"Set display mode"},{icon_class:"cross-circle",title:"Close",on_click:function(){window.location="${h.url_for( controller='visualization', action='list' )}"}}],{tooltip_config:{placement:"bottom"}});var r=["Squish","Pack"],q={};_.each(r,function(A){q[A]=function(){z.model.set("default_mode",A);z.model.get("tracks").each(function(B){B.set("mode",A)})}});make_popupmenu(s.$el.find(".chevron-expand"),q);s.$el.attr("style","float: right");$("#right .unified-panel-header-inner").append(s.$el)},run_tool_on_dataset:function(r){var q=this.model.get("tool"),t=q.get("name"),s=this.model.get("dataset");q.set_input_values(r.get("values"));$.when(q.rerun(s)).then(function(u){});show_modal("Running "+t+" on complete dataset",t+" is running on dataset '"+s.get("name")+"'. Outputs are in the dataset's history.",{Ok:function(){hide_modal()}})},add_track:function(t){var r=this,s=this.model.get("parameter_tree");r.model.add_track(t);var q=new l({model:t,canvas_manager:r.canvas_manager});q.on("run_on_dataset",r.run_tool_on_dataset,r);r.track_collection_container.append(q.$el);q.$el.hover(function(){var v=s.get_leaf(t.get("settings").get("values"));var u=_.pluck(s.get_connected_nodes(v),"id");o.select(r.tool_param_tree_view.$el[0]).selectAll("g.node").filter(function(w){return _.find(u,function(x){return x===w.id})!==undefined}).style("fill","#f00")},function(){o.select(r.tool_param_tree_view.$el[0]).selectAll("g.node").style("fill","#000")});return t},handle_node_clicks:function(){var q=this,r=this.model.get("parameter_tree"),t=this.model.get("regions"),s=o.select(this.tool_param_tree_view.$el[0]).selectAll("g.node");s.on("click",function(z,w){var v=q.model.get("tool"),y=q.model.get("dataset"),x=r.get_node_settings(z),u=$.Deferred();if(x.length>=10){show_modal("Whoa there cowboy!","You clicked on a node to try "+q.model.get("tool").get("name")+" with "+x.length+" different combinations of settings. You can only run 10 jobs at a time.",{Ok:function(){hide_modal();u.resolve(false)}})}else{u.resolve(true)}$.when(u).then(function(A){if(!A){return}var B=_.map(x,function(C){var D=new c({settings:C,regions:t,mode:q.model.get("default_mode")});q.add_track(D);return D});_.each(B,function(D,C){setTimeout(function(){v.set_input_values(D.get("settings").get("values"));$.when(v.rerun(y,t)).then(function(F){var G=_.extend({data_url:galaxy_paths.get("raw_data_url"),converted_datasets_state_url:galaxy_paths.get("dataset_state_url")},F.first().get("track_config")),E=h.object_from_template(G,q,null);E.data_manager.set("data_type","raw_data");E.prefs.block_color=q.block_color;E.prefs.reverse_strand_color=q.reverse_strand_color;D.set("track",E)})},C*10000)})})})}});return{SweepsterVisualization:i,SweepsterVisualizationView:e}});
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/packed/viz/trackster/filters.js
--- /dev/null
+++ b/static/scripts/packed/viz/trackster/filters.js
@@ -0,0 +1,1 @@
+define(["libs/underscore"],function(c){var f=c.extend;var a=function(g){this.manager=null;this.name=g.name;this.index=g.index;this.tool_id=g.tool_id;this.tool_exp_name=g.tool_exp_name};f(a.prototype,{to_dict:function(){return{name:this.name,index:this.index,tool_id:this.tool_id,tool_exp_name:this.tool_exp_name}}});var d=function(i,h,g){return $("<a/>").attr("href","javascript:void(0);").attr("title",i).addClass("icon-button").addClass(h).tooltip().click(g)};var e=function(o){a.call(this,o);this.low=("low" in o?o.low:-Number.MAX_VALUE);this.high=("high" in o?o.high:Number.MAX_VALUE);this.min=("min" in o?o.min:Number.MAX_VALUE);this.max=("max" in o?o.max:-Number.MAX_VALUE);this.container=null;this.slider=null;this.slider_label=null;var k=function(p,q,r){p.click(function(){var w=q.text(),u=parseFloat(r.slider("option","max")),t=(u<=1?4:u<=1000000?u.toString().length:6),v=false,s=$(this).parents(".slider-row");s.addClass("input");if(r.slider("option","values")){t=2*t+1;v=true}q.text("");$("<input type='text'/>").attr("size",t).attr("maxlength",t).attr("value",w).appendTo(q).focus().select().click(function(x){x.stopPropagation()}).blur(function(){$(this).remove();q.text(w);s.removeClass("input")}).keyup(function(B){if(B.keyCode===27){$(this).trigger("blur")}else{if(B.keyCode===13){var z=r.slider("option","min"),x=r.slider("option","max"),A=function(C){return(isNaN(C)||C>x||C<z)},y=$(this).val();if(!v){y=parseFloat(y);if(A(y)){alert("Parameter value must be in the range ["+z+"-"+x+"]");return $(this)}}else{y=y.split("-");y=[parseFloat(y[0]),parseFloat(y[1])];if(A(y[0])||A(y[1])){alert("Parameter value must be in the range ["+z+"-"+x+"]");return $(this)}}r.slider((v?"values":"value"),y);s.removeClass("input")}}})})};var h=this;h.parent_div=$("<div/>").addClass("filter-row slider-row");var g=$("<div/>").addClass("elt-label").appendTo(h.parent_div),m=$("<span/>").addClass("slider-name").text(h.name+" ").appendTo(g),i=$("<span/>").text(this.low+"-"+this.high),j=$("<span/>").addClass("slider-value").appendTo(g).append("[").append(i).append("]");h.values_span=i;var l=$("<div/>").addClass("slider").appendTo(h.parent_div);h.control_element=$("<div/>").attr("id",h.name+"-filter-control").appendTo(l);h.control_element.slider({range:true,min:this.min,max:this.max,step:this.get_slider_step(this.min,this.max),values:[this.low,this.high],slide:function(p,q){h.slide(p,q)},change:function(p,q){h.control_element.slider("option","slide").call(h.control_element,p,q)}});h.slider=h.control_element;h.slider_label=i;k(j,i,h.control_element);var n=$("<div/>").addClass("display-controls").appendTo(h.parent_div);this.transparency_icon=d("Use filter for data transparency","layer-transparent",function(){if(h.manager.alpha_filter!==h){h.manager.alpha_filter=h;h.manager.parent_div.find(".layer-transparent").removeClass("active").hide();h.transparency_icon.addClass("active").show()}else{h.manager.alpha_filter=null;h.transparency_icon.removeClass("active")}h.manager.track.request_draw(true,true)}).appendTo(n).hide();this.height_icon=d("Use filter for data height","arrow-resize-090",function(){if(h.manager.height_filter!==h){h.manager.height_filter=h;h.manager.parent_div.find(".arrow-resize-090").removeClass("active").hide();h.height_icon.addClass("active").show()}else{h.manager.height_filter=null;h.height_icon.removeClass("active")}h.manager.track.request_draw(true,true)}).appendTo(n).hide();h.parent_div.hover(function(){h.transparency_icon.show();h.height_icon.show()},function(){if(h.manager.alpha_filter!==h){h.transparency_icon.hide()}if(h.manager.height_filter!==h){h.height_icon.hide()}});$("<div style='clear: both;'/>").appendTo(h.parent_div)};f(e.prototype,{to_dict:function(){var g=a.prototype.to_dict.call(this);return f(g,{type:"number",min:this.min,max:this.max,low:this.low,high:this.high})},copy:function(){return new e({name:this.name,index:this.index,tool_id:this.tool_id,tool_exp_name:this.tool_exp_name})},get_slider_step:function(i,g){var h=g-i;return(h<=2?0.01:1)},slide:function(i,j){var h=j.values;this.values_span.text(h[0]+"-"+h[1]);this.low=h[0];this.high=h[1];var g=this;setTimeout(function(){if(h[0]===g.low&&h[1]===g.high){g.manager.track.request_draw(true,true)}},25)},applies_to:function(g){if(g.length>this.index){return true}return false},_keep_val:function(g){return(isNaN(g)||(g>=this.low&&g<=this.high))},keep:function(h){if(!this.applies_to(h)){return true}var k=this;var l=h[this.index];if(l instanceof Array){var j=true;for(var g=0;g<l.length;g++){if(!this._keep_val(l[g])){j=false;break}}return j}else{return this._keep_val(h[this.index])}},update_attrs:function(k){var g=false;if(!this.applies_to(k)){return g}var h=k[this.index];if(!(h instanceof Array)){h=[h]}for(var j=0;j<h.length;j++){var l=h[j];if(l<this.min){this.min=Math.floor(l);g=true}if(l>this.max){this.max=Math.ceil(l);g=true}}return g},update_ui_elt:function(){if(this.min<this.max){this.parent_div.show()}else{this.parent_div.hide()}var h=this.slider.slider("option","min"),g=this.slider.slider("option","max");if(this.min<h||this.max>g){this.slider.slider("option","min",this.min);this.slider.slider("option","max",this.max);this.slider.slider("option","step",this.get_slider_step(this.min,this.max));this.slider.slider("option","values",[this.min,this.max])}}});var b=function(j,p){this.track=j;this.alpha_filter=null;this.height_filter=null;this.filters=[];this.parent_div=$("<div/>").addClass("filters").hide();this.parent_div.bind("drag",function(i){i.stopPropagation()}).click(function(i){i.stopPropagation()}).bind("dblclick",function(i){i.stopPropagation()}).bind("keydown",function(i){i.stopPropagation()});if(p&&"filters" in p){var g=("alpha_filter" in p?p.alpha_filter:null),k=("height_filter" in p?p.height_filter:null),m=p.filters,h;for(var n=0;n<m.length;n++){if(m[n].type==="number"){h=new e(m[n]);this.add_filter(h);if(h.name===g){this.alpha_filter=h;h.transparency_icon.addClass("active").show()}if(h.name===k){this.height_filter=h;h.height_icon.addClass("active").show()}}else{console.log("ERROR: unsupported filter: ",name,type)}}if("visible" in p&&p.visible){this.parent_div.show()}}if(this.filters.length!==0){var q=$("<div/>").addClass("param-row").appendTo(this.parent_div);var o=$("<input type='submit'/>").attr("value","Run on complete dataset").appendTo(q);var l=this;o.click(function(){l.run_on_dataset()})}};f(b.prototype,{show:function(){this.parent_div.show()},hide:function(){this.parent_div.hide()},toggle:function(){this.parent_div.toggle()},visible:function(){return this.parent_div.is(":visible")},to_dict:function(){var k={},j=[],h;for(var g=0;g<this.filters.length;g++){h=this.filters[g];j.push(h.to_dict())}k.filters=j;k.alpha_filter=(this.alpha_filter?this.alpha_filter.name:null);k.height_filter=(this.height_filter?this.height_filter.name:null);k.visible=this.parent_div.is(":visible");return k},copy:function(h){var j=new b(h);for(var g=0;g<this.filters.length;g++){j.add_filter(this.filters[g].copy())}return j},add_filter:function(g){g.manager=this;this.parent_div.append(g.parent_div);this.filters.push(g)},remove_all:function(){this.filters=[];this.parent_div.children().remove()},init_filters:function(){for(var g=0;g<this.filters.length;g++){var h=this.filters[g];h.update_ui_elt()}},clear_filters:function(){for(var g=0;g<this.filters.length;g++){var h=this.filters[g];h.slider.slider("option","values",[h.min,h.max])}this.alpha_filter=null;this.height_filter=null;this.parent_div.find(".icon-button").hide()},run_on_dataset:function(){var n=function(q,i,p){if(!(i in q)){q[i]=p}return q[i]};var m={},o,g;for(var l=0;l<this.filters.length;l++){o=this.filters[l];if(o.tool_id){if(o.min!==o.low){g=n(m,o.tool_id,[]);g[g.length]=o.tool_exp_name+" >= "+o.low}if(o.max!==o.high){g=n(m,o.tool_id,[]);g[g.length]=o.tool_exp_name+" <= "+o.high}}}var h=[];for(var k in m){h[h.length]=[k,m[k]]}(function j(u,r){var p=r[0],q=p[0],t=p[1],s="("+t.join(") and (")+")",i={cond:s,input:u,target_dataset_id:u,tool_id:q};r=r.slice(1);$.getJSON(run_tool_url,i,function(v){if(v.error){show_modal("Filter Dataset","Error running tool "+q,{Close:hide_modal})}else{if(r.length===0){show_modal("Filtering Dataset","Filter(s) are running on the complete dataset. Outputs are in dataset's history.",{Close:hide_modal})}else{j(v.dataset_id,r)}}})})(this.track.dataset_id,h)}});return{FiltersManager:b,NumberFilter:e}});
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 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 extend=_.extend;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 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 SummaryTreePainter=function(data,view_start,view_end,prefs,mode){Painter.call(this,data,view_start,view_end,prefs,mode)};SummaryTreePainter.prototype.default_prefs={show_counts:false};SummaryTreePainter.prototype.draw=function(ctx,width,height,w_scale){var view_start=this.view_start,points=this.data.data,max=(this.prefs.histogram_max?this.prefs.histogram_max:this.data.max),base_y=height;delta_x_px=Math.ceil(this.data.delta*w_scale);ctx.save();for(var i=0,len=points.length;i<len;i++){var x=Math.floor((points[i][0]-view_start)*w_scale);var y=points[i][1];if(!y){continue}var y_px=y/max*height;if(y!==0&&y_px<1){y_px=1}ctx.fillStyle=this.prefs.block_color;ctx.fillRect(x,base_y-y_px,delta_x_px,y_px);var text_padding_req_x=4;if(this.prefs.show_counts&&(ctx.measureText(y).width+text_padding_req_x)<delta_x_px){ctx.fillStyle=this.prefs.label_color;ctx.textAlign="center";ctx.fillText(y,x+(delta_x_px/2),10)}}ctx.restore()};var LinePainter=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][1])}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][1])}this.prefs.max_value=max_value}};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 pref_color=parseInt(this.prefs.color.slice(1),16),pref_r=(pref_color&16711680)>>16,pref_g=(pref_color&65280)>>8,pref_b=pref_color&255;for(var i=0,len=data.length;i<len;i++){ctx.fillStyle=ctx.strokeStyle=this.prefs.color;x_scaled=Math.round((data[i][0]-view_start-1)*w_scale);y=data[i][1];var top_overflow=false,bot_overflow=false;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=this.prefs.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())};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)+this.get_bottom_padding(width)},get_top_padding:function(width){return 0},get_bottom_padding:function(width){return Math.max(Math.round(this.get_row_height()/2),5)},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;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((feature_start<view_end&&feature_end>view_start)&&(this.mode==="Dense"||slot!==null)){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])}}ctx.restore();feature_mapper.y_translation=this.get_top_padding(width);return feature_mapper},draw_element:function(ctx,mode,feature,slot,tile_low,tile_high,w_scale,y_scale,width){console.log("WARNING: Unimplemented function.");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]-1,feature_name=feature[3],feature_strand=feature[4],f_start=Math.floor(Math.max(0,(feature_start-tile_low)*w_scale)),f_end=Math.ceil(Math.min(width,Math.max(0,(feature_end-tile_low)*w_scale))),draw_start=f_start,draw_end=f_end,y_center=(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_center+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_center+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_center+1,f_end-f_start,thick_height)}}else{var cur_y_center,cur_height;if(mode==="Squish"||mode==="Dense"){cur_y_center=y_center+Math.floor(SQUISH_FEATURE_HEIGHT/2)+1;cur_height=1}else{if(feature_strand){cur_y_center=y_center;cur_height=thick_height}else{cur_y_center+=(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_center,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)*w_scale)),block_end=Math.ceil(Math.min(width,Math.max((block[1]-1-tile_low)*w_scale))),last_block_start,last_block_end;if(block_start>block_end){continue}ctx.fillStyle=block_color;ctx.fillRect(block_start,y_center+(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_center+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_center+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_center)}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_center+1,f_end-f_start,ws_height);ctx.fillRect(f_start,cur_y_center+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_center+8);draw_end+=ctx.measureText(feature_name).width+LABEL_SPACING}else{ctx.textAlign="right";ctx.fillText(feature_name,f_start-LABEL_SPACING,y_center+8);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){FeaturePainter.call(this,data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler);this.ref_seq=(ref_seq?ref_seq.data:null)};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},draw_read:function(ctx,mode,w_scale,y_center,tile_low,tile_high,feature_start,cigar,strand,orig_seq){ctx.textAlign="center";var tile_region=[tile_low,tile_high],base_offset=0,seq_offset=0,gap=0,char_width_px=ctx.canvas.manager.char_width_px,block_color=(strand==="+"?this.prefs.block_color:this.prefs.reverse_strand_color);var draw_last=[];if((mode==="Pack"||this.mode==="Auto")&&orig_seq!==undefined&&w_scale>char_width_px){gap=Math.round(w_scale/2)}if(!cigar){cigar=[[0,orig_seq.length]]}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];if(cig_op==="H"||cig_op==="S"){base_offset-=cig_len}var seq_start=(feature_start-1)+base_offset,s_start=Math.floor(Math.max(0,(seq_start-tile_low)*w_scale)),s_end=Math.floor(Math.max(0,(seq_start+cig_len-tile_low)*w_scale));if(s_start===s_end){s_end+=1}switch(cig_op){case"H":break;case"S":case"M":case"=":if(is_overlap([seq_start,seq_start+cig_len],tile_region)){var seq=orig_seq.slice(seq_offset,seq_offset+cig_len);if(gap>0){ctx.fillStyle=block_color;ctx.fillRect(s_start-gap,y_center+1,s_end-s_start,9);ctx.fillStyle=CONNECTOR_COLOR;for(var c=0,str_len=seq.length;c<str_len;c++){if(this.prefs.show_differences){if(this.ref_seq){var ref_char=this.ref_seq[seq_start-tile_low+c];if(!ref_char||ref_char.toLowerCase()===seq[c].toLowerCase()){continue}}else{continue}}if(seq_start+c>=tile_low&&seq_start+c<=tile_high){var c_start=Math.floor(Math.max(0,(seq_start+c-tile_low)*w_scale));ctx.fillText(seq[c],c_start,y_center+9)}}}else{ctx.fillStyle=block_color;ctx.fillRect(s_start,y_center+4,s_end-s_start,SQUISH_FEATURE_HEIGHT)}}seq_offset+=cig_len;base_offset+=cig_len;break;case"N":ctx.fillStyle=CONNECTOR_COLOR;ctx.fillRect(s_start-gap,y_center+5,s_end-s_start,1);base_offset+=cig_len;break;case"D":ctx.fillStyle="red";ctx.fillRect(s_start-gap,y_center+4,s_end-s_start,3);base_offset+=cig_len;break;case"P":break;case"I":var insert_x_coord=s_start-gap;if(is_overlap([seq_start,seq_start+cig_len],tile_region)){var seq=orig_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")&&orig_seq!==undefined&&w_scale>char_width_px){ctx.fillStyle="yellow";ctx.fillRect(x_center-gap,y_center-9,s_end-s_start,9);draw_last[draw_last.length]={type:"triangle",data:[insert_x_coord,y_center+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_center)}}else{ctx.fillStyle="yellow";ctx.fillRect(x_center,y_center+(this.mode!=="Dense"?2:5),s_end-s_start,(mode!=="Dense"?SQUISH_FEATURE_HEIGHT:DENSE_FEATURE_HEIGHT))}}else{if((mode==="Pack"||this.mode==="Auto")&&orig_seq!==undefined&&w_scale>char_width_px){draw_last.push({type:"text",data:[seq.length,insert_x_coord,y_center+9]})}else{}}}seq_offset+=cig_len;break;case"X":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,(feature_start-tile_low)*w_scale)),f_end=Math.ceil(Math.min(width,Math.max(0,(feature_end-tile_low)*w_scale))),y_center=(mode==="Dense"?0:(0+slot))*y_scale,label_color=this.prefs.label_color,gap=0;if((mode==="Pack"||this.mode==="Auto")&&w_scale>ctx.canvas.manager.char_width_px){var gap=Math.round(w_scale/2)}if(feature[5] instanceof Array){var b1_start=Math.floor(Math.max(0,(feature[4][0]-tile_low)*w_scale)),b1_end=Math.ceil(Math.min(width,Math.max(0,(feature[4][1]-tile_low)*w_scale))),b2_start=Math.floor(Math.max(0,(feature[5][0]-tile_low)*w_scale)),b2_end=Math.ceil(Math.min(width,Math.max(0,(feature[5][1]-tile_low)*w_scale))),connector=true;if(feature[4][1]>=tile_low&&feature[4][0]<=tile_high&&feature[4][2]){this.draw_read(ctx,mode,w_scale,y_center,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_center,tile_low,tile_high,feature[5][0],feature[5][2],feature[5][3],feature[5][4])}else{connector=false}if(connector&&b2_start>b1_end){ctx.fillStyle=CONNECTOR_COLOR;dashedLine(ctx,b1_end-gap,y_center+5,b2_start-gap,y_center+5)}}else{this.draw_read(ctx,mode,w_scale,y_center,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;var tile_index=1;if(tile_index===0&&f_start-ctx.measureText(feature_name).width<0){ctx.textAlign="left";ctx.fillText(feature_name,f_end+LABEL_SPACING-gap,y_center+8)}else{ctx.textAlign="right";ctx.fillText(feature_name,f_start-LABEL_SPACING-gap,y_center+8)}}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_center){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_center,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{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:"4169E1",neg_color:"FF8C00"};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()};return{Scaler:Scaler,SummaryTreePainter:SummaryTreePainter,LinePainter:LinePainter,LinkedFeaturePainter:LinkedFeaturePainter,ReadPainter:ReadPainter,ArcLinkedFeaturePainter:ArcLinkedFeaturePainter,DiagonalHeatmapPainter:DiagonalHeatmapPainter}});
\ No newline at end of file
+define(["libs/underscore"],function(_){var extend=_.extend;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 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 SummaryTreePainter=function(data,view_start,view_end,prefs,mode){Painter.call(this,data,view_start,view_end,prefs,mode)};SummaryTreePainter.prototype.default_prefs={show_counts:false};SummaryTreePainter.prototype.draw=function(ctx,width,height,w_scale){var view_start=this.view_start,points=this.data.data,max=(this.prefs.histogram_max?this.prefs.histogram_max:this.data.max),base_y=height;delta_x_px=Math.ceil(this.data.delta*w_scale);ctx.save();for(var i=0,len=points.length;i<len;i++){var x=Math.floor((points[i][0]-view_start)*w_scale);var y=points[i][1];if(!y){continue}var y_px=y/max*height;if(y!==0&&y_px<1){y_px=1}ctx.fillStyle=this.prefs.block_color;ctx.fillRect(x,base_y-y_px,delta_x_px,y_px);var text_padding_req_x=4;if(this.prefs.show_counts&&(ctx.measureText(y).width+text_padding_req_x)<delta_x_px){ctx.fillStyle=this.prefs.label_color;ctx.textAlign="center";ctx.fillText(y,x+(delta_x_px/2),10)}}ctx.restore()};var LinePainter=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][1])}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][1])}this.prefs.max_value=max_value}};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 pref_color=parseInt(this.prefs.color.slice(1),16),pref_r=(pref_color&16711680)>>16,pref_g=(pref_color&65280)>>8,pref_b=pref_color&255;for(var i=0,len=data.length;i<len;i++){ctx.fillStyle=ctx.strokeStyle=this.prefs.color;x_scaled=Math.round((data[i][0]-view_start-1)*w_scale);y=data[i][1];var top_overflow=false,bot_overflow=false;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=this.prefs.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())};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)+this.get_bottom_padding(width)},get_top_padding:function(width){return 0},get_bottom_padding:function(width){return Math.max(Math.round(this.get_row_height()/2),5)},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;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((feature_start<view_end&&feature_end>view_start)&&(this.mode==="Dense"||slot!==null)){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])}}ctx.restore();feature_mapper.y_translation=this.get_top_padding(width);return feature_mapper},draw_element:function(ctx,mode,feature,slot,tile_low,tile_high,w_scale,y_scale,width){console.log("WARNING: Unimplemented function.");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]-1,feature_name=feature[3],feature_strand=feature[4],f_start=Math.floor(Math.max(0,(feature_start-tile_low)*w_scale)),f_end=Math.ceil(Math.min(width,Math.max(0,(feature_end-tile_low)*w_scale))),draw_start=f_start,draw_end=f_end,y_center=(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_center+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_center+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_center+1,f_end-f_start,thick_height)}}else{var cur_y_center,cur_height;if(mode==="Squish"||mode==="Dense"){cur_y_center=y_center+Math.floor(SQUISH_FEATURE_HEIGHT/2)+1;cur_height=1}else{if(feature_strand){cur_y_center=y_center;cur_height=thick_height}else{cur_y_center+=(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_center,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)*w_scale)),block_end=Math.ceil(Math.min(width,Math.max((block[1]-1-tile_low)*w_scale))),last_block_start,last_block_end;if(block_start>block_end){continue}ctx.fillStyle=block_color;ctx.fillRect(block_start,y_center+(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_center+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_center+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_center)}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_center+1,f_end-f_start,ws_height);ctx.fillRect(f_start,cur_y_center+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_center+8);draw_end+=ctx.measureText(feature_name).width+LABEL_SPACING}else{ctx.textAlign="right";ctx.fillText(feature_name,f_start-LABEL_SPACING,y_center+8);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){FeaturePainter.call(this,data,view_start,view_end,prefs,mode,alpha_scaler,height_scaler);this.ref_seq=(ref_seq?ref_seq.data:null)};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},draw_read:function(ctx,mode,w_scale,y_center,tile_low,tile_high,feature_start,cigar,strand,ref_seq){ctx.textAlign="center";var tile_region=[tile_low,tile_high],base_offset=0,seq_offset=0,gap=0,char_width_px=ctx.canvas.manager.char_width_px,block_color=(strand==="+"?this.prefs.block_color:this.prefs.reverse_strand_color);var draw_last=[];if((mode==="Pack"||this.mode==="Auto")&&ref_seq!==undefined&&w_scale>char_width_px){gap=Math.round(w_scale/2)}if(!cigar){cigar=[[0,ref_seq.length]]}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];if(cig_op==="H"||cig_op==="S"){base_offset-=cig_len}var seq_start=(feature_start-1)+base_offset,s_start=Math.floor(Math.max(0,(seq_start-tile_low)*w_scale)),s_end=Math.floor(Math.max(0,(seq_start+cig_len-tile_low)*w_scale));if(s_start===s_end){s_end+=1}switch(cig_op){case"H":break;case"S":case"M":case"=":if(is_overlap([seq_start,seq_start+cig_len],tile_region)){var seq=ref_seq.slice(seq_offset-1,seq_offset+cig_len);if(gap>0){ctx.fillStyle=block_color;ctx.fillRect(s_start-gap,y_center+1,s_end-s_start,9);ctx.fillStyle=CONNECTOR_COLOR;for(var c=0,str_len=seq.length;c<str_len;c++){if(this.prefs.show_differences){if(this.ref_seq){var ref_char=this.ref_seq[seq_start-tile_low+c];if(!ref_char||ref_char.toLowerCase()===seq[c].toLowerCase()){continue}}else{continue}}if(seq_start+c>=tile_low&&seq_start+c<=tile_high){var c_start=Math.floor(Math.max(0,(seq_start+c-tile_low)*w_scale));ctx.fillText(seq[c],c_start,y_center+9)}}}else{ctx.fillStyle=block_color;ctx.fillRect(s_start,y_center+4,s_end-s_start,SQUISH_FEATURE_HEIGHT)}}seq_offset+=cig_len;base_offset+=cig_len;break;case"N":ctx.fillStyle=CONNECTOR_COLOR;ctx.fillRect(s_start-gap,y_center+5,s_end-s_start,1);base_offset+=cig_len;break;case"D":ctx.fillStyle="red";ctx.fillRect(s_start-gap,y_center+4,s_end-s_start,3);base_offset+=cig_len;break;case"P":break;case"I":var insert_x_coord=s_start-gap;if(is_overlap([seq_start,seq_start+cig_len],tile_region)){var seq=ref_seq.slice(seq_offset-1,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")&&ref_seq!==undefined&&w_scale>char_width_px){ctx.fillStyle="yellow";ctx.fillRect(x_center-gap,y_center-9,s_end-s_start,9);draw_last[draw_last.length]={type:"triangle",data:[insert_x_coord,y_center+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_center)}}else{ctx.fillStyle="yellow";ctx.fillRect(x_center,y_center+(this.mode!=="Dense"?2:5),s_end-s_start,(mode!=="Dense"?SQUISH_FEATURE_HEIGHT:DENSE_FEATURE_HEIGHT))}}else{if((mode==="Pack"||this.mode==="Auto")&&ref_seq!==undefined&&w_scale>char_width_px){draw_last.push({type:"text",data:[seq.length,insert_x_coord,y_center+9]})}else{}}}seq_offset+=cig_len;break;case"X":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,(feature_start-tile_low)*w_scale)),f_end=Math.ceil(Math.min(width,Math.max(0,(feature_end-tile_low)*w_scale))),y_center=(mode==="Dense"?0:(0+slot))*y_scale,label_color=this.prefs.label_color,gap=0;if((mode==="Pack"||this.mode==="Auto")&&w_scale>ctx.canvas.manager.char_width_px){var gap=Math.round(w_scale/2)}if(feature[5] instanceof Array){var b1_start=Math.floor(Math.max(0,(feature[4][0]-tile_low)*w_scale)),b1_end=Math.ceil(Math.min(width,Math.max(0,(feature[4][1]-tile_low)*w_scale))),b2_start=Math.floor(Math.max(0,(feature[5][0]-tile_low)*w_scale)),b2_end=Math.ceil(Math.min(width,Math.max(0,(feature[5][1]-tile_low)*w_scale))),connector=true;if(feature[4][1]>=tile_low&&feature[4][0]<=tile_high&&feature[4][2]){this.draw_read(ctx,mode,w_scale,y_center,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_center,tile_low,tile_high,feature[5][0],feature[5][2],feature[5][3],feature[5][4])}else{connector=false}if(connector&&b2_start>b1_end){ctx.fillStyle=CONNECTOR_COLOR;dashedLine(ctx,b1_end-gap,y_center+5,b2_start-gap,y_center+5)}}else{this.draw_read(ctx,mode,w_scale,y_center,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;var tile_index=1;if(tile_index===0&&f_start-ctx.measureText(feature_name).width<0){ctx.textAlign="left";ctx.fillText(feature_name,f_end+LABEL_SPACING-gap,y_center+8)}else{ctx.textAlign="right";ctx.fillText(feature_name,f_start-LABEL_SPACING-gap,y_center+8)}}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_center){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_center,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{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:"4169E1",neg_color:"FF8C00"};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()};return{Scaler:Scaler,SummaryTreePainter:SummaryTreePainter,LinePainter:LinePainter,LinkedFeaturePainter:LinkedFeaturePainter,ReadPainter:ReadPainter,ArcLinkedFeaturePainter:ArcLinkedFeaturePainter,DiagonalHeatmapPainter:DiagonalHeatmapPainter}});
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/packed/viz/trackster/tracks.js
--- a/static/scripts/packed/viz/trackster/tracks.js
+++ b/static/scripts/packed/viz/trackster/tracks.js
@@ -1,1 +1,1 @@
-define(["libs/underscore","viz/visualization","viz/trackster/util","viz/trackster/slotting","viz/trackster/painters"],function(ae,y,m,v,M){var r=ae.extend;var R=m.get_random_color;var Y=function(af){return("isResolved" in af)};var o={};var l=function(af,ag){o[af.attr("id")]=ag};var n=function(af,ah,aj,ai){aj=".group";var ag={};o[af.attr("id")]=ai;af.bind("drag",{handle:"."+ah,relative:true},function(ar,at){var aq=$(this),aw=$(this).parent(),an=aw.children(),ap=o[$(this).attr("id")],am,al,au,ak,ao;al=$(this).parents(aj);if(al.length!==0){au=al.position().top;ak=au+al.outerHeight();if(at.offsetY<au){$(this).insertBefore(al);var av=o[al.attr("id")];av.remove_drawable(ap);av.container.add_drawable_before(ap,av);return}else{if(at.offsetY>ak){$(this).insertAfter(al);var av=o[al.attr("id")];av.remove_drawable(ap);av.container.add_drawable(ap);return}}}al=null;for(ao=0;ao<an.length;ao++){am=$(an.get(ao));au=am.position().top;ak=au+am.outerHeight();if(am.is(aj)&&this!==am.get(0)&&at.offsetY>=au&&at.offsetY<=ak){if(at.offsetY-au<ak-at.offsetY){am.find(".content-div").prepend(this)}else{am.find(".content-div").append(this)}if(ap.container){ap.container.remove_drawable(ap)}o[am.attr("id")].add_drawable(ap);return}}for(ao=0;ao<an.length;ao++){am=$(an.get(ao));if(at.offsetY<am.position().top&&!(am.hasClass("reference-track")||am.hasClass("intro"))){break}}if(ao===an.length){if(this!==an.get(ao-1)){aw.append(this);o[aw.attr("id")].move_drawable(ap,ao)}}else{if(this!==an.get(ao)){$(this).insertBefore(an.get(ao));o[aw.attr("id")].move_drawable(ap,(at.deltaY>0?ao-1:ao))}}}).bind("dragstart",function(){ag["border-top"]=af.css("border-top");ag["border-bottom"]=af.css("border-bottom");$(this).css({"border-top":"1px solid blue","border-bottom":"1px solid blue"})}).bind("dragend",function(){$(this).css(ag)})};exports.moveable=n;var ad=16,H=9,E=20,B=100,J=12000,U=400,L=5000,x=100,p="There was an error in indexing this dataset. ",K="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",F="No data for this chrom/contig.",w="Preparing data. This can take a while for a large dataset. If the visualization is saved and closed, preparation will continue in the background.",z="Tool cannot be rerun: ",a="Loading data...",X="Ready for display",S=10,I=20;function Z(ag,af){if(!af){af=0}var ah=Math.pow(10,af);return Math.round(ag*ah)/ah}var s=function(ag,af,ai){if(!s.id_counter){s.id_counter=0}this.id=s.id_counter++;this.name=ai.name;this.view=ag;this.container=af;this.config=new G({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name}],saved_values:ai.prefs,onchange:function(){this.track.set_name(this.track.config.values.name)}});this.prefs=this.config.values;this.drag_handle_class=ai.drag_handle_class;this.is_overview=false;this.action_icons={};this.content_visible=true;this.container_div=this.build_container_div();this.header_div=this.build_header_div();if(this.header_div){this.container_div.append(this.header_div);this.icons_div=$("<div/>").css("float","left").hide().appendTo(this.header_div);this.build_action_icons(this.action_icons_def);this.header_div.append($("<div style='clear: both'/>"));this.header_div.dblclick(function(aj){aj.stopPropagation()});var ah=this;this.container_div.hover(function(){ah.icons_div.show()},function(){ah.icons_div.hide()});$("<div style='clear: both'/>").appendTo(this.container_div)}};s.prototype.action_icons_def=[{name:"toggle_icon",title:"Hide/show content",css_class:"toggle",on_click_fn:function(af){if(af.content_visible){af.action_icons.toggle_icon.addClass("toggle-expand").removeClass("toggle");af.hide_contents();af.content_visible=false}else{af.action_icons.toggle_icon.addClass("toggle").removeClass("toggle-expand");af.content_visible=true;af.show_contents()}}},{name:"settings_icon",title:"Edit settings",css_class:"settings-icon",on_click_fn:function(ag){var ai=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},af=function(){ag.config.update_from_form($(".dialog-box"));hide_modal();$(window).unbind("keypress.check_enter_esc")},ah=function(aj){if((aj.keyCode||aj.which)===27){ai()}else{if((aj.keyCode||aj.which)===13){af()}}};$(window).bind("keypress.check_enter_esc",ah);show_modal("Configure",ag.config.build_form(),{Cancel:ai,OK:af})}},{name:"remove_icon",title:"Remove",css_class:"remove-icon",on_click_fn:function(af){$(".bs-tooltip").remove();af.remove()}}];r(s.prototype,{init:function(){},changed:function(){this.view.changed()},can_draw:function(){if(this.enabled&&this.content_visible){return true}return false},request_draw:function(){},_draw:function(){},to_dict:function(){},set_name:function(af){this.old_name=this.name;this.name=af;this.name_div.text(this.name)},revert_name:function(){if(this.old_name){this.name=this.old_name;this.name_div.text(this.name)}},remove:function(){this.changed();this.container.remove_drawable(this);var af=this.view;this.container_div.hide(0,function(){$(this).remove();af.update_intro_div()})},build_container_div:function(){},build_header_div:function(){},add_action_icon:function(ag,al,ak,aj,af,ai){var ah=this;this.action_icons[ag]=$("<a/>").attr("href","javascript:void(0);").attr("title",al).addClass("icon-button").addClass(ak).tooltip().click(function(){aj(ah)}).appendTo(this.icons_div);if(ai){this.action_icons[ag].hide()}},build_action_icons:function(af){var ah;for(var ag=0;ag<af.length;ag++){ah=af[ag];this.add_action_icon(ah.name,ah.title,ah.css_class,ah.on_click_fn,ah.prepend,ah.hide)}},update_icons:function(){},hide_contents:function(){},show_contents:function(){},get_drawables:function(){}});var A=function(ag,af,ah){s.call(this,ag,af,ah);this.obj_type=ah.obj_type;this.drawables=[]};r(A.prototype,s.prototype,{unpack_drawables:function(ah){this.drawables=[];var ag;for(var af=0;af<ah.length;af++){ag=q(ah[af],this.view,this);this.add_drawable(ag)}},init:function(){for(var af=0;af<this.drawables.length;af++){this.drawables[af].init()}},_draw:function(){for(var af=0;af<this.drawables.length;af++){this.drawables[af]._draw()}},to_dict:function(){var ag=[];for(var af=0;af<this.drawables.length;af++){ag.push(this.drawables[af].to_dict())}return{name:this.name,prefs:this.prefs,obj_type:this.obj_type,drawables:ag}},add_drawable:function(af){this.drawables.push(af);af.container=this;this.changed()},add_drawable_before:function(ah,af){this.changed();var ag=this.drawables.indexOf(af);if(ag!==-1){this.drawables.splice(ag,0,ah);return true}return false},replace_drawable:function(ah,af,ag){var ai=this.drawables.indexOf(ah);if(ai!==-1){this.drawables[ai]=af;if(ag){ah.container_div.replaceWith(af.container_div)}this.changed()}return ai},remove_drawable:function(ag){var af=this.drawables.indexOf(ag);if(af!==-1){this.drawables.splice(af,1);ag.container=null;this.changed();return true}return false},move_drawable:function(ag,ah){var af=this.drawables.indexOf(ag);if(af!==-1){this.drawables.splice(af,1);this.drawables.splice(ah,0,ag);this.changed();return true}return false},get_drawables:function(){return this.drawables}});var Q=function(ag,af,ai){r(ai,{obj_type:"DrawableGroup",drag_handle_class:"group-handle"});A.call(this,ag,af,ai);this.content_div=$("<div/>").addClass("content-div").attr("id","group_"+this.id+"_content_div").appendTo(this.container_div);l(this.container_div,this);l(this.content_div,this);n(this.container_div,this.drag_handle_class,".group",this);this.filters_manager=new aa(this);this.header_div.after(this.filters_manager.parent_div);this.saved_filters_managers=[];if("drawables" in ai){this.unpack_drawables(ai.drawables)}if("filters" in ai){var ah=this.filters_manager;this.filters_manager=new aa(this,ai.filters);ah.parent_div.replaceWith(this.filters_manager.parent_div);if(ai.filters.visible){this.setup_multitrack_filtering()}}};r(Q.prototype,s.prototype,A.prototype,{action_icons_def:[s.prototype.action_icons_def[0],s.prototype.action_icons_def[1],{name:"composite_icon",title:"Show composite track",css_class:"layers-stack",on_click_fn:function(af){$(".bs-tooltip").remove();af.show_composite_track()}},{name:"filters_icon",title:"Filters",css_class:"filters-icon",on_click_fn:function(af){if(af.filters_manager.visible()){af.filters_manager.clear_filters();af._restore_filter_managers()}else{af.setup_multitrack_filtering();af.request_draw(true)}af.filters_manager.toggle()}},s.prototype.action_icons_def[2]],build_container_div:function(){var af=$("<div/>").addClass("group").attr("id","group_"+this.id);if(this.container){this.container.content_div.append(af)}return af},build_header_div:function(){var af=$("<div/>").addClass("track-header");af.append($("<div/>").addClass(this.drag_handle_class));this.name_div=$("<div/>").addClass("track-name").text(this.name).appendTo(af);return af},hide_contents:function(){this.tiles_div.hide()},show_contents:function(){this.tiles_div.show();this.request_draw()},update_icons:function(){var ah=this.drawables.length;if(ah===0){this.action_icons.composite_icon.hide();this.action_icons.filters_icon.hide()}else{if(ah===1){if(this.drawables[0] instanceof h){this.action_icons.composite_icon.show()}this.action_icons.filters_icon.hide()}else{var ao,an,al,ar=true,aj=this.drawables[0].get_type(),af=0;for(ao=0;ao<ah;ao++){al=this.drawables[ao];if(al.get_type()!==aj){can_composite=false;break}if(al instanceof d){af++}}if(ar||af===1){this.action_icons.composite_icon.show()}else{this.action_icons.composite_icon.hide();$(".bs-tooltip").remove()}if(af>1&&af===this.drawables.length){var at={},ag;al=this.drawables[0];for(an=0;an<al.filters_manager.filters.length;an++){ag=al.filters_manager.filters[an];at[ag.name]=[ag]}for(ao=1;ao<this.drawables.length;ao++){al=this.drawables[ao];for(an=0;an<al.filters_manager.filters.length;an++){ag=al.filters_manager.filters[an];if(ag.name in at){at[ag.name].push(ag)}}}this.filters_manager.remove_all();var ai,ak,am,ap;for(var aq in at){ai=at[aq];if(ai.length===af){ak=new V({name:ai[0].name,index:ai[0].index});this.filters_manager.add_filter(ak)}}if(this.filters_manager.filters.length>0){this.action_icons.filters_icon.show()}else{this.action_icons.filters_icon.hide()}}else{this.action_icons.filters_icon.hide()}}}},_restore_filter_managers:function(){for(var af=0;af<this.drawables.length;af++){this.drawables[af].filters_manager=this.saved_filters_managers[af]}this.saved_filters_managers=[]},setup_multitrack_filtering:function(){if(this.filters_manager.filters.length>0){this.saved_filters_managers=[];for(var af=0;af<this.drawables.length;af++){drawable=this.drawables[af];this.saved_filters_managers.push(drawable.filters_manager);drawable.filters_manager=this.filters_manager}}this.filters_manager.init_filters()},show_composite_track:function(){var aj=[];for(var ag=0;ag<this.drawables.length;ag++){aj.push(this.drawables[ag].name)}var ah="Composite Track of "+this.drawables.length+" tracks ("+aj.join(", ")+")";var ai=new h(this.view,this.view,{name:ah,drawables:this.drawables});var af=this.container.replace_drawable(this,ai,true);ai.request_draw()},add_drawable:function(af){A.prototype.add_drawable.call(this,af);this.update_icons()},remove_drawable:function(af){A.prototype.remove_drawable.call(this,af);this.update_icons()},to_dict:function(){if(this.filters_manager.visible()){this._restore_filter_managers()}var af=r(A.prototype.to_dict.call(this),{filters:this.filters_manager.to_dict()});if(this.filters_manager.visible()){this.setup_multitrack_filtering()}return af},request_draw:function(af,ah){for(var ag=0;ag<this.drawables.length;ag++){this.drawables[ag].request_draw(af,ah)}}});var ac=function(af){r(af,{obj_type:"View"});A.call(this,"View",af.container,af);this.chrom=null;this.vis_id=af.vis_id;this.dbkey=af.dbkey;this.label_tracks=[];this.tracks_to_be_redrawn=[];this.max_low=0;this.max_high=0;this.zoom_factor=3;this.min_separation=30;this.has_changes=false;this.load_chroms_deferred=null;this.init();this.canvas_manager=new CanvasManager(this.container.get(0).ownerDocument);this.reset()};ae.extend(ac.prototype,Backbone.Events);r(ac.prototype,A.prototype,{init:function(){this.requested_redraw=false;var ah=this.container,af=this;this.top_container=$("<div/>").addClass("top-container").appendTo(ah);this.browser_content_div=$("<div/>").addClass("content").css("position","relative").appendTo(ah);this.bottom_container=$("<div/>").addClass("bottom-container").appendTo(ah);this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(this.top_container);this.viewport_container=$("<div/>").addClass("viewport-container").attr("id","viewport-container").appendTo(this.browser_content_div);this.content_div=this.viewport_container;l(this.viewport_container,af);this.intro_div=$("<div/>").addClass("intro").appendTo(this.viewport_container).hide();var ai=$("<div/>").text("Add Datasets to Visualization").addClass("action-button").appendTo(this.intro_div).click(function(){add_datasets(add_datasets_url,add_track_async_url,function(aj){ae.each(aj,function(ak){af.add_drawable(q(ak,af,af))})})});this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.bottom_container);this.nav_container=$("<div/>").addClass("trackster-nav-container").prependTo(this.top_container);this.nav=$("<div/>").addClass("trackster-nav").appendTo(this.nav_container);this.overview=$("<div/>").addClass("overview").appendTo(this.bottom_container);this.overview_viewport=$("<div/>").addClass("overview-viewport").appendTo(this.overview);this.overview_close=$("<a/>").attr("href","javascript:void(0);").attr("title","Close overview").addClass("icon-button overview-close tooltip").hide().appendTo(this.overview_viewport);this.overview_highlight=$("<div/>").addClass("overview-highlight").hide().appendTo(this.overview_viewport);this.overview_box_background=$("<div/>").addClass("overview-boxback").appendTo(this.overview_viewport);this.overview_box=$("<div/>").addClass("overview-box").appendTo(this.overview_viewport);this.default_overview_height=this.overview_box.height();this.nav_controls=$("<div/>").addClass("nav-controls").appendTo(this.nav);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").append("<option value=''>Loading</option>").appendTo(this.nav_controls);var ag=function(aj){if(aj.type==="focusout"||(aj.keyCode||aj.which)===13||(aj.keyCode||aj.which)===27){if((aj.keyCode||aj.which)!==27){af.go_to($(this).val())}$(this).hide();$(this).val("");af.location_span.show();af.chrom_select.show()}};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keyup focusout",ag).appendTo(this.nav_controls);this.location_span=$("<span/>").addClass("location").attr("original-title","Click to change location").tooltip({placement:"bottom"}).appendTo(this.nav_controls);this.location_span.click(function(){af.location_span.hide();af.chrom_select.hide();af.nav_input.val(af.chrom+":"+af.low+"-"+af.high);af.nav_input.css("display","inline-block");af.nav_input.select();af.nav_input.focus();af.nav_input.autocomplete({source:function(al,aj){var am=[],ak=$.map(af.get_drawables(),function(an){return an.data_manager.search_features(al.term).success(function(ao){am=am.concat(ao)})});$.when.apply($,ak).done(function(){aj($.map(am,function(an){return{label:an[0],value:an[1]}}))})}})});if(this.vis_id!==undefined){this.hidden_input=$("<input/>").attr("type","hidden").val(this.vis_id).appendTo(this.nav_controls)}this.zo_link=$("<a/>").attr("id","zoom-out").attr("title","Zoom out").tooltip({placement:"bottom"}).click(function(){af.zoom_out();af.request_redraw()}).appendTo(this.nav_controls);this.zi_link=$("<a/>").attr("id","zoom-in").attr("title","Zoom in").tooltip({placement:"bottom"}).click(function(){af.zoom_in();af.request_redraw()}).appendTo(this.nav_controls);this.load_chroms_deferred=this.load_chroms({low:0});this.chrom_select.bind("change",function(){af.change_chrom(af.chrom_select.val())});this.browser_content_div.click(function(aj){$(this).find("input").trigger("blur")});this.browser_content_div.bind("dblclick",function(aj){af.zoom_in(aj.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(aj,ak){this.current_x=ak.offsetX}).bind("drag",function(aj,al){var am=al.offsetX-this.current_x;this.current_x=al.offsetX;var ak=Math.round(am/af.viewport_container.width()*(af.max_high-af.max_low));af.move_delta(-ak)});this.overview_close.click(function(){af.reset_overview()});this.viewport_container.bind("draginit",function(aj,ak){if(aj.clientX>af.viewport_container.width()-16){return false}}).bind("dragstart",function(aj,ak){ak.original_low=af.low;ak.current_height=aj.clientY;ak.current_x=ak.offsetX}).bind("drag",function(al,an){var aj=$(this);var ao=an.offsetX-an.current_x;var ak=aj.scrollTop()-(al.clientY-an.current_height);aj.scrollTop(ak);an.current_height=al.clientY;an.current_x=an.offsetX;var am=Math.round(ao/af.viewport_container.width()*(af.high-af.low));af.move_delta(am)}).bind("mousewheel",function(al,an,ak,aj){if(ak){ak*=50;var am=Math.round(-ak/af.viewport_container.width()*(af.high-af.low));af.move_delta(am)}});this.top_labeltrack.bind("dragstart",function(aj,ak){return $("<div />").css({height:af.browser_content_div.height()+af.top_labeltrack.height()+af.nav_labeltrack.height()+1,top:"0px",position:"absolute","background-color":"#ccf",opacity:0.5,"z-index":1000}).appendTo($(this))}).bind("drag",function(an,ao){$(ao.proxy).css({left:Math.min(an.pageX,ao.startX)-af.container.offset().left,width:Math.abs(an.pageX-ao.startX)});var ak=Math.min(an.pageX,ao.startX)-af.container.offset().left,aj=Math.max(an.pageX,ao.startX)-af.container.offset().left,am=(af.high-af.low),al=af.viewport_container.width();af.update_location(Math.round(ak/al*am)+af.low,Math.round(aj/al*am)+af.low)}).bind("dragend",function(ao,ap){var ak=Math.min(ao.pageX,ap.startX),aj=Math.max(ao.pageX,ap.startX),am=(af.high-af.low),al=af.viewport_container.width(),an=af.low;af.low=Math.round(ak/al*am)+an;af.high=Math.round(aj/al*am)+an;$(ap.proxy).remove();af.request_redraw()});this.add_label_track(new ab(this,{content_div:this.top_labeltrack}));this.add_label_track(new ab(this,{content_div:this.nav_labeltrack}));$(window).bind("resize",function(){if(this.resize_timer){clearTimeout(this.resize_timer)}this.resize_timer=setTimeout(function(){af.resize_window()},500)});$(document).bind("redraw",function(){af.redraw()});this.reset();$(window).trigger("resize")},changed:function(){this.has_changes=true},update_intro_div:function(){if(this.drawables.length===0){this.intro_div.show()}else{this.intro_div.hide()}},trigger_navigate:function(ag,ai,af,aj){if(this.timer){clearTimeout(this.timer)}if(aj){var ah=this;this.timer=setTimeout(function(){ah.trigger("navigate",ag+":"+ai+"-"+af)},500)}else{view.trigger("navigate",ag+":"+ai+"-"+af)}},update_location:function(af,ah){this.location_span.text(commatize(af)+" - "+commatize(ah));this.nav_input.val(this.chrom+":"+commatize(af)+"-"+commatize(ah));var ag=view.chrom_select.val();if(ag!==""){this.trigger_navigate(ag,view.low,view.high,true)}},load_chroms:function(ah){ah.num=x;var af=this,ag=$.Deferred();$.ajax({url:chrom_url+"/"+this.dbkey,data:ah,dataType:"json",success:function(aj){if(aj.chrom_info.length===0){return}if(aj.reference){af.add_label_track(new C(af))}af.chrom_data=aj.chrom_info;var am='<option value="">Select Chrom/Contig</option>';for(var al=0,ai=af.chrom_data.length;al<ai;al++){var ak=af.chrom_data[al].chrom;am+='<option value="'+ak+'">'+ak+"</option>"}if(aj.prev_chroms){am+='<option value="previous">Previous '+x+"</option>"}if(aj.next_chroms){am+='<option value="next">Next '+x+"</option>"}af.chrom_select.html(am);af.chrom_start_index=aj.start_index;ag.resolve(aj)},error:function(){alert("Could not load chroms for this dbkey:",af.dbkey)}});return ag},change_chrom:function(ak,ag,am){var ah=this;if(!ah.chrom_data){ah.load_chroms_deferred.then(function(){ah.change_chrom(ak,ag,am)});return}if(!ak||ak==="None"){return}if(ak==="previous"){ah.load_chroms({low:this.chrom_start_index-x});return}if(ak==="next"){ah.load_chroms({low:this.chrom_start_index+x});return}var al=$.grep(ah.chrom_data,function(an,ao){return an.chrom===ak})[0];if(al===undefined){ah.load_chroms({chrom:ak},function(){ah.change_chrom(ak,ag,am)});return}else{if(ak!==ah.chrom){ah.chrom=ak;ah.chrom_select.val(ah.chrom);ah.max_high=al.len-1;ah.reset();ah.request_redraw(true);for(var aj=0,af=ah.drawables.length;aj<af;aj++){var ai=ah.drawables[aj];if(ai.init){ai.init()}}if(ah.reference_track){ah.reference_track.init()}}if(ag!==undefined&&am!==undefined){ah.low=Math.max(ag,0);ah.high=Math.min(am,ah.max_high)}else{ah.low=0;ah.high=ah.max_high}ah.reset_overview();ah.request_redraw()}},go_to:function(aj){aj=aj.replace(/ |,/g,"");var an=this,af,ai,ag=aj.split(":"),al=ag[0],am=ag[1];if(am!==undefined){try{var ak=am.split("-");af=parseInt(ak[0],10);ai=parseInt(ak[1],10)}catch(ah){return false}}an.change_chrom(al,af,ai)},move_fraction:function(ah){var af=this;var ag=af.high-af.low;this.move_delta(ah*ag)},move_delta:function(ai){var af=this;var ah=af.high-af.low;if(af.low-ai<af.max_low){af.low=af.max_low;af.high=af.max_low+ah}else{if(af.high-ai>af.max_high){af.high=af.max_high;af.low=af.max_high-ah}else{af.high-=ai;af.low-=ai}}af.request_redraw();var ag=af.chrom_select.val();this.trigger_navigate(ag,af.low,af.high,true)},add_drawable:function(af){A.prototype.add_drawable.call(this,af);af.init();this.changed();this.update_intro_div()},add_label_track:function(af){af.view=this;af.init();this.label_tracks.push(af)},remove_drawable:function(ah,ag){A.prototype.remove_drawable.call(this,ah);if(ag){var af=this;ah.container_div.hide(0,function(){$(this).remove();af.update_intro_div()})}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},request_redraw:function(an,af,am,ao){var al=this,ak=(ao?[ao]:al.drawables),ah;var ag;for(var aj=0;aj<ak.length;aj++){ag=ak[aj];ah=-1;for(var ai=0;ai<al.tracks_to_be_redrawn.length;ai++){if(al.tracks_to_be_redrawn[ai][0]===ag){ah=ai;break}}if(ah<0){al.tracks_to_be_redrawn.push([ag,af,am])}else{al.tracks_to_be_redrawn[aj][1]=af;al.tracks_to_be_redrawn[aj][2]=am}}if(!this.requested_redraw){requestAnimationFrame(function(){al._redraw(an)});this.requested_redraw=true}},_redraw:function(ap){this.requested_redraw=false;var am=this.low,ai=this.high;if(am<this.max_low){am=this.max_low}if(ai>this.max_high){ai=this.max_high}var ao=this.high-this.low;if(this.high!==0&&ao<this.min_separation){ai=am+this.min_separation}this.low=Math.floor(am);this.high=Math.ceil(ai);this.update_location(this.low,this.high);this.resolution_b_px=(this.high-this.low)/this.viewport_container.width();this.resolution_px_b=this.viewport_container.width()/(this.high-this.low);var af=(this.low/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var al=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var aq=13;this.overview_box.css({left:af,width:Math.max(aq,al)}).show();if(al<aq){this.overview_box.css("left",af-(aq-al)/2)}if(this.overview_highlight){this.overview_highlight.css({left:af,width:al})}if(!ap){var ah,ag,an;for(var aj=0,ak=this.tracks_to_be_redrawn.length;aj<ak;aj++){ah=this.tracks_to_be_redrawn[aj][0];ag=this.tracks_to_be_redrawn[aj][1];an=this.tracks_to_be_redrawn[aj][2];if(ah){ah._draw(ag,an)}}this.tracks_to_be_redrawn=[];for(aj=0,ak=this.label_tracks.length;aj<ak;aj++){this.label_tracks[aj]._draw()}}},zoom_in:function(ag,ah){if(this.max_high===0||this.high-this.low<=this.min_separation){return}var ai=this.high-this.low,aj=ai/2+this.low,af=(ai/this.zoom_factor)/2;if(ag){aj=ag/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(aj-af);this.high=Math.round(aj+af);this.changed();this.request_redraw()},zoom_out:function(){if(this.max_high===0){return}var ag=this.high-this.low,ah=ag/2+this.low,af=(ag*this.zoom_factor)/2;this.low=Math.round(ah-af);this.high=Math.round(ah+af);this.changed();this.request_redraw()},resize_window:function(){this.viewport_container.height(this.container.height()-this.top_container.height()-this.bottom_container.height());this.request_redraw()},set_overview:function(ah){if(this.overview_drawable){if(this.overview_drawable.dataset_id===ah.dataset_id){return}this.overview_viewport.find(".track").remove()}var ag=ah.copy({content_div:this.overview_viewport}),af=this;ag.header_div.hide();ag.is_overview=true;af.overview_drawable=ag;this.overview_drawable.postdraw_actions=function(){af.overview_highlight.show().height(af.overview_drawable.content_div.height());af.overview_viewport.height(af.overview_drawable.content_div.height()+af.overview_box.outerHeight());af.overview_close.show();af.resize_window()};af.overview_drawable.request_draw();this.changed()},reset_overview:function(){$(".bs-tooltip").remove();this.overview_viewport.find(".track-tile").remove();this.overview_viewport.height(this.default_overview_height);this.overview_box.height(this.default_overview_height);this.overview_close.hide();this.overview_highlight.hide();view.resize_window();view.overview_drawable=null}});var t=function(ah,am,ai){this.track=ah;this.name=am.name;this.params=[];var au=am.params;for(var aj=0;aj<au.length;aj++){var ao=au[aj],ag=ao.name,at=ao.label,ak=unescape(ao.html),av=ao.value,aq=ao.type;if(aq==="number"){this.params.push(new f(ag,at,ak,(ag in ai?ai[ag]:av),ao.min,ao.max))}else{if(aq==="select"){this.params.push(new O(ag,at,ak,(ag in ai?ai[ag]:av)))}else{console.log("WARNING: unrecognized tool parameter type:",ag,aq)}}}this.parent_div=$("<div/>").addClass("dynamic-tool").hide();this.parent_div.bind("drag",function(ax){ax.stopPropagation()}).click(function(ax){ax.stopPropagation()}).bind("dblclick",function(ax){ax.stopPropagation()});var ar=$("<div class='tool-name'>").appendTo(this.parent_div).text(this.name);var ap=this.params;var an=this;$.each(this.params,function(ay,aB){var aA=$("<div>").addClass("param-row").appendTo(an.parent_div);var ax=$("<div>").addClass("param-label").text(aB.label).appendTo(aA);var az=$("<div/>").addClass("param-input").html(aB.html).appendTo(aA);az.find(":input").val(aB.value);$("<div style='clear: both;'/>").appendTo(aA)});this.parent_div.find("input").click(function(){$(this).select()});var aw=$("<div>").addClass("param-row").appendTo(this.parent_div);var al=$("<input type='submit'>").attr("value","Run on complete dataset").appendTo(aw);var af=$("<input type='submit'>").attr("value","Run on visible region").css("margin-left","3em").appendTo(aw);af.click(function(){an.run_on_region()});al.click(function(){an.run_on_dataset()});if("visible" in ai&&ai.visible){this.parent_div.show()}};r(t.prototype,{update_params:function(){for(var af=0;af<this.params.length;af++){this.params[af].update_value()}},state_dict:function(){var ag={};for(var af=0;af<this.params.length;af++){ag[this.params[af].name]=this.params[af].value}ag.visible=this.parent_div.is(":visible");return ag},get_param_values_dict:function(){var af={};this.parent_div.find(":input").each(function(){var ag=$(this).attr("name"),ah=$(this).val();af[ag]=ah});return af},get_param_values:function(){var af=[];this.parent_div.find(":input").each(function(){var ag=$(this).attr("name"),ah=$(this).val();if(ag){af[af.length]=ah}});return af},run_on_dataset:function(){var af=this;af.run({target_dataset_id:this.track.original_dataset_id,tool_id:af.name},null,function(ag){show_modal(af.name+" is Running",af.name+" is running on the complete dataset. Tool outputs are in dataset's history.",{Close:hide_modal})})},run_on_region:function(){var ag={target_dataset_id:this.track.original_dataset_id,action:"rerun",tool_id:this.name,regions:[{chrom:this.track.view.chrom,start:this.track.view.low,end:this.track.view.high}]},ak=this.track,ah=ag.tool_id+ak.tool_region_and_parameters_str(ag.chrom,ag.low,ag.high),af;if(ak.container===view){var aj=new Q(view,view,{name:this.name});var ai=ak.container.replace_drawable(ak,aj,false);aj.container_div.insertBefore(ak.view.content_div.children()[ai]);aj.add_drawable(ak);ak.container_div.appendTo(aj.content_div);af=aj}else{af=ak.container}var al=new ak.constructor(view,af,{name:ah,hda_ldda:"hda"});al.init_for_tool_data();al.change_mode(ak.mode);al.set_filters_manager(ak.filters_manager.copy(al));al.update_icons();af.add_drawable(al);al.tiles_div.text("Starting job.");this.update_params();this.run(ag,al,function(am){al.set_dataset(new Dataset(am));al.tiles_div.text("Running job.");al.init()})},run:function(af,ah,ai){af.inputs=this.get_param_values_dict();var ag=new ServerStateDeferred({ajax_settings:{url:galaxy_paths.get("tool_url"),data:JSON.stringify(af),dataType:"json",contentType:"application/json",type:"POST"},interval:2000,success_fn:function(aj){return aj!=="pending"}});$.when(ag.go()).then(function(aj){if(aj==="no converter"){ah.container_div.addClass("error");ah.content_div.text(K)}else{if(aj.error){ah.container_div.addClass("error");ah.content_div.text(z+aj.message)}else{ai(aj)}}})}});var O=function(ag,af,ah,ai){this.name=ag;this.label=af;this.html=$(ah);this.value=ai};r(O.prototype,{update_value:function(){this.value=$(this.html).val()}});var f=function(ah,ag,aj,ak,ai,af){O.call(this,ah,ag,aj,ak);this.min=ai;this.max=af};r(f.prototype,O.prototype,{update_value:function(){O.prototype.update_value.call(this);this.value=parseFloat(this.value)}});var g=function(af){this.manager=null;this.name=af.name;this.index=af.index;this.tool_id=af.tool_id;this.tool_exp_name=af.tool_exp_name};r(g.prototype,{to_dict:function(){return{name:this.name,index:this.index,tool_id:this.tool_id,tool_exp_name:this.tool_exp_name}}});var c=function(ah,ag,af){return $("<a/>").attr("href","javascript:void(0);").attr("title",ah).addClass("icon-button").addClass(ag).tooltip().click(af)};var V=function(an){g.call(this,an);this.low=("low" in an?an.low:-Number.MAX_VALUE);this.high=("high" in an?an.high:Number.MAX_VALUE);this.min=("min" in an?an.min:Number.MAX_VALUE);this.max=("max" in an?an.max:-Number.MAX_VALUE);this.container=null;this.slider=null;this.slider_label=null;var aj=function(ao,ap,aq){ao.click(function(){var aw=ap.text(),au=parseFloat(aq.slider("option","max")),at=(au<=1?4:au<=1000000?au.toString().length:6),av=false,ar=$(this).parents(".slider-row");ar.addClass("input");if(aq.slider("option","values")){at=2*at+1;av=true}ap.text("");$("<input type='text'/>").attr("size",at).attr("maxlength",at).attr("value",aw).appendTo(ap).focus().select().click(function(ax){ax.stopPropagation()}).blur(function(){$(this).remove();ap.text(aw);ar.removeClass("input")}).keyup(function(aB){if(aB.keyCode===27){$(this).trigger("blur")}else{if(aB.keyCode===13){var az=aq.slider("option","min"),ax=aq.slider("option","max"),aA=function(aC){return(isNaN(aC)||aC>ax||aC<az)},ay=$(this).val();if(!av){ay=parseFloat(ay);if(aA(ay)){alert("Parameter value must be in the range ["+az+"-"+ax+"]");return $(this)}}else{ay=ay.split("-");ay=[parseFloat(ay[0]),parseFloat(ay[1])];if(aA(ay[0])||aA(ay[1])){alert("Parameter value must be in the range ["+az+"-"+ax+"]");return $(this)}}aq.slider((av?"values":"value"),ay);ar.removeClass("input")}}})})};var ag=this;ag.parent_div=$("<div/>").addClass("filter-row slider-row");var af=$("<div/>").addClass("elt-label").appendTo(ag.parent_div),al=$("<span/>").addClass("slider-name").text(ag.name+" ").appendTo(af),ah=$("<span/>").text(this.low+"-"+this.high),ai=$("<span/>").addClass("slider-value").appendTo(af).append("[").append(ah).append("]");ag.values_span=ah;var ak=$("<div/>").addClass("slider").appendTo(ag.parent_div);ag.control_element=$("<div/>").attr("id",ag.name+"-filter-control").appendTo(ak);ag.control_element.slider({range:true,min:this.min,max:this.max,step:this.get_slider_step(this.min,this.max),values:[this.low,this.high],slide:function(ao,ap){ag.slide(ao,ap)},change:function(ao,ap){ag.control_element.slider("option","slide").call(ag.control_element,ao,ap)}});ag.slider=ag.control_element;ag.slider_label=ah;aj(ai,ah,ag.control_element);var am=$("<div/>").addClass("display-controls").appendTo(ag.parent_div);this.transparency_icon=c("Use filter for data transparency","layer-transparent",function(){if(ag.manager.alpha_filter!==ag){ag.manager.alpha_filter=ag;ag.manager.parent_div.find(".layer-transparent").removeClass("active").hide();ag.transparency_icon.addClass("active").show()}else{ag.manager.alpha_filter=null;ag.transparency_icon.removeClass("active")}ag.manager.track.request_draw(true,true)}).appendTo(am).hide();this.height_icon=c("Use filter for data height","arrow-resize-090",function(){if(ag.manager.height_filter!==ag){ag.manager.height_filter=ag;ag.manager.parent_div.find(".arrow-resize-090").removeClass("active").hide();ag.height_icon.addClass("active").show()}else{ag.manager.height_filter=null;ag.height_icon.removeClass("active")}ag.manager.track.request_draw(true,true)}).appendTo(am).hide();ag.parent_div.hover(function(){ag.transparency_icon.show();ag.height_icon.show()},function(){if(ag.manager.alpha_filter!==ag){ag.transparency_icon.hide()}if(ag.manager.height_filter!==ag){ag.height_icon.hide()}});$("<div style='clear: both;'/>").appendTo(ag.parent_div)};r(V.prototype,{to_dict:function(){var af=g.prototype.to_dict.call(this);return r(af,{type:"number",min:this.min,max:this.max,low:this.low,high:this.high})},copy:function(){return new V({name:this.name,index:this.index,tool_id:this.tool_id,tool_exp_name:this.tool_exp_name})},get_slider_step:function(ah,af){var ag=af-ah;return(ag<=2?0.01:1)},slide:function(ah,ai){var ag=ai.values;this.values_span.text(ag[0]+"-"+ag[1]);this.low=ag[0];this.high=ag[1];var af=this;setTimeout(function(){if(ag[0]===af.low&&ag[1]===af.high){af.manager.track.request_draw(true,true)}},25)},applies_to:function(af){if(af.length>this.index){return true}return false},_keep_val:function(af){return(isNaN(af)||(af>=this.low&&af<=this.high))},keep:function(ag){if(!this.applies_to(ag)){return true}var ai=this;var aj=ag[this.index];if(aj instanceof Array){var ah=true;for(var af=0;af<aj.length;af++){if(!this._keep_val(aj[af])){ah=false;break}}return ah}else{return this._keep_val(ag[this.index])}},update_attrs:function(ai){var af=false;if(!this.applies_to(ai)){return af}var ag=ai[this.index];if(!(ag instanceof Array)){ag=[ag]}for(var ah=0;ah<ag.length;ah++){var aj=ag[ah];if(aj<this.min){this.min=Math.floor(aj);af=true}if(aj>this.max){this.max=Math.ceil(aj);af=true}}return af},update_ui_elt:function(){if(this.min<this.max){this.parent_div.show()}else{this.parent_div.hide()}var ag=this.slider.slider("option","min"),af=this.slider.slider("option","max");if(this.min<ag||this.max>af){this.slider.slider("option","min",this.min);this.slider.slider("option","max",this.max);this.slider.slider("option","step",this.get_slider_step(this.min,this.max));this.slider.slider("option","values",[this.min,this.max])}}});var aa=function(ah,an){this.track=ah;this.alpha_filter=null;this.height_filter=null;this.filters=[];this.parent_div=$("<div/>").addClass("filters").hide();this.parent_div.bind("drag",function(ap){ap.stopPropagation()}).click(function(ap){ap.stopPropagation()}).bind("dblclick",function(ap){ap.stopPropagation()}).bind("keydown",function(ap){ap.stopPropagation()});if(an&&"filters" in an){var af=("alpha_filter" in an?an.alpha_filter:null),ai=("height_filter" in an?an.height_filter:null),ak=an.filters,ag;for(var al=0;al<ak.length;al++){if(ak[al].type==="number"){ag=new V(ak[al]);this.add_filter(ag);if(ag.name===af){this.alpha_filter=ag;ag.transparency_icon.addClass("active").show()}if(ag.name===ai){this.height_filter=ag;ag.height_icon.addClass("active").show()}}else{console.log("ERROR: unsupported filter: ",name,type)}}if("visible" in an&&an.visible){this.parent_div.show()}}if(this.filters.length!==0){var ao=$("<div/>").addClass("param-row").appendTo(this.parent_div);var am=$("<input type='submit'/>").attr("value","Run on complete dataset").appendTo(ao);var aj=this;am.click(function(){aj.run_on_dataset()})}};r(aa.prototype,{show:function(){this.parent_div.show()},hide:function(){this.parent_div.hide()},toggle:function(){this.parent_div.toggle()},visible:function(){return this.parent_div.is(":visible")},to_dict:function(){var ai={},ah=[],ag;for(var af=0;af<this.filters.length;af++){ag=this.filters[af];ah.push(ag.to_dict())}ai.filters=ah;ai.alpha_filter=(this.alpha_filter?this.alpha_filter.name:null);ai.height_filter=(this.height_filter?this.height_filter.name:null);ai.visible=this.parent_div.is(":visible");return ai},copy:function(ag){var ah=new aa(ag);for(var af=0;af<this.filters.length;af++){ah.add_filter(this.filters[af].copy())}return ah},add_filter:function(af){af.manager=this;this.parent_div.append(af.parent_div);this.filters.push(af)},remove_all:function(){this.filters=[];this.parent_div.children().remove()},init_filters:function(){for(var af=0;af<this.filters.length;af++){var ag=this.filters[af];ag.update_ui_elt()}},clear_filters:function(){for(var af=0;af<this.filters.length;af++){var ag=this.filters[af];ag.slider.slider("option","values",[ag.min,ag.max])}this.alpha_filter=null;this.height_filter=null;this.parent_div.find(".icon-button").hide()},run_on_dataset:function(){var al=function(ap,an,ao){if(!(an in ap)){ap[an]=ao}return ap[an]};var ak={},am,af;for(var aj=0;aj<this.filters.length;aj++){am=this.filters[aj];if(am.tool_id){if(am.min!==am.low){af=al(ak,am.tool_id,[]);af[af.length]=am.tool_exp_name+" >= "+am.low}if(am.max!==am.high){af=al(ak,am.tool_id,[]);af[af.length]=am.tool_exp_name+" <= "+am.high}}}var ag=[];for(var ai in ak){ag[ag.length]=[ai,ak[ai]]}(function ah(au,aq){var ao=aq[0],ap=ao[0],at=ao[1],ar="("+at.join(") and (")+")",an={cond:ar,input:au,target_dataset_id:au,tool_id:ap},aq=aq.slice(1);$.getJSON(run_tool_url,an,function(av){if(av.error){show_modal("Filter Dataset","Error running tool "+ap,{Close:hide_modal})}else{if(aq.length===0){show_modal("Filtering Dataset","Filter(s) are running on the complete dataset. Outputs are in dataset's history.",{Close:hide_modal})}else{ah(av.dataset_id,aq)}}})})(this.track.dataset_id,ag)}});var D=function(af,ag){M.Scaler.call(this,ag);this.filter=af};D.prototype.gen_val=function(af){if(this.filter.high===Number.MAX_VALUE||this.filter.low===-Number.MAX_VALUE||this.filter.low===this.filter.high){return this.default_val}return((parseFloat(af[this.filter.index])-this.filter.low)/(this.filter.high-this.filter.low))};var G=function(af){this.track=af.track;this.params=af.params;this.values={};this.restore_values((af.saved_values?af.saved_values:{}));this.onchange=af.onchange};r(G.prototype,{restore_values:function(af){var ag=this;$.each(this.params,function(ah,ai){if(af[ai.key]!==undefined){ag.values[ai.key]=af[ai.key]}else{ag.values[ai.key]=ai.default_value}})},build_form:function(){var ai=this;var af=$("<div />");var ah;function ag(an,aj){for(var ar=0;ar<an.length;ar++){ah=an[ar];if(ah.hidden){continue}var al="param_"+ar;var aw=ai.values[ah.key];var ay=$("<div class='form-row' />").appendTo(aj);ay.append($("<label />").attr("for",al).text(ah.label+":"));if(ah.type==="bool"){ay.append($('<input type="checkbox" />').attr("id",al).attr("name",al).attr("checked",aw))}else{if(ah.type==="text"){ay.append($('<input type="text"/>').attr("id",al).val(aw).click(function(){$(this).select()}))}else{if(ah.type==="select"){var au=$("<select />").attr("id",al);for(var ap=0;ap<ah.options.length;ap++){$("<option/>").text(ah.options[ap].label).attr("value",ah.options[ap].value).appendTo(au)}au.val(aw);ay.append(au)}else{if(ah.type==="color"){var ax=$("<div/>").appendTo(ay),at=$("<input />").attr("id",al).attr("name",al).val(aw).css("float","left").appendTo(ax).click(function(aA){$(".bs-tooltip").removeClass("in");var az=$(this).siblings(".bs-tooltip").addClass("in");az.css({left:$(this).position().left+$(this).width()+5,top:$(this).position().top-($(az).height()/2)+($(this).height()/2)}).show();az.click(function(aB){aB.stopPropagation()});$(document).bind("click.color-picker",function(){az.hide();$(document).unbind("click.color-picker")});aA.stopPropagation()}),aq=$("<a href='javascript:void(0)'/>").addClass("icon-button arrow-circle").appendTo(ax).attr("title","Set new random color").tooltip(),av=$("<div class='bs-tooltip right' style='position: absolute;' />").appendTo(ax).hide(),am=$("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(av),ak=$("<div class='tooltip-arrow'></div>").appendTo(av),ao=$.farbtastic(am,{width:100,height:100,callback:at,color:aw});ax.append($("<div/>").css("clear","both"));(function(az){aq.click(function(){az.setColor(R())})})(ao)}else{ay.append($("<input />").attr("id",al).attr("name",al).val(aw))}}}}if(ah.help){ay.append($("<div class='help'/>").text(ah.help))}}}ag(this.params,af);return af},update_from_form:function(af){var ah=this;var ag=false;$.each(this.params,function(ai,ak){if(!ak.hidden){var al="param_"+ai;var aj=af.find("#"+al).val();if(ak.type==="float"){aj=parseFloat(aj)}else{if(ak.type==="int"){aj=parseInt(aj)}else{if(ak.type==="bool"){aj=af.find("#"+al).is(":checked")}}}if(aj!==ah.values[ak.key]){ah.values[ak.key]=aj;ag=true}}});if(ag){this.onchange();this.track.changed()}}});var b=function(af,aj,ah,ag,ai){this.track=af;this.region=aj;this.low=aj.get("start");this.high=aj.get("end");this.resolution=ah;this.html_elt=$("<div class='track-tile'/>").append(ag).height($(ag).attr("height"));this.data=ai;this.stale=false};b.prototype.predisplay_actions=function(){};var k=function(af,ak,ah,ag,ai,aj){b.call(this,af,ak,ah,ag,ai);this.max_val=aj};r(k.prototype,b.prototype);var P=function(ai,aq,aj,ah,al,at,am,au,ag,ap){b.call(this,ai,aq,aj,ah,al);this.mode=am;this.all_slotted=ag;this.feature_mapper=ap;this.has_icons=false;if(au){this.has_icons=true;var an=this;ah=this.html_elt.children()[0],message_div=$("<div/>").addClass("tile-message").css({height:E-1,width:ah.width}).prependTo(this.html_elt);var ao=new GenomeRegion({chrom:ai.view.chrom,start:this.low,end:this.high}),ar=al.length,ak=$("<a href='javascript:void(0);'/>").addClass("icon more-down").attr("title","For speed, only the first "+ar+" features in this region were obtained from server. Click to get more data including depth").tooltip().appendTo(message_div),af=$("<a href='javascript:void(0);'/>").addClass("icon more-across").attr("title","For speed, only the first "+ar+" features in this region were obtained from server. Click to get more data excluding depth").tooltip().appendTo(message_div);ak.click(function(){an.stale=true;ai.data_manager.get_more_data(ao,ai.mode,an.resolution,{},ai.data_manager.DEEP_DATA_REQ);$(".bs-tooltip").hide();ai.request_draw(true)}).dblclick(function(av){av.stopPropagation()});af.click(function(){an.stale=true;ai.data_manager.get_more_data(ao,ai.mode,an.resolution,{},ai.data_manager.BROAD_DATA_REQ);$(".bs-tooltip").hide();ai.request_draw(true)}).dblclick(function(av){av.stopPropagation()})}};r(P.prototype,b.prototype);P.prototype.predisplay_actions=function(){var ag=this,af={};if(ag.mode!=="Pack"){return}$(this.html_elt).hover(function(){this.hovered=true;$(this).mousemove()},function(){this.hovered=false;$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()}).mousemove(function(ar){if(!this.hovered){return}var am=$(this).offset(),aq=ar.pageX-am.left,ap=ar.pageY-am.top,aw=ag.feature_mapper.get_feature_data(aq,ap),an=(aw?aw[0]:null);$(this).parents(".track-content").children(".overlay").children(".feature-popup").each(function(){if(!an||$(this).attr("id")!==an.toString()){$(this).remove()}});if(aw){var ai=af[an];if(!ai){var an=aw[0],at={name:aw[3],start:aw[1],end:aw[2],strand:aw[4]},al=ag.track.filters_manager.filters,ak;for(var ao=0;ao<al.length;ao++){ak=al[ao];at[ak.name]=aw[ak.index]}var ai=$("<div/>").attr("id",an).addClass("feature-popup"),ax=$("<table/>"),av,au,ay;for(av in at){au=at[av];ay=$("<tr/>").appendTo(ax);$("<th/>").appendTo(ay).text(av);$("<td/>").attr("align","left").appendTo(ay).text(typeof(au)==="number"?Z(au,2):au)}ai.append($("<div class='feature-popup-inner'>").append(ax));af[an]=ai}ai.appendTo($(this).parents(".track-content").children(".overlay"));var aj=aq+parseInt(ag.html_elt.css("left"))-ai.width()/2,ah=ap+parseInt(ag.html_elt.css("top"))+7;ai.css("left",aj+"px").css("top",ah+"px")}else{if(!ar.isPropagationStopped()){ar.stopPropagation();$(this).siblings().each(function(){$(this).trigger(ar)})}}}).mouseleave(function(){$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()})};var i=function(ag,af,ah){r(ah,{drag_handle_class:"draghandle"});s.call(this,ag,af,ah);this.dataset=new Dataset({id:ah.dataset_id,hda_ldda:ah.hda_ldda});this.dataset_check_type="converted_datasets_state";this.data_url_extra_params={};this.data_query_wait=("data_query_wait" in ah?ah.data_query_wait:L);this.data_manager=("data_manager" in ah?ah.data_manager:new y.GenomeDataManager({dataset:this.dataset,data_mode_compatible:this.data_and_mode_compatible,can_subset:this.can_subset}));this.min_height_px=16;this.max_height_px=800;this.visible_height_px=0;this.content_div=$("<div class='track-content'>").appendTo(this.container_div);if(this.container){this.container.content_div.append(this.container_div);if(!("resize" in ah)||ah.resize){this.add_resize_handle()}}};r(i.prototype,s.prototype,{action_icons_def:[{name:"mode_icon",title:"Set display mode",css_class:"chevron-expand",on_click_fn:function(){}},s.prototype.action_icons_def[0],{name:"overview_icon",title:"Set as overview",css_class:"overview-icon",on_click_fn:function(af){af.view.set_overview(af)}},s.prototype.action_icons_def[1],{name:"filters_icon",title:"Filters",css_class:"filters-icon",on_click_fn:function(af){if(af.filters_manager.visible()){af.filters_manager.clear_filters()}else{af.filters_manager.init_filters()}af.filters_manager.toggle()}},{name:"tools_icon",title:"Tool",css_class:"hammer",on_click_fn:function(af){af.dynamic_tool_div.toggle();if(af.dynamic_tool_div.is(":visible")){af.set_name(af.name+af.tool_region_and_parameters_str())}else{af.revert_name()}$(".bs-tooltip").remove()}},{name:"param_space_viz_icon",title:"Tool parameter space visualization",css_class:"arrow-split",on_click_fn:function(af){var ai='<strong>Tool</strong>: <%= track.tool.name %><br/><strong>Dataset</strong>: <%= track.name %><br/><strong>Region(s)</strong>: <select name="regions"><option value="cur">current viewing area</option><option value="bookmarks">bookmarks</option><option value="both">current viewing area and bookmarks</option></select>',ah=ae.template(ai,{track:af});var ak=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},ag=function(){var am=$('select[name="regions"] option:selected').val(),ao,al=new GenomeRegion({chrom:view.chrom,start:view.low,end:view.high}),an=ae.map($(".bookmark"),function(ap){return new GenomeRegion({from_str:$(ap).children(".position").text()})});if(am==="cur"){ao=[al]}else{if(am==="bookmarks"){ao=an}else{ao=[al].concat(an)}}hide_modal();window.location.href=galaxy_paths.get("sweepster_url")+"?"+$.param({dataset_id:af.dataset_id,hda_ldda:af.hda_ldda,regions:JSON.stringify(new Backbone.Collection(ao).toJSON())})},aj=function(al){if((al.keyCode||al.which)===27){ak()}else{if((al.keyCode||al.which)===13){ag()}}};show_modal("Visualize tool parameter space and output from different parameter settings?",ah,{No:ak,Yes:ag})}},s.prototype.action_icons_def[2]],can_draw:function(){if(this.dataset_id&&s.prototype.can_draw.call(this)){return true}return false},build_container_div:function(){return $("<div/>").addClass("track").attr("id","track_"+this.id).css("position","relative")},build_header_div:function(){var af=$("<div class='track-header'/>");if(this.view.editor){this.drag_div=$("<div/>").addClass(this.drag_handle_class).appendTo(af)}this.name_div=$("<div/>").addClass("track-name").appendTo(af).text(this.name).attr("id",this.name.replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase());return af},on_resize:function(){},add_resize_handle:function(){var af=this;var ai=false;var ah=false;var ag=$("<div class='track-resize'>");$(af.container_div).hover(function(){if(af.content_visible){ai=true;ag.show()}},function(){ai=false;if(!ah){ag.hide()}});ag.hide().bind("dragstart",function(aj,ak){ah=true;ak.original_height=$(af.content_div).height()}).bind("drag",function(ak,al){var aj=Math.min(Math.max(al.original_height+al.deltaY,af.min_height_px),af.max_height_px);$(af.tiles_div).css("height",aj);af.visible_height_px=(af.max_height_px===aj?0:aj);af.on_resize()}).bind("dragend",function(aj,ak){af.tile_cache.clear();ah=false;if(!ai){ag.hide()}af.config.values.height=af.visible_height_px;af.changed()}).appendTo(af.container_div)},set_display_modes:function(ai,al){this.display_modes=ai;this.mode=(al?al:(this.config&&this.config.values.mode?this.config.values.mode:this.display_modes[0]));this.action_icons.mode_icon.attr("title","Set display mode (now: "+this.mode+")");var ag=this,aj={};for(var ah=0,af=ag.display_modes.length;ah<af;ah++){var ak=ag.display_modes[ah];aj[ak]=function(am){return function(){ag.change_mode(am);ag.icons_div.show();ag.container_div.mouseleave(function(){ag.icons_div.hide()})}}(ak)}make_popupmenu(this.action_icons.mode_icon,aj)},build_action_icons:function(){s.prototype.build_action_icons.call(this,this.action_icons_def);if(this.display_modes!==undefined){this.set_display_modes(this.display_modes)}},hide_contents:function(){this.tiles_div.hide();this.container_div.find(".yaxislabel, .track-resize").hide()},show_contents:function(){this.tiles_div.show();this.container_div.find(".yaxislabel, .track-resize").show();this.request_draw()},get_type:function(){if(this instanceof ab){return"LabelTrack"}else{if(this instanceof C){return"ReferenceTrack"}else{if(this instanceof j){return"LineTrack"}else{if(this instanceof W){return"ReadTrack"}else{if(this instanceof T){return"VcfTrack"}else{if(this instanceof h){return"CompositeTrack"}else{if(this instanceof d){return"FeatureTrack"}}}}}}}return""},init:function(){var ag=this;ag.enabled=false;ag.tile_cache.clear();ag.data_manager.clear();ag.content_div.css("height","auto");ag.tiles_div.children().remove();ag.container_div.removeClass("nodata error pending");if(!ag.dataset_id){return}var af=$.Deferred(),ah={hda_ldda:ag.hda_ldda,data_type:this.dataset_check_type,chrom:ag.view.chrom};$.getJSON(this.dataset.url(),ah,function(ai){if(!ai||ai==="error"||ai.kind==="error"){ag.container_div.addClass("error");ag.tiles_div.text(p);if(ai.message){var aj=$(" <a href='javascript:void(0);'></a>").text("View error").click(function(){show_modal("Trackster Error","<pre>"+ai.message+"</pre>",{Close:hide_modal})});ag.tiles_div.append(aj)}}else{if(ai==="no converter"){ag.container_div.addClass("error");ag.tiles_div.text(K)}else{if(ai==="no data"||(ai.data!==undefined&&(ai.data===null||ai.data.length===0))){ag.container_div.addClass("nodata");ag.tiles_div.text(F)}else{if(ai==="pending"){ag.container_div.addClass("pending");ag.tiles_div.html(w);setTimeout(function(){ag.init()},ag.data_query_wait)}else{if(ai==="data"||ai.status==="data"){if(ai.valid_chroms){ag.valid_chroms=ai.valid_chroms;ag.update_icons()}ag.tiles_div.text(X);if(ag.view.chrom){ag.tiles_div.text("");ag.tiles_div.css("height",ag.visible_height_px+"px");ag.enabled=true;$.when(ag.predraw_init()).done(function(){af.resolve();ag.container_div.removeClass("nodata error pending");ag.request_draw()})}else{af.resolve()}}}}}}});this.update_icons();return af},predraw_init:function(){},get_drawables:function(){return this}});var N=function(ah,ag,ai){i.call(this,ah,ag,ai);var af=this;n(af.container_div,af.drag_handle_class,".group",af);this.filters_manager=new aa(this,("filters" in ai?ai.filters:null));this.data_manager.set("filters_manager",this.filters_manager);this.filters_available=false;this.tool=("tool" in ai&&ai.tool?new t(this,ai.tool,ai.tool_state):null);this.tile_cache=new y.Cache(S);if(this.header_div){this.set_filters_manager(this.filters_manager);if(this.tool){this.dynamic_tool_div=this.tool.parent_div;this.header_div.after(this.dynamic_tool_div)}}this.tiles_div=$("<div/>").addClass("tiles").appendTo(this.content_div);this.overlay_div=$("<div/>").addClass("overlay").appendTo(this.content_div);if(ai.mode){this.change_mode(ai.mode)}};r(N.prototype,s.prototype,i.prototype,{action_icons_def:i.prototype.action_icons_def.concat([{name:"show_more_rows_icon",title:"To minimize track height, not all feature rows are displayed. Click to display more rows.",css_class:"exclamation",on_click_fn:function(af){$(".bs-tooltip").remove();af.slotters[af.view.resolution_px_b].max_rows*=2;af.request_draw(true)},hide:true}]),copy:function(af){var ag=this.to_dict();r(ag,{data_manager:this.data_manager});var ah=new this.constructor(this.view,af,ag);ah.change_mode(this.mode);ah.enabled=this.enabled;return ah},set_filters_manager:function(af){this.filters_manager=af;this.header_div.after(this.filters_manager.parent_div)},to_dict:function(){return{track_type:this.get_type(),name:this.name,hda_ldda:this.hda_ldda,dataset_id:this.dataset_id,prefs:this.prefs,mode:this.mode,filters:this.filters_manager.to_dict(),tool_state:(this.tool?this.tool.state_dict():{})}},change_mode:function(ag){var af=this;af.mode=ag;af.config.values.mode=ag;af.tile_cache.clear();af.request_draw();this.action_icons.mode_icon.attr("title","Set display mode (now: "+af.mode+")");return af},update_icons:function(){var af=this;if(af.filters_available){af.action_icons.filters_icon.show()}else{af.action_icons.filters_icon.hide()}if(af.tool){af.action_icons.tools_icon.show();af.action_icons.param_space_viz_icon.show()}else{af.action_icons.tools_icon.hide();af.action_icons.param_space_viz_icon.hide()}},_gen_tile_cache_key:function(ag,ah,af){return ag+"_"+ah+"_"+af},request_draw:function(ag,af){this.view.request_redraw(false,ag,af,this)},before_draw:function(){},_draw:function(ag,aq){if(!this.can_draw()){return}var ao=this.view.low,ak=this.view.high,am=ak-ao,ah=this.view.container.width(),at=this.view.resolution_px_b,aj=this.view.resolution_b_px;if(this.is_overview){ao=this.view.max_low;ak=this.view.max_high;aj=(view.max_high-view.max_low)/ah;at=1/aj}this.before_draw();this.tiles_div.children().addClass("remove");var af=Math.floor(ao/(aj*U)),an=true,ar=[],al=function(au){return(au&&"track" in au)};while((af*U*aj)<ak){var ap=this.draw_helper(ag,ah,af,aj,this.tiles_div,at);if(al(ap)){ar.push(ap)}else{an=false}af+=1}if(!aq){this.tiles_div.children(".remove").removeClass("remove").remove()}var ai=this;if(an){this.tiles_div.children(".remove").remove();ai.postdraw_actions(ar,ah,at,aq)}},postdraw_actions:function(ah,ai,ak,af){var aj=false;for(var ag=0;ag<ah.length;ag++){if(ah[ag].has_icons){aj=true;break}}if(aj){for(var ag=0;ag<ah.length;ag++){tile=ah[ag];if(!tile.has_icons){tile.html_elt.css("padding-top",E)}}}},draw_helper:function(af,ar,ax,av,ak,al,at){var aq=this,aA=this._gen_tile_cache_key(ar,al,ax),ai=this._get_tile_bounds(ax,av);if(!at){at={}}var az=(af?undefined:aq.tile_cache.get_elt(aA));if(az){aq.show_tile(az,ak,al);return az}var ao=true;var aw=aq.data_manager.get_data(ai,aq.mode,av,aq.data_url_extra_params);if(Y(aw)){ao=false}var am;if(view.reference_track&&al>view.canvas_manager.char_width_px){am=view.reference_track.data_manager.get_data(ai,aq.mode,av,view.reference_track.data_url_extra_params);if(Y(am)){ao=false}}if(ao){r(aw,at.more_tile_data);var an=aq.mode;if(an==="Auto"){an=aq.get_mode(aw);aq.update_auto_mode(an)}var ah=aq.view.canvas_manager.new_canvas(),ay=ai.get("start"),ag=ai.get("end"),ar=Math.ceil((ag-ay)*al)+aq.left_offset,ap=aq.get_canvas_height(aw,an,al,ar);ah.width=ar;ah.height=ap;var au=ah.getContext("2d");au.translate(this.left_offset,0);var az=aq.draw_tile(aw,au,an,av,ai,al,am);if(az!==undefined){aq.tile_cache.set_elt(aA,az);aq.show_tile(az,ak,al)}return az}var aj=$.Deferred();$.when(aw,am).then(function(){view.request_redraw(false,false,false,aq);aj.resolve()});return aj},get_canvas_height:function(af,ah,ai,ag){return this.visible_height_px},draw_tile:function(af,ag,ak,ai,aj,al,ah){console.log("Warning: TiledTrack.draw_tile() not implemented.")},show_tile:function(ah,aj,ak){var ag=this,af=ah.html_elt;ah.predisplay_actions();var ai=(ah.low-(this.is_overview?this.view.max_low:this.view.low))*ak;if(this.left_offset){ai-=this.left_offset}af.css({position:"absolute",top:0,left:ai});if(af.hasClass("remove")){af.removeClass("remove")}else{aj.append(af)}ag.after_show_tile(ah)},after_show_tile:function(af){this.max_height_px=Math.max(this.max_height_px,af.html_elt.height());af.html_elt.parent().children().css("height",this.max_height_px+"px");var ag=this.max_height_px;if(this.visible_height_px!==0){ag=Math.min(this.max_height_px,this.visible_height_px)}this.tiles_div.css("height",ag+"px")},_get_tile_bounds:function(af,ag){var ai=Math.floor(af*U*ag),aj=Math.ceil(U*ag),ah=(ai+aj<=this.view.max_high?ai+aj:this.view.max_high);return new GenomeRegion({chrom:this.view.chrom,start:ai,end:ah})},tool_region_and_parameters_str:function(ah,af,ai){var ag=this,aj=(ah!==undefined&&af!==undefined&&ai!==undefined?ah+":"+af+"-"+ai:"all");return" - region=["+aj+"], parameters=["+ag.tool.get_param_values().join(", ")+"]"},data_and_mode_compatible:function(af,ag){return true},can_subset:function(af){return false},init_for_tool_data:function(){this.data_manager.set("data_type","raw_data");this.data_query_wait=1000;this.dataset_check_type="state";this.normal_postdraw_actions=this.postdraw_actions;this.postdraw_actions=function(ah,ai,ak,af){var ag=this;ag.normal_postdraw_actions(ah,ai,ak,af);ag.dataset_check_type="converted_datasets_state";ag.data_query_wait=L;var aj=new ServerStateDeferred({url:ag.dataset_state_url,url_params:{dataset_id:ag.dataset_id,hda_ldda:ag.hda_ldda},interval:ag.data_query_wait,success_fn:function(al){return al!=="pending"}});$.when(aj.go()).then(function(){ag.data_manager.set("data_type","data")});ag.postdraw_actions=ag.normal_postdraw_actions}}});var ab=function(ag,af){var ah={resize:false};i.call(this,ag,af,ah);this.container_div.addClass("label-track")};r(ab.prototype,i.prototype,{build_header_div:function(){},init:function(){this.enabled=true},_draw:function(){var ah=this.view,ai=ah.high-ah.low,al=Math.floor(Math.pow(10,Math.floor(Math.log(ai)/Math.log(10)))),af=Math.floor(ah.low/al)*al,aj=this.view.container.width(),ag=$("<div style='position: relative; height: 1.3em;'></div>");while(af<ah.high){var ak=(af-ah.low)/ai*aj;ag.append($("<div class='label'>"+commatize(af)+"</div>").css({position:"absolute",left:ak-1}));af+=al}this.content_div.children(":first").remove();this.content_div.append(ag)}});var h=function(ag,af,aj){N.call(this,ag,af,aj);this.drawables=[];this.left_offset=0;if("drawables" in aj){var ai;for(var ah=0;ah<aj.drawables.length;ah++){ai=aj.drawables[ah];this.drawables[ah]=q(ai,ag,null);if(ai.left_offset>this.left_offset){this.left_offset=ai.left_offset}}this.enabled=true}if(this.drawables.length!==0){this.set_display_modes(this.drawables[0].display_modes,this.drawables[0].mode)}this.update_icons();this.obj_type="CompositeTrack"};r(h.prototype,N.prototype,{action_icons_def:[{name:"composite_icon",title:"Show individual tracks",css_class:"layers-stack",on_click_fn:function(af){$(".bs-tooltip").remove();af.show_group()}}].concat(N.prototype.action_icons_def),to_dict:A.prototype.to_dict,add_drawable:A.prototype.add_drawable,unpack_drawables:A.prototype.unpack_drawables,change_mode:function(af){N.prototype.change_mode.call(this,af);for(var ag=0;ag<this.drawables.length;ag++){this.drawables[ag].change_mode(af)}},init:function(){var ah=[];for(var ag=0;ag<this.drawables.length;ag++){ah.push(this.drawables[ag].init())}var af=this;$.when.apply($,ah).then(function(){af.enabled=true;af.request_draw()})},update_icons:function(){this.action_icons.filters_icon.hide();this.action_icons.tools_icon.hide();this.action_icons.param_space_viz_icon.hide()},can_draw:s.prototype.can_draw,draw_helper:function(ag,aw,aC,az,an,ap,ax){var av=this,aG=this._gen_tile_cache_key(aw,ap,aC),ak=this._get_tile_bounds(aC,az);if(!ax){ax={}}var aF=(ag?undefined:av.tile_cache.get_elt(aG));if(aF){av.show_tile(aF,an,ap);return aF}var ao=[],av,at=true,aA,aq;for(var aB=0;aB<this.drawables.length;aB++){av=this.drawables[aB];aA=av.data_manager.get_data(ak,av.mode,az,av.data_url_extra_params);if(Y(aA)){at=false}ao.push(aA);aq=null;if(view.reference_track&&ap>view.canvas_manager.char_width_px){aq=view.reference_track.data_manager.get_data(ak,av.mode,az,view.reference_track.data_url_extra_params);if(Y(aq)){at=false}}ao.push(aq)}if(at){r(aA,ax.more_tile_data);this.tile_predraw_init();var aj=av.view.canvas_manager.new_canvas(),al=av._get_tile_bounds(aC,az),aD=ak.get("start"),ah=ak.get("end"),aE=0,aw=Math.ceil((ah-aD)*ap)+this.left_offset,au=0,ai=[],aB;var af=0;for(aB=0;aB<this.drawables.length;aB++,aE+=2){av=this.drawables[aB];aA=ao[aE];var ar=av.mode;if(ar==="Auto"){ar=av.get_mode(aA);av.update_auto_mode(ar)}ai.push(ar);af=av.get_canvas_height(aA,ar,ap,aw);if(af>au){au=af}}aj.width=aw;aj.height=(ax.height?ax.height:au);aE=0;var ay=aj.getContext("2d");ay.translate(this.left_offset,0);ay.globalAlpha=0.5;ay.globalCompositeOperation="source-over";for(aB=0;aB<this.drawables.length;aB++,aE+=2){av=this.drawables[aB];aA=ao[aE];aq=ao[aE+1];aF=av.draw_tile(aA,ay,ai[aB],az,ak,ap,aq)}this.tile_cache.set_elt(aG,aF);this.show_tile(aF,an,ap);return aF}var am=$.Deferred(),av=this;$.when.apply($,ao).then(function(){view.request_redraw(false,false,false,av);am.resolve()});return am},show_group:function(){var ai=new Q(this.view,this.container,{name:this.name}),af;for(var ah=0;ah<this.drawables.length;ah++){af=this.drawables[ah];af.update_icons();ai.add_drawable(af);af.container=ai;ai.content_div.append(af.container_div)}var ag=this.container.replace_drawable(this,ai,true);ai.request_draw()},tile_predraw_init:function(){var ai=Number.MAX_VALUE,af=-ai,ag;for(var ah=0;ah<this.drawables.length;ah++){ag=this.drawables[ah];if(ag instanceof j){if(ag.prefs.min_value<ai){ai=ag.prefs.min_value}if(ag.prefs.max_value>af){af=ag.prefs.max_value}}}for(var ah=0;ah<this.drawables.length;ah++){ag=this.drawables[ah];ag.prefs.min_value=ai;ag.prefs.max_value=af}},postdraw_actions:function(ah,ak,am,ag){N.prototype.postdraw_actions.call(this,ah,ak,am,ag);var aj=-1;for(var ai=0;ai<ah.length;ai++){var af=ah[ai].html_elt.find("canvas").height();if(af>aj){aj=af}}for(var ai=0;ai<ah.length;ai++){var al=ah[ai];if(al.html_elt.find("canvas").height()!==aj){this.draw_helper(true,ak,al.index,al.resolution,al.html_elt.parent(),am,{height:aj});al.html_elt.remove()}}}});var C=function(af){N.call(this,af,{content_div:af.top_labeltrack},{resize:false});af.reference_track=this;this.left_offset=200;this.visible_height_px=12;this.container_div.addClass("reference-track");this.content_div.css("background","none");this.content_div.css("min-height","0px");this.content_div.css("border","none");this.data_url=reference_url+"/"+this.view.dbkey;this.data_url_extra_params={reference:true};this.data_manager=new ReferenceTrackDataManager({data_url:this.data_url});this.hide_contents()};r(C.prototype,s.prototype,N.prototype,{build_header_div:function(){},init:function(){this.data_manager.clear();this.enabled=true},can_draw:s.prototype.can_draw,draw_helper:function(aj,ah,af,ag,ak,al,ai){if(al>this.view.canvas_manager.char_width_px){return N.prototype.draw_helper.call(this,aj,ah,af,ag,ak,al,ai)}else{this.hide_contents();return null}},draw_tile:function(an,ao,aj,ai,al,ap){var ah=this;if(ap>this.view.canvas_manager.char_width_px){if(an.data===null){this.hide_contents();return}var ag=ao.canvas;ao.font=ao.canvas.manager.default_font;ao.textAlign="center";an=an.data;for(var ak=0,am=an.length;ak<am;ak++){var af=Math.floor(ak*ap);ao.fillText(an[ak],af,10)}this.show_contents();return new b(ah,al,ai,ag,an)}this.hide_contents()}});var j=function(ah,ag,ai){var af=this;this.display_modes=["Histogram","Line","Filled","Intensity"];this.mode="Histogram";N.call(this,ah,ag,ai);this.hda_ldda=ai.hda_ldda;this.dataset_id=ai.dataset_id;this.original_dataset_id=this.dataset_id;this.left_offset=0;this.config=new G({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"color",label:"Color",type:"color",default_value:R()},{key:"min_value",label:"Min Value",type:"float",default_value:undefined},{key:"max_value",label:"Max Value",type:"float",default_value:undefined},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:32,hidden:true}],saved_values:ai.prefs,onchange:function(){af.set_name(af.prefs.name);af.vertical_range=af.prefs.max_value-af.prefs.min_value;af.set_min_value(af.prefs.min_value);af.set_max_value(af.prefs.max_value)}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height;this.vertical_range=this.config.values.max_value-this.config.values.min_value};r(j.prototype,s.prototype,N.prototype,{on_resize:function(){this.request_draw(true)},set_min_value:function(af){this.prefs.min_value=af;$("#linetrack_"+this.dataset_id+"_minval").text(this.prefs.min_value);this.tile_cache.clear();this.request_draw()},set_max_value:function(af){this.prefs.max_value=af;$("#linetrack_"+this.dataset_id+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.request_draw()},predraw_init:function(){var af=this;af.vertical_range=undefined;return $.getJSON(af.dataset.url(),{data_type:"data",stats:true,chrom:af.view.chrom,low:0,high:af.view.max_high,hda_ldda:af.hda_ldda,dataset_id:af.dataset_id},function(ag){af.container_div.addClass("line-track");var aj=ag.data;if(isNaN(parseFloat(af.prefs.min_value))||isNaN(parseFloat(af.prefs.max_value))){var ah=aj.min,al=aj.max;ah=Math.floor(Math.min(0,Math.max(ah,aj.mean-2*aj.sd)));al=Math.ceil(Math.max(0,Math.min(al,aj.mean+2*aj.sd)));af.prefs.min_value=ah;af.prefs.max_value=al;$("#track_"+af.dataset_id+"_minval").val(af.prefs.min_value);$("#track_"+af.dataset_id+"_maxval").val(af.prefs.max_value)}af.vertical_range=af.prefs.max_value-af.prefs.min_value;af.total_frequency=aj.total_frequency;af.container_div.find(".yaxislabel").remove();var ak=$("<div/>").text(Z(af.prefs.min_value,3)).make_text_editable({num_cols:6,on_finish:function(am){$(".bs-tooltip").remove();var am=parseFloat(am);if(!isNaN(am)){af.set_min_value(am)}},help_text:"Set min value"}).addClass("yaxislabel bottom").attr("id","linetrack_"+af.dataset_id+"_minval").prependTo(af.container_div),ai=$("<div/>").text(Z(af.prefs.max_value,3)).make_text_editable({num_cols:6,on_finish:function(am){$(".bs-tooltip").remove();var am=parseFloat(am);if(!isNaN(am)){af.set_max_value(am)}},help_text:"Set max value"}).addClass("yaxislabel top").attr("id","linetrack_"+af.dataset_id+"_maxval").prependTo(af.container_div)})},draw_tile:function(ao,am,ah,ag,aj,an){var af=am.canvas,ai=aj.get("start"),al=aj.get("end"),ak=new M.LinePainter(ao.data,ai,al,this.prefs,ah);ak.draw(am,af.width,af.height,an);return new b(this,aj,ag,af,ao.data)},can_subset:function(af){return false}});var u=function(ah,ag,ai){var af=this;this.display_modes=["Heatmap"];this.mode="Heatmap";N.call(this,ah,ag,ai);this.hda_ldda=ai.hda_ldda;this.dataset_id=ai.dataset_id;this.original_dataset_id=this.dataset_id;this.left_offset=0;this.config=new G({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"pos_color",label:"Positive Color",type:"color",default_value:"4169E1"},{key:"negative_color",label:"Negative Color",type:"color",default_value:"FF8C00"},{key:"min_value",label:"Min Value",type:"float",default_value:0},{key:"max_value",label:"Max Value",type:"float",default_value:1},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:500,hidden:true}],saved_values:ai.prefs,onchange:function(){af.set_name(af.prefs.name);af.vertical_range=af.prefs.max_value-af.prefs.min_value;af.set_min_value(af.prefs.min_value);af.set_max_value(af.prefs.max_value)}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height;this.vertical_range=this.config.values.max_value-this.config.values.min_value};r(u.prototype,s.prototype,N.prototype,{on_resize:function(){this.request_draw(true)},set_min_value:function(af){this.prefs.min_value=af;this.tile_cache.clear();this.request_draw()},set_max_value:function(af){this.prefs.max_value=af;this.tile_cache.clear();this.request_draw()},draw_tile:function(ap,an,ak,ai,ag,ao){var ah=an.canvas,af=this._get_tile_bounds(ag,ai),aj=af[0],am=af[1],al=new M.DiagonalHeatmapPainter(ap.data,aj,am,this.prefs,ak);al.draw(an,ah.width,ah.height,ao);return new b(this,ag,ai,ah,ap.data)}});var d=function(ai,ah,ak){var ag=this;this.display_modes=["Auto","Coverage","Dense","Squish","Pack"];N.call(this,ai,ah,ak);var aj=R(),af=R([aj,"#ffffff"]);this.config=new G({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block color",type:"color",default_value:aj},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:af},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true,help:"Show the number of items in each bin when drawing summary histogram"},{key:"histogram_max",label:"Histogram maximum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"connector_style",label:"Connector style",type:"select",default_value:"fishbones",options:[{label:"Line with arrows",value:"fishbone"},{label:"Arcs",value:"arcs"}]},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:this.visible_height_px,hidden:true}],saved_values:ak.prefs,onchange:function(){ag.set_name(ag.prefs.name);ag.tile_cache.clear();ag.set_painter_from_config();ag.request_draw()}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height;this.container_div.addClass("feature-track");this.hda_ldda=ak.hda_ldda;this.dataset_id=ak.dataset_id;this.original_dataset_id=ak.dataset_id;this.show_labels_scale=0.001;this.showing_details=false;this.summary_draw_height=30;this.slotters={};this.start_end_dct={};this.left_offset=200;this.set_painter_from_config()};r(d.prototype,s.prototype,N.prototype,{set_dataset:function(af){this.dataset_id=af.get("id");this.hda_ldda=af.get("hda_ldda");this.dataset=af;this.data_manager.set("dataset",af)},set_painter_from_config:function(){if(this.config.values.connector_style==="arcs"){this.painter=M.ArcLinkedFeaturePainter}else{this.painter=M.LinkedFeaturePainter}},before_draw:function(){this.max_height_px=0},postdraw_actions:function(av,ap,ak,aj){N.prototype.postdraw_actions.call(this,av,aj);var ao=this,ar;if(ao.mode==="Coverage"){var ag=-1;for(ar=0;ar<av.length;ar++){var aq=av[ar].max_val;if(aq>ag){ag=aq}}for(ar=0;ar<av.length;ar++){var ax=av[ar];if(ax.max_val!==ag){ax.html_elt.remove();ao.draw_helper(true,ap,ax.index,ax.resolution,ax.html_elt.parent(),ak,{more_tile_data:{max:ag}})}}}if(ao.filters_manager){var al=ao.filters_manager.filters;for(var au=0;au<al.length;au++){al[au].update_ui_elt()}var aw=false,af,am;for(ar=0;ar<av.length;ar++){if(av[ar].data.length){af=av[ar].data[0];for(var au=0;au<al.length;au++){am=al[au];if(am.applies_to(af)&&am.min!==am.max){aw=true;break}}}}if(ao.filters_available!==aw){ao.filters_available=aw;if(!ao.filters_available){ao.filters_manager.hide()}ao.update_icons()}}this.container_div.find(".yaxislabel").remove();var ai=av[0];if(ai instanceof k){var an=(this.prefs.histogram_max?this.prefs.histogram_max:ai.max_val),ah=$("<div/>").text(an).make_text_editable({num_cols:12,on_finish:function(ay){$(".bs-tooltip").remove();var ay=parseFloat(ay);ao.prefs.histogram_max=(!isNaN(ay)?ay:null);ao.tile_cache.clear();ao.request_draw()},help_text:"Set max value; leave blank to use default"}).addClass("yaxislabel top").css("color",this.prefs.label_color);this.container_div.prepend(ah)}if(ai instanceof P){var at=true;for(ar=0;ar<av.length;ar++){if(!av[ar].all_slotted){at=false;break}}if(!at){this.action_icons.show_more_rows_icon.show()}else{this.action_icons.show_more_rows_icon.hide()}}else{this.action_icons.show_more_rows_icon.hide()}},update_auto_mode:function(af){var af;if(this.mode==="Auto"){if(af==="no_detail"){af="feature spans"}else{if(af==="summary_tree"){af="coverage histogram"}}this.action_icons.mode_icon.attr("title","Set display mode (now: Auto/"+af+")")}},incremental_slots:function(aj,af,ai){var ag=this.view.canvas_manager.dummy_context,ah=this.slotters[aj];if(!ah||(ah.mode!==ai)){ah=new (v.FeatureSlotter)(aj,ai,B,function(ak){return ag.measureText(ak)});this.slotters[aj]=ah}return ah.slot_features(af)},get_mode:function(af){if(af.dataset_type==="summary_tree"){mode="summary_tree"}else{if(af.extra_info==="no_detail"||this.is_overview){mode="no_detail"}else{if(this.view.high-this.view.low>J){mode="Squish"}else{mode="Pack"}}}return mode},get_canvas_height:function(af,aj,ak,ag){if(aj==="summary_tree"||aj==="Coverage"){return this.summary_draw_height}else{var ai=this.incremental_slots(ak,af.data,aj);var ah=new (this.painter)(null,null,null,this.prefs,aj);return Math.max(ad,ah.get_required_height(ai,ag))}},draw_tile:function(ap,au,ar,av,ai,am,ah){var at=this,ag=au.canvas,aB=ai.get("start"),af=ai.get("end"),aj=this.left_offset;if(ar==="summary_tree"||ar==="Coverage"){var aD=new M.SummaryTreePainter(ap,aB,af,this.prefs);aD.draw(au,ag.width,ag.height,am);return new k(at,ai,av,ag,ap.data,ap.max)}var al=[],aq=this.slotters[am].slots;all_slotted=true;if(ap.data){var an=this.filters_manager.filters;for(var aw=0,ay=ap.data.length;aw<ay;aw++){var ak=ap.data[aw];var ax=false;var ao;for(var aA=0,aF=an.length;aA<aF;aA++){ao=an[aA];ao.update_attrs(ak);if(!ao.keep(ak)){ax=true;break}}if(!ax){al.push(ak);if(!(ak[0] in aq)){all_slotted=false}}}}var aE=(this.filters_manager.alpha_filter?new D(this.filters_manager.alpha_filter):null);var aC=(this.filters_manager.height_filter?new D(this.filters_manager.height_filter):null);var aD=new (this.painter)(al,aB,af,this.prefs,ar,aE,aC,ah);var az=null;au.fillStyle=this.prefs.block_color;au.font=au.canvas.manager.default_font;au.textAlign="right";if(ap.data){az=aD.draw(au,ag.width,ag.height,am,aq);az.translation=-aj}return new P(at,ai,av,ag,ap.data,am,ar,ap.message,all_slotted,az)},data_and_mode_compatible:function(af,ag){if(ag==="Auto"){return true}else{if(ag==="Coverage"){return af.dataset_type==="summary_tree"}else{if(af.extra_info==="no_detail"||af.dataset_type==="summary_tree"){return false}else{return true}}}},can_subset:function(af){if(af.dataset_type==="summary_tree"||af.message||af.extra_info==="no_detail"){return false}return true}});var T=function(ag,af,ah){d.call(this,ag,af,ah);this.config=new G({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block color",type:"color",default_value:R()},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true}],saved_values:ah.prefs,onchange:function(){this.track.set_name(this.track.prefs.name);this.track.tile_cache.clear();this.track.request_draw()}});this.prefs=this.config.values;this.painter=M.ReadPainter};r(T.prototype,s.prototype,N.prototype,d.prototype);var W=function(ah,ag,aj){d.call(this,ah,ag,aj);var ai=R(),af=R([ai,"#ffffff"]);this.config=new G({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block and sense strand color",type:"color",default_value:ai},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:af},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_differences",label:"Show differences only",type:"bool",default_value:true},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"histogram_max",label:"Histogram maximum",type:"float",default_value:null,help:"Clear value to set automatically"},{key:"mode",type:"string",default_value:this.mode,hidden:true}],saved_values:aj.prefs,onchange:function(){this.track.set_name(this.track.prefs.name);this.track.tile_cache.clear();this.track.request_draw()}});this.prefs=this.config.values;this.painter=M.ReadPainter;this.update_icons()};r(W.prototype,s.prototype,N.prototype,d.prototype);var e={LineTrack:j,FeatureTrack:d,VcfTrack:T,ReadTrack:W,CompositeTrack:h,DrawableGroup:Q};var q=function(ah,ag,af){if("copy" in ah){return ah.copy(af)}else{var ai=ah.obj_type;if(!ai){ai=ah.track_type}return new e[ai](ag,af,ah)}};return{View:ac,DrawableGroup:Q,LineTrack:j,FeatureTrack:d,DiagonalHeatmapTrack:u,ReadTrack:W,VcfTrack:T,CompositeTrack:h,object_from_template:q}});
\ No newline at end of file
+define(["libs/underscore","viz/visualization","viz/trackster/util","viz/trackster/slotting","viz/trackster/painters","mvc/data","viz/trackster/filters","viz/trackster_ui"],function(ad,y,l,v,M,ab,i,q){var r=ad.extend;var R=l.get_random_color;var ae=function(af,ah,ag){$.ajax({url:af,data:{"f-dbkey":view.dbkey},error:function(){alert("Grid failed")},success:function(ai){show_modal("Select datasets for new tracks",ai,{Cancel:function(){hide_modal()},Add:function(){var aj=[];$("input[name=id]:checked,input[name=ldda_ids]:checked").each(function(){var ak={data_type:"track_config",hda_ldda:"hda"},al=$(this).val();if($(this).attr("name")!=="id"){ak.hda_ldda="ldda"}aj[aj.length]=$.ajax({url:ah+"/"+al,data:ak,dataType:"json"})});$.when.apply($,aj).then(function(){var ak=(arguments[0] instanceof Array?$.map(arguments,function(al){return al[0]}):[arguments[0]]);ag(ak)});hide_modal()}})}})};var X=function(af){return("isResolved" in af)};var n={};var k=function(af,ag){n[af.attr("id")]=ag};var m=function(af,ah,aj,ai){aj=".group";var ag={};n[af.attr("id")]=ai;af.bind("drag",{handle:"."+ah,relative:true},function(ar,at){var aq=$(this),aw=$(this).parent(),an=aw.children(),ap=n[$(this).attr("id")],am,al,au,ak,ao;al=$(this).parents(aj);if(al.length!==0){au=al.position().top;ak=au+al.outerHeight();if(at.offsetY<au){$(this).insertBefore(al);var av=n[al.attr("id")];av.remove_drawable(ap);av.container.add_drawable_before(ap,av);return}else{if(at.offsetY>ak){$(this).insertAfter(al);var av=n[al.attr("id")];av.remove_drawable(ap);av.container.add_drawable(ap);return}}}al=null;for(ao=0;ao<an.length;ao++){am=$(an.get(ao));au=am.position().top;ak=au+am.outerHeight();if(am.is(aj)&&this!==am.get(0)&&at.offsetY>=au&&at.offsetY<=ak){if(at.offsetY-au<ak-at.offsetY){am.find(".content-div").prepend(this)}else{am.find(".content-div").append(this)}if(ap.container){ap.container.remove_drawable(ap)}n[am.attr("id")].add_drawable(ap);return}}for(ao=0;ao<an.length;ao++){am=$(an.get(ao));if(at.offsetY<am.position().top&&!(am.hasClass("reference-track")||am.hasClass("intro"))){break}}if(ao===an.length){if(this!==an.get(ao-1)){aw.append(this);n[aw.attr("id")].move_drawable(ap,ao)}}else{if(this!==an.get(ao)){$(this).insertBefore(an.get(ao));n[aw.attr("id")].move_drawable(ap,(at.deltaY>0?ao-1:ao))}}}).bind("dragstart",function(){ag["border-top"]=af.css("border-top");ag["border-bottom"]=af.css("border-bottom");$(this).css({"border-top":"1px solid blue","border-bottom":"1px solid blue"})}).bind("dragend",function(){$(this).css(ag)})};exports.moveable=m;var ac=16,H=9,E=20,B=100,J=12000,U=400,L=5000,x=100,o="There was an error in indexing this dataset. ",K="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",F="No data for this chrom/contig.",w="Preparing data. This can take a while for a large dataset. If the visualization is saved and closed, preparation will continue in the background.",z="Tool cannot be rerun: ",a="Loading data...",W="Ready for display",S=10,I=20;function Y(ag,af){if(!af){af=0}var ah=Math.pow(10,af);return Math.round(ag*ah)/ah}var s=function(ag,af,ai){if(!s.id_counter){s.id_counter=0}this.id=s.id_counter++;this.name=ai.name;this.view=ag;this.container=af;this.config=new G({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name}],saved_values:ai.prefs,onchange:function(){this.track.set_name(this.track.config.values.name)}});this.prefs=this.config.values;this.drag_handle_class=ai.drag_handle_class;this.is_overview=false;this.action_icons={};this.content_visible=true;this.container_div=this.build_container_div();this.header_div=this.build_header_div();if(this.header_div){this.container_div.append(this.header_div);this.icons_div=$("<div/>").css("float","left").hide().appendTo(this.header_div);this.build_action_icons(this.action_icons_def);this.header_div.append($("<div style='clear: both'/>"));this.header_div.dblclick(function(aj){aj.stopPropagation()});var ah=this;this.container_div.hover(function(){ah.icons_div.show()},function(){ah.icons_div.hide()});$("<div style='clear: both'/>").appendTo(this.container_div)}};s.prototype.action_icons_def=[{name:"toggle_icon",title:"Hide/show content",css_class:"toggle",on_click_fn:function(af){if(af.content_visible){af.action_icons.toggle_icon.addClass("toggle-expand").removeClass("toggle");af.hide_contents();af.content_visible=false}else{af.action_icons.toggle_icon.addClass("toggle").removeClass("toggle-expand");af.content_visible=true;af.show_contents()}}},{name:"settings_icon",title:"Edit settings",css_class:"settings-icon",on_click_fn:function(ag){var ai=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},af=function(){ag.config.update_from_form($(".dialog-box"));hide_modal();$(window).unbind("keypress.check_enter_esc")},ah=function(aj){if((aj.keyCode||aj.which)===27){ai()}else{if((aj.keyCode||aj.which)===13){af()}}};$(window).bind("keypress.check_enter_esc",ah);show_modal("Configure",ag.config.build_form(),{Cancel:ai,OK:af})}},{name:"remove_icon",title:"Remove",css_class:"remove-icon",on_click_fn:function(af){$(".bs-tooltip").remove();af.remove()}}];r(s.prototype,{init:function(){},changed:function(){this.view.changed()},can_draw:function(){if(this.enabled&&this.content_visible){return true}return false},request_draw:function(){},_draw:function(){},to_dict:function(){},set_name:function(af){this.old_name=this.name;this.name=af;this.name_div.text(this.name)},revert_name:function(){if(this.old_name){this.name=this.old_name;this.name_div.text(this.name)}},remove:function(){this.changed();this.container.remove_drawable(this);var af=this.view;this.container_div.hide(0,function(){$(this).remove();af.update_intro_div()})},build_container_div:function(){},build_header_div:function(){},add_action_icon:function(ag,al,ak,aj,af,ai){var ah=this;this.action_icons[ag]=$("<a/>").attr("href","javascript:void(0);").attr("title",al).addClass("icon-button").addClass(ak).tooltip().click(function(){aj(ah)}).appendTo(this.icons_div);if(ai){this.action_icons[ag].hide()}},build_action_icons:function(af){var ah;for(var ag=0;ag<af.length;ag++){ah=af[ag];this.add_action_icon(ah.name,ah.title,ah.css_class,ah.on_click_fn,ah.prepend,ah.hide)}},update_icons:function(){},hide_contents:function(){},show_contents:function(){},get_drawables:function(){}});var A=function(ag,af,ah){s.call(this,ag,af,ah);this.obj_type=ah.obj_type;this.drawables=[]};r(A.prototype,s.prototype,{unpack_drawables:function(ah){this.drawables=[];var ag;for(var af=0;af<ah.length;af++){ag=p(ah[af],this.view,this);this.add_drawable(ag)}},init:function(){for(var af=0;af<this.drawables.length;af++){this.drawables[af].init()}},_draw:function(){for(var af=0;af<this.drawables.length;af++){this.drawables[af]._draw()}},to_dict:function(){var ag=[];for(var af=0;af<this.drawables.length;af++){ag.push(this.drawables[af].to_dict())}return{name:this.name,prefs:this.prefs,obj_type:this.obj_type,drawables:ag}},add_drawable:function(af){this.drawables.push(af);af.container=this;this.changed()},add_drawable_before:function(ah,af){this.changed();var ag=this.drawables.indexOf(af);if(ag!==-1){this.drawables.splice(ag,0,ah);return true}return false},replace_drawable:function(ah,af,ag){var ai=this.drawables.indexOf(ah);if(ai!==-1){this.drawables[ai]=af;if(ag){ah.container_div.replaceWith(af.container_div)}this.changed()}return ai},remove_drawable:function(ag){var af=this.drawables.indexOf(ag);if(af!==-1){this.drawables.splice(af,1);ag.container=null;this.changed();return true}return false},move_drawable:function(ag,ah){var af=this.drawables.indexOf(ag);if(af!==-1){this.drawables.splice(af,1);this.drawables.splice(ah,0,ag);this.changed();return true}return false},get_drawables:function(){return this.drawables}});var Q=function(ag,af,ai){r(ai,{obj_type:"DrawableGroup",drag_handle_class:"group-handle"});A.call(this,ag,af,ai);this.content_div=$("<div/>").addClass("content-div").attr("id","group_"+this.id+"_content_div").appendTo(this.container_div);k(this.container_div,this);k(this.content_div,this);m(this.container_div,this.drag_handle_class,".group",this);this.filters_manager=new i.FiltersManager(this);this.header_div.after(this.filters_manager.parent_div);this.saved_filters_managers=[];if("drawables" in ai){this.unpack_drawables(ai.drawables)}if("filters" in ai){var ah=this.filters_manager;this.filters_manager=new i.FiltersManager(this,ai.filters);ah.parent_div.replaceWith(this.filters_manager.parent_div);if(ai.filters.visible){this.setup_multitrack_filtering()}}};r(Q.prototype,s.prototype,A.prototype,{action_icons_def:[s.prototype.action_icons_def[0],s.prototype.action_icons_def[1],{name:"composite_icon",title:"Show composite track",css_class:"layers-stack",on_click_fn:function(af){$(".bs-tooltip").remove();af.show_composite_track()}},{name:"filters_icon",title:"Filters",css_class:"filters-icon",on_click_fn:function(af){if(af.filters_manager.visible()){af.filters_manager.clear_filters();af._restore_filter_managers()}else{af.setup_multitrack_filtering();af.request_draw(true)}af.filters_manager.toggle()}},s.prototype.action_icons_def[2]],build_container_div:function(){var af=$("<div/>").addClass("group").attr("id","group_"+this.id);if(this.container){this.container.content_div.append(af)}return af},build_header_div:function(){var af=$("<div/>").addClass("track-header");af.append($("<div/>").addClass(this.drag_handle_class));this.name_div=$("<div/>").addClass("track-name").text(this.name).appendTo(af);return af},hide_contents:function(){this.tiles_div.hide()},show_contents:function(){this.tiles_div.show();this.request_draw()},update_icons:function(){var ah=this.drawables.length;if(ah===0){this.action_icons.composite_icon.hide();this.action_icons.filters_icon.hide()}else{if(ah===1){if(this.drawables[0] instanceof f){this.action_icons.composite_icon.show()}this.action_icons.filters_icon.hide()}else{var ao,an,al,ar=true,aj=this.drawables[0].get_type(),af=0;for(ao=0;ao<ah;ao++){al=this.drawables[ao];if(al.get_type()!==aj){can_composite=false;break}if(al instanceof c){af++}}if(ar||af===1){this.action_icons.composite_icon.show()}else{this.action_icons.composite_icon.hide();$(".bs-tooltip").remove()}if(af>1&&af===this.drawables.length){var at={},ag;al=this.drawables[0];for(an=0;an<al.filters_manager.filters.length;an++){ag=al.filters_manager.filters[an];at[ag.name]=[ag]}for(ao=1;ao<this.drawables.length;ao++){al=this.drawables[ao];for(an=0;an<al.filters_manager.filters.length;an++){ag=al.filters_manager.filters[an];if(ag.name in at){at[ag.name].push(ag)}}}this.filters_manager.remove_all();var ai,ak,am,ap;for(var aq in at){ai=at[aq];if(ai.length===af){ak=new i.NumberFilter({name:ai[0].name,index:ai[0].index});this.filters_manager.add_filter(ak)}}if(this.filters_manager.filters.length>0){this.action_icons.filters_icon.show()}else{this.action_icons.filters_icon.hide()}}else{this.action_icons.filters_icon.hide()}}}},_restore_filter_managers:function(){for(var af=0;af<this.drawables.length;af++){this.drawables[af].filters_manager=this.saved_filters_managers[af]}this.saved_filters_managers=[]},setup_multitrack_filtering:function(){if(this.filters_manager.filters.length>0){this.saved_filters_managers=[];for(var af=0;af<this.drawables.length;af++){drawable=this.drawables[af];this.saved_filters_managers.push(drawable.filters_manager);drawable.filters_manager=this.filters_manager}}this.filters_manager.init_filters()},show_composite_track:function(){var aj=[];for(var ag=0;ag<this.drawables.length;ag++){aj.push(this.drawables[ag].name)}var ah="Composite Track of "+this.drawables.length+" tracks ("+aj.join(", ")+")";var ai=new f(this.view,this.view,{name:ah,drawables:this.drawables});var af=this.container.replace_drawable(this,ai,true);ai.request_draw()},add_drawable:function(af){A.prototype.add_drawable.call(this,af);this.update_icons()},remove_drawable:function(af){A.prototype.remove_drawable.call(this,af);this.update_icons()},to_dict:function(){if(this.filters_manager.visible()){this._restore_filter_managers()}var af=r(A.prototype.to_dict.call(this),{filters:this.filters_manager.to_dict()});if(this.filters_manager.visible()){this.setup_multitrack_filtering()}return af},request_draw:function(af,ah){for(var ag=0;ag<this.drawables.length;ag++){this.drawables[ag].request_draw(af,ah)}}});var aa=function(af){r(af,{obj_type:"View"});A.call(this,"View",af.container,af);this.chrom=null;this.vis_id=af.vis_id;this.dbkey=af.dbkey;this.label_tracks=[];this.tracks_to_be_redrawn=[];this.max_low=0;this.max_high=0;this.zoom_factor=3;this.min_separation=30;this.has_changes=false;this.load_chroms_deferred=null;this.init();this.canvas_manager=new y.CanvasManager(this.container.get(0).ownerDocument);this.reset()};ad.extend(aa.prototype,Backbone.Events);r(aa.prototype,A.prototype,{init:function(){this.requested_redraw=false;var ah=this.container,af=this;this.top_container=$("<div/>").addClass("top-container").appendTo(ah);this.browser_content_div=$("<div/>").addClass("content").css("position","relative").appendTo(ah);this.bottom_container=$("<div/>").addClass("bottom-container").appendTo(ah);this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(this.top_container);this.viewport_container=$("<div/>").addClass("viewport-container").attr("id","viewport-container").appendTo(this.browser_content_div);this.content_div=this.viewport_container;k(this.viewport_container,af);this.intro_div=$("<div/>").addClass("intro").appendTo(this.viewport_container).hide();var ai=$("<div/>").text("Add Datasets to Visualization").addClass("action-button").appendTo(this.intro_div).click(function(){ae(add_datasets_url,add_track_async_url,function(aj){ad.each(aj,function(ak){af.add_drawable(p(ak,af,af))})})});this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.bottom_container);this.nav_container=$("<div/>").addClass("trackster-nav-container").prependTo(this.top_container);this.nav=$("<div/>").addClass("trackster-nav").appendTo(this.nav_container);this.overview=$("<div/>").addClass("overview").appendTo(this.bottom_container);this.overview_viewport=$("<div/>").addClass("overview-viewport").appendTo(this.overview);this.overview_close=$("<a/>").attr("href","javascript:void(0);").attr("title","Close overview").addClass("icon-button overview-close tooltip").hide().appendTo(this.overview_viewport);this.overview_highlight=$("<div/>").addClass("overview-highlight").hide().appendTo(this.overview_viewport);this.overview_box_background=$("<div/>").addClass("overview-boxback").appendTo(this.overview_viewport);this.overview_box=$("<div/>").addClass("overview-box").appendTo(this.overview_viewport);this.default_overview_height=this.overview_box.height();this.nav_controls=$("<div/>").addClass("nav-controls").appendTo(this.nav);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").append("<option value=''>Loading</option>").appendTo(this.nav_controls);var ag=function(aj){if(aj.type==="focusout"||(aj.keyCode||aj.which)===13||(aj.keyCode||aj.which)===27){if((aj.keyCode||aj.which)!==27){af.go_to($(this).val())}$(this).hide();$(this).val("");af.location_span.show();af.chrom_select.show()}};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keyup focusout",ag).appendTo(this.nav_controls);this.location_span=$("<span/>").addClass("location").attr("original-title","Click to change location").tooltip({placement:"bottom"}).appendTo(this.nav_controls);this.location_span.click(function(){af.location_span.hide();af.chrom_select.hide();af.nav_input.val(af.chrom+":"+af.low+"-"+af.high);af.nav_input.css("display","inline-block");af.nav_input.select();af.nav_input.focus();af.nav_input.autocomplete({source:function(al,aj){var am=[],ak=$.map(af.get_drawables(),function(an){return an.data_manager.search_features(al.term).success(function(ao){am=am.concat(ao)})});$.when.apply($,ak).done(function(){aj($.map(am,function(an){return{label:an[0],value:an[1]}}))})}})});if(this.vis_id!==undefined){this.hidden_input=$("<input/>").attr("type","hidden").val(this.vis_id).appendTo(this.nav_controls)}this.zo_link=$("<a/>").attr("id","zoom-out").attr("title","Zoom out").tooltip({placement:"bottom"}).click(function(){af.zoom_out();af.request_redraw()}).appendTo(this.nav_controls);this.zi_link=$("<a/>").attr("id","zoom-in").attr("title","Zoom in").tooltip({placement:"bottom"}).click(function(){af.zoom_in();af.request_redraw()}).appendTo(this.nav_controls);this.load_chroms_deferred=this.load_chroms({low:0});this.chrom_select.bind("change",function(){af.change_chrom(af.chrom_select.val())});this.browser_content_div.click(function(aj){$(this).find("input").trigger("blur")});this.browser_content_div.bind("dblclick",function(aj){af.zoom_in(aj.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(aj,ak){this.current_x=ak.offsetX}).bind("drag",function(aj,al){var am=al.offsetX-this.current_x;this.current_x=al.offsetX;var ak=Math.round(am/af.viewport_container.width()*(af.max_high-af.max_low));af.move_delta(-ak)});this.overview_close.click(function(){af.reset_overview()});this.viewport_container.bind("draginit",function(aj,ak){if(aj.clientX>af.viewport_container.width()-16){return false}}).bind("dragstart",function(aj,ak){ak.original_low=af.low;ak.current_height=aj.clientY;ak.current_x=ak.offsetX}).bind("drag",function(al,an){var aj=$(this);var ao=an.offsetX-an.current_x;var ak=aj.scrollTop()-(al.clientY-an.current_height);aj.scrollTop(ak);an.current_height=al.clientY;an.current_x=an.offsetX;var am=Math.round(ao/af.viewport_container.width()*(af.high-af.low));af.move_delta(am)}).bind("mousewheel",function(al,an,ak,aj){if(ak){ak*=50;var am=Math.round(-ak/af.viewport_container.width()*(af.high-af.low));af.move_delta(am)}});this.top_labeltrack.bind("dragstart",function(aj,ak){return $("<div />").css({height:af.browser_content_div.height()+af.top_labeltrack.height()+af.nav_labeltrack.height()+1,top:"0px",position:"absolute","background-color":"#ccf",opacity:0.5,"z-index":1000}).appendTo($(this))}).bind("drag",function(an,ao){$(ao.proxy).css({left:Math.min(an.pageX,ao.startX)-af.container.offset().left,width:Math.abs(an.pageX-ao.startX)});var ak=Math.min(an.pageX,ao.startX)-af.container.offset().left,aj=Math.max(an.pageX,ao.startX)-af.container.offset().left,am=(af.high-af.low),al=af.viewport_container.width();af.update_location(Math.round(ak/al*am)+af.low,Math.round(aj/al*am)+af.low)}).bind("dragend",function(ao,ap){var ak=Math.min(ao.pageX,ap.startX),aj=Math.max(ao.pageX,ap.startX),am=(af.high-af.low),al=af.viewport_container.width(),an=af.low;af.low=Math.round(ak/al*am)+an;af.high=Math.round(aj/al*am)+an;$(ap.proxy).remove();af.request_redraw()});this.add_label_track(new Z(this,{content_div:this.top_labeltrack}));this.add_label_track(new Z(this,{content_div:this.nav_labeltrack}));$(window).bind("resize",function(){if(this.resize_timer){clearTimeout(this.resize_timer)}this.resize_timer=setTimeout(function(){af.resize_window()},500)});$(document).bind("redraw",function(){af.redraw()});this.reset();$(window).trigger("resize")},changed:function(){this.has_changes=true},update_intro_div:function(){if(this.drawables.length===0){this.intro_div.show()}else{this.intro_div.hide()}},trigger_navigate:function(ag,ai,af,aj){if(this.timer){clearTimeout(this.timer)}if(aj){var ah=this;this.timer=setTimeout(function(){ah.trigger("navigate",ag+":"+ai+"-"+af)},500)}else{view.trigger("navigate",ag+":"+ai+"-"+af)}},update_location:function(af,ah){this.location_span.text(commatize(af)+" - "+commatize(ah));this.nav_input.val(this.chrom+":"+commatize(af)+"-"+commatize(ah));var ag=view.chrom_select.val();if(ag!==""){this.trigger_navigate(ag,view.low,view.high,true)}},load_chroms:function(ah){ah.num=x;var af=this,ag=$.Deferred();$.ajax({url:chrom_url+"/"+this.dbkey,data:ah,dataType:"json",success:function(aj){if(aj.chrom_info.length===0){return}if(aj.reference){af.add_label_track(new C(af))}af.chrom_data=aj.chrom_info;var am='<option value="">Select Chrom/Contig</option>';for(var al=0,ai=af.chrom_data.length;al<ai;al++){var ak=af.chrom_data[al].chrom;am+='<option value="'+ak+'">'+ak+"</option>"}if(aj.prev_chroms){am+='<option value="previous">Previous '+x+"</option>"}if(aj.next_chroms){am+='<option value="next">Next '+x+"</option>"}af.chrom_select.html(am);af.chrom_start_index=aj.start_index;ag.resolve(aj)},error:function(){alert("Could not load chroms for this dbkey:",af.dbkey)}});return ag},change_chrom:function(ak,ag,am){var ah=this;if(!ah.chrom_data){ah.load_chroms_deferred.then(function(){ah.change_chrom(ak,ag,am)});return}if(!ak||ak==="None"){return}if(ak==="previous"){ah.load_chroms({low:this.chrom_start_index-x});return}if(ak==="next"){ah.load_chroms({low:this.chrom_start_index+x});return}var al=$.grep(ah.chrom_data,function(an,ao){return an.chrom===ak})[0];if(al===undefined){ah.load_chroms({chrom:ak},function(){ah.change_chrom(ak,ag,am)});return}else{if(ak!==ah.chrom){ah.chrom=ak;ah.chrom_select.val(ah.chrom);ah.max_high=al.len-1;ah.reset();ah.request_redraw(true);for(var aj=0,af=ah.drawables.length;aj<af;aj++){var ai=ah.drawables[aj];if(ai.init){ai.init()}}if(ah.reference_track){ah.reference_track.init()}}if(ag!==undefined&&am!==undefined){ah.low=Math.max(ag,0);ah.high=Math.min(am,ah.max_high)}else{ah.low=0;ah.high=ah.max_high}ah.reset_overview();ah.request_redraw()}},go_to:function(aj){aj=aj.replace(/ |,/g,"");var an=this,af,ai,ag=aj.split(":"),al=ag[0],am=ag[1];if(am!==undefined){try{var ak=am.split("-");af=parseInt(ak[0],10);ai=parseInt(ak[1],10)}catch(ah){return false}}an.change_chrom(al,af,ai)},move_fraction:function(ah){var af=this;var ag=af.high-af.low;this.move_delta(ah*ag)},move_delta:function(ai){var af=this;var ah=af.high-af.low;if(af.low-ai<af.max_low){af.low=af.max_low;af.high=af.max_low+ah}else{if(af.high-ai>af.max_high){af.high=af.max_high;af.low=af.max_high-ah}else{af.high-=ai;af.low-=ai}}af.request_redraw();var ag=af.chrom_select.val();this.trigger_navigate(ag,af.low,af.high,true)},add_drawable:function(af){A.prototype.add_drawable.call(this,af);af.init();this.changed();this.update_intro_div()},add_label_track:function(af){af.view=this;af.init();this.label_tracks.push(af)},remove_drawable:function(ah,ag){A.prototype.remove_drawable.call(this,ah);if(ag){var af=this;ah.container_div.hide(0,function(){$(this).remove();af.update_intro_div()})}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},request_redraw:function(an,af,am,ao){var al=this,ak=(ao?[ao]:al.drawables),ah;var ag;for(var aj=0;aj<ak.length;aj++){ag=ak[aj];ah=-1;for(var ai=0;ai<al.tracks_to_be_redrawn.length;ai++){if(al.tracks_to_be_redrawn[ai][0]===ag){ah=ai;break}}if(ah<0){al.tracks_to_be_redrawn.push([ag,af,am])}else{al.tracks_to_be_redrawn[aj][1]=af;al.tracks_to_be_redrawn[aj][2]=am}}if(!this.requested_redraw){requestAnimationFrame(function(){al._redraw(an)});this.requested_redraw=true}},_redraw:function(ap){this.requested_redraw=false;var am=this.low,ai=this.high;if(am<this.max_low){am=this.max_low}if(ai>this.max_high){ai=this.max_high}var ao=this.high-this.low;if(this.high!==0&&ao<this.min_separation){ai=am+this.min_separation}this.low=Math.floor(am);this.high=Math.ceil(ai);this.update_location(this.low,this.high);this.resolution_b_px=(this.high-this.low)/this.viewport_container.width();this.resolution_px_b=this.viewport_container.width()/(this.high-this.low);var af=(this.low/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var al=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var aq=13;this.overview_box.css({left:af,width:Math.max(aq,al)}).show();if(al<aq){this.overview_box.css("left",af-(aq-al)/2)}if(this.overview_highlight){this.overview_highlight.css({left:af,width:al})}if(!ap){var ah,ag,an;for(var aj=0,ak=this.tracks_to_be_redrawn.length;aj<ak;aj++){ah=this.tracks_to_be_redrawn[aj][0];ag=this.tracks_to_be_redrawn[aj][1];an=this.tracks_to_be_redrawn[aj][2];if(ah){ah._draw(ag,an)}}this.tracks_to_be_redrawn=[];for(aj=0,ak=this.label_tracks.length;aj<ak;aj++){this.label_tracks[aj]._draw()}}},zoom_in:function(ag,ah){if(this.max_high===0||this.high-this.low<=this.min_separation){return}var ai=this.high-this.low,aj=ai/2+this.low,af=(ai/this.zoom_factor)/2;if(ag){aj=ag/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(aj-af);this.high=Math.round(aj+af);this.changed();this.request_redraw()},zoom_out:function(){if(this.max_high===0){return}var ag=this.high-this.low,ah=ag/2+this.low,af=(ag*this.zoom_factor)/2;this.low=Math.round(ah-af);this.high=Math.round(ah+af);this.changed();this.request_redraw()},resize_window:function(){this.viewport_container.height(this.container.height()-this.top_container.height()-this.bottom_container.height());this.request_redraw()},set_overview:function(ah){if(this.overview_drawable){if(this.overview_drawable.dataset_id===ah.dataset_id){return}this.overview_viewport.find(".track").remove()}var ag=ah.copy({content_div:this.overview_viewport}),af=this;ag.header_div.hide();ag.is_overview=true;af.overview_drawable=ag;this.overview_drawable.postdraw_actions=function(){af.overview_highlight.show().height(af.overview_drawable.content_div.height());af.overview_viewport.height(af.overview_drawable.content_div.height()+af.overview_box.outerHeight());af.overview_close.show();af.resize_window()};af.overview_drawable.request_draw();this.changed()},reset_overview:function(){$(".bs-tooltip").remove();this.overview_viewport.find(".track-tile").remove();this.overview_viewport.height(this.default_overview_height);this.overview_box.height(this.default_overview_height);this.overview_close.hide();this.overview_highlight.hide();view.resize_window();view.overview_drawable=null}});var t=function(ah,am,ai){this.track=ah;this.name=am.name;this.params=[];var au=am.params;for(var aj=0;aj<au.length;aj++){var ao=au[aj],ag=ao.name,at=ao.label,ak=unescape(ao.html),av=ao.value,aq=ao.type;if(aq==="number"){this.params.push(new e(ag,at,ak,(ag in ai?ai[ag]:av),ao.min,ao.max))}else{if(aq==="select"){this.params.push(new O(ag,at,ak,(ag in ai?ai[ag]:av)))}else{console.log("WARNING: unrecognized tool parameter type:",ag,aq)}}}this.parent_div=$("<div/>").addClass("dynamic-tool").hide();this.parent_div.bind("drag",function(ax){ax.stopPropagation()}).click(function(ax){ax.stopPropagation()}).bind("dblclick",function(ax){ax.stopPropagation()});var ar=$("<div class='tool-name'>").appendTo(this.parent_div).text(this.name);var ap=this.params;var an=this;$.each(this.params,function(ay,aB){var aA=$("<div>").addClass("param-row").appendTo(an.parent_div);var ax=$("<div>").addClass("param-label").text(aB.label).appendTo(aA);var az=$("<div/>").addClass("param-input").html(aB.html).appendTo(aA);az.find(":input").val(aB.value);$("<div style='clear: both;'/>").appendTo(aA)});this.parent_div.find("input").click(function(){$(this).select()});var aw=$("<div>").addClass("param-row").appendTo(this.parent_div);var al=$("<input type='submit'>").attr("value","Run on complete dataset").appendTo(aw);var af=$("<input type='submit'>").attr("value","Run on visible region").css("margin-left","3em").appendTo(aw);af.click(function(){an.run_on_region()});al.click(function(){an.run_on_dataset()});if("visible" in ai&&ai.visible){this.parent_div.show()}};r(t.prototype,{update_params:function(){for(var af=0;af<this.params.length;af++){this.params[af].update_value()}},state_dict:function(){var ag={};for(var af=0;af<this.params.length;af++){ag[this.params[af].name]=this.params[af].value}ag.visible=this.parent_div.is(":visible");return ag},get_param_values_dict:function(){var af={};this.parent_div.find(":input").each(function(){var ag=$(this).attr("name"),ah=$(this).val();af[ag]=ah});return af},get_param_values:function(){var af=[];this.parent_div.find(":input").each(function(){var ag=$(this).attr("name"),ah=$(this).val();if(ag){af[af.length]=ah}});return af},run_on_dataset:function(){var af=this;af.run({target_dataset_id:this.track.original_dataset_id,tool_id:af.name},null,function(ag){show_modal(af.name+" is Running",af.name+" is running on the complete dataset. Tool outputs are in dataset's history.",{Close:hide_modal})})},run_on_region:function(){var ag={target_dataset_id:this.track.original_dataset_id,action:"rerun",tool_id:this.name,regions:[{chrom:this.track.view.chrom,start:this.track.view.low,end:this.track.view.high}]},ak=this.track,ah=ag.tool_id+ak.tool_region_and_parameters_str(ag.chrom,ag.low,ag.high),af;if(ak.container===view){var aj=new Q(view,view,{name:this.name});var ai=ak.container.replace_drawable(ak,aj,false);aj.container_div.insertBefore(ak.view.content_div.children()[ai]);aj.add_drawable(ak);ak.container_div.appendTo(aj.content_div);af=aj}else{af=ak.container}var al=new ak.constructor(view,af,{name:ah,hda_ldda:"hda"});al.init_for_tool_data();al.change_mode(ak.mode);al.set_filters_manager(ak.filters_manager.copy(al));al.update_icons();af.add_drawable(al);al.tiles_div.text("Starting job.");this.update_params();this.run(ag,al,function(am){al.set_dataset(new ab.Dataset(am));al.tiles_div.text("Running job.");al.init()})},run:function(af,ah,ai){af.inputs=this.get_param_values_dict();var ag=new l.ServerStateDeferred({ajax_settings:{url:galaxy_paths.get("tool_url"),data:JSON.stringify(af),dataType:"json",contentType:"application/json",type:"POST"},interval:2000,success_fn:function(aj){return aj!=="pending"}});$.when(ag.go()).then(function(aj){if(aj==="no converter"){ah.container_div.addClass("error");ah.content_div.text(K)}else{if(aj.error){ah.container_div.addClass("error");ah.content_div.text(z+aj.message)}else{ai(aj)}}})}});var O=function(ag,af,ah,ai){this.name=ag;this.label=af;this.html=$(ah);this.value=ai};r(O.prototype,{update_value:function(){this.value=$(this.html).val()}});var e=function(ah,ag,aj,ak,ai,af){O.call(this,ah,ag,aj,ak);this.min=ai;this.max=af};r(e.prototype,O.prototype,{update_value:function(){O.prototype.update_value.call(this);this.value=parseFloat(this.value)}});var D=function(af,ag){M.Scaler.call(this,ag);this.filter=af};D.prototype.gen_val=function(af){if(this.filter.high===Number.MAX_VALUE||this.filter.low===-Number.MAX_VALUE||this.filter.low===this.filter.high){return this.default_val}return((parseFloat(af[this.filter.index])-this.filter.low)/(this.filter.high-this.filter.low))};var G=function(af){this.track=af.track;this.params=af.params;this.values={};this.restore_values((af.saved_values?af.saved_values:{}));this.onchange=af.onchange};r(G.prototype,{restore_values:function(af){var ag=this;$.each(this.params,function(ah,ai){if(af[ai.key]!==undefined){ag.values[ai.key]=af[ai.key]}else{ag.values[ai.key]=ai.default_value}})},build_form:function(){var ai=this;var af=$("<div />");var ah;function ag(an,aj){for(var ar=0;ar<an.length;ar++){ah=an[ar];if(ah.hidden){continue}var al="param_"+ar;var aw=ai.values[ah.key];var ay=$("<div class='form-row' />").appendTo(aj);ay.append($("<label />").attr("for",al).text(ah.label+":"));if(ah.type==="bool"){ay.append($('<input type="checkbox" />').attr("id",al).attr("name",al).attr("checked",aw))}else{if(ah.type==="text"){ay.append($('<input type="text"/>').attr("id",al).val(aw).click(function(){$(this).select()}))}else{if(ah.type==="select"){var au=$("<select />").attr("id",al);for(var ap=0;ap<ah.options.length;ap++){$("<option/>").text(ah.options[ap].label).attr("value",ah.options[ap].value).appendTo(au)}au.val(aw);ay.append(au)}else{if(ah.type==="color"){var ax=$("<div/>").appendTo(ay),at=$("<input />").attr("id",al).attr("name",al).val(aw).css("float","left").appendTo(ax).click(function(aA){$(".bs-tooltip").removeClass("in");var az=$(this).siblings(".bs-tooltip").addClass("in");az.css({left:$(this).position().left+$(this).width()+5,top:$(this).position().top-($(az).height()/2)+($(this).height()/2)}).show();az.click(function(aB){aB.stopPropagation()});$(document).bind("click.color-picker",function(){az.hide();$(document).unbind("click.color-picker")});aA.stopPropagation()}),aq=$("<a href='javascript:void(0)'/>").addClass("icon-button arrow-circle").appendTo(ax).attr("title","Set new random color").tooltip(),av=$("<div class='bs-tooltip right' style='position: absolute;' />").appendTo(ax).hide(),am=$("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(av),ak=$("<div class='tooltip-arrow'></div>").appendTo(av),ao=$.farbtastic(am,{width:100,height:100,callback:at,color:aw});ax.append($("<div/>").css("clear","both"));(function(az){aq.click(function(){az.setColor(R())})})(ao)}else{ay.append($("<input />").attr("id",al).attr("name",al).val(aw))}}}}if(ah.help){ay.append($("<div class='help'/>").text(ah.help))}}}ag(this.params,af);return af},update_from_form:function(af){var ah=this;var ag=false;$.each(this.params,function(ai,ak){if(!ak.hidden){var al="param_"+ai;var aj=af.find("#"+al).val();if(ak.type==="float"){aj=parseFloat(aj)}else{if(ak.type==="int"){aj=parseInt(aj)}else{if(ak.type==="bool"){aj=af.find("#"+al).is(":checked")}}}if(aj!==ah.values[ak.key]){ah.values[ak.key]=aj;ag=true}}});if(ag){this.onchange();this.track.changed()}}});var b=function(af,aj,ah,ag,ai){this.track=af;this.region=aj;this.low=aj.get("start");this.high=aj.get("end");this.resolution=ah;this.html_elt=$("<div class='track-tile'/>").append(ag).height($(ag).attr("height"));this.data=ai;this.stale=false};b.prototype.predisplay_actions=function(){};var j=function(af,ak,ah,ag,ai,aj){b.call(this,af,ak,ah,ag,ai);this.max_val=aj};r(j.prototype,b.prototype);var P=function(ai,aq,aj,ah,al,at,am,au,ag,ap){b.call(this,ai,aq,aj,ah,al);this.mode=am;this.all_slotted=ag;this.feature_mapper=ap;this.has_icons=false;if(au){this.has_icons=true;var an=this;ah=this.html_elt.children()[0],message_div=$("<div/>").addClass("tile-message").css({height:E-1,width:ah.width}).prependTo(this.html_elt);var ao=new y.GenomeRegion({chrom:ai.view.chrom,start:this.low,end:this.high}),ar=al.length,ak=$("<a href='javascript:void(0);'/>").addClass("icon more-down").attr("title","For speed, only the first "+ar+" features in this region were obtained from server. Click to get more data including depth").tooltip().appendTo(message_div),af=$("<a href='javascript:void(0);'/>").addClass("icon more-across").attr("title","For speed, only the first "+ar+" features in this region were obtained from server. Click to get more data excluding depth").tooltip().appendTo(message_div);ak.click(function(){an.stale=true;ai.data_manager.get_more_data(ao,ai.mode,an.resolution,{},ai.data_manager.DEEP_DATA_REQ);$(".bs-tooltip").hide();ai.request_draw(true)}).dblclick(function(av){av.stopPropagation()});af.click(function(){an.stale=true;ai.data_manager.get_more_data(ao,ai.mode,an.resolution,{},ai.data_manager.BROAD_DATA_REQ);$(".bs-tooltip").hide();ai.request_draw(true)}).dblclick(function(av){av.stopPropagation()})}};r(P.prototype,b.prototype);P.prototype.predisplay_actions=function(){var ag=this,af={};if(ag.mode!=="Pack"){return}$(this.html_elt).hover(function(){this.hovered=true;$(this).mousemove()},function(){this.hovered=false;$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()}).mousemove(function(ar){if(!this.hovered){return}var am=$(this).offset(),aq=ar.pageX-am.left,ap=ar.pageY-am.top,aw=ag.feature_mapper.get_feature_data(aq,ap),an=(aw?aw[0]:null);$(this).parents(".track-content").children(".overlay").children(".feature-popup").each(function(){if(!an||$(this).attr("id")!==an.toString()){$(this).remove()}});if(aw){var ai=af[an];if(!ai){var an=aw[0],at={name:aw[3],start:aw[1],end:aw[2],strand:aw[4]},al=ag.track.filters_manager.filters,ak;for(var ao=0;ao<al.length;ao++){ak=al[ao];at[ak.name]=aw[ak.index]}var ai=$("<div/>").attr("id",an).addClass("feature-popup"),ax=$("<table/>"),av,au,ay;for(av in at){au=at[av];ay=$("<tr/>").appendTo(ax);$("<th/>").appendTo(ay).text(av);$("<td/>").attr("align","left").appendTo(ay).text(typeof(au)==="number"?Y(au,2):au)}ai.append($("<div class='feature-popup-inner'>").append(ax));af[an]=ai}ai.appendTo($(this).parents(".track-content").children(".overlay"));var aj=aq+parseInt(ag.html_elt.css("left"))-ai.width()/2,ah=ap+parseInt(ag.html_elt.css("top"))+7;ai.css("left",aj+"px").css("top",ah+"px")}else{if(!ar.isPropagationStopped()){ar.stopPropagation();$(this).siblings().each(function(){$(this).trigger(ar)})}}}).mouseleave(function(){$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()})};var g=function(ag,af,ah){r(ah,{drag_handle_class:"draghandle"});s.call(this,ag,af,ah);this.dataset=new ab.Dataset({id:ah.dataset_id,hda_ldda:ah.hda_ldda});this.dataset_check_type="converted_datasets_state";this.data_url_extra_params={};this.data_query_wait=("data_query_wait" in ah?ah.data_query_wait:L);this.data_manager=("data_manager" in ah?ah.data_manager:new y.GenomeDataManager({dataset:this.dataset,data_mode_compatible:this.data_and_mode_compatible,can_subset:this.can_subset}));this.min_height_px=16;this.max_height_px=800;this.visible_height_px=0;this.content_div=$("<div class='track-content'>").appendTo(this.container_div);if(this.container){this.container.content_div.append(this.container_div);if(!("resize" in ah)||ah.resize){this.add_resize_handle()}}};r(g.prototype,s.prototype,{action_icons_def:[{name:"mode_icon",title:"Set display mode",css_class:"chevron-expand",on_click_fn:function(){}},s.prototype.action_icons_def[0],{name:"overview_icon",title:"Set as overview",css_class:"overview-icon",on_click_fn:function(af){af.view.set_overview(af)}},s.prototype.action_icons_def[1],{name:"filters_icon",title:"Filters",css_class:"filters-icon",on_click_fn:function(af){if(af.filters_manager.visible()){af.filters_manager.clear_filters()}else{af.filters_manager.init_filters()}af.filters_manager.toggle()}},{name:"tools_icon",title:"Tool",css_class:"hammer",on_click_fn:function(af){af.dynamic_tool_div.toggle();if(af.dynamic_tool_div.is(":visible")){af.set_name(af.name+af.tool_region_and_parameters_str())}else{af.revert_name()}$(".bs-tooltip").remove()}},{name:"param_space_viz_icon",title:"Tool parameter space visualization",css_class:"arrow-split",on_click_fn:function(af){var ai='<strong>Tool</strong>: <%= track.tool.name %><br/><strong>Dataset</strong>: <%= track.name %><br/><strong>Region(s)</strong>: <select name="regions"><option value="cur">current viewing area</option><option value="bookmarks">bookmarks</option><option value="both">current viewing area and bookmarks</option></select>',ah=ad.template(ai,{track:af});var ak=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},ag=function(){var am=$('select[name="regions"] option:selected').val(),ao,al=new y.GenomeRegion({chrom:view.chrom,start:view.low,end:view.high}),an=ad.map($(".bookmark"),function(ap){return new y.GenomeRegion({from_str:$(ap).children(".position").text()})});if(am==="cur"){ao=[al]}else{if(am==="bookmarks"){ao=an}else{ao=[al].concat(an)}}hide_modal();window.location.href=galaxy_paths.get("sweepster_url")+"?"+$.param({dataset_id:af.dataset_id,hda_ldda:af.hda_ldda,regions:JSON.stringify(new Backbone.Collection(ao).toJSON())})},aj=function(al){if((al.keyCode||al.which)===27){ak()}else{if((al.keyCode||al.which)===13){ag()}}};show_modal("Visualize tool parameter space and output from different parameter settings?",ah,{No:ak,Yes:ag})}},s.prototype.action_icons_def[2]],can_draw:function(){if(this.dataset_id&&s.prototype.can_draw.call(this)){return true}return false},build_container_div:function(){return $("<div/>").addClass("track").attr("id","track_"+this.id).css("position","relative")},build_header_div:function(){var af=$("<div class='track-header'/>");if(this.view.editor){this.drag_div=$("<div/>").addClass(this.drag_handle_class).appendTo(af)}this.name_div=$("<div/>").addClass("track-name").appendTo(af).text(this.name).attr("id",this.name.replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase());return af},on_resize:function(){},add_resize_handle:function(){var af=this;var ai=false;var ah=false;var ag=$("<div class='track-resize'>");$(af.container_div).hover(function(){if(af.content_visible){ai=true;ag.show()}},function(){ai=false;if(!ah){ag.hide()}});ag.hide().bind("dragstart",function(aj,ak){ah=true;ak.original_height=$(af.content_div).height()}).bind("drag",function(ak,al){var aj=Math.min(Math.max(al.original_height+al.deltaY,af.min_height_px),af.max_height_px);$(af.tiles_div).css("height",aj);af.visible_height_px=(af.max_height_px===aj?0:aj);af.on_resize()}).bind("dragend",function(aj,ak){af.tile_cache.clear();ah=false;if(!ai){ag.hide()}af.config.values.height=af.visible_height_px;af.changed()}).appendTo(af.container_div)},set_display_modes:function(ai,al){this.display_modes=ai;this.mode=(al?al:(this.config&&this.config.values.mode?this.config.values.mode:this.display_modes[0]));this.action_icons.mode_icon.attr("title","Set display mode (now: "+this.mode+")");var ag=this,aj={};for(var ah=0,af=ag.display_modes.length;ah<af;ah++){var ak=ag.display_modes[ah];aj[ak]=function(am){return function(){ag.change_mode(am);ag.icons_div.show();ag.container_div.mouseleave(function(){ag.icons_div.hide()})}}(ak)}make_popupmenu(this.action_icons.mode_icon,aj)},build_action_icons:function(){s.prototype.build_action_icons.call(this,this.action_icons_def);if(this.display_modes!==undefined){this.set_display_modes(this.display_modes)}},hide_contents:function(){this.tiles_div.hide();this.container_div.find(".yaxislabel, .track-resize").hide()},show_contents:function(){this.tiles_div.show();this.container_div.find(".yaxislabel, .track-resize").show();this.request_draw()},get_type:function(){if(this instanceof Z){return"LabelTrack"}else{if(this instanceof C){return"ReferenceTrack"}else{if(this instanceof h){return"LineTrack"}else{if(this instanceof V){return"ReadTrack"}else{if(this instanceof T){return"VcfTrack"}else{if(this instanceof f){return"CompositeTrack"}else{if(this instanceof c){return"FeatureTrack"}}}}}}}return""},init:function(){var ag=this;ag.enabled=false;ag.tile_cache.clear();ag.data_manager.clear();ag.content_div.css("height","auto");ag.tiles_div.children().remove();ag.container_div.removeClass("nodata error pending");if(!ag.dataset_id){return}var af=$.Deferred(),ah={hda_ldda:ag.hda_ldda,data_type:this.dataset_check_type,chrom:ag.view.chrom};$.getJSON(this.dataset.url(),ah,function(ai){if(!ai||ai==="error"||ai.kind==="error"){ag.container_div.addClass("error");ag.tiles_div.text(o);if(ai.message){var aj=$(" <a href='javascript:void(0);'></a>").text("View error").click(function(){show_modal("Trackster Error","<pre>"+ai.message+"</pre>",{Close:hide_modal})});ag.tiles_div.append(aj)}}else{if(ai==="no converter"){ag.container_div.addClass("error");ag.tiles_div.text(K)}else{if(ai==="no data"||(ai.data!==undefined&&(ai.data===null||ai.data.length===0))){ag.container_div.addClass("nodata");ag.tiles_div.text(F)}else{if(ai==="pending"){ag.container_div.addClass("pending");ag.tiles_div.html(w);setTimeout(function(){ag.init()},ag.data_query_wait)}else{if(ai==="data"||ai.status==="data"){if(ai.valid_chroms){ag.valid_chroms=ai.valid_chroms;ag.update_icons()}ag.tiles_div.text(W);if(ag.view.chrom){ag.tiles_div.text("");ag.tiles_div.css("height",ag.visible_height_px+"px");ag.enabled=true;$.when(ag.predraw_init()).done(function(){af.resolve();ag.container_div.removeClass("nodata error pending");ag.request_draw()})}else{af.resolve()}}}}}}});this.update_icons();return af},predraw_init:function(){},get_drawables:function(){return this}});var N=function(ah,ag,ai){g.call(this,ah,ag,ai);var af=this;m(af.container_div,af.drag_handle_class,".group",af);this.filters_manager=new i.FiltersManager(this,("filters" in ai?ai.filters:null));this.data_manager.set("filters_manager",this.filters_manager);this.filters_available=false;this.tool=("tool" in ai&&ai.tool?new t(this,ai.tool,ai.tool_state):null);this.tile_cache=new y.Cache(S);if(this.header_div){this.set_filters_manager(this.filters_manager);if(this.tool){this.dynamic_tool_div=this.tool.parent_div;this.header_div.after(this.dynamic_tool_div)}}this.tiles_div=$("<div/>").addClass("tiles").appendTo(this.content_div);this.overlay_div=$("<div/>").addClass("overlay").appendTo(this.content_div);if(ai.mode){this.change_mode(ai.mode)}};r(N.prototype,s.prototype,g.prototype,{action_icons_def:g.prototype.action_icons_def.concat([{name:"show_more_rows_icon",title:"To minimize track height, not all feature rows are displayed. Click to display more rows.",css_class:"exclamation",on_click_fn:function(af){$(".bs-tooltip").remove();af.slotters[af.view.resolution_px_b].max_rows*=2;af.request_draw(true)},hide:true}]),copy:function(af){var ag=this.to_dict();r(ag,{data_manager:this.data_manager});var ah=new this.constructor(this.view,af,ag);ah.change_mode(this.mode);ah.enabled=this.enabled;return ah},set_filters_manager:function(af){this.filters_manager=af;this.header_div.after(this.filters_manager.parent_div)},to_dict:function(){return{track_type:this.get_type(),name:this.name,hda_ldda:this.hda_ldda,dataset_id:this.dataset_id,prefs:this.prefs,mode:this.mode,filters:this.filters_manager.to_dict(),tool_state:(this.tool?this.tool.state_dict():{})}},change_mode:function(ag){var af=this;af.mode=ag;af.config.values.mode=ag;af.tile_cache.clear();af.request_draw();this.action_icons.mode_icon.attr("title","Set display mode (now: "+af.mode+")");return af},update_icons:function(){var af=this;if(af.filters_available){af.action_icons.filters_icon.show()}else{af.action_icons.filters_icon.hide()}if(af.tool){af.action_icons.tools_icon.show();af.action_icons.param_space_viz_icon.show()}else{af.action_icons.tools_icon.hide();af.action_icons.param_space_viz_icon.hide()}},_gen_tile_cache_key:function(ag,ah,af){return ag+"_"+ah+"_"+af},request_draw:function(ag,af){this.view.request_redraw(false,ag,af,this)},before_draw:function(){},_draw:function(ag,aq){if(!this.can_draw()){return}var ao=this.view.low,ak=this.view.high,am=ak-ao,ah=this.view.container.width(),at=this.view.resolution_px_b,aj=this.view.resolution_b_px;if(this.is_overview){ao=this.view.max_low;ak=this.view.max_high;aj=(view.max_high-view.max_low)/ah;at=1/aj}this.before_draw();this.tiles_div.children().addClass("remove");var af=Math.floor(ao/(aj*U)),an=true,ar=[],al=function(au){return(au&&"track" in au)};while((af*U*aj)<ak){var ap=this.draw_helper(ag,ah,af,aj,this.tiles_div,at);if(al(ap)){ar.push(ap)}else{an=false}af+=1}if(!aq){this.tiles_div.children(".remove").removeClass("remove").remove()}var ai=this;if(an){this.tiles_div.children(".remove").remove();ai.postdraw_actions(ar,ah,at,aq)}},postdraw_actions:function(ah,ai,ak,af){var aj=false;for(var ag=0;ag<ah.length;ag++){if(ah[ag].has_icons){aj=true;break}}if(aj){for(var ag=0;ag<ah.length;ag++){tile=ah[ag];if(!tile.has_icons){tile.html_elt.css("padding-top",E)}}}},draw_helper:function(af,ar,ax,av,ak,al,at){var aq=this,aA=this._gen_tile_cache_key(ar,al,ax),ai=this._get_tile_bounds(ax,av);if(!at){at={}}var az=(af?undefined:aq.tile_cache.get_elt(aA));if(az){aq.show_tile(az,ak,al);return az}var ao=true;var aw=aq.data_manager.get_data(ai,aq.mode,av,aq.data_url_extra_params);if(X(aw)){ao=false}var am;if(view.reference_track&&al>view.canvas_manager.char_width_px){am=view.reference_track.data_manager.get_data(ai,aq.mode,av,view.reference_track.data_url_extra_params);if(X(am)){ao=false}}if(ao){r(aw,at.more_tile_data);var an=aq.mode;if(an==="Auto"){an=aq.get_mode(aw);aq.update_auto_mode(an)}var ah=aq.view.canvas_manager.new_canvas(),ay=ai.get("start"),ag=ai.get("end"),ar=Math.ceil((ag-ay)*al)+aq.left_offset,ap=aq.get_canvas_height(aw,an,al,ar);ah.width=ar;ah.height=ap;var au=ah.getContext("2d");au.translate(this.left_offset,0);var az=aq.draw_tile(aw,au,an,av,ai,al,am);if(az!==undefined){aq.tile_cache.set_elt(aA,az);aq.show_tile(az,ak,al)}return az}var aj=$.Deferred();$.when(aw,am).then(function(){view.request_redraw(false,false,false,aq);aj.resolve()});return aj},get_canvas_height:function(af,ah,ai,ag){return this.visible_height_px},draw_tile:function(af,ag,ak,ai,aj,al,ah){console.log("Warning: TiledTrack.draw_tile() not implemented.")},show_tile:function(ah,aj,ak){var ag=this,af=ah.html_elt;ah.predisplay_actions();var ai=(ah.low-(this.is_overview?this.view.max_low:this.view.low))*ak;if(this.left_offset){ai-=this.left_offset}af.css({position:"absolute",top:0,left:ai});if(af.hasClass("remove")){af.removeClass("remove")}else{aj.append(af)}ag.after_show_tile(ah)},after_show_tile:function(af){this.max_height_px=Math.max(this.max_height_px,af.html_elt.height());af.html_elt.parent().children().css("height",this.max_height_px+"px");var ag=this.max_height_px;if(this.visible_height_px!==0){ag=Math.min(this.max_height_px,this.visible_height_px)}this.tiles_div.css("height",ag+"px")},_get_tile_bounds:function(af,ag){var ai=Math.floor(af*U*ag),aj=Math.ceil(U*ag),ah=(ai+aj<=this.view.max_high?ai+aj:this.view.max_high);return new y.GenomeRegion({chrom:this.view.chrom,start:ai,end:ah})},tool_region_and_parameters_str:function(ah,af,ai){var ag=this,aj=(ah!==undefined&&af!==undefined&&ai!==undefined?ah+":"+af+"-"+ai:"all");return" - region=["+aj+"], parameters=["+ag.tool.get_param_values().join(", ")+"]"},data_and_mode_compatible:function(af,ag){return true},can_subset:function(af){return false},init_for_tool_data:function(){this.data_manager.set("data_type","raw_data");this.data_query_wait=1000;this.dataset_check_type="state";this.normal_postdraw_actions=this.postdraw_actions;this.postdraw_actions=function(ah,ai,ak,af){var ag=this;ag.normal_postdraw_actions(ah,ai,ak,af);ag.dataset_check_type="converted_datasets_state";ag.data_query_wait=L;var aj=new l.ServerStateDeferred({url:ag.dataset_state_url,url_params:{dataset_id:ag.dataset_id,hda_ldda:ag.hda_ldda},interval:ag.data_query_wait,success_fn:function(al){return al!=="pending"}});$.when(aj.go()).then(function(){ag.data_manager.set("data_type","data")});ag.postdraw_actions=ag.normal_postdraw_actions}}});var Z=function(ag,af){var ah={resize:false};g.call(this,ag,af,ah);this.container_div.addClass("label-track")};r(Z.prototype,g.prototype,{build_header_div:function(){},init:function(){this.enabled=true},_draw:function(){var ah=this.view,ai=ah.high-ah.low,al=Math.floor(Math.pow(10,Math.floor(Math.log(ai)/Math.log(10)))),af=Math.floor(ah.low/al)*al,aj=this.view.container.width(),ag=$("<div style='position: relative; height: 1.3em;'></div>");while(af<ah.high){var ak=(af-ah.low)/ai*aj;ag.append($("<div class='label'>"+commatize(af)+"</div>").css({position:"absolute",left:ak-1}));af+=al}this.content_div.children(":first").remove();this.content_div.append(ag)}});var f=function(ag,af,aj){N.call(this,ag,af,aj);this.drawables=[];this.left_offset=0;if("drawables" in aj){var ai;for(var ah=0;ah<aj.drawables.length;ah++){ai=aj.drawables[ah];this.drawables[ah]=p(ai,ag,null);if(ai.left_offset>this.left_offset){this.left_offset=ai.left_offset}}this.enabled=true}if(this.drawables.length!==0){this.set_display_modes(this.drawables[0].display_modes,this.drawables[0].mode)}this.update_icons();this.obj_type="CompositeTrack"};r(f.prototype,N.prototype,{action_icons_def:[{name:"composite_icon",title:"Show individual tracks",css_class:"layers-stack",on_click_fn:function(af){$(".bs-tooltip").remove();af.show_group()}}].concat(N.prototype.action_icons_def),to_dict:A.prototype.to_dict,add_drawable:A.prototype.add_drawable,unpack_drawables:A.prototype.unpack_drawables,change_mode:function(af){N.prototype.change_mode.call(this,af);for(var ag=0;ag<this.drawables.length;ag++){this.drawables[ag].change_mode(af)}},init:function(){var ah=[];for(var ag=0;ag<this.drawables.length;ag++){ah.push(this.drawables[ag].init())}var af=this;$.when.apply($,ah).then(function(){af.enabled=true;af.request_draw()})},update_icons:function(){this.action_icons.filters_icon.hide();this.action_icons.tools_icon.hide();this.action_icons.param_space_viz_icon.hide()},can_draw:s.prototype.can_draw,draw_helper:function(ag,aw,aC,az,an,ap,ax){var av=this,aG=this._gen_tile_cache_key(aw,ap,aC),ak=this._get_tile_bounds(aC,az);if(!ax){ax={}}var aF=(ag?undefined:av.tile_cache.get_elt(aG));if(aF){av.show_tile(aF,an,ap);return aF}var ao=[],av,at=true,aA,aq;for(var aB=0;aB<this.drawables.length;aB++){av=this.drawables[aB];aA=av.data_manager.get_data(ak,av.mode,az,av.data_url_extra_params);if(X(aA)){at=false}ao.push(aA);aq=null;if(view.reference_track&&ap>view.canvas_manager.char_width_px){aq=view.reference_track.data_manager.get_data(ak,av.mode,az,view.reference_track.data_url_extra_params);if(X(aq)){at=false}}ao.push(aq)}if(at){r(aA,ax.more_tile_data);this.tile_predraw_init();var aj=av.view.canvas_manager.new_canvas(),al=av._get_tile_bounds(aC,az),aD=ak.get("start"),ah=ak.get("end"),aE=0,aw=Math.ceil((ah-aD)*ap)+this.left_offset,au=0,ai=[],aB;var af=0;for(aB=0;aB<this.drawables.length;aB++,aE+=2){av=this.drawables[aB];aA=ao[aE];var ar=av.mode;if(ar==="Auto"){ar=av.get_mode(aA);av.update_auto_mode(ar)}ai.push(ar);af=av.get_canvas_height(aA,ar,ap,aw);if(af>au){au=af}}aj.width=aw;aj.height=(ax.height?ax.height:au);aE=0;var ay=aj.getContext("2d");ay.translate(this.left_offset,0);ay.globalAlpha=0.5;ay.globalCompositeOperation="source-over";for(aB=0;aB<this.drawables.length;aB++,aE+=2){av=this.drawables[aB];aA=ao[aE];aq=ao[aE+1];aF=av.draw_tile(aA,ay,ai[aB],az,ak,ap,aq)}this.tile_cache.set_elt(aG,aF);this.show_tile(aF,an,ap);return aF}var am=$.Deferred(),av=this;$.when.apply($,ao).then(function(){view.request_redraw(false,false,false,av);am.resolve()});return am},show_group:function(){var ai=new Q(this.view,this.container,{name:this.name}),af;for(var ah=0;ah<this.drawables.length;ah++){af=this.drawables[ah];af.update_icons();ai.add_drawable(af);af.container=ai;ai.content_div.append(af.container_div)}var ag=this.container.replace_drawable(this,ai,true);ai.request_draw()},tile_predraw_init:function(){var ai=Number.MAX_VALUE,af=-ai,ag;for(var ah=0;ah<this.drawables.length;ah++){ag=this.drawables[ah];if(ag instanceof h){if(ag.prefs.min_value<ai){ai=ag.prefs.min_value}if(ag.prefs.max_value>af){af=ag.prefs.max_value}}}for(var ah=0;ah<this.drawables.length;ah++){ag=this.drawables[ah];ag.prefs.min_value=ai;ag.prefs.max_value=af}},postdraw_actions:function(ah,ak,am,ag){N.prototype.postdraw_actions.call(this,ah,ak,am,ag);var aj=-1;for(var ai=0;ai<ah.length;ai++){var af=ah[ai].html_elt.find("canvas").height();if(af>aj){aj=af}}for(var ai=0;ai<ah.length;ai++){var al=ah[ai];if(al.html_elt.find("canvas").height()!==aj){this.draw_helper(true,ak,al.index,al.resolution,al.html_elt.parent(),am,{height:aj});al.html_elt.remove()}}}});var C=function(af){N.call(this,af,{content_div:af.top_labeltrack},{resize:false});af.reference_track=this;this.left_offset=200;this.visible_height_px=12;this.container_div.addClass("reference-track");this.content_div.css("background","none");this.content_div.css("min-height","0px");this.content_div.css("border","none");this.data_url=reference_url+"/"+this.view.dbkey;this.data_url_extra_params={reference:true};this.data_manager=new y.ReferenceTrackDataManager({data_url:this.data_url});this.hide_contents()};r(C.prototype,s.prototype,N.prototype,{build_header_div:function(){},init:function(){this.data_manager.clear();this.enabled=true},can_draw:s.prototype.can_draw,draw_helper:function(aj,ah,af,ag,ak,al,ai){if(al>this.view.canvas_manager.char_width_px){return N.prototype.draw_helper.call(this,aj,ah,af,ag,ak,al,ai)}else{this.hide_contents();return null}},draw_tile:function(an,ao,aj,ai,al,ap){var ah=this;if(ap>this.view.canvas_manager.char_width_px){if(an.data===null){this.hide_contents();return}var ag=ao.canvas;ao.font=ao.canvas.manager.default_font;ao.textAlign="center";an=an.data;for(var ak=0,am=an.length;ak<am;ak++){var af=Math.floor(ak*ap);ao.fillText(an[ak],af,10)}this.show_contents();return new b(ah,al,ai,ag,an)}this.hide_contents()}});var h=function(ah,ag,ai){var af=this;this.display_modes=["Histogram","Line","Filled","Intensity"];this.mode="Histogram";N.call(this,ah,ag,ai);this.hda_ldda=ai.hda_ldda;this.dataset_id=ai.dataset_id;this.original_dataset_id=this.dataset_id;this.left_offset=0;this.config=new G({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"color",label:"Color",type:"color",default_value:R()},{key:"min_value",label:"Min Value",type:"float",default_value:undefined},{key:"max_value",label:"Max Value",type:"float",default_value:undefined},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:32,hidden:true}],saved_values:ai.prefs,onchange:function(){af.set_name(af.prefs.name);af.vertical_range=af.prefs.max_value-af.prefs.min_value;af.set_min_value(af.prefs.min_value);af.set_max_value(af.prefs.max_value)}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height;this.vertical_range=this.config.values.max_value-this.config.values.min_value};r(h.prototype,s.prototype,N.prototype,{on_resize:function(){this.request_draw(true)},set_min_value:function(af){this.prefs.min_value=af;$("#linetrack_"+this.dataset_id+"_minval").text(this.prefs.min_value);this.tile_cache.clear();this.request_draw()},set_max_value:function(af){this.prefs.max_value=af;$("#linetrack_"+this.dataset_id+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.request_draw()},predraw_init:function(){var af=this;af.vertical_range=undefined;return $.getJSON(af.dataset.url(),{data_type:"data",stats:true,chrom:af.view.chrom,low:0,high:af.view.max_high,hda_ldda:af.hda_ldda},function(ag){af.container_div.addClass("line-track");var aj=ag.data;if(isNaN(parseFloat(af.prefs.min_value))||isNaN(parseFloat(af.prefs.max_value))){var ah=aj.min,al=aj.max;ah=Math.floor(Math.min(0,Math.max(ah,aj.mean-2*aj.sd)));al=Math.ceil(Math.max(0,Math.min(al,aj.mean+2*aj.sd)));af.prefs.min_value=ah;af.prefs.max_value=al;$("#track_"+af.dataset_id+"_minval").val(af.prefs.min_value);$("#track_"+af.dataset_id+"_maxval").val(af.prefs.max_value)}af.vertical_range=af.prefs.max_value-af.prefs.min_value;af.total_frequency=aj.total_frequency;af.container_div.find(".yaxislabel").remove();var ak=$("<div/>").text(Y(af.prefs.min_value,3)).make_text_editable({num_cols:6,on_finish:function(am){$(".bs-tooltip").remove();var am=parseFloat(am);if(!isNaN(am)){af.set_min_value(am)}},help_text:"Set min value"}).addClass("yaxislabel bottom").attr("id","linetrack_"+af.dataset_id+"_minval").prependTo(af.container_div),ai=$("<div/>").text(Y(af.prefs.max_value,3)).make_text_editable({num_cols:6,on_finish:function(am){$(".bs-tooltip").remove();var am=parseFloat(am);if(!isNaN(am)){af.set_max_value(am)}},help_text:"Set max value"}).addClass("yaxislabel top").attr("id","linetrack_"+af.dataset_id+"_maxval").prependTo(af.container_div)})},draw_tile:function(ao,am,ah,ag,aj,an){var af=am.canvas,ai=aj.get("start"),al=aj.get("end"),ak=new M.LinePainter(ao.data,ai,al,this.prefs,ah);ak.draw(am,af.width,af.height,an);return new b(this,aj,ag,af,ao.data)},can_subset:function(af){return false}});var u=function(ah,ag,ai){var af=this;this.display_modes=["Heatmap"];this.mode="Heatmap";N.call(this,ah,ag,ai);this.hda_ldda=ai.hda_ldda;this.dataset_id=ai.dataset_id;this.original_dataset_id=this.dataset_id;this.left_offset=0;this.config=new G({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"pos_color",label:"Positive Color",type:"color",default_value:"4169E1"},{key:"negative_color",label:"Negative Color",type:"color",default_value:"FF8C00"},{key:"min_value",label:"Min Value",type:"float",default_value:0},{key:"max_value",label:"Max Value",type:"float",default_value:1},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:500,hidden:true}],saved_values:ai.prefs,onchange:function(){af.set_name(af.prefs.name);af.vertical_range=af.prefs.max_value-af.prefs.min_value;af.set_min_value(af.prefs.min_value);af.set_max_value(af.prefs.max_value)}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height;this.vertical_range=this.config.values.max_value-this.config.values.min_value};r(u.prototype,s.prototype,N.prototype,{on_resize:function(){this.request_draw(true)},set_min_value:function(af){this.prefs.min_value=af;this.tile_cache.clear();this.request_draw()},set_max_value:function(af){this.prefs.max_value=af;this.tile_cache.clear();this.request_draw()},draw_tile:function(ap,an,ak,ai,ag,ao){var ah=an.canvas,af=this._get_tile_bounds(ag,ai),aj=af[0],am=af[1],al=new M.DiagonalHeatmapPainter(ap.data,aj,am,this.prefs,ak);al.draw(an,ah.width,ah.height,ao);return new b(this,ag,ai,ah,ap.data)}});var c=function(ai,ah,ak){var ag=this;this.display_modes=["Auto","Coverage","Dense","Squish","Pack"];N.call(this,ai,ah,ak);var aj=R(),af=R([aj,"#ffffff"]);this.config=new G({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block color",type:"color",default_value:aj},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:af},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true,help:"Show the number of items in each bin when drawing summary histogram"},{key:"histogram_max",label:"Histogram maximum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"connector_style",label:"Connector style",type:"select",default_value:"fishbones",options:[{label:"Line with arrows",value:"fishbone"},{label:"Arcs",value:"arcs"}]},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:this.visible_height_px,hidden:true}],saved_values:ak.prefs,onchange:function(){ag.set_name(ag.prefs.name);ag.tile_cache.clear();ag.set_painter_from_config();ag.request_draw()}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height;this.container_div.addClass("feature-track");this.hda_ldda=ak.hda_ldda;this.dataset_id=ak.dataset_id;this.original_dataset_id=ak.dataset_id;this.show_labels_scale=0.001;this.showing_details=false;this.summary_draw_height=30;this.slotters={};this.start_end_dct={};this.left_offset=200;this.set_painter_from_config()};r(c.prototype,s.prototype,N.prototype,{set_dataset:function(af){this.dataset_id=af.get("id");this.hda_ldda=af.get("hda_ldda");this.dataset=af;this.data_manager.set("dataset",af)},set_painter_from_config:function(){if(this.config.values.connector_style==="arcs"){this.painter=M.ArcLinkedFeaturePainter}else{this.painter=M.LinkedFeaturePainter}},before_draw:function(){this.max_height_px=0},postdraw_actions:function(av,ap,ak,aj){N.prototype.postdraw_actions.call(this,av,aj);var ao=this,ar;if(ao.mode==="Coverage"){var ag=-1;for(ar=0;ar<av.length;ar++){var aq=av[ar].max_val;if(aq>ag){ag=aq}}for(ar=0;ar<av.length;ar++){var ax=av[ar];if(ax.max_val!==ag){ax.html_elt.remove();ao.draw_helper(true,ap,ax.index,ax.resolution,ax.html_elt.parent(),ak,{more_tile_data:{max:ag}})}}}if(ao.filters_manager){var al=ao.filters_manager.filters;for(var au=0;au<al.length;au++){al[au].update_ui_elt()}var aw=false,af,am;for(ar=0;ar<av.length;ar++){if(av[ar].data.length){af=av[ar].data[0];for(var au=0;au<al.length;au++){am=al[au];if(am.applies_to(af)&&am.min!==am.max){aw=true;break}}}}if(ao.filters_available!==aw){ao.filters_available=aw;if(!ao.filters_available){ao.filters_manager.hide()}ao.update_icons()}}this.container_div.find(".yaxislabel").remove();var ai=av[0];if(ai instanceof j){var an=(this.prefs.histogram_max?this.prefs.histogram_max:ai.max_val),ah=$("<div/>").text(an).make_text_editable({num_cols:12,on_finish:function(ay){$(".bs-tooltip").remove();var ay=parseFloat(ay);ao.prefs.histogram_max=(!isNaN(ay)?ay:null);ao.tile_cache.clear();ao.request_draw()},help_text:"Set max value; leave blank to use default"}).addClass("yaxislabel top").css("color",this.prefs.label_color);this.container_div.prepend(ah)}if(ai instanceof P){var at=true;for(ar=0;ar<av.length;ar++){if(!av[ar].all_slotted){at=false;break}}if(!at){this.action_icons.show_more_rows_icon.show()}else{this.action_icons.show_more_rows_icon.hide()}}else{this.action_icons.show_more_rows_icon.hide()}},update_auto_mode:function(af){var af;if(this.mode==="Auto"){if(af==="no_detail"){af="feature spans"}else{if(af==="summary_tree"){af="coverage histogram"}}this.action_icons.mode_icon.attr("title","Set display mode (now: Auto/"+af+")")}},incremental_slots:function(aj,af,ai){var ag=this.view.canvas_manager.dummy_context,ah=this.slotters[aj];if(!ah||(ah.mode!==ai)){ah=new (v.FeatureSlotter)(aj,ai,B,function(ak){return ag.measureText(ak)});this.slotters[aj]=ah}return ah.slot_features(af)},get_mode:function(af){if(af.dataset_type==="summary_tree"){mode="summary_tree"}else{if(af.extra_info==="no_detail"||this.is_overview){mode="no_detail"}else{if(this.view.high-this.view.low>J){mode="Squish"}else{mode="Pack"}}}return mode},get_canvas_height:function(af,aj,ak,ag){if(aj==="summary_tree"||aj==="Coverage"){return this.summary_draw_height}else{var ai=this.incremental_slots(ak,af.data,aj);var ah=new (this.painter)(null,null,null,this.prefs,aj);return Math.max(ac,ah.get_required_height(ai,ag))}},draw_tile:function(ap,au,ar,av,ai,am,ah){var at=this,ag=au.canvas,aB=ai.get("start"),af=ai.get("end"),aj=this.left_offset;if(ar==="summary_tree"||ar==="Coverage"){var aD=new M.SummaryTreePainter(ap,aB,af,this.prefs);aD.draw(au,ag.width,ag.height,am);return new j(at,ai,av,ag,ap.data,ap.max)}var al=[],aq=this.slotters[am].slots;all_slotted=true;if(ap.data){var an=this.filters_manager.filters;for(var aw=0,ay=ap.data.length;aw<ay;aw++){var ak=ap.data[aw];var ax=false;var ao;for(var aA=0,aF=an.length;aA<aF;aA++){ao=an[aA];ao.update_attrs(ak);if(!ao.keep(ak)){ax=true;break}}if(!ax){al.push(ak);if(!(ak[0] in aq)){all_slotted=false}}}}var aE=(this.filters_manager.alpha_filter?new D(this.filters_manager.alpha_filter):null);var aC=(this.filters_manager.height_filter?new D(this.filters_manager.height_filter):null);var aD=new (this.painter)(al,aB,af,this.prefs,ar,aE,aC,ah);var az=null;au.fillStyle=this.prefs.block_color;au.font=au.canvas.manager.default_font;au.textAlign="right";if(ap.data){az=aD.draw(au,ag.width,ag.height,am,aq);az.translation=-aj}return new P(at,ai,av,ag,ap.data,am,ar,ap.message,all_slotted,az)},data_and_mode_compatible:function(af,ag){if(ag==="Auto"){return true}else{if(ag==="Coverage"){return af.dataset_type==="summary_tree"}else{if(af.extra_info==="no_detail"||af.dataset_type==="summary_tree"){return false}else{return true}}}},can_subset:function(af){if(af.dataset_type==="summary_tree"||af.message||af.extra_info==="no_detail"){return false}return true}});var T=function(ag,af,ah){c.call(this,ag,af,ah);this.config=new G({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block color",type:"color",default_value:R()},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true}],saved_values:ah.prefs,onchange:function(){this.track.set_name(this.track.prefs.name);this.track.tile_cache.clear();this.track.request_draw()}});this.prefs=this.config.values;this.painter=M.ReadPainter};r(T.prototype,s.prototype,N.prototype,c.prototype);var V=function(ah,ag,aj){c.call(this,ah,ag,aj);var ai=R(),af=R([ai,"#ffffff"]);this.config=new G({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block and sense strand color",type:"color",default_value:ai},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:af},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_differences",label:"Show differences only",type:"bool",default_value:true},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"histogram_max",label:"Histogram maximum",type:"float",default_value:null,help:"Clear value to set automatically"},{key:"mode",type:"string",default_value:this.mode,hidden:true}],saved_values:aj.prefs,onchange:function(){this.track.set_name(this.track.prefs.name);this.track.tile_cache.clear();this.track.request_draw()}});this.prefs=this.config.values;this.painter=M.ReadPainter;this.update_icons()};r(V.prototype,s.prototype,N.prototype,c.prototype);var d={LineTrack:h,FeatureTrack:c,VcfTrack:T,ReadTrack:V,CompositeTrack:f,DrawableGroup:Q};var p=function(ah,ag,af){if("copy" in ah){return ah.copy(af)}else{var ai=ah.obj_type;if(!ai){ai=ah.track_type}return new d[ai](ag,af,ah)}};return{View:aa,DrawableGroup:Q,LineTrack:h,FeatureTrack:c,DiagonalHeatmapTrack:u,ReadTrack:V,VcfTrack:T,CompositeTrack:f,object_from_template:p,add_datasets:ae}});
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/packed/viz/trackster/util.js
--- a/static/scripts/packed/viz/trackster/util.js
+++ b/static/scripts/packed/viz/trackster/util.js
@@ -1,1 +1,1 @@
-define(function(){exports={};exports.get_random_color=function(a){if(!a){a="#ffffff"}if(typeof(a)==="string"){a=[a]}for(var j=0;j<a.length;j++){a[j]=parseInt(a[j].slice(1),16)}var n=function(t,s,i){return((t*299)+(s*587)+(i*114))/1000};var e=function(v,u,w,s,i,t){return(Math.max(v,s)-Math.min(v,s))+(Math.max(u,i)-Math.min(u,i))+(Math.max(w,t)-Math.min(w,t))};var g,o,f,k,q,h,r,c,d,b,p,m=false,l=0;do{g=Math.round(Math.random()*16777215);o=(g&16711680)>>16;f=(g&65280)>>8;k=g&255;d=n(o,f,k);m=true;for(j=0;j<a.length;j++){q=a[j];h=(q&16711680)>>16;r=(q&65280)>>8;c=q&255;b=n(h,r,c);p=e(o,f,k,h,r,c);if((Math.abs(d-b)<40)||(p<200)){m=false;break}}l++}while(!m&&l<=10);return"#"+(16777216+g).toString(16).substr(1,6)};return exports});
\ No newline at end of file
+define(function(){exports={};exports.ServerStateDeferred=Backbone.Model.extend({defaults:{ajax_settings:{},interval:1000,success_fn:function(a){return true}},go:function(){var d=$.Deferred(),c=this,f=c.get("ajax_settings"),e=c.get("success_fn"),b=c.get("interval"),a=function(){$.ajax(f).success(function(g){if(e(g)){d.resolve(g)}else{setTimeout(a,b)}})};a();return d}});exports.get_random_color=function(a){if(!a){a="#ffffff"}if(typeof(a)==="string"){a=[a]}for(var j=0;j<a.length;j++){a[j]=parseInt(a[j].slice(1),16)}var n=function(t,s,i){return((t*299)+(s*587)+(i*114))/1000};var e=function(v,u,w,s,i,t){return(Math.max(v,s)-Math.min(v,s))+(Math.max(u,i)-Math.min(u,i))+(Math.max(w,t)-Math.min(w,t))};var g,o,f,k,q,h,r,c,d,b,p,m=false,l=0;do{g=Math.round(Math.random()*16777215);o=(g&16711680)>>16;f=(g&65280)>>8;k=g&255;d=n(o,f,k);m=true;for(j=0;j<a.length;j++){q=a[j];h=(q&16711680)>>16;r=(q&65280)>>8;c=q&255;b=n(h,r,c);p=e(o,f,k,h,r,c);if((Math.abs(d-b)<40)||(p<200)){m=false;break}}l++}while(!m&&l<=10);return"#"+(16777216+g).toString(16).substr(1,6)};return exports});
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/packed/viz/trackster_ui.js
--- a/static/scripts/packed/viz/trackster_ui.js
+++ b/static/scripts/packed/viz/trackster_ui.js
@@ -1,1 +1,1 @@
-define(["base","libs/underscore","viz/trackster/slotting","viz/trackster/painters","viz/trackster/tracks"],function(f,c,g,d,b){var a=b.object_from_template;var e=f.Base.extend({initialize:function(h){this.baseURL=h},createButtonMenu:function(){var h=this,j=create_icon_buttons_menu([{icon_class:"plus-button",title:"Add tracks",on_click:function(){add_datasets(add_datasets_url,add_track_async_url,function(k){c.each(k,function(l){view.add_drawable(trackster_ui.object_from_template(l,view,view))})})}},{icon_class:"block--plus",title:"Add group",on_click:function(){view.add_drawable(new b.DrawableGroup(view,view,{name:"New Group"}))}},{icon_class:"bookmarks",title:"Bookmarks",on_click:function(){parent.force_right_panel(($("div#right").css("right")=="0px"?"hide":"show"))}},{icon_class:"globe",title:"Circster",on_click:function(){window.location=h.baseURL+"visualization/circster?id="+view.vis_id}},{icon_class:"disk--arrow",title:"Save",on_click:function(){show_modal("Saving...","progress");var k=[];$(".bookmark").each(function(){k.push({position:$(this).children(".position").text(),annotation:$(this).children(".annotation").text()})});var l=(view.overview_drawable?view.overview_drawable.name:null),m={id:view.vis_id,title:view.name,dbkey:view.dbkey,type:"trackster",datasets:view.to_dict(),viewport:{chrom:view.chrom,start:view.low,end:view.high,overview:l},bookmarks:k};$.ajax({url:galaxy_paths.get("visualization_url"),type:"POST",dataType:"json",data:{vis_json:JSON.stringify(m)}}).success(function(n){hide_modal();view.vis_id=n.vis_id;view.has_changes=false;window.history.pushState({},"",n.url+window.location.hash)}).error(function(){show_modal("Could Not Save","Could not save visualization. Please try again later.",{Close:hide_modal})})}},{icon_class:"cross-circle",title:"Close",on_click:function(){window.location=h.baseURL+"visualization/list"}}],{tooltip_config:{placement:"bottom"}});this.buttonMenu=j;return j},add_bookmarks:function(){var h=this.baseURL;show_modal("Select dataset for new bookmarks","progress");$.ajax({url:this.baseURL+"/visualization/list_histories",data:{"f-dbkey":view.dbkey},error:function(){alert("Grid failed")},success:function(j){show_modal("Select dataset for new bookmarks",j,{Cancel:function(){hide_modal()},Insert:function(){$("input[name=id]:checked,input[name=ldda_ids]:checked").first().each(function(){var k,l=$(this).val();if($(this).attr("name")==="id"){k={hda_id:l}}else{k={ldda_id:l}}$.ajax({url:this.baseURL+"/visualization/bookmarks_from_dataset",data:k,dataType:"json"}).then(function(m){for(i=0;i<m.data.length;i++){var n=m.data[i];add_bookmark(n[0],n[1])}})});hide_modal()}})}})},add_bookmark:function(m,k,h){var o=$("#bookmarks-container"),q=$("<div/>").addClass("bookmark").appendTo(o);var r=$("<div/>").addClass("position").appendTo(q),n=$("<a href=''/>").text(m).appendTo(r).click(function(){view.go_to(m);return false}),l=$("<div/>").text(k).appendTo(q);if(h){var p=$("<div/>").addClass("delete-icon-container").prependTo(q).click(function(){q.slideUp("fast");q.remove();view.has_changes=true;return false}),j=$("<a href=''/>").addClass("icon-button delete").appendTo(p);l.make_text_editable({num_rows:3,use_textarea:true,help_text:"Edit bookmark note"}).addClass("annotation")}view.has_changes=true;return q},create_visualization:function(l,h,k,m,j){view=new b.View(l);view.editor=true;$.when(view.load_chroms_deferred).then(function(){if(h){var v=h.chrom,n=h.start,s=h.end,p=h.overview;if(v&&(n!==undefined)&&s){view.change_chrom(v,n,s)}}if(k){var q,o,r;for(var t=0;t<k.length;t++){view.add_drawable(a(k[t],view,view))}}view.update_intro_div();var w;for(var t=0;t<view.drawables.length;t++){if(view.drawables[t].name===p){view.set_overview(view.drawables[t]);break}}if(m){var u;for(var t=0;t<m.length;t++){u=m[t];add_bookmark(u.position,u.annotation,j)}}view.has_changes=false});return view},init_keyboard_nav:function(h){$(document).keydown(function(j){if($(j.srcElement).is(":input")){return}switch(j.which){case 37:h.move_fraction(0.25);break;case 38:var k=Math.round(h.viewport_container.height()/15);h.viewport_container.scrollTop(h.viewport_container.scrollTop()-20);break;case 39:h.move_fraction(-0.25);break;case 40:var k=Math.round(h.viewport_container.height()/15);h.viewport_container.scrollTop(h.viewport_container.scrollTop()+20);break}})}});return{object_from_template:a,TracksterUI:e}});
\ No newline at end of file
+define(["base","libs/underscore","viz/trackster/slotting","viz/trackster/painters","viz/trackster/tracks"],function(f,c,g,d,b){var a=b.object_from_template;var e=f.Base.extend({initialize:function(h){this.baseURL=h},createButtonMenu:function(){var h=this,j=create_icon_buttons_menu([{icon_class:"plus-button",title:"Add tracks",on_click:function(){b.add_datasets(add_datasets_url,add_track_async_url,function(k){c.each(k,function(l){view.add_drawable(a(l,view,view))})})}},{icon_class:"block--plus",title:"Add group",on_click:function(){view.add_drawable(new b.DrawableGroup(view,view,{name:"New Group"}))}},{icon_class:"bookmarks",title:"Bookmarks",on_click:function(){parent.force_right_panel(($("div#right").css("right")=="0px"?"hide":"show"))}},{icon_class:"globe",title:"Circster",on_click:function(){window.location=h.baseURL+"visualization/circster?id="+view.vis_id}},{icon_class:"disk--arrow",title:"Save",on_click:function(){show_modal("Saving...","progress");var k=[];$(".bookmark").each(function(){k.push({position:$(this).children(".position").text(),annotation:$(this).children(".annotation").text()})});var l=(view.overview_drawable?view.overview_drawable.name:null),m={id:view.vis_id,title:view.name,dbkey:view.dbkey,type:"trackster",datasets:view.to_dict(),viewport:{chrom:view.chrom,start:view.low,end:view.high,overview:l},bookmarks:k};$.ajax({url:galaxy_paths.get("visualization_url"),type:"POST",dataType:"json",data:{vis_json:JSON.stringify(m)}}).success(function(n){hide_modal();view.vis_id=n.vis_id;view.has_changes=false;window.history.pushState({},"",n.url+window.location.hash)}).error(function(){show_modal("Could Not Save","Could not save visualization. Please try again later.",{Close:hide_modal})})}},{icon_class:"cross-circle",title:"Close",on_click:function(){window.location=h.baseURL+"visualization/list"}}],{tooltip_config:{placement:"bottom"}});this.buttonMenu=j;return j},add_bookmarks:function(){var h=this,j=this.baseURL;show_modal("Select dataset for new bookmarks","progress");$.ajax({url:this.baseURL+"/visualization/list_histories",data:{"f-dbkey":view.dbkey},error:function(){alert("Grid failed")},success:function(k){show_modal("Select dataset for new bookmarks",k,{Cancel:function(){hide_modal()},Insert:function(){$("input[name=id]:checked,input[name=ldda_ids]:checked").first().each(function(){var l,m=$(this).val();if($(this).attr("name")==="id"){l={hda_id:m}}else{l={ldda_id:m}}$.ajax({url:this.baseURL+"/visualization/bookmarks_from_dataset",data:l,dataType:"json"}).then(function(n){for(i=0;i<n.data.length;i++){var o=n.data[i];h.add_bookmark(o[0],o[1])}})});hide_modal()}})}})},add_bookmark:function(m,k,h){var o=$("#bookmarks-container"),q=$("<div/>").addClass("bookmark").appendTo(o);var r=$("<div/>").addClass("position").appendTo(q),n=$("<a href=''/>").text(m).appendTo(r).click(function(){view.go_to(m);return false}),l=$("<div/>").text(k).appendTo(q);if(h){var p=$("<div/>").addClass("delete-icon-container").prependTo(q).click(function(){q.slideUp("fast");q.remove();view.has_changes=true;return false}),j=$("<a href=''/>").addClass("icon-button delete").appendTo(p);l.make_text_editable({num_rows:3,use_textarea:true,help_text:"Edit bookmark note"}).addClass("annotation")}view.has_changes=true;return q},create_visualization:function(n,h,m,o,l){var k=this,j=new b.View(n);j.editor=true;$.when(j.load_chroms_deferred).then(function(){if(h){var x=h.chrom,p=h.start,u=h.end,r=h.overview;if(x&&(p!==undefined)&&u){j.change_chrom(x,p,u)}}if(m){var s,q,t;for(var v=0;v<m.length;v++){j.add_drawable(a(m[v],j,j))}}j.update_intro_div();var y;for(var v=0;v<j.drawables.length;v++){if(j.drawables[v].name===r){j.set_overview(j.drawables[v]);break}}if(o){var w;for(var v=0;v<o.length;v++){w=o[v];k.add_bookmark(w.position,w.annotation,l)}}j.has_changes=false});return j},init_keyboard_nav:function(h){$(document).keydown(function(j){if($(j.srcElement).is(":input")){return}switch(j.which){case 37:h.move_fraction(0.25);break;case 38:var k=Math.round(h.viewport_container.height()/15);h.viewport_container.scrollTop(h.viewport_container.scrollTop()-20);break;case 39:h.move_fraction(-0.25);break;case 40:var k=Math.round(h.viewport_container.height()/15);h.viewport_container.scrollTop(h.viewport_container.scrollTop()+20);break}})}});return{object_from_template:a,TracksterUI:e}});
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/packed/viz/visualization.js
--- a/static/scripts/packed/viz/visualization.js
+++ b/static/scripts/packed/viz/visualization.js
@@ -1,1 +1,1 @@
-(function(){var k=function(u){return("isResolved" in u)};var f=Backbone.Model.extend({defaults:{ajax_settings:{},interval:1000,success_fn:function(u){return true}},go:function(){var x=$.Deferred(),w=this,z=w.get("ajax_settings"),y=w.get("success_fn"),v=w.get("interval"),u=function(){$.ajax(z).success(function(A){if(y(A)){x.resolve(A)}else{setTimeout(u,v)}})};u();return x}});var g=function(u){this.default_font=u!==undefined?u:"9px Monaco, Lucida Console, monospace";this.dummy_canvas=this.new_canvas();this.dummy_context=this.dummy_canvas.getContext("2d");this.dummy_context.font=this.default_font;this.char_width_px=this.dummy_context.measureText("A").width;this.patterns={};this.load_pattern("right_strand","/visualization/strand_right.png");this.load_pattern("left_strand","/visualization/strand_left.png");this.load_pattern("right_strand_inv","/visualization/strand_right_inv.png");this.load_pattern("left_strand_inv","/visualization/strand_left_inv.png")};_.extend(g.prototype,{load_pattern:function(u,y){var v=this.patterns,w=this.dummy_context,x=new Image();x.src=galaxy_paths.attributes.image_path+y;x.onload=function(){v[u]=w.createPattern(x,"repeat")}},get_pattern:function(u){return this.patterns[u]},new_canvas:function(){var u=$("<canvas/>")[0];if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(u)}u.manager=this;return u}});var q=Backbone.Model.extend({defaults:{num_elements:20,obj_cache:null,key_ary:null},initialize:function(u){this.clear()},get_elt:function(v){var w=this.attributes.obj_cache,x=this.attributes.key_ary,u=x.indexOf(v);if(u!==-1){if(w[v].stale){x.splice(u,1);delete w[v]}else{this.move_key_to_end(v,u)}}return w[v]},set_elt:function(v,x){var y=this.attributes.obj_cache,z=this.attributes.key_ary,w=this.attributes.num_elements;if(!y[v]){if(z.length>=w){var u=z.shift();delete y[u]}z.push(v)}y[v]=x;return x},move_key_to_end:function(v,u){this.attributes.key_ary.splice(u,1);this.attributes.key_ary.push(v)},clear:function(){this.attributes.obj_cache={};this.attributes.key_ary=[]},size:function(){return this.attributes.key_ary.length}});var d=q.extend({defaults:_.extend({},q.prototype.defaults,{dataset:null,filters_manager:null,data_type:"data",genome_wide_summary_data:null,data_mode_compatible:function(u,v){return true},can_subset:function(u){return false}}),data_is_ready:function(){var w=this.get("dataset"),v=$.Deferred(),u=new f({ajax_settings:{url:this.get("dataset").url(),data:{hda_ldda:w.get("hda_ldda"),data_type:"state"},dataType:"json"},interval:5000,success_fn:function(x){return x!=="pending"}});$.when(u.go()).then(function(x){v.resolve(x==="ok"||x==="data")});return v},search_features:function(u){var v=this.get("dataset"),w={query:u,dataset_id:v.id,hda_ldda:v.get("hda_ldda"),data_type:"features"};return $.getJSON(v.url(),w)},load_data:function(C,B,v,A){var x={data_type:this.get("data_type"),chrom:C.get("chrom"),low:C.get("start"),high:C.get("end"),mode:B,resolution:v},y=this.get("dataset");if(y){x.dataset_id=y.id;x.hda_ldda=y.get("hda_ldda")}$.extend(x,A);var D=this.get("filters_manager");if(D){var E=[];var u=D.filters;for(var z=0;z<u.length;z++){E.push(u[z].name)}x.filter_cols=JSON.stringify(E)}var w=this;return $.getJSON(y.url(),x,function(F){w.set_data(C,F)})},get_data:function(A,z,w,y){var B=this.get_elt(A);if(B&&(k(B)||this.get("data_mode_compatible")(B,z))){return B}var C=this.get("key_ary"),v=this.get("obj_cache"),D,u;for(var x=0;x<C.length;x++){D=C[x];u=new h({from_str:D});if(u.contains(A)){B=v[D];if(k(B)||(this.get("data_mode_compatible")(B,z)&&this.get("can_subset")(B))){this.move_key_to_end(D,x);return B}}}B=this.load_data(A,z,w,y);this.set_data(A,B);return B},set_data:function(v,u){this.set_elt(v,u)},DEEP_DATA_REQ:"deep",BROAD_DATA_REQ:"breadth",get_more_data:function(C,B,x,A,y){var E=this.get_elt(C);if(!(E&&this.get("data_mode_compatible")(E,B))){console.log("ERROR: no current data for: ",dataset,C.toString(),B,x,A);return}E.stale=true;var w=C.get("start");if(y===this.DEEP_DATA_REQ){$.extend(A,{start_val:E.data.length+1})}else{if(y===this.BROAD_DATA_REQ){w=(E.max_high?E.max_high:E.data[E.data.length-1][2])+1}}var D=C.copy().set("start",w);var v=this,z=this.load_data(D,B,x,A),u=$.Deferred();this.set_data(C,u);$.when(z).then(function(F){if(F.data){F.data=E.data.concat(F.data);if(F.max_low){F.max_low=E.max_low}if(F.message){F.message=F.message.replace(/[0-9]+/,F.data.length)}}v.set_data(C,F);u.resolve(F)});return u},get_elt:function(u){return q.prototype.get_elt.call(this,u.toString())},set_elt:function(v,u){return q.prototype.set_elt.call(this,v.toString(),u)}});var o=d.extend({load_data:function(u,x,y,v,w){if(v>1){return{data:null}}return d.prototype.load_data.call(this,u,x,y,v,w)}});var c=Backbone.Model.extend({defaults:{name:null,key:null,chroms_info:null},get_chroms_info:function(){return this.attributes.chroms_info.chrom_info}});var h=Backbone.RelationalModel.extend({defaults:{chrom:null,start:0,end:0,DIF_CHROMS:1000,BEFORE:1001,CONTAINS:1002,OVERLAP_START:1003,OVERLAP_END:1004,CONTAINED_BY:1005,AFTER:1006},initialize:function(v){if(v.from_str){var x=v.from_str.split(":"),w=x[0],u=x[1].split("-");this.set({chrom:w,start:parseInt(u[0],10),end:parseInt(u[1],10)})}},copy:function(){return new h({chrom:this.get("chrom"),start:this.get("start"),end:this.get("end")})},length:function(){return this.get("end")-this.get("start")},toString:function(){return this.get("chrom")+":"+this.get("start")+"-"+this.get("end")},toJSON:function(){return{chrom:this.get("chrom"),start:this.get("start"),end:this.get("end")}},compute_overlap:function(B){var v=this.get("chrom"),A=B.get("chrom"),z=this.get("start"),x=B.get("start"),y=this.get("end"),w=B.get("end"),u;if(v&&A&&v!==A){return this.get("DIF_CHROMS")}if(z<x){if(y<x){u=this.get("BEFORE")}else{if(y<=w){u=this.get("OVERLAP_START")}else{u=this.get("CONTAINS")}}}else{if(z>w){u=this.get("AFTER")}else{if(y<=w){u=this.get("CONTAINED_BY")}else{u=this.get("OVERLAP_END")}}}return u},contains:function(u){return this.compute_overlap(u)===this.get("CONTAINS")},overlaps:function(u){return _.intersection([this.compute_overlap(u)],[this.get("DIF_CHROMS"),this.get("BEFORE"),this.get("AFTER")]).length===0}});var m=Backbone.Collection.extend({model:h});var e=Backbone.RelationalModel.extend({defaults:{region:null,note:""},relations:[{type:Backbone.HasOne,key:"region",relatedModel:h}]});var r=Backbone.Collection.extend({model:e});var b=Backbone.Model.extend({defaults:{data:null,min:0,max:0},initialize:function(v){var u=_.flatten(_.map(this.get("data"),function(w){if(w.data.length!==0){return _.map(w.data,function(x){return x[1]})}else{return 0}}));this.set("max",_.max(u));this.set("min",_.min(u))}});var n=Backbone.RelationalModel.extend({defaults:{data:null,min:0,max:0},initialize:function(v){var u=_.max(this.get("data"),function(w){if(!w||typeof w==="string"){return 0}return w[1]});this.attributes.max=(u&&typeof u!=="string"?u[1]:0)}});var t=Dataset.extend({initialize:function(u){this.set("id",u.dataset_id);var w=this.get("genome_wide_data");if(w){var v=(this.get("track_type")==="LineTrack"?b:n);this.set("genome_wide_data",new v(w))}}});var p=Backbone.RelationalModel.extend({defaults:{id:"",title:"",type:"",dbkey:"",tracks:null},relations:[{type:Backbone.HasMany,key:"tracks",relatedModel:t}],url:function(){return galaxy_paths.get("visualization_url")},save:function(){return $.ajax({url:this.url(),type:"POST",dataType:"json",data:{vis_json:JSON.stringify(this)}})}});var l=p.extend({defaults:_.extend({},p.prototype.defaults,{bookmarks:null,viewport:null})});var a=Backbone.Model.extend({});var i=Backbone.Router.extend({initialize:function(v){this.view=v.view;this.route(/([\w]+)$/,"change_location");this.route(/([\w]+\:[\d,]+-[\d,]+)$/,"change_location");var u=this;u.view.on("navigate",function(w){u.navigate(w)})},change_location:function(u){this.view.go_to(u)}});var j=function(u,w,v){$.ajax({url:u,data:{"f-dbkey":view.dbkey},error:function(){alert("Grid failed")},success:function(x){show_modal("Select datasets for new tracks",x,{Cancel:function(){hide_modal()},Add:function(){var y=[];$("input[name=id]:checked,input[name=ldda_ids]:checked").each(function(){var z={data_type:"track_config",hda_ldda:"hda"},A=$(this).val();if($(this).attr("name")!=="id"){z.hda_ldda="ldda"}y[y.length]=$.ajax({url:w+"/"+A,data:z,dataType:"json"})});$.when.apply($,y).then(function(){var z=(arguments[0] instanceof Array?$.map(arguments,function(A){return A[0]}):[arguments[0]]);v(z)});hide_modal()}})}})};var s=(function(){if(typeof module!=="undefined"&&module.exports){return module.exports}else{if(typeof define==="function"&&define.amd){s={};define(function(){return s});return s}else{return window}}})();s.BrowserBookmark=e;s.BrowserBookmarkCollection=r;s.Cache=q;s.CanvasManager=g;s.Genome=c;s.GenomeDataManager=d;s.GenomeRegion=h;s.GenomeRegionCollection=m;s.GenomeVisualization=l;s.GenomeWideBigWigData=b;s.GenomeWideSummaryTreeData=n;s.ReferenceTrackDataManager=o;s.ServerStateDeferred=f;s.TrackBrowserRouter=i;s.TrackConfig=a;s.Visualization=p;s.add_datasets=j}).call(this);
\ No newline at end of file
+define(["libs/underscore","mvc/data","viz/trackster/util"],function(q,h,j){var i=function(s){return("isResolved" in s)};var e=function(s){this.default_font=s!==undefined?s:"9px Monaco, Lucida Console, monospace";this.dummy_canvas=this.new_canvas();this.dummy_context=this.dummy_canvas.getContext("2d");this.dummy_context.font=this.default_font;this.char_width_px=this.dummy_context.measureText("A").width;this.patterns={};this.load_pattern("right_strand","/visualization/strand_right.png");this.load_pattern("left_strand","/visualization/strand_left.png");this.load_pattern("right_strand_inv","/visualization/strand_right_inv.png");this.load_pattern("left_strand_inv","/visualization/strand_left_inv.png")};q.extend(e.prototype,{load_pattern:function(s,w){var t=this.patterns,u=this.dummy_context,v=new Image();v.src=galaxy_paths.attributes.image_path+w;v.onload=function(){t[s]=u.createPattern(v,"repeat")}},get_pattern:function(s){return this.patterns[s]},new_canvas:function(){var s=$("<canvas/>")[0];if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(s)}s.manager=this;return s}});var o=Backbone.Model.extend({defaults:{num_elements:20,obj_cache:null,key_ary:null},initialize:function(s){this.clear()},get_elt:function(t){var u=this.attributes.obj_cache,v=this.attributes.key_ary,s=v.indexOf(t);if(s!==-1){if(u[t].stale){v.splice(s,1);delete u[t]}else{this.move_key_to_end(t,s)}}return u[t]},set_elt:function(t,v){var w=this.attributes.obj_cache,x=this.attributes.key_ary,u=this.attributes.num_elements;if(!w[t]){if(x.length>=u){var s=x.shift();delete w[s]}x.push(t)}w[t]=v;return v},move_key_to_end:function(t,s){this.attributes.key_ary.splice(s,1);this.attributes.key_ary.push(t)},clear:function(){this.attributes.obj_cache={};this.attributes.key_ary=[]},size:function(){return this.attributes.key_ary.length}});var c=o.extend({defaults:q.extend({},o.prototype.defaults,{dataset:null,filters_manager:null,data_type:"data",data_mode_compatible:function(s,t){return true},can_subset:function(s){return false}}),data_is_ready:function(){var u=this.get("dataset"),t=$.Deferred(),s=new j.ServerStateDeferred({ajax_settings:{url:this.get("dataset").url(),data:{hda_ldda:u.get("hda_ldda"),data_type:"state"},dataType:"json"},interval:5000,success_fn:function(v){return v!=="pending"}});$.when(s.go()).then(function(v){t.resolve(v==="ok"||v==="data")});return t},search_features:function(s){var t=this.get("dataset"),u={query:s,hda_ldda:t.get("hda_ldda"),data_type:"features"};return $.getJSON(t.url(),u)},load_data:function(A,z,t,y){var w=this.get("dataset"),v={data_type:this.get("data_type"),chrom:A.get("chrom"),low:A.get("start"),high:A.get("end"),mode:z,resolution:t,hda_ldda:w.get("hda_ldda")};$.extend(v,y);var C=this.get("filters_manager");if(C){var D=[];var s=C.filters;for(var x=0;x<s.length;x++){D.push(s[x].name)}v.filter_cols=JSON.stringify(D)}var u=this,B=$.getJSON(w.url(),v,function(E){u.set_data(A,E)});this.set_data(A,B);return B},get_data:function(y,x,u,w){var z=this.get_elt(y);if(z&&(i(z)||this.get("data_mode_compatible")(z,x))){return z}var A=this.get("key_ary"),t=this.get("obj_cache"),B,s;for(var v=0;v<A.length;v++){B=A[v];s=new f({from_str:B});if(s.contains(y)){z=t[B];if(i(z)||(this.get("data_mode_compatible")(z,x)&&this.get("can_subset")(z))){this.move_key_to_end(B,v);return z}}}return this.load_data(y,x,u,w)},set_data:function(t,s){this.set_elt(t,s)},DEEP_DATA_REQ:"deep",BROAD_DATA_REQ:"breadth",get_more_data:function(A,z,v,y,w){var C=this._mark_stale(A);if(!(C&&this.get("data_mode_compatible")(C,z))){console.log("ERROR: problem with getting more data: current data is not compatible");return}var u=A.get("start");if(w===this.DEEP_DATA_REQ){$.extend(y,{start_val:C.data.length+1})}else{if(w===this.BROAD_DATA_REQ){u=(C.max_high?C.max_high:C.data[C.data.length-1][2])+1}}var B=A.copy().set("start",u);var t=this,x=this.load_data(B,z,v,y),s=$.Deferred();this.set_data(A,s);$.when(x).then(function(D){if(D.data){D.data=C.data.concat(D.data);if(D.max_low){D.max_low=C.max_low}if(D.message){D.message=D.message.replace(/[0-9]+/,D.data.length)}}t.set_data(A,D);s.resolve(D)});return s},get_more_detailed_data:function(v,x,t,w,u){var s=this._mark_stale(v);if(!s){console.log("ERROR getting more detailed data: no current data");return}if(!u){u={}}var x;if(s.dataset_type==="bigwig"){u.num_samples=s.data.length*w}else{if(s.dataset_type==="summary_tree"){u.level=s.level+1}}return this.load_data(v,x,t,u)},_mark_stale:function(t){var s=this.get_elt(t);if(!s){console.log("ERROR: no data to mark as stale: ",this.get("dataset"),t.toString())}s.stale=true;return s},get_elt:function(s){return o.prototype.get_elt.call(this,s.toString())},set_elt:function(t,s){return o.prototype.set_elt.call(this,t.toString(),s)}});var m=c.extend({initialize:function(s){var t=new Backbone.Model();t.urlRoot=s.data_url;this.set("dataset",t)},load_data:function(u,v,s,t){console.log(u,v,s);if(s>1){return{data:null}}return c.prototype.load_data.call(this,u,v,s,t)}});var b=Backbone.Model.extend({defaults:{name:null,key:null,chroms_info:null},initialize:function(s){this.id=s.dbkey},get_chroms_info:function(){return this.attributes.chroms_info.chrom_info},get_chrom_region:function(s){var t=q.find(this.get_chroms_info(),function(u){return u.chrom==s});return new f({chrom:t.chrom,end:t.len})}});var f=Backbone.RelationalModel.extend({defaults:{chrom:null,start:0,end:0,DIF_CHROMS:1000,BEFORE:1001,CONTAINS:1002,OVERLAP_START:1003,OVERLAP_END:1004,CONTAINED_BY:1005,AFTER:1006},initialize:function(t){if(t.from_str){var v=t.from_str.split(":"),u=v[0],s=v[1].split("-");this.set({chrom:u,start:parseInt(s[0],10),end:parseInt(s[1],10)})}},copy:function(){return new f({chrom:this.get("chrom"),start:this.get("start"),end:this.get("end")})},length:function(){return this.get("end")-this.get("start")},toString:function(){return this.get("chrom")+":"+this.get("start")+"-"+this.get("end")},toJSON:function(){return{chrom:this.get("chrom"),start:this.get("start"),end:this.get("end")}},compute_overlap:function(z){var t=this.get("chrom"),y=z.get("chrom"),x=this.get("start"),v=z.get("start"),w=this.get("end"),u=z.get("end"),s;if(t&&y&&t!==y){return this.get("DIF_CHROMS")}if(x<v){if(w<v){s=this.get("BEFORE")}else{if(w<=u){s=this.get("OVERLAP_START")}else{s=this.get("CONTAINS")}}}else{if(x>u){s=this.get("AFTER")}else{if(w<=u){s=this.get("CONTAINED_BY")}else{s=this.get("OVERLAP_END")}}}return s},contains:function(s){return this.compute_overlap(s)===this.get("CONTAINS")},overlaps:function(s){return q.intersection([this.compute_overlap(s)],[this.get("DIF_CHROMS"),this.get("BEFORE"),this.get("AFTER")]).length===0}});var l=Backbone.Collection.extend({model:f});var d=Backbone.RelationalModel.extend({defaults:{region:null,note:""},relations:[{type:Backbone.HasOne,key:"region",relatedModel:f}]});var p=Backbone.Collection.extend({model:d});var r=h.Dataset.extend({initialize:function(s){this.set("id",s.dataset_id);var t=new c({dataset:this});this.set("data_manager",t);var u=this.get("preloaded_data");if(u){t.set("num_elements",u.data.length);q.each(u.data,function(v){t.set_data(v.region,v)})}},get_genome_wide_data:function(s){var t=this.get("data_manager");return q.map(s.get("chroms_info").chrom_info,function(u){return t.get_elt(new f({chrom:u.chrom,start:0,end:u.len}))})}});var n=Backbone.RelationalModel.extend({defaults:{id:"",title:"",type:"",dbkey:"",tracks:null},relations:[{type:Backbone.HasMany,key:"tracks",relatedModel:r}],url:function(){return galaxy_paths.get("visualization_url")},save:function(){return $.ajax({url:this.url(),type:"POST",dataType:"json",data:{vis_json:JSON.stringify(this)}})}});var k=n.extend({defaults:q.extend({},n.prototype.defaults,{bookmarks:null,viewport:null})});var a=Backbone.Model.extend({});var g=Backbone.Router.extend({initialize:function(t){this.view=t.view;this.route(/([\w]+)$/,"change_location");this.route(/([\w]+\:[\d,]+-[\d,]+)$/,"change_location");var s=this;s.view.on("navigate",function(u){s.navigate(u)})},change_location:function(s){this.view.go_to(s)}});return{BrowserBookmark:d,BrowserBookmarkCollection:p,Cache:o,CanvasManager:e,Genome:b,GenomeDataManager:c,GenomeRegion:f,GenomeRegionCollection:l,GenomeVisualization:k,ReferenceTrackDataManager:m,TrackBrowserRouter:g,TrackConfig:a,Visualization:n}});
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/templates/compile_templates.py
--- a/static/scripts/templates/compile_templates.py
+++ b/static/scripts/templates/compile_templates.py
@@ -46,153 +46,99 @@
from glob import glob
from subprocess import call
-from shutil import copyfile
-from os import path
+import os
from optparse import OptionParser
-from HTMLParser import HTMLParser
+import re
-import logging
-log = logging.getLogger( __name__ )
+import logging as log
+log.basicConfig(
+ #level = log.DEBUG,
+ name = __name__
+)
COMPILED_DIR = 'compiled'
COMPILED_EXT = '.js'
COMPILE_CMD_STR = "handlebars %s -f %s"
COMPILE_MINIMIZE_SWITCH = ' -m'
+TEMPLATE_TAG = 'script'
+TEMPLATE_TYPE = 'text/template'
+HELPER_TYPE = 'text/javascript'
+
# both of these are off by default for backward compat
DEFAULT_MINIMIZATION = False
DEFAULT_MULTI_EXT = None #'.html'
# ------------------------------------------------------------------------------
-class HTMLMultiTemplateParser( HTMLParser ):
- """Parses multiple templates from an HTML file, saving them to a map of:
- { id : template_text, ... }
+def parse_html_tag_attrs( string ):
+ attrs = {}
+ for match in re.finditer( r'(?P<key>\w+?)=[\'|\"](?P<val>.*?)[\'|\"]', string, re.DOTALL | re.MULTILINE ):
+ match = match.groupdict()
+ key = match[ 'key' ]
+ val = match[ 'val' ]
+ attrs[ key ] = val
+ return attrs
+
+def split_on_html_tag( string, tag ):
+ tag_pattern = r'<%s\s*(?P<attrs>.*?)>(?P<body>.*?)</%s>' % ( tag, tag )
+ log.debug( tag_pattern )
+ tag_pattern = re.compile( tag_pattern, re.MULTILINE | re.DOTALL )
+
+ found_list = re.findall( tag_pattern, string )
+ for attrs, body in found_list:
+ yield ( parse_html_tag_attrs( attrs ), body )
+
+def filter_on_tag_type( generator, type_attr_to_match ):
+ for attrs, body in generator:
+ log.debug( 'attrs: %s', str( attrs ) )
+ if( ( 'type' in attrs )
+ and ( attrs[ 'type' ] == type_attr_to_match ) ):
+ yield attrs, body
+
- Templates must:
- * be within the TEMPLATE_TAG
- * TEMPLATE_TAG must have a type attribute
- * that attr must == TEMPLATE_TYPE
- * TEMPLATE_TAG cannot be nested within one another
- * TEMPLATE_TAG must have an id attribute
- """
- TEMPLATE_TAG = 'script'
- TEMPLATE_TYPES = [ 'text/template' ]
-
- HELPER_TAG = 'script'
- HELPER_TYPES = [ 'text/javascript' ]
-
- def __init__( self ):
- HTMLParser.__init__( self )
- self.templates = {}
- self.curr_template_id = None
- self.template_data = ''
-
- self.helpers = {}
- self.curr_helper_id = None
- self.helper_data = ''
-
- def is_template_tag( self, tag, attr_dict ):
- # both tag and type attr must match
- return ( ( tag == self.TEMPLATE_TAG )
- and ( 'type' in attr_dict )
- and ( attr_dict[ 'type' ] in self.TEMPLATE_TYPES ) )
-
- def is_helper_tag( self, tag, attr_dict ):
- # both tag and type attr must match
- return ( ( tag == self.HELPER_TAG )
- and ( 'type' in attr_dict )
- and ( attr_dict[ 'type' ] in self.HELPER_TYPES ) )
-
- def handle_starttag( self, tag, attrs ):
- attr_dict = dict( attrs )
- if self.is_template_tag( tag, attr_dict ):
- log.debug( "\t template tag: %s, %s", tag, str( attr_dict ) );
-
- # as far as I know these tags can't/shouldn't nest/overlap
- #pre: not already inside a template/helper tag
- assert self.curr_template_id == None, "Found nested template tag: %s" % ( self.curr_template_id )
- assert self.curr_helper_id == None, "Found template tag inside helper: %s" % ( self.curr_helper_id )
- #pre: must have an id
- assert 'id' in attr_dict, "No id attribute in template: " + str( attr_dict )
-
- self.curr_template_id = attr_dict[ 'id' ]
-
- elif self.is_helper_tag( tag, attr_dict ):
- log.debug( "\t helper tag: %s, %s", tag, str( attr_dict ) );
-
- #pre: not already inside a template/helper tag
- assert self.curr_helper_id == None, "Found nested helper tag: %s" % ( self.curr_helper_id )
- assert self.curr_template_id == None, "Found helper tag inside template: %s" % ( self.curr_template_id )
- #pre: must have an id
- assert 'id' in attr_dict, "No id attribute in helper: " + str( attr_dict )
-
- self.curr_helper_id = attr_dict[ 'id' ]
-
- def handle_endtag( self, tag ):
- if( ( tag == self.TEMPLATE_TAG )
- and ( self.curr_template_id ) ):
- log.debug( "\t ending template tag :", tag, self.curr_template_id );
-
- # store the template data by the id
- if self.template_data:
- self.templates[ self.curr_template_id ] = self.template_data
-
- #! reset for next template
- self.curr_template_id = None
- self.template_data = ''
-
- elif( ( tag == self.HELPER_TAG )
- and ( self.curr_helper_id ) ):
- log.debug( "\t ending helper tag :", tag, self.curr_template_id );
-
- # store the template data by the id
- if self.helper_data:
- self.helpers[ self.curr_helper_id ] = self.helper_data
-
- #! reset for next template
- self.curr_helper_id = None
- self.helper_data = ''
-
- def handle_data(self, data):
- data = data.strip()
- if data:
- if self.curr_template_id:
- log.debug( "\t template text :", data );
- self.template_data += data
-
- elif self.curr_helper_id:
- log.debug( "\t helper js fn :", data );
- self.helper_data += data
-
-
# ------------------------------------------------------------------------------
def break_multi_template( multi_template_filename ):
"""parse the multi template, writing each template into a new handlebars tmpl and returning their names"""
template_filenames = []
- parser = HTMLMultiTemplateParser()
# parse the multi template
print "\nBreaking multi-template file %s into individual templates and helpers:" % ( multi_template_filename )
with open( multi_template_filename, 'r' ) as multi_template_file:
- # wish I could use a gen here
- parser.feed( multi_template_file.read() )
+ multi_template_file_text = multi_template_file.read()
- # after breaking, write each indiv. template and save the names
- for template_id, template_text in parser.templates.items():
+ # write a template file for each template (name based on id in tag)
+ tag_generator = split_on_html_tag( multi_template_file_text, TEMPLATE_TAG )
+ for attrs, template_text in filter_on_tag_type( tag_generator, TEMPLATE_TYPE ):
+ if( 'id' not in attrs ):
+ log.warning( 'Template has no "id". attrs: %s' %( str( attrs ) ) )
+ continue
+
+ template_id = attrs[ 'id' ]
+ template_text = template_text.strip()
handlebar_template_filename = template_id + '.handlebars'
with open( handlebar_template_filename, 'w' ) as handlebar_template_file:
handlebar_template_file.write( template_text )
+ log.debug( "%s\n%s\n", template_id, template_text )
template_filenames.append( handlebar_template_filename )
- # write all helpers to a 'helper-' prefixed js file in the compilation dir
- if parser.helpers:
- helper_filename = 'helpers-' + path.splitext( multi_template_filename )[0] + '.js'
- helper_filename = path.join( COMPILED_DIR, helper_filename )
+ ## write all helpers to a single 'helper-' prefixed js file in the compilation dir
+ helper_fns = []
+ # same tag, different type
+ tag_generator = split_on_html_tag( multi_template_file_text, TEMPLATE_TAG )
+ for attrs, helper_text in filter_on_tag_type( tag_generator, HELPER_TYPE ):
+ helper_text = helper_text.strip()
+ print '(helper):', ( attrs[ 'id' ] if 'id' in attrs else '(No id)' )
+
+ helper_fns.append( helper_text )
+
+ if helper_fns:
+ # prefix original filename (in compiled dir) and write all helper funcs to that file
+ helper_filename = 'helpers-' + os.path.splitext( multi_template_filename )[0] + '.js'
+ helper_filename = os.path.join( COMPILED_DIR, helper_filename )
with open( helper_filename, 'w' ) as helper_file:
- for helper_fn_name, helper_fn in parser.helpers.items():
- print '(helper)', helper_fn_name
- helper_file.write( helper_fn + '\n' )
+ helper_file.write( '\n'.join( helper_fns ) )
+ print '(helper functions written to %s)' % helper_filename
print '\n'.join( template_filenames )
return template_filenames
@@ -204,8 +150,8 @@
Use the basename of the template file for the outputed js.
"""
- template_basename = path.splitext( path.split( template_filename )[1] )[0]
- compiled_filename = path.join( COMPILED_DIR, template_basename + COMPILED_EXT )
+ template_basename = os.path.splitext( os.path.split( template_filename )[1] )[0]
+ compiled_filename = os.path.join( COMPILED_DIR, template_basename + COMPILED_EXT )
command_string = COMPILE_CMD_STR % ( template_filename, compiled_filename )
if minimize:
@@ -233,6 +179,7 @@
# if desired, break up any passed-in or found multi template files
# adding the names of the new single templates to those needing compilation
+ multi_template_template_filenames = []
if options.multi_ext:
multi_templates = []
if len( args ) >= 1:
@@ -241,10 +188,10 @@
multi_templates = glob( '*' + options.multi_ext )
for multi_template_filename in multi_templates:
- handlebars_templates.extend( break_multi_template( multi_template_filename ) )
+ multi_template_template_filenames.extend( break_multi_template( multi_template_filename ) )
# unique filenames only (Q&D)
- handlebars_templates = list( set( handlebars_templates ) )
+ handlebars_templates = list( set( handlebars_templates + multi_template_template_filenames ) )
# compile the templates
print "\nCompiling templates:"
@@ -260,6 +207,12 @@
print ',\n'.join( filenames_w_possible_errors )
print "\nCall this script with the '-h' for more help"
+ # delete multi template intermediate files
+ print "\nCleaning up intermediate multi-template template files:"
+ for filename in multi_template_template_filenames:
+ print 'removing', filename
+ os.remove( filename )
+
# ------------------------------------------------------------------------------
if __name__ == '__main__':
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/templates/compiled/helpers-common-templates.js
--- a/static/scripts/templates/compiled/helpers-common-templates.js
+++ b/static/scripts/templates/compiled/helpers-common-templates.js
@@ -3,6 +3,11 @@
Handlebars.registerPartial( 'clearFloatDiv', function( options ){
return '<div class="clear"></div>';
});
+/** Renders a warning in a (mostly css) highlighted, iconned warning box
+ */
+Handlebars.registerHelper( 'warningmessagesmall', function( options ){
+ return '<div class="warningmessagesmall"><strong>' + options.fn( this ) + '</strong></div>'
+});
/** Renders a glx style icon-button (see IconButton in mvc/ui.js)
* can be used in either of the following ways:
* within a template: {{> iconButton buttonData}}
@@ -29,9 +34,4 @@
buffer += '>' + ( ( buttonData.enabled )?( '</a>' ):( '</span>' ) );
return buffer;
-});
-/** Renders a warning in a (mostly css) highlighted, iconned warning box
- */
-Handlebars.registerHelper( 'warningmessagesmall', function( options ){
- return '<div class="warningmessagesmall"><strong>' + options.fn( this ) + '</strong></div>'
-});
+});
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/templates/compiled/template-history-displayApps.js
--- /dev/null
+++ b/static/scripts/templates/compiled/template-history-displayApps.js
@@ -0,0 +1,42 @@
+(function() {
+ var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
+templates['template-history-displayApps'] = template(function (Handlebars,depth0,helpers,partials,data) {
+ helpers = helpers || Handlebars.helpers;
+ var stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;
+
+function program1(depth0,data) {
+
+ var buffer = "", stack1, foundHelper;
+ buffer += "\n ";
+ foundHelper = helpers.label;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.label; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\n ";
+ stack1 = depth0.links;
+ stack1 = helpers.each.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(2, program2, data)});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n <br />\n";
+ return buffer;}
+function program2(depth0,data) {
+
+ var buffer = "", stack1, foundHelper;
+ buffer += "\n <a target=\"";
+ foundHelper = helpers.target;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.target; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\" href=\"";
+ foundHelper = helpers.href;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.href; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\">";
+ foundHelper = helpers.text;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.text; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "</a>\n ";
+ return buffer;}
+
+ stack1 = depth0.displayApps;
+ stack1 = helpers.each.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(1, program1, data)});
+ if(stack1 || stack1 === 0) { return stack1; }
+ else { return ''; }});
+})();
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/templates/compiled/template-history-downloadLinks.js
--- /dev/null
+++ b/static/scripts/templates/compiled/template-history-downloadLinks.js
@@ -0,0 +1,55 @@
+(function() {
+ var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
+templates['template-history-downloadLinks'] = template(function (Handlebars,depth0,helpers,partials,data) {
+ helpers = helpers || Handlebars.helpers;
+ var stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;
+
+function program1(depth0,data) {
+
+ var buffer = "", stack1, foundHelper;
+ buffer += "\n<div popupmenu=\"dataset-";
+ foundHelper = helpers.id;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.id; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "-popup\">\n <a class=\"action-button\" href=\"";
+ foundHelper = helpers.download_url;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.download_url; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\">Download Dataset</a>\n <a>Additional Files</a>\n ";
+ stack1 = depth0.meta_files;
+ stack1 = helpers.each.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(2, program2, data)});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n</div>\n<div style=\"float:left;\" class=\"menubutton split popup\" id=\"dataset-";
+ foundHelper = helpers.id;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.id; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "-popup\">\n <a href=\"";
+ foundHelper = helpers.download_url;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.download_url; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\" title=\"Download\" class=\"icon-button disk tooltip\"></a>\n</div>\n";
+ return buffer;}
+function program2(depth0,data) {
+
+ var buffer = "", stack1, foundHelper;
+ buffer += "\n <a class=\"action-button\" href=\"";
+ foundHelper = helpers.meta_download_url;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.meta_download_url; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\">Download ";
+ foundHelper = helpers.meta_file_type;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.meta_file_type; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "</a>\n ";
+ return buffer;}
+
+function program4(depth0,data) {
+
+
+ return "\n<a href=\"\" title=\"Download\" class=\"icon-button disk tooltip\"></a>\n";}
+
+ stack1 = depth0.meta_files;
+ stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.program(4, program4, data),fn:self.program(1, program1, data)});
+ if(stack1 || stack1 === 0) { return stack1; }
+ else { return ''; }});
+})();
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/templates/compiled/tool_search.js
--- a/static/scripts/templates/compiled/tool_search.js
+++ b/static/scripts/templates/compiled/tool_search.js
@@ -9,7 +9,7 @@
foundHelper = helpers.search_hint_string;
if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
else { stack1 = depth0.search_hint_string; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "\" id=\"tool-search-query\" autocomplete=\"off\" class=\"search-query parent-width\" />\n<a id=\"search-clear-btn\" class=\"icon-button cross-circle tooltip\" title=\"clear search (esc)\"></a>\n<img src=\"";
+ buffer += escapeExpression(stack1) + "\" id=\"tool-search-query\" autocomplete=\"off\" class=\"search-query parent-width\" />\n<a id=\"search-clear-btn\" class=\"tooltip\" title=\"clear search (esc)\"></a>\n<img src=\"";
foundHelper = helpers.spinner_url;
if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
else { stack1 = depth0.spinner_url; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/templates/history-templates.html
--- a/static/scripts/templates/history-templates.html
+++ b/static/scripts/templates/history-templates.html
@@ -51,6 +51,23 @@
{{/warningmessagesmall}}
</script>
+<script type="text/template" class="template-history" id="template-history-downloadLinks">
+{{#if meta_files}}
+<div popupmenu="dataset-{{id}}-popup">
+ <a class="action-button" href="{{download_url}}">Download Dataset</a>
+ <a>Additional Files</a>
+ {{#each meta_files}}
+ <a class="action-button" href="{{meta_download_url}}">Download {{meta_file_type}}</a>
+ {{/each}}
+</div>
+<div style="float:left;" class="menubutton split popup" id="dataset-{{id}}-popup">
+ <a href="{{download_url}}" title="Download" class="icon-button disk tooltip"></a>
+</div>
+{{else}}
+<a href="" title="Download" class="icon-button disk tooltip"></a>
+{{/if}}
+</script>
+
<script type="text/template" class="template-history" id="template-history-tagArea">
{{! TODO: move to mvc/tag.js templates }}
<div class="tag-area" style="display: none;">
@@ -70,3 +87,12 @@
</div></script>
+<script type="text/template" class="template-history" id="template-history-displayApps">
+{{#each displayApps}}
+ {{label}}
+ {{#each links}}
+ <a target="{{target}}" href="{{href}}">{{text}}</a>
+ {{/each}}
+ <br />
+{{/each}}
+</script>
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/templates/template-history-annotationArea.handlebars
--- a/static/scripts/templates/template-history-annotationArea.handlebars
+++ /dev/null
@@ -1,7 +0,0 @@
-{{! TODO: move to mvc/annotations.js templates, editable-text }}
-<div id="{{ id }}-annotation-area" class="annotation-area" style="display: none;">
- <strong>Annotation:</strong>
- <div id="{{ id }}-anotation-elt" class="annotation-elt tooltip editable-text"
- style="margin: 1px 0px 1px 0px" title="Edit dataset annotation">
- </div>
-</div>
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/templates/template-history-failedMetaData.handlebars
--- a/static/scripts/templates/template-history-failedMetaData.handlebars
+++ /dev/null
@@ -1,4 +0,0 @@
-{{#warningmessagesmall}}
-An error occurred setting the metadata for this dataset.
-You may be able to <a href="{{ edit_url }}" target="galaxy_main">set it manually or retry auto-detection</a>.
-{{/warningmessagesmall}}
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/templates/template-history-hdaSummary.handlebars
--- a/static/scripts/templates/template-history-hdaSummary.handlebars
+++ /dev/null
@@ -1,13 +0,0 @@
-<div class="hda-summary">
- {{ misc_blurb }}<br />
- format: <span class="{{ data_type }}">{{ data_type }}</span>,
- database:
- {{#if dbkey_unknown_and_editable }}
- <a href="{{ edit_url }}" target="galaxy_main">{{ metadata_dbkey }}</a>
- {{else}}
- <span class="{{ metadata_dbkey }}">{{ metadata_dbkey }}</span>
- {{/if}}
-</div>
-{{#if misc_info}}
-<div class="hda-info">{{ misc_info }}</div>
-{{/if}}
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/templates/template-history-tagArea.handlebars
--- a/static/scripts/templates/template-history-tagArea.handlebars
+++ /dev/null
@@ -1,6 +0,0 @@
-{{! TODO: move to mvc/tag.js templates }}
-<div class="tag-area" style="display: none;">
- <strong>Tags:</strong>
- <div class="tag-elt">
- </div>
-</div>
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/templates/template-history-titleLink.handlebars
--- a/static/scripts/templates/template-history-titleLink.handlebars
+++ /dev/null
@@ -1,1 +0,0 @@
-<a href="javascript:void(0);"><span class="historyItemTitle">{{ hid }}: {{ name }}</span></a>
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/templates/template-history-warning-messages.handlebars
--- a/static/scripts/templates/template-history-warning-messages.handlebars
+++ /dev/null
@@ -1,23 +0,0 @@
-{{#if deleted}}{{#warningmessagesmall}}
- This dataset has been deleted.
- {{#if undelete_url}}
- Click <a href="{{ undelete_url }}" class="historyItemUndelete" id="historyItemUndeleter-{{ id }}"
- target="galaxy_history">here</a> to undelete it
- {{#if purge_url}}
- or <a href="{{ purge_url }}" class="historyItemPurge" id="historyItemPurger-{{ id }}"
- target="galaxy_history">here</a> to immediately remove it from disk
- {{/if}}
- {{/if}}
-{{/warningmessagesmall}}{{/if}}
-
-{{#if purged}}{{#warningmessagesmall}}
- This dataset has been deleted and removed from disk.
-{{/warningmessagesmall}}{{/if}}
-
-{{#unless visible}}{{#warningmessagesmall}}
- This dataset has been hidden.
- {{#if unhide_url}}
- Click <a href="{{ unhide_url }}" class="historyItemUnhide" id="historyItemUnhider-{{ id }}"
- target="galaxy_history">here</a> to unhide it
- {{/if}}
-{{/warningmessagesmall}}{{/unless}}
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/templates/template-iconButton.handlebars
--- a/static/scripts/templates/template-iconButton.handlebars
+++ /dev/null
@@ -1,2 +0,0 @@
-{{! alternate template-based icon-button }}
-{{> iconButton this}}
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/templates/template-warningmessagesmall.handlebars
--- a/static/scripts/templates/template-warningmessagesmall.handlebars
+++ /dev/null
@@ -1,2 +0,0 @@
-{{! renders a warning in a (mostly css) highlighted, iconned warning box }}
- <div class="warningmessagesmall"><strong>{{{ warning }}}</strong></div>
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/templates/tool_search.handlebars
--- a/static/scripts/templates/tool_search.handlebars
+++ b/static/scripts/templates/tool_search.handlebars
@@ -1,3 +1,3 @@
<input type="text" name="query" value="{{search_hint_string}}" id="tool-search-query" autocomplete="off" class="search-query parent-width" />
-<a id="search-clear-btn" class="icon-button cross-circle tooltip" title="clear search (esc)"></a>
+<a id="search-clear-btn" class="tooltip" title="clear search (esc)"></a><img src="{{spinner_url}}" id="search-spinner" class="search-spinner"/>
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/viz/circster.js
--- a/static/scripts/viz/circster.js
+++ b/static/scripts/viz/circster.js
@@ -1,4 +1,4 @@
-define( ["libs/d3", "viz/visualization"], function( d3, visualization ) {
+define(["libs/underscore", "libs/d3", "viz/visualization"], function(_, d3, visualization) {
// General backbone style inheritence
var Base = function() { this.initialize && this.initialize.apply(this, arguments); }; Base.extend = Backbone.Model.extend;
@@ -28,6 +28,18 @@
});
/**
+ * A label track.
+ */
+// FIXME: merge with tracks.js LabelTrack
+var LabelTrack = Backbone.Model.extend({
+ defaults: {
+ prefs: {
+ color: '#ccc'
+ }
+ }
+});
+
+/**
* Renders a full circster visualization.
*/
var CircsterView = Backbone.View.extend({
@@ -38,6 +50,7 @@
this.genome = options.genome;
this.dataset_arc_height = options.dataset_arc_height;
this.track_gap = 5;
+ this.label_arc_height = 20;
},
render: function() {
@@ -47,8 +60,10 @@
height = self.$el.height(),
// Compute radius start based on model, will be centered
// and fit entirely inside element by default.
- init_radius_start = ( Math.min(width, height)/2 -
- this.model.get('tracks').length * (this.dataset_arc_height + this.track_gap) );
+ init_radius_start = Math.min(width, height) / 2 -
+ this.model.get('tracks').length * (this.dataset_arc_height + this.track_gap) -
+ (this.label_arc_height + this.track_gap),
+ tracks = this.model.get('tracks');
// Set up SVG element.
var svg = d3.select(self.$el[0])
@@ -59,31 +74,61 @@
// Set up zooming, dragging.
.append('svg:g')
.call(d3.behavior.zoom().on('zoom', function() {
+ // Do zoom.
svg.attr("transform",
"translate(" + d3.event.translate + ")" +
" scale(" + d3.event.scale + ")");
- var utils = new SVGUtils();
- var visible_elts = d3.selectAll('path').filter(function(d, i) {
+
+ // Update visible elements with more data.
+ var utils = new SVGUtils(),
+ tracks_and_chroms_to_update = {};
+
+ tracks.each(function(t) {
+ tracks_and_chroms_to_update[t.id] = [];
+ });
+
+ d3.selectAll('path.chrom-data').filter(function(d, i) {
return utils.is_visible(this, svg);
+ }).each(function(d, i) {
+ var elt_data = $.data(this, 'chrom_data');
+ tracks_and_chroms_to_update[elt_data.track.id].push(elt_data.chrom);
});
- visible_elts.each(function(d, i) {
- // TODO: redraw visible elements.
+
+ /*
+ _.each(_.pairs(tracks_and_chroms_to_update), function(track_and_chroms) {
+ var track = tracks.get(track_and_chroms[0])
+ chroms = track_and_chroms[1];
+
+ _.each(chroms, function(chr_name) {
+ var chr_region = self.genome.get_chrom_region(chr_name),
+ data_deferred = track.get('data_manager').get_more_detailed_data(chr_region, 'Coverage', 0, d3.event.scale);
+
+ $.when(data_deferred).then(function(data) {
+ console.log("got more detailed data", data);
+ })
+ })
+
+
});
+
+ // TODO: update tracks and chroms.
+ console.log(tracks_and_chroms_to_update);
+ */
}))
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.append('svg:g');
// -- Render each dataset in the visualization. --
- this.model.get('tracks').each(function(track, index) {
- var dataset = track.get('genome_wide_data'),
- radius_start = init_radius_start + index * (dataset_arc_height + self.track_gap),
- track_renderer_class = (dataset instanceof visualization.GenomeWideBigWigData ?
+ tracks.each(function(track, index) {
+ var radius_start = init_radius_start + index * (dataset_arc_height + self.track_gap),
+ track_renderer_class = (track.get('track_type') === 'LineTrack' ?
CircsterBigWigTrackRenderer :
CircsterSummaryTreeTrackRenderer );
var track_renderer = new track_renderer_class({
track: track,
+ track_index: index,
radius_start: radius_start,
radius_end: radius_start + dataset_arc_height,
genome: self.genome,
@@ -93,47 +138,67 @@
track_renderer.render(svg);
});
+
+ // -- Render chromosome labels. --
+ var radius_start = init_radius_start + tracks.length * (dataset_arc_height + self.track_gap) + self.track_gap;
+ var chrom_labels_track = new CircsterLabelTrackRenderer({
+ track: new LabelTrack(),
+ track_index: tracks.length,
+ radius_start: radius_start,
+ radius_end: radius_start,
+ genome: self.genome,
+ total_gap: self.total_gap
+ });
+
+ chrom_labels_track.render(svg);
}
});
var CircsterTrackRenderer = Base.extend( {
- initialize: function( options ) {
+ initialize: function(options) {
this.options = options;
+ this.options.bg_stroke = 'ccc';
+ this.options.bg_fill = 'ccc';
},
- render: function( svg ) {
- // Draw background arcs for each chromosome.
- var genome_arcs = this.chroms_layout(),
+ render: function(svg) {
+ // Create track group element.
+ var track_group_elt = svg.append("g").attr("id", "parent-" + this.options.track_index);
+
+ // Render background arcs.
+ var genome_arcs = this._chroms_layout(),
radius_start = this.options.radius_start,
radius_end = this.options.radius_end,
- track_parent_elt = svg.append("g").attr("id", "inner-arc"),
arc_gen = d3.svg.arc()
- .innerRadius(radius_start)
- .outerRadius(radius_end),
- // Draw arcs.
- chroms_elts = track_parent_elt.selectAll("#inner-arc>path")
- .data(genome_arcs).enter().append("path")
- .attr("d", arc_gen)
- .style("stroke", "#ccc")
- .style("fill", "#ccc")
- .append("title").text(function(d) { return d.data.chrom; });
+ .innerRadius(radius_start)
+ .outerRadius(radius_end),
- // Render data.
- this.render_data(track_parent_elt);
+ chroms_elts = track_group_elt.selectAll('g')
+ .data(genome_arcs).enter().append('svg:g');
+
+ // Draw arcs.
+ chroms_elts.append("path")
+ .attr("d", arc_gen)
+ .style("stroke", this.options.bg_stroke)
+ .style("fill", this.options.bg_fill)
+ .append("title").text(function(d) { return d.data.chrom; });
+
+ // Render track data.
+ this.render_data(track_group_elt);
// Apply prefs.
var prefs = this.options.track.get('prefs'),
block_color = prefs.block_color;
if (!block_color) { block_color = prefs.color; }
- track_parent_elt.selectAll('path.chrom-data').style('stroke', block_color).style('fill', block_color);
+ track_group_elt.selectAll('path.chrom-data').style('stroke', block_color).style('fill', block_color);
},
/**
* Returns arc layouts for genome's chromosomes/contigs. Arcs are arranged in a circle
* separated by gaps.
*/
- chroms_layout: function() {
+ _chroms_layout: function() {
// Setup chroms layout using pie.
var chroms_info = this.options.genome.get_chroms_info(),
pie_layout = d3.layout.pie().value(function(d) { return d.len; }).sort(null),
@@ -159,13 +224,18 @@
*/
render_data: function(svg) {
var self = this,
- chrom_arcs = this.chroms_layout(),
- dataset = this.options.track.get('genome_wide_data'),
+ chrom_arcs = this._chroms_layout(),
+ track = this.options.track,
r_start = this.options.radius_start,
r_end = this.options.radius_end,
+
+ genome_wide_data = track.get_genome_wide_data(this.options.genome),
// Merge chroms layout with data.
- layout_and_data = _.zip(chrom_arcs, dataset.get('data')),
+ layout_and_data = _.zip(chrom_arcs, genome_wide_data),
+
+ // Get min, max in data.
+ bounds = this.get_bounds(genome_wide_data),
// Do dataset layout for each chromosome's data using pie layout.
chroms_data_layout = _.map(layout_and_data, function(chrom_info) {
@@ -173,7 +243,7 @@
data = chrom_info[1];
return self.render_chrom_data(svg, chrom_arc, data,
r_start, r_end,
- dataset.get('min'), dataset.get('max'));
+ bounds.min, bounds.max);
});
return chroms_data_layout;
@@ -181,13 +251,49 @@
});
/**
+ * Render chromosome labels.
+ */
+var CircsterLabelTrackRenderer = CircsterTrackRenderer.extend({
+
+ initialize: function(options) {
+ this.options = options;
+ this.options.bg_stroke = 'fff';
+ this.options.bg_fill = 'fff';
+ },
+
+ /**
+ * Render labels.
+ */
+ render_data: function(svg) {
+ // Add chromosome label where it will fit; an alternative labeling mechanism
+ // would be nice for small chromosomes.
+ var chrom_arcs = svg.selectAll('g');
+
+ chrom_arcs.selectAll('path')
+ .attr('id', function(d) { return 'label-' + d.data.chrom; })
+
+ chrom_arcs.append("svg:text")
+ .filter(function(d) {
+ return d.endAngle - d.startAngle > 0.08;
+ })
+ .attr('text-anchor', 'middle')
+ .append("svg:textPath")
+ .attr("xlink:href", function(d) { return "#label-" + d.data.chrom; })
+ .attr('startOffset', '25%')
+ .text(function(d) {
+ return d.data.chrom;
+ });
+ }
+});
+
+/**
* Rendered for quantitative data.
*/
var CircsterQuantitativeTrackRenderer = CircsterTrackRenderer.extend({
/**
* Renders quantitative data with the form [x, value] and assumes data is equally spaced across
- * chromosome.
+ * chromosome. Attachs a dict with track and chrom name information to DOM element.
*/
render_quantitative_data: function(svg, chrom_arc, data, inner_radius, outer_radius, min, max) {
// Radius scaler.
@@ -212,14 +318,25 @@
.angle(line.angle());
// Render data.
- var parent = svg.datum(data);
-
- parent.append("path")
- .attr("class", "chrom-data")
- .attr("d", area);
- }
+ var parent = svg.datum(data),
+ path = parent.append("path")
+ .attr("class", "chrom-data")
+ .attr("d", area);
-})
+ // Attach dict with track and chrom info for path.
+ $.data(path[0][0], "chrom_data", {
+ track: this.options.track,
+ chrom: chrom_arc.data.chrom
+ });
+ },
+
+ /**
+ * Returns an object with min, max attributes denoting the minimum and maximum
+ * values for the track.
+ */
+ get_bounds: function() {}
+
+});
/**
* Layout for summary tree data in a circster visualization.
@@ -235,7 +352,19 @@
return null;
}
- return this.render_quantitative_data(svg, chrom_arc, chrom_data[0], inner_radius, outer_radius, min, max);
+ return this.render_quantitative_data(svg, chrom_arc, chrom_data.data, inner_radius, outer_radius, min, max);
+ },
+
+ get_bounds: function(data) {
+ // Get max across data.
+ var max_data = _.map(data, function(d) {
+ if (!d || typeof d === 'string') { return 0; }
+ return d.max;
+ });
+ return {
+ min: 0,
+ max: (max_data && typeof max_data !== 'string' ? _.max(max_data) : 0)
+ };
}
});
@@ -252,6 +381,27 @@
if (data.length === 0) { return; }
return this.render_quantitative_data(svg, chrom_arc, data, inner_radius, outer_radius, min, max);
+ },
+
+ get_bounds: function(data) {
+ // Set max across dataset by extracting all values, flattening them into a
+ // single array, and getting the min and max.
+ var values = _.flatten( _.map(data, function(d) {
+ if (d) {
+ // Each data point has the form [position, value], so return all values.
+ return _.map(d.data, function(p) {
+ return p[1];
+ });
+ }
+ else {
+ return 0;
+ }
+ }) );
+
+ return {
+ min: _.min(values),
+ max: _.max(values)
+ };
}
});
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/viz/scatterplot.js
--- a/static/scripts/viz/scatterplot.js
+++ b/static/scripts/viz/scatterplot.js
@@ -1,3 +1,9 @@
+define([
+ "../libs/underscore",
+ "../libs/d3",
+ "../mvc/base-mvc"
+
+], function(){
/* =============================================================================
todo:
outside this:
@@ -355,11 +361,11 @@
};
}
-//// ugh...this seems like the wrong way to use models
-//var ScatterplotModel = BaseModel.extend( LoggableMixin ).extend({
-// logger : console
-//});
-
+//==============================================================================
+/**
+ * Scatterplot control UI as a backbone view
+ *
+ */
var ScatterplotView = BaseView.extend( LoggableMixin ).extend({
//logger : console,
tagName : 'form',
@@ -370,15 +376,19 @@
},
initialize : function( attributes ){
- if( !attributes.dataset ){
+ if( !attributes || !attributes.dataset ){
throw( "ScatterplotView requires a dataset" );
} else {
this.dataset = attributes.dataset;
}
+
+ // passed from mako helper
+ //TODO: integrate to galaxyPaths
this.apiDatasetsURL = attributes.apiDatasetsURL;
+
+ // set up the basic chart infrastructure with config (if any)
this.chartConfig = attributes.chartConfig || {};
this.log( 'this.chartConfig:', this.chartConfig );
-
this.plot = new TwoVarScatterplot( this.chartConfig );
},
@@ -422,18 +432,23 @@
},
renderScatterplot : function(){
+ // parse the column values for both
+ // indeces (for the data fetch) and names (for the graph)
var view = this,
- url = this.apiDatasetsURL + '/' + this.dataset.id + '?data_type=raw_data&';
+ url = this.apiDatasetsURL + '/' + this.dataset.id + '?data_type=raw_data&',
+
xSelector = this.$el.find( '[name="x-column"]' ),
xVal = xSelector.val(),
xName = xSelector.children( '[value="' + xVal + '"]' ).text(),
+
ySelector = this.$el.find( '[name="y-column"]' ),
yVal = ySelector.val(),
yName = ySelector.children( '[value="' + yVal + '"]' ).text();
- //TODO
this.log( xName, yName );
+
this.chartConfig.xLabel = xName;
this.chartConfig.yLabel = yName;
+
//TODO: alter directly
view.plot.updateConfig( this.chartConfig );
@@ -441,6 +456,7 @@
//TODO: other vals: max, start, page
//TODO: chart config
+ // fetch the data, sending chosen columns to the server
url += jQuery.param({
columns : '[' + [ xVal, yVal ] + ']'
});
@@ -450,6 +466,7 @@
url : url,
dataType : 'json',
success : function( response ){
+ //TODO: server sends back an endpoint, cache for next pagination request
view.endpoint = response.endpoint;
view.plot.render(
// pull apart first two regardless of number of columns
@@ -464,3 +481,8 @@
}
});
+//==============================================================================
+return {
+ //TwoVarScatterplot : TwoVarScatterplot,
+ ScatterplotView : ScatterplotView
+};});
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/viz/trackster/painters.js
--- a/static/scripts/viz/trackster/painters.js
+++ b/static/scripts/viz/trackster/painters.js
@@ -729,7 +729,7 @@
/**
* Draw a single read.
*/
- draw_read: function(ctx, mode, w_scale, y_center, tile_low, tile_high, feature_start, cigar, strand, orig_seq) {
+ draw_read: function(ctx, mode, w_scale, y_center, tile_low, tile_high, feature_start, cigar, strand, ref_seq) {
ctx.textAlign = "center";
var tile_region = [tile_low, tile_high],
base_offset = 0,
@@ -743,12 +743,12 @@
// Gap is needed so that read is offset and hence first base can be drawn on read.
// TODO-FIX: using this gap offsets reads so that their start is not visually in sync with other tracks.
- if ((mode === "Pack" || this.mode === "Auto") && orig_seq !== undefined && w_scale > char_width_px) {
+ if ((mode === "Pack" || this.mode === "Auto") && ref_seq !== undefined && w_scale > char_width_px) {
gap = Math.round(w_scale/2);
}
if (!cigar) {
// If no cigar string, then assume all matches
- cigar = [ [0, orig_seq.length] ];
+ cigar = [ [0, ref_seq.length] ];
}
for (var cig_id = 0, len = cigar.length; cig_id < len; cig_id++) {
var cig = cigar[cig_id],
@@ -782,7 +782,8 @@
case "=": // Equals.
if (is_overlap([seq_start, seq_start + cig_len], tile_region)) {
// Draw.
- var seq = orig_seq.slice(seq_offset, seq_offset + cig_len);
+ // -1 b/c sequence data is 1-based but painter is 0-based.
+ var seq = ref_seq.slice(seq_offset - 1, seq_offset + cig_len);
if (gap > 0) {
ctx.fillStyle = block_color;
ctx.fillRect(s_start - gap, y_center + 1, s_end - s_start, 9);
@@ -838,7 +839,8 @@
var insert_x_coord = s_start - gap;
if (is_overlap([seq_start, seq_start + cig_len], tile_region)) {
- var seq = orig_seq.slice(seq_offset, seq_offset + cig_len);
+ // -1 b/c sequence data is 1-based but painter is 0-based.
+ var seq = ref_seq.slice(seq_offset - 1, seq_offset + cig_len);
// Insertion point is between the sequence start and the previous base: (-gap) moves
// back from sequence start to insertion point.
if (this.prefs.show_insertions) {
@@ -849,7 +851,7 @@
// Draw sequence.
// X center is offset + start - <half_sequence_length>
var x_center = s_start - (s_end - s_start)/2;
- if ( (mode === "Pack" || this.mode === "Auto") && orig_seq !== undefined && w_scale > char_width_px) {
+ if ( (mode === "Pack" || this.mode === "Auto") && ref_seq !== undefined && w_scale > char_width_px) {
// Draw sequence container.
ctx.fillStyle = "yellow";
ctx.fillRect(x_center - gap, y_center - 9, s_end - s_start, 9);
@@ -885,7 +887,7 @@
}
}
else {
- if ( (mode === "Pack" || this.mode === "Auto") && orig_seq !== undefined && w_scale > char_width_px) {
+ if ( (mode === "Pack" || this.mode === "Auto") && ref_seq !== undefined && w_scale > char_width_px) {
// Show insertions with a single number at the insertion point.
draw_last.push( { type: "text", data: [seq.length, insert_x_coord, y_center + 9] } );
}
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/viz/trackster/tracks.js
--- a/static/scripts/viz/trackster/tracks.js
+++ b/static/scripts/viz/trackster/tracks.js
@@ -1,11 +1,63 @@
define( ["libs/underscore", "viz/visualization", "viz/trackster/util",
"viz/trackster/slotting", "viz/trackster/painters", "mvc/data",
- "viz/trackster/filters" ],
- function( _, visualization, util, slotting, painters, data, filters_mod ) {
+ "viz/trackster/filters", "viz/trackster_ui" ],
+ function( _, visualization, util, slotting, painters, data, filters_mod, trackster_ui_mod ) {
var extend = _.extend;
var get_random_color = util.get_random_color;
+/**
+ * Use a popup grid to add more datasets.
+ */
+var add_datasets = function(dataset_url, add_track_async_url, success_fn) {
+ $.ajax({
+ url: dataset_url,
+ data: { "f-dbkey": view.dbkey },
+ error: function() { alert( "Grid failed" ); },
+ success: function(table_html) {
+ show_modal(
+ "Select datasets for new tracks",
+ table_html, {
+ "Cancel": function() {
+ hide_modal();
+ },
+ "Add": function() {
+ var requests = [];
+ $('input[name=id]:checked,input[name=ldda_ids]:checked').each(function() {
+ var data = {
+ data_type: 'track_config',
+ 'hda_ldda': 'hda'
+ },
+ id = $(this).val();
+ if ($(this).attr("name") !== "id") {
+ data.hda_ldda = 'ldda';
+ }
+ requests[requests.length] = $.ajax({
+ url: add_track_async_url + "/" + id,
+ data: data,
+ dataType: "json"
+ });
+ });
+ // To preserve order, wait until there are definitions for all tracks and then add
+ // them sequentially.
+ $.when.apply($, requests).then(function() {
+ // jQuery always returns an Array for arguments, so need to look at first element
+ // to determine whether multiple requests were made and consequently how to
+ // map arguments to track definitions.
+ var track_defs = (arguments[0] instanceof Array ?
+ $.map(arguments, function(arg) { return arg[0]; }) :
+ [ arguments[0] ]
+ );
+ success_fn(track_defs);
+ });
+ hide_modal();
+ }
+ }
+ );
+ }
+ });
+};
+
/**
* Helper to determine if object is jQuery deferred.
@@ -3427,7 +3479,7 @@
this.content_div.css("border", "none");
this.data_url = reference_url + "/" + this.view.dbkey;
this.data_url_extra_params = {reference: true};
- this.data_manager = new ReferenceTrackDataManager({
+ this.data_manager = new visualization.ReferenceTrackDataManager({
data_url: this.data_url
});
this.hide_contents();
@@ -3550,8 +3602,7 @@
track.vertical_range = undefined;
return $.getJSON( track.dataset.url(),
{ data_type: 'data', stats: true, chrom: track.view.chrom, low: 0,
- high: track.view.max_high, hda_ldda: track.hda_ldda, dataset_id:
- track.dataset_id }, function(result) {
+ high: track.view.max_high, hda_ldda: track.hda_ldda }, function(result) {
track.container_div.addClass( "line-track" );
var data = result.data;
if ( isNaN(parseFloat(track.prefs.min_value)) || isNaN(parseFloat(track.prefs.max_value)) ) {
@@ -4197,7 +4248,8 @@
ReadTrack: ReadTrack,
VcfTrack: VcfTrack,
CompositeTrack: CompositeTrack,
- object_from_template: object_from_template
+ object_from_template: object_from_template,
+ add_datasets: add_datasets
};
// End trackster_module encapsulation
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/viz/trackster_ui.js
--- a/static/scripts/viz/trackster_ui.js
+++ b/static/scripts/viz/trackster_ui.js
@@ -1,4 +1,4 @@
-define( ["base","libs/underscore","viz/trackster/slotting", "viz/trackster/painters","viz/trackster/tracks"], function( base, _, slotting, painters, tracks ) {
+define( ["base","libs/underscore","viz/trackster/slotting", "viz/trackster/painters", "viz/trackster/tracks" ], function( base, _, slotting, painters, tracks ) {
/************************************************************************
* Functions used for creating and managing the Trackster user interface.
@@ -19,9 +19,9 @@
var self = this,
menu = create_icon_buttons_menu([
{ icon_class: 'plus-button', title: 'Add tracks', on_click: function() {
- add_datasets(add_datasets_url, add_track_async_url, function(tracks) {
+ tracks.add_datasets(add_datasets_url, add_track_async_url, function(tracks) {
_.each(tracks, function(track) {
- view.add_drawable( trackster_ui.object_from_template(track, view, view) );
+ view.add_drawable( object_from_template(track, view, view) );
});
});
} },
@@ -99,7 +99,8 @@
* Use a popup to select a dataset of create bookmarks from
*/
add_bookmarks: function() {
- var baseURL = this.baseURL;
+ var self = this,
+ baseURL = this.baseURL;
show_modal( "Select dataset for new bookmarks", "progress" );
$.ajax({
url: this.baseURL + "/visualization/list_histories",
@@ -129,7 +130,7 @@
}).then( function(data) {
for( i = 0; i < data.data.length; i++ ) {
var row = data.data[i];
- add_bookmark( row[0], row[1] );
+ self.add_bookmark( row[0], row[1] );
}
});
});
@@ -183,7 +184,8 @@
create_visualization: function(view_config, viewport_config, drawables_config, bookmarks_config, editable) {
// Create view.
- view = new tracks.View(view_config);
+ var self = this,
+ view = new tracks.View(view_config);
view.editor = true;
$.when( view.load_chroms_deferred ).then(function() {
// Viewport config.
@@ -227,7 +229,7 @@
var bookmark;
for (var i = 0; i < bookmarks_config.length; i++) {
bookmark = bookmarks_config[i];
- add_bookmark(bookmark['position'], bookmark['annotation'], editable);
+ self.add_bookmark(bookmark['position'], bookmark['annotation'], editable);
}
}
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 static/scripts/viz/visualization.js
--- a/static/scripts/viz/visualization.js
+++ b/static/scripts/viz/visualization.js
@@ -1,4 +1,4 @@
-define( ["mvc/data", "viz/trackster/util" ], function(data, util) {
+define( ["libs/underscore", "mvc/data", "viz/trackster/util" ], function(_, data_mod, util_mod) {
/**
* Model, view, and controller objects for Galaxy visualization framework.
@@ -138,7 +138,6 @@
dataset: null,
filters_manager: null,
data_type: "data",
- genome_wide_summary_data: null,
data_mode_compatible: function(entry, mode) { return true; },
can_subset: function(entry) { return false; }
}),
@@ -150,7 +149,7 @@
data_is_ready: function() {
var dataset = this.get('dataset'),
ready_deferred = $.Deferred(),
- ss_deferred = new util.ServerStateDeferred({
+ ss_deferred = new util_mod.ServerStateDeferred({
ajax_settings: {
url: this.get('dataset').url(),
data: {
@@ -176,7 +175,6 @@
var dataset = this.get('dataset'),
params = {
query: query,
- dataset_id: dataset.id,
hda_ldda: dataset.get('hda_ldda'),
data_type: 'features'
};
@@ -184,26 +182,23 @@
},
/**
- * Load data from server; returns Deferred object that resolves when data is available.
+ * Load data from server and manages data entries. Adds a Deferred to manager
+ * for region; when data becomes available, replaces Deferred with data.
+ * Returns the Deferred that resolves when data is available.
*/
load_data: function(region, mode, resolution, extra_params) {
// Setup data request params.
- var params = {
+ var dataset = this.get('dataset'),
+ params = {
"data_type": this.get('data_type'),
"chrom": region.get('chrom'),
"low": region.get('start'),
"high": region.get('end'),
"mode": mode,
- "resolution": resolution
- },
- dataset = this.get('dataset');
-
- // ReferenceDataManager does not have dataset.
- if (dataset) {
- params.dataset_id = dataset.id;
- params.hda_ldda = dataset.get('hda_ldda');
- }
-
+ "resolution": resolution,
+ "hda_ldda": dataset.get('hda_ldda')
+ };
+
$.extend(params, extra_params);
// Add track filters to params.
@@ -218,10 +213,13 @@
}
// Do request.
- var manager = this;
- return $.getJSON(dataset.url(), params, function (result) {
- manager.set_data(region, result);
- });
+ var manager = this,
+ entry = $.getJSON(dataset.url(), params, function (result) {
+ manager.set_data(region, result);
+ });
+
+ this.set_data(region, entry);
+ return entry;
},
/**
@@ -268,12 +266,8 @@
}
}
}
-
- // Load data from server. The deferred is immediately saved until the
- // data is ready, it then replaces itself with the actual data.
- entry = this.load_data(region, mode, resolution, extra_params);
- this.set_data(region, entry);
- return entry;
+
+ return this.load_data(region, mode, resolution, extra_params);
},
/**
@@ -293,15 +287,11 @@
* Gets more data for a region using either a depth-first or a breadth-first approach.
*/
get_more_data: function(region, mode, resolution, extra_params, req_type) {
- //
- // Get current data from cache and mark as stale.
- //
- var cur_data = this.get_elt(region);
- if ( !(cur_data && this.get('data_mode_compatible')(cur_data, mode)) ) {
- console.log("ERROR: no current data for: ", dataset, region.toString(), mode, resolution, extra_params);
+ var cur_data = this._mark_stale(region);
+ if (!(cur_data && this.get('data_mode_compatible')(cur_data, mode))) {
+ console.log('ERROR: problem with getting more data: current data is not compatible');
return;
}
- cur_data.stale = true;
//
// Set parameters based on request type.
@@ -346,6 +336,43 @@
});
return new_data_available;
},
+
+ /**
+ * Returns more detailed data for an entry.
+ */
+ get_more_detailed_data: function(region, mode, resolution, detail_multiplier, extra_params) {
+ // Mark current entry as stale.
+ var cur_data = this._mark_stale(region);
+ if (!cur_data) {
+ console.log("ERROR getting more detailed data: no current data");
+ return;
+ }
+
+ if (!extra_params) { extra_params = {}; }
+
+ // Use additional parameters to get more detailed data.
+ var mode;
+ if (cur_data.dataset_type === 'bigwig') {
+ extra_params.num_samples = cur_data.data.length * detail_multiplier;
+ }
+ else if (cur_data.dataset_type === 'summary_tree') {
+ extra_params.level = cur_data.level + 1;
+ }
+
+ return this.load_data(region, mode, resolution, extra_params);
+ },
+
+ /**
+ * Marks cache data as stale.
+ */
+ _mark_stale: function(region) {
+ var entry = this.get_elt(region);
+ if (!entry) {
+ console.log("ERROR: no data to mark as stale: ", this.get('dataset'), region.toString());
+ }
+ entry.stale = true;
+ return entry;
+ },
/**
* Get data from the cache.
@@ -363,13 +390,21 @@
});
var ReferenceTrackDataManager = GenomeDataManager.extend({
- load_data: function(low, high, mode, resolution, extra_params) {
+ initialize: function(options) {
+ // Use generic object in place of dataset and set urlRoot to fetch data.
+ var dataset_placeholder = new Backbone.Model();
+ dataset_placeholder.urlRoot = options.data_url;
+ this.set('dataset', dataset_placeholder);
+ },
+
+ load_data: function(region, mode, resolution, extra_params) {
+ console.log(region, mode, resolution);
if (resolution > 1) {
// Now that data is pre-fetched before draw, we don't load reference tracks
// unless it's at the bottom level.
return { data: null };
}
- return GenomeDataManager.prototype.load_data.call(this, low, high, mode, resolution, extra_params);
+ return GenomeDataManager.prototype.load_data.call(this, region, mode, resolution, extra_params);
}
});
@@ -382,9 +417,29 @@
key: null,
chroms_info: null
},
+
+ initialize: function(options) {
+ this.id = options.dbkey;
+ },
+ /**
+ * Shorthand for getting to chromosome information.
+ */
get_chroms_info: function() {
return this.attributes.chroms_info.chrom_info;
+ },
+
+ /**
+ * Returns a GenomeRegion object denoting a complete chromosome.
+ */
+ get_chrom_region: function(chr_name) {
+ var chrom_info = _.find(this.get_chroms_info(), function(chrom_info) {
+ return chrom_info.chrom == chr_name;
+ });
+ return new GenomeRegion({
+ chrom: chrom_info.chrom,
+ end: chrom_info.len
+ });
}
});
@@ -533,69 +588,52 @@
model: BrowserBookmark
});
-var GenomeWideBigWigData = Backbone.Model.extend({
- defaults: {
- data: null,
- min: 0,
- max: 0
- },
-
- initialize: function(options) {
- // Set max across dataset by extracting all values, flattening them into a
- // single array, and getting the min and max.
- var values = _.flatten( _.map(this.get('data'), function(d) {
- if (d.data.length !== 0) {
- // Each data point has the form [position, value], so return all values.
- return _.map(d.data, function(p) {
- return p[1];
- });
- }
- else {
- return 0;
- }
- }) );
- this.set('max', _.max(values));
- this.set('min', _.min(values));
- }
-});
-
-/**
- * Genome-wide summary tree dataset.
- */
-var GenomeWideSummaryTreeData = Backbone.RelationalModel.extend({
- defaults: {
- data: null,
- min: 0,
- max: 0
- },
-
- initialize: function(options) {
- // Set max across dataset.
- var max_data = _.max(this.get('data'), function(d) {
- if (!d || typeof d === 'string') { return 0; }
- return d[1];
- });
- this.attributes.max = (max_data && typeof max_data !== 'string' ? max_data[1] : 0);
- }
-});
-
/**
* A track of data in a genome visualization.
*/
// TODO: rename to Track and merge with Trackster's Track object.
-var BackboneTrack = data.Dataset.extend({
+var BackboneTrack = data_mod.Dataset.extend({
initialize: function(options) {
// Dataset id is unique ID for now.
this.set('id', options.dataset_id);
- // Create genome-wide dataset if available.
- var genome_wide_data = this.get('genome_wide_data');
- if (genome_wide_data) {
- var gwd_class = (this.get('track_type') === 'LineTrack' ?
- GenomeWideBigWigData : GenomeWideSummaryTreeData);
- this.set('genome_wide_data', new gwd_class(genome_wide_data));
+ // Set up data manager.
+ var data_manager = new GenomeDataManager({
+ dataset: this
+ });
+ this.set('data_manager', data_manager);
+
+ // If there's preloaded data, add it to data manager.
+ var preloaded_data = this.get('preloaded_data');
+ if (preloaded_data) {
+ // Increase size to accomodate all preloaded data.
+ data_manager.set('num_elements', preloaded_data.data.length);
+
+ // Put data into manager.
+ _.each(preloaded_data.data, function(entry) {
+ data_manager.set_data(entry.region, entry);
+ });
}
+ },
+
+ /**
+ * Returns an array of data with each entry representing one chromosome/contig
+ * of data.
+ */
+ get_genome_wide_data: function(genome) {
+ var data_manager = this.get('data_manager');
+
+ // Map chromosome data into track data.
+ return _.map(genome.get('chroms_info').chrom_info, function(chrom_info) {
+ return data_manager.get_elt(
+ new GenomeRegion({
+ chrom: chrom_info.chrom,
+ start: 0,
+ end: chrom_info.len
+ })
+ );
+ });
}
});
@@ -688,62 +726,6 @@
}
});
-/**
- * -- Helper functions.
- */
-
-/**
- * Use a popup grid to add more datasets.
- */
-var add_datasets = function(dataset_url, add_track_async_url, success_fn) {
- $.ajax({
- url: dataset_url,
- data: { "f-dbkey": view.dbkey },
- error: function() { alert( "Grid failed" ); },
- success: function(table_html) {
- show_modal(
- "Select datasets for new tracks",
- table_html, {
- "Cancel": function() {
- hide_modal();
- },
- "Add": function() {
- var requests = [];
- $('input[name=id]:checked,input[name=ldda_ids]:checked').each(function() {
- var data = {
- data_type: 'track_config',
- 'hda_ldda': 'hda'
- },
- id = $(this).val();
- if ($(this).attr("name") !== "id") {
- data['hda_ldda'] = 'ldda';
- }
- requests[requests.length] = $.ajax({
- url: add_track_async_url + "/" + id,
- data: data,
- dataType: "json"
- });
- });
- // To preserve order, wait until there are definitions for all tracks and then add
- // them sequentially.
- $.when.apply($, requests).then(function() {
- // jQuery always returns an Array for arguments, so need to look at first element
- // to determine whether multiple requests were made and consequently how to
- // map arguments to track definitions.
- var track_defs = (arguments[0] instanceof Array ?
- $.map(arguments, function(arg) { return arg[0]; }) :
- [ arguments[0] ]
- );
- success_fn(track_defs);
- });
- hide_modal();
- }
- }
- );
- }
- });
-};
-
return {
BrowserBookmark: BrowserBookmark,
BrowserBookmarkCollection: BrowserBookmarkCollection,
@@ -754,13 +736,10 @@
GenomeRegion: GenomeRegion,
GenomeRegionCollection: GenomeRegionCollection,
GenomeVisualization: GenomeVisualization,
- GenomeWideBigWigData: GenomeWideBigWigData,
- GenomeWideSummaryTreeData: GenomeWideSummaryTreeData,
ReferenceTrackDataManager: ReferenceTrackDataManager,
TrackBrowserRouter: TrackBrowserRouter,
TrackConfig: TrackConfig,
- Visualization: Visualization,
- add_datasets: add_datasets
+ Visualization: Visualization
};
});
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/admin/tool_shed_repository/browse_repository.mako
--- a/templates/admin/tool_shed_repository/browse_repository.mako
+++ b/templates/admin/tool_shed_repository/browse_repository.mako
@@ -18,6 +18,9 @@
<li><a class="action-button" id="repository-${repository.id}-popup" class="menubutton">Repository Actions</a></li><div popupmenu="repository-${repository.id}-popup"><a class="action-button" href="${h.url_for( controller='admin_toolshed', action='manage_repository', id=trans.security.encode_id( repository.id ) )}">Manage repository</a>
+ %if has_readme:
+ <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='view_readme', id=trans.security.encode_id( repository.id ) )}">View README</a>
+ %endif
<a class="action-button" href="${h.url_for( controller='admin_toolshed', action='check_for_updates', id=trans.security.encode_id( repository.id ) )}">Get repository updates</a><a class="action-button" href="${h.url_for( controller='admin_toolshed', action='deactivate_or_uninstall_repository', id=trans.security.encode_id( repository.id ) )}">Deactivate or uninstall repository</a>
%if repository.tool_dependencies:
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/admin/tool_shed_repository/browse_tool_dependency.mako
--- a/templates/admin/tool_shed_repository/browse_tool_dependency.mako
+++ b/templates/admin/tool_shed_repository/browse_tool_dependency.mako
@@ -19,8 +19,11 @@
<ul class="manage-table-actions"><li><a class="action-button" id="tool_dependency-${tool_dependency.id}-popup" class="menubutton">Repository Actions</a></li><div popupmenu="tool_dependency-${tool_dependency.id}-popup">
+ <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='manage_repository', id=trans.security.encode_id( repository.id ) )}">Manage repository</a>
+ %if has_readme:
+ <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='view_readme', id=trans.security.encode_id( repository.id ) )}">View README</a>
+ %endif
<a class="action-button" href="${h.url_for( controller='admin_toolshed', action='browse_repository', id=trans.security.encode_id( repository.id ) )}">Browse repository files</a>
- <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='manage_repository', id=trans.security.encode_id( repository.id ) )}">Manage repository</a><a class="action-button" href="${h.url_for( controller='admin_toolshed', action='check_for_updates', id=trans.security.encode_id( repository.id ) )}">Get repository updates</a><a class="action-button" href="${h.url_for( controller='admin_toolshed', action='deactivate_or_uninstall_repository', id=trans.security.encode_id( repository.id ) )}">Deactivate or uninstall repository</a><a class="action-button" href="${h.url_for( controller='admin_toolshed', action='manage_tool_dependencies', tool_dependency_ids=tool_dependency_ids )}">Manage tool dependencies</a>
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/admin/tool_shed_repository/deactivate_or_uninstall_repository.mako
--- a/templates/admin/tool_shed_repository/deactivate_or_uninstall_repository.mako
+++ b/templates/admin/tool_shed_repository/deactivate_or_uninstall_repository.mako
@@ -6,8 +6,11 @@
<ul class="manage-table-actions"><li><a class="action-button" id="repository-${repository.id}-popup" class="menubutton">Repository Actions</a></li><div popupmenu="repository-${repository.id}-popup">
+ <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='manage_repository', id=trans.security.encode_id( repository.id ) )}">Manage repository</a>
+ %if has_readme:
+ <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='view_readme', id=trans.security.encode_id( repository.id ) )}">View README</a>
+ %endif
<a class="action-button" href="${h.url_for( controller='admin_toolshed', action='browse_repository', id=trans.security.encode_id( repository.id ) )}">Browse repository files</a>
- <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='manage_repository', id=trans.security.encode_id( repository.id ) )}">Manage repository</a><a class="action-button" href="${h.url_for( controller='admin_toolshed', action='check_for_updates', id=trans.security.encode_id( repository.id ) )}">Get repository updates</a>
%if repository.tool_dependencies:
<% tool_dependency_ids = [ trans.security.encode_id( td.id ) for td in repository.tool_dependencies ] %>
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/admin/tool_shed_repository/manage_repository.mako
--- a/templates/admin/tool_shed_repository/manage_repository.mako
+++ b/templates/admin/tool_shed_repository/manage_repository.mako
@@ -11,6 +11,9 @@
%elif can_install:
<a class="action-button" href="${h.url_for( controller='admin_toolshed', action='manage_repository', id=trans.security.encode_id( repository.id ), operation='install' )}">Install</a>
%else:
+ %if has_readme:
+ <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='view_readme', id=trans.security.encode_id( repository.id ) )}">View README</a>
+ %endif
<a class="action-button" href="${h.url_for( controller='admin_toolshed', action='browse_repository', id=trans.security.encode_id( repository.id ) )}">Browse repository files</a><a class="action-button" href="${h.url_for( controller='admin_toolshed', action='check_for_updates', id=trans.security.encode_id( repository.id ) )}">Get repository updates</a>
%if repository.includes_tools:
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/admin/tool_shed_repository/manage_tool_dependencies.mako
--- a/templates/admin/tool_shed_repository/manage_tool_dependencies.mako
+++ /dev/null
@@ -1,79 +0,0 @@
-<%inherit file="/base.mako"/>
-<%namespace file="/message.mako" import="render_msg" />
-
-<% import os %>
-
-<br/><br/>
-<ul class="manage-table-actions">
- <li><a class="action-button" id="repository-${repository.id}-popup" class="menubutton">Repository Actions</a></li>
- <div popupmenu="repository-${repository.id}-popup">
- <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='manage_repository', id=trans.security.encode_id( repository.id ) )}">Manage repository</a>
- <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='browse_repository', id=trans.security.encode_id( repository.id ) )}">Browse repository files</a>
- <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='check_for_updates', id=trans.security.encode_id( repository.id ) )}">Get repository updates</a>
- %if repository.includes_tools:
- <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='set_tool_versions', id=trans.security.encode_id( repository.id ) )}">Set tool versions</a>
- %endif
- <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='deactivate_or_uninstall_repository', id=trans.security.encode_id( repository.id ) )}">Deactivate or uninstall repository</a>
- </div>
-</ul>
-
-%if message:
- ${render_msg( message, status )}
-%endif
-
-<div class="toolForm">
- <div class="toolFormTitle">Repository '${repository.name}' tool dependencies</div>
- <div class="toolFormBody">
- <div class="form-row">
- <table class="grid">
- %for tool_dependency in repository.tool_dependencies:
- <%
- name = tool_dependency.name
- version = tool_dependency.version
- type = tool_dependency.type
- installed = tool_dependency.status == 'trans.model.ToolDependency.installation_status.INSTALLED
- install_dir = tool_dependency.installation_directory( trans.app )
- %>
- <tr>
- <td bgcolor="#D8D8D8">
- <div style="float: left; margin-left: 1px;" class="menubutton split popup" id="dependency-${tool_dependency.id}-popup">
- %if not installed:
- <a class="view-info" href="${h.url_for( controller='admin_toolshed', action='manage_tool_dependencies', operation='browse', tool_dependency_id=trans.security.encode_id( tool_dependency.id ) )}">
- <b>Name</b>
- </a>
- <div popupmenu="dependency-${tool_dependency.id}-popup">
- <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='manage_tool_dependencies', operation='install', tool_dependency_id=trans.security.encode_id( tool_dependency.id ) )}">Install this tool dependency</a>
- </div>
- %else:
- <a class="view-info" href="${h.url_for( controller='admin_toolshed', action='manage_tool_dependencies', operation='browse', tool_dependency_id=trans.security.encode_id( tool_dependency.id ) )}">
- <b>Name</b>
- </a>
- <div popupmenu="dependency-${tool_dependency.id}-popup">
- <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='manage_tool_dependencies', operation='uninstall', tool_dependency_id=trans.security.encode_id( tool_dependency.id ) )}">Uninstall this tool dependency</a>
- </div>
- %endif
- </div>
- </td>
- <td bgcolor="#D8D8D8">${name}</td>
- </tr>
- <tr><th>Version</th><td>${version}</td></tr>
- <tr><th>Type</th><td>${type}</td></tr>
- <tr>
- <th>Install directory</th>
- <td>
- %if not installed:
- This dependency is not currently installed
- %else:
- <a class="view-info" href="${h.url_for( controller='admin_toolshed', action='browse_tool_dependency', id=trans.security.encode_id( tool_dependency.id ), repository_id=trans.security.encode_id( repository.id ) )}">
- ${install_dir}
- </a>
- %endif
- </td>
- </tr>
- <tr><th>Installed</th><td>${not installed}</td></tr>
- %endfor
- </table>
- <div style="clear: both"></div>
- </div>
- </div>
-</div>
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/admin/tool_shed_repository/reselect_tool_panel_section.mako
--- a/templates/admin/tool_shed_repository/reselect_tool_panel_section.mako
+++ b/templates/admin/tool_shed_repository/reselect_tool_panel_section.mako
@@ -1,6 +1,7 @@
<%inherit file="/base.mako"/><%namespace file="/message.mako" import="render_msg" /><%namespace file="/admin/tool_shed_repository/common.mako" import="render_tool_dependency_section" />
+<%namespace file="/webapps/community/common/common.mako" import="render_readme" />
%if message:
${render_msg( message, status )}
@@ -42,3 +43,6 @@
</form></div></div>
+%if readme_text:
+ ${render_readme( readme_text )}
+%endif
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/admin/tool_shed_repository/select_tool_panel_section.mako
--- a/templates/admin/tool_shed_repository/select_tool_panel_section.mako
+++ b/templates/admin/tool_shed_repository/select_tool_panel_section.mako
@@ -1,6 +1,7 @@
<%inherit file="/base.mako"/><%namespace file="/message.mako" import="render_msg" /><%namespace file="/admin/tool_shed_repository/common.mako" import="render_tool_dependency_section" />
+<%namespace file="/webapps/community/common/common.mako" import="render_readme" />
%if message:
${render_msg( message, status )}
@@ -77,13 +78,5 @@
</div></div>
%if readme_text:
- <div class="toolForm">
- <div class="toolFormTitle">Repository README file (may contain important installation or license information)</div>
- <div class="toolFormBody">
- <input type="hidden" name="readme_text" value="${readme_text}"/>
- <div class="form-row">
- <pre>${readme_text}</pre>
- </div>
- </div>
- </div>
+ ${render_readme( readme_text )}
%endif
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/admin/tool_shed_repository/uninstall_tool_dependencies.mako
--- a/templates/admin/tool_shed_repository/uninstall_tool_dependencies.mako
+++ b/templates/admin/tool_shed_repository/uninstall_tool_dependencies.mako
@@ -3,6 +3,24 @@
<% import os %>
+<br/><br/>
+<ul class="manage-table-actions">
+ <li><a class="action-button" id="repository-${repository.id}-popup" class="menubutton">Repository Actions</a></li>
+ <div popupmenu="repository-${repository.id}-popup">
+ %if has_readme:
+ <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='view_readme', id=trans.security.encode_id( repository.id ) )}">View README</a>
+ %endif
+ <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='browse_repository', id=trans.security.encode_id( repository.id ) )}">Browse repository files</a>
+ <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='manage_repository', id=trans.security.encode_id( repository.id ) )}">Manage repository</a>
+ <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='check_for_updates', id=trans.security.encode_id( repository.id ) )}">Get repository updates</a>
+ %if repository.tool_dependencies:
+ <% tool_dependency_ids = [ trans.security.encode_id( td.id ) for td in repository.tool_dependencies ] %>
+ <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='manage_tool_dependencies', tool_dependency_ids=tool_dependency_ids )}">Manage tool dependencies</a>
+ %endif
+ <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='deactivate_or_uninstall_repository', id=trans.security.encode_id( repository.id ) )}">Deactivate or uninstall repository</a>
+ </div>
+</ul>
+
%if message:
${render_msg( message, status )}
%endif
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/admin/tool_shed_repository/view_tool_metadata.mako
--- a/templates/admin/tool_shed_repository/view_tool_metadata.mako
+++ b/templates/admin/tool_shed_repository/view_tool_metadata.mako
@@ -5,6 +5,9 @@
<ul class="manage-table-actions"><li><a class="action-button" id="repository-${repository.id}-popup" class="menubutton">Repository Actions</a></li><div popupmenu="repository-${repository.id}-popup">
+ %if repository_metadata and 'readme' in repository_metadata:
+ <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='view_readme', id=trans.security.encode_id( repository.id ) )}">View README</a>
+ %endif
<a class="action-button" href="${h.url_for( controller='admin_toolshed', action='browse_repository', id=trans.security.encode_id( repository.id ) )}">Browse repository files</a><a class="action-button" href="${h.url_for( controller='admin_toolshed', action='manage_repository', id=trans.security.encode_id( repository.id ) )}">Manage repository</a><a class="action-button" href="${h.url_for( controller='admin_toolshed', action='check_for_updates', id=trans.security.encode_id( repository.id ) )}">Get repository updates</a>
@@ -20,10 +23,10 @@
${render_msg( message, status )}
%endif
-%if metadata:
+%if tool_metadata:
<p/><div class="toolForm">
- <div class="toolFormTitle">${metadata[ 'name' ]} tool metadata</div>
+ <div class="toolFormTitle">${tool_metadata[ 'name' ]} tool metadata</div><div class="toolFormBody"><div class="form-row"><table width="100%">
@@ -32,41 +35,41 @@
</div><div class="form-row"><label>Name:</label>
- ${metadata[ 'name' ]}
+ ${tool_metadata[ 'name' ]}
<div style="clear: both"></div></div>
- %if 'description' in metadata:
+ %if 'description' in tool_metadata:
<div class="form-row"><label>Description:</label>
- ${metadata[ 'description' ]}
+ ${tool_metadata[ 'description' ]}
<div style="clear: both"></div></div>
%endif
- %if 'id' in metadata:
+ %if 'id' in tool_metadata:
<div class="form-row"><label>Id:</label>
- ${metadata[ 'id' ]}
+ ${tool_metadata[ 'id' ]}
<div style="clear: both"></div></div>
%endif
- %if 'guid' in metadata:
+ %if 'guid' in tool_metadata:
<div class="form-row"><label>Guid:</label>
- ${metadata[ 'guid' ]}
+ ${tool_metadata[ 'guid' ]}
<div style="clear: both"></div></div>
%endif
- %if 'version' in metadata:
+ %if 'version' in tool_metadata:
<div class="form-row"><label>Version:</label>
- ${metadata[ 'version' ]}
+ ${tool_metadata[ 'version' ]}
<div style="clear: both"></div></div>
%endif
- %if 'version_string_cmd' in metadata:
+ %if 'version_string_cmd' in tool_metadata:
<div class="form-row"><label>Version command string:</label>
- ${metadata[ 'version_string_cmd' ]}
+ ${tool_metadata[ 'version_string_cmd' ]}
<div style="clear: both"></div></div>
%endif
@@ -81,7 +84,7 @@
%for guid in tool_lineage:
<tr><td>
- %if guid == metadata[ 'guid' ]:
+ %if guid == tool_metadata[ 'guid' ]:
${guid} <b>(this tool)</b>
%else:
${guid}
@@ -100,8 +103,8 @@
</table></div><%
- if 'requirements' in metadata:
- requirements = metadata[ 'requirements' ]
+ if 'requirements' in tool_metadata:
+ requirements = tool_metadata[ 'requirements' ]
else:
requirements = None
%>
@@ -172,8 +175,8 @@
</table></div><%
- if 'tests' in metadata:
- tests = metadata[ 'tests' ]
+ if 'tests' in tool_metadata:
+ tests = tool_metadata[ 'tests' ]
else:
tests = None
%>
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/base_panels.mako
--- a/templates/base_panels.mako
+++ b/templates/base_panels.mako
@@ -48,7 +48,17 @@
<!--[if lt IE 7]>
${h.js( 'libs/IE/IE7', 'libs/IE/ie7-recalc' )}
<![endif]-->
- ${h.js( 'libs/jquery/jquery', 'libs/json2', 'libs/bootstrap', 'libs/underscore', 'libs/backbone/backbone', 'libs/backbone/backbone-relational', 'libs/handlebars.runtime', 'mvc/ui', 'galaxy.base' )}
+ ${h.js(
+ 'libs/jquery/jquery',
+ 'libs/json2',
+ 'libs/bootstrap',
+ 'libs/underscore',
+ 'libs/backbone/backbone',
+ 'libs/backbone/backbone-relational',
+ 'libs/handlebars.runtime',
+ 'mvc/ui',
+ 'galaxy.base'
+ )}
<script type="text/javascript">
// Set up needed paths.
var galaxy_paths = new GalaxyPaths({
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/library/common/browse_library_opt.mako
--- /dev/null
+++ b/templates/library/common/browse_library_opt.mako
@@ -0,0 +1,622 @@
+<%namespace file="/message.mako" import="render_msg" />
+<%namespace file="/library/common/library_item_info.mako" import="render_library_item_info" />
+<%namespace file="/library/common/common.mako" import="render_actions_on_multiple_items" />
+<%namespace file="/library/common/common.mako" import="render_compression_types_help" />
+<%namespace file="/library/common/common.mako" import="common_javascripts" />
+
+<%!
+ def inherit(context):
+ if context.get('use_panels'):
+ return '/webapps/galaxy/base_panels.mako'
+ else:
+ return '/base.mako'
+%>
+<%inherit file="${inherit(context)}"/>
+
+<%def name="init()">
+<%
+ self.has_left_panel=False
+ self.has_right_panel=False
+ self.message_box_visible=False
+ self.active_view="user"
+ self.overlay_visible=False
+ self.has_accessible_datasets = False
+%>
+</%def>
+
+##
+## Override methods from base.mako and base_panels.mako
+##
+<%def name="center_panel()">
+ <div style="overflow: auto; height: 100%;">
+ <div class="page-container" style="padding: 10px;">
+ ${render_content()}
+ </div>
+ </div>
+</%def>
+
+## Render the grid's basic elements. Each of these elements can be subclassed.
+<%def name="body()">
+ ${render_content()}
+</%def>
+
+<%def name="title()">Browse data library</%def>
+<%def name="stylesheets()">
+ ${parent.stylesheets()}
+ ${h.css( "library" )}
+</%def>
+
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ ${h.js("libs/json2")}
+ ${h.js("libs/jquery/jstorage")}
+ ${common_javascripts()}
+ ${self.grid_javascripts()}
+</%def>
+
+<%def name="grid_javascripts()">
+ <script type="text/javascript">
+ var init_libraries = function() {
+ var storage_id = "library-expand-state-${trans.security.encode_id(library.id)}";
+
+ var restore_folder_state = function() {
+ var state = $.jStorage.get(storage_id);
+ if (state) {
+ for (var id in state) {
+ if (state[id] === true) {
+ var row = $("#" + id),
+ index = row.parent().children().index(row);
+ row.addClass("expanded").show();
+ row.siblings().filter("tr[parent='" + index + "']").show();
+ }
+ }
+ }
+ };
+
+ var save_folder_state = function() {
+ var state = {};
+ $("tr.folderRow").each( function() {
+ var folder = $(this);
+ state[folder.attr("id")] = folder.hasClass("expanded");
+ });
+ $.jStorage.set(storage_id, state);
+ };
+
+ $("#library-grid").each(function() {
+ var child_of_parent_cache = {};
+ // Recursively fill in children and descendents of each row
+ var process_row = function(q, parents) {
+ // Find my index
+ var parent = q.parent(),
+ this_level = child_of_parent_cache[parent] || (child_of_parent_cache[parent] = parent.children());
+
+ var index = this_level.index(q);
+ // Find my immediate children
+ var children = $(par_child_dict[index]);
+ // Recursively handle them
+ var descendents = children;
+ children.each( function() {
+ child_descendents = process_row( $(this), parents.add(q) );
+ descendents = descendents.add(child_descendents);
+ });
+ // Set up expand / hide link
+ var expand_fn = function() {
+ if ( q.hasClass("expanded") ) {
+ descendents.hide();
+ descendents.removeClass("expanded");
+ q.removeClass("expanded");
+ } else {
+ children.show();
+ q.addClass("expanded");
+ }
+ save_folder_state();
+ };
+ $("." + q.attr("id") + "-click").click(expand_fn);
+ // Check/uncheck boxes in subfolders.
+ q.children("td").children("input[type=checkbox]").click( function() {
+ if ( $(this).is(":checked") ) {
+ descendents.find("input[type=checkbox]").attr("checked", true);
+ } else {
+ descendents.find("input[type=checkbox]").attr("checked", false);
+ // If you uncheck a lower level checkbox, uncheck the boxes above it
+ // (since deselecting a child means the parent is not fully selected any more).
+ parents.children("td").children("input[type=checkbox]").attr("checked", false);
+ }
+ });
+ // return descendents for use by parent
+ return descendents;
+ }
+
+ // Initialize dict[parent_id] = rows_which_have_that_parent_id_as_parent_attr
+ var par_child_dict = {},
+ no_parent = [];
+
+ $(this).find("tbody tr").each( function() {
+ if ( $(this).attr("parent")) {
+ var parent = $(this).attr("parent");
+ if (par_child_dict[parent] !== undefined) {
+ par_child_dict[parent].push(this);
+ } else {
+ par_child_dict[parent] = [this];
+ }
+ } else {
+ no_parent.push(this);
+ }
+ });
+
+ $(no_parent).each( function() {
+ descendents = process_row( $(this), $([]) );
+ descendents.hide();
+ });
+ });
+
+ restore_folder_state();
+ };
+ $(function() {
+ init_libraries();
+ });
+
+ // Looks for changes in dataset state using an async request. Keeps
+ // calling itself (via setTimeout) until all datasets are in a terminal
+ // state.
+ var updater = function ( tracked_datasets ) {
+ // Check if there are any items left to track
+ var empty = true;
+ for ( i in tracked_datasets ) {
+ empty = false;
+ break;
+ }
+ if ( ! empty ) {
+ setTimeout( function() { updater_callback( tracked_datasets ) }, 3000 );
+ }
+ };
+ var updater_callback = function ( tracked_datasets ) {
+ // Build request data
+ var ids = []
+ var states = []
+ $.each( tracked_datasets, function ( id, state ) {
+ ids.push( id );
+ states.push( state );
+ });
+ // Make ajax call
+ $.ajax( {
+ type: "POST",
+ url: "${h.url_for( controller='library_common', action='library_item_updates' )}",
+ dataType: "json",
+ data: { ids: ids.join( "," ), states: states.join( "," ) },
+ success : function ( data ) {
+ $.each( data, function( id, val ) {
+ // Replace HTML
+ var cell = $("#libraryItem-" + id).find("#libraryItemInfo");
+ cell.html( val.html );
+ // If new state was terminal, stop tracking
+ if (( val.state == "ok") || ( val.state == "error") || ( val.state == "empty") || ( val.state == "deleted" ) || ( val.state == "discarded" )) {
+ delete tracked_datasets[ parseInt(id) ];
+ } else {
+ tracked_datasets[ parseInt(id) ] = val.state;
+ }
+ });
+ updater( tracked_datasets );
+ },
+ error: function() {
+ // Just retry, like the old method, should try to be smarter
+ updater( tracked_datasets );
+ }
+ });
+ };
+ </script>
+</%def>
+
+<%def name="render_dataset( cntrller, ldda, library_dataset, can_modify, can_manage, selected, library, folder, pad, parent, row_counter, tracked_datasets, show_deleted=False, simple=False )">
+ <%
+ ## The received ldda must always be a LibraryDatasetDatasetAssociation object. The object id passed to methods
+ ## from the drop down menu should be the ldda id to prevent id collision ( which could happen when displaying
+ ## children, which are always lddas ). We also need to make sure we're displaying the latest version of this
+ ## library_dataset, so we display the attributes from the ldda.
+
+ from galaxy.web.controllers.library_common import branch_deleted
+
+ is_admin = trans.user_is_admin() and cntrller == 'library_admin'
+ current_version = ( ldda == library_dataset.library_dataset_dataset_association )
+ if current_version and ldda.state not in ( 'ok', 'error', 'empty', 'deleted', 'discarded' ):
+ tracked_datasets[ldda.id] = ldda.state
+ # SM: This causes a query to be emitted, but it quickly goes down a
+ # rabbit hole of many possible inheritable cases. It may not be
+ # possible to easily eliminate the extra query from this call.
+ info_association, inherited = ldda.get_info_association( restrict=True )
+ form_type = trans.model.FormDefinition.types.LIBRARY_INFO_TEMPLATE
+ %>
+ %if current_version and ( not ldda.library_dataset.deleted or show_deleted ):
+ <tr class="datasetRow"
+ %if parent is not None:
+ parent="${parent}"
+ %endif
+ id="libraryItem-${ldda.id}">
+ <td style="padding-left: ${pad+20}px;">
+ <input style="float: left;" type="checkbox" name="ldda_ids" id="${trans.security.encode_id( ldda.id )}" value="${trans.security.encode_id( ldda.id )}"
+ %if selected:
+ checked="checked"
+ %endif
+ />
+ %if simple:
+ <label for="${trans.security.encode_id( ldda.id )}">${ util.unicodify( ldda.name )}</label>
+ %else:
+ <div style="float: left; margin-left: 1px;" class="menubutton split popup" id="dataset-${ldda.id}-popup">
+ <a class="view-info" href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">
+ %if ldda.library_dataset.deleted:
+ <div class="libraryItem-error">${util.unicodify( ldda.name )}</div>
+ %else:
+ ${util.unicodify( ldda.name )}
+ %endif
+ </a>
+ </div>
+ %if not library.deleted:
+ <div popupmenu="dataset-${ldda.id}-popup">
+ %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and can_modify:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='ldda_edit_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit information</a>
+ <a class="action-button" href="${h.url_for( controller='library_common', action='move_library_item', cntrller=cntrller, item_type='ldda', item_id=trans.security.encode_id( ldda.id ), source_library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Move this dataset</a>
+ %else:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">View information</a>
+ %endif
+ %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and can_modify and not info_association:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='add_template', cntrller=cntrller, item_type='ldda', form_type=form_type, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), ldda_id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">Use template</a>
+ %endif
+ %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and can_modify and info_association:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='edit_template', cntrller=cntrller, item_type='ldda', form_type=form_type, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), ldda_id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit template</a>
+ <a class="action-button" href="${h.url_for( controller='library_common', action='delete_template', cntrller=cntrller, item_type='ldda', form_type=form_type, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), ldda_id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">Unuse template</a>
+ %endif
+ %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and can_manage:
+ %if not trans.app.security_agent.dataset_is_public( ldda.dataset ):
+ <a class="action-button" href="${h.url_for( controller='library_common', action='make_library_item_public', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_type='ldda', id=trans.security.encode_id( ldda.dataset.id ), use_panels=use_panels, show_deleted=show_deleted )}">Make public</a>
+ %endif
+ <a class="action-button" href="${h.url_for( controller='library_common', action='ldda_permissions', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit permissions</a>
+ %endif
+ %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and can_modify:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), replace_id=trans.security.encode_id( library_dataset.id ), show_deleted=show_deleted )}">Upload a new version of this dataset</a>
+ %endif
+ %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and ldda.has_data:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='import_datasets_to_histories', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), ldda_ids=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">Import this dataset into selected histories</a>
+ <a class="action-button" href="${h.url_for( controller='library_common', action='download_dataset_from_folder', cntrller=cntrller, id=trans.security.encode_id( ldda.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels )}">Download this dataset</a>
+ %endif
+ %if can_modify:
+ %if not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.deleted:
+ <a class="action-button" confirm="Click OK to delete dataset '${util.unicodify( ldda.name )}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Delete this dataset</a>
+ %elif not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.purged and ldda.library_dataset.deleted:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='undelete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Undelete this dataset</a>
+ %endif
+ %endif
+ </div>
+ %endif
+ %endif
+ </td>
+ % if not simple:
+ <td id="libraryItemInfo">${render_library_item_info( ldda )}</td>
+ <td>${ldda.extension}</td>
+ % endif
+ <td>${ldda.create_time.strftime( "%Y-%m-%d" )}</td>
+ <td>${ldda.get_size( nice_size=True )}</td>
+ </tr>
+ <%
+ my_row = row_counter.count
+ row_counter.increment()
+ %>
+ %endif
+</%def>
+
+<%def name="format_delta( tdelta )">
+ <%
+ from datetime import datetime
+ return "%d.%.6d" % ( tdelta.seconds, tdelta.microseconds )
+ %>
+</%def>
+
+<%def name="render_folder( cntrller, folder, folder_pad, created_ldda_ids, library, hidden_folder_ids, tracked_datasets, show_deleted=False, parent=None, row_counter=None, root_folder=False, simple=False )">
+ <%
+ from galaxy.web.controllers.library_common import active_folders, active_folders_and_library_datasets, activatable_folders_and_library_datasets, map_library_datasets_to_lddas, branch_deleted, datasets_for_lddas
+
+ # SM: DELETEME
+ from datetime import datetime, timedelta
+ import logging
+ log = logging.getLogger( __name__ )
+
+ is_admin = trans.user_is_admin() and cntrller == 'library_admin'
+ has_accessible_library_datasets = trans.app.security_agent.has_accessible_library_datasets( trans, folder, trans.user, current_user_roles, search_downward=False )
+
+ if root_folder:
+ pad = folder_pad
+ expander = h.url_for("/static/images/silk/resultset_bottom.png")
+ folder_img = h.url_for("/static/images/silk/folder_page.png")
+ else:
+ pad = folder_pad + 20
+ expander = h.url_for("/static/images/silk/resultset_next.png")
+ folder_img = h.url_for("/static/images/silk/folder.png")
+ # SM: If this is a comma-delimited list of LDDAs, then split them up
+ # into a list. For anything else, turn created_ldda_ids into a single
+ # item list.
+ if created_ldda_ids:
+ created_ldda_ids = util.listify( created_ldda_ids )
+ if str( folder.id ) in hidden_folder_ids:
+ return ""
+ my_row = None
+ if is_admin:
+ can_add = can_modify = can_manage = True
+ elif cntrller in [ 'library' ]:
+ can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, folder )
+ if not can_access:
+ can_show, folder_ids = \
+ trans.app.security_agent.show_library_item( trans.user,
+ current_user_roles,
+ folder,
+ [ trans.app.security_agent.permitted_actions.LIBRARY_ADD,
+ trans.app.security_agent.permitted_actions.LIBRARY_MODIFY,
+ trans.app.security_agent.permitted_actions.LIBRARY_MANAGE ] )
+ if not can_show:
+ return ""
+ can_add = trans.app.security_agent.can_add_library_item( current_user_roles, folder )
+ can_modify = trans.app.security_agent.can_modify_library_item( current_user_roles, folder )
+ can_manage = trans.app.security_agent.can_manage_library_item( current_user_roles, folder )
+ else:
+ can_add = can_modify = can_manage = False
+
+ form_type = trans.model.FormDefinition.types.LIBRARY_INFO_TEMPLATE
+ info_association, inherited = folder.get_info_association( restrict=True )
+ %>
+ %if not root_folder and ( not folder.deleted or show_deleted ):
+ <% encoded_id = trans.security.encode_id( folder.id ) %>
+ <tr id="folder-${encoded_id}" class="folderRow libraryOrFolderRow"
+ %if parent is not None:
+ parent="${parent}"
+ style="display: none;"
+ %endif
+ >
+ <td style="padding-left: ${folder_pad}px;">
+ <input type="checkbox" class="folderCheckbox"/>
+ <span class="expandLink folder-${encoded_id}-click">
+ <div style="float: left; margin-left: 2px;" class="menubutton split popup" id="folder_img-${folder.id}-popup">
+ <a class="folder-${encoded_id}-click" href="javascript:void(0);">
+ <span class="rowIcon"></span>
+ %if folder.deleted:
+ <div class="libraryItem-error">${folder.name}</div>
+ %else:
+ ${folder.name}
+ %endif
+ </a>
+ </div>
+ </span>
+ %if not library.deleted:
+ <div popupmenu="folder_img-${folder.id}-popup">
+ %if not branch_deleted( folder ) and can_add:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add datasets</a>
+ <a class="action-button" href="${h.url_for( controller='library_common', action='create_folder', cntrller=cntrller, parent_id=trans.security.encode_id( folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add sub-folder</a>
+ %endif
+ %if not branch_deleted( folder ):
+ %if has_accessible_library_datasets:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='import_datasets_to_histories', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Select datasets for import into selected histories</a>
+ %endif
+ %if can_modify:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='folder_info', cntrller=cntrller, id=trans.security.encode_id( folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit information</a>
+ <a class="action-button" href="${h.url_for( controller='library_common', action='move_library_item', cntrller=cntrller, item_type='folder', item_id=trans.security.encode_id( folder.id ), source_library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Move this folder</a>
+ %else:
+ <a class="action-button" class="view-info" href="${h.url_for( controller='library_common', action='folder_info', cntrller=cntrller, id=trans.security.encode_id( folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">View information</a>
+ %endif
+ %endif
+ %if not branch_deleted( folder ) and can_modify and not info_association:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='add_template', cntrller=cntrller, item_type='folder', form_type=form_type, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Use template</a>
+ %endif
+ %if not branch_deleted( folder ) and can_modify and info_association:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='edit_template', cntrller=cntrller, item_type='folder', form_type=form_type, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit template</a>
+ <a class="action-button" href="${h.url_for( controller='library_common', action='delete_template', cntrller=cntrller, item_type='folder', form_type=form_type, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Unuse template</a>
+ %endif
+ %if not branch_deleted( folder ) and can_manage:
+ %if not trans.app.security_agent.folder_is_public( folder ):
+ <a class="action-button" href="${h.url_for( controller='library_common', action='make_library_item_public', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_type='folder', id=trans.security.encode_id( folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Make public</a>
+ %endif
+ <a class="action-button" href="${h.url_for( controller='library_common', action='folder_permissions', cntrller=cntrller, id=trans.security.encode_id( folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit permissions</a>
+ %endif
+ %if can_modify:
+ %if not library.deleted and not folder.deleted:
+ <a class="action-button" confirm="Click OK to delete the folder '${folder.name}.'" href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Delete this folder</a>
+ %elif not library.deleted and folder.deleted and not folder.purged:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='undelete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Undelete this folder</a>
+ %endif
+ %endif
+ </div>
+ %endif
+ <td>
+ %if folder.description:
+ ${folder.description}
+ %endif
+ <td colspan="3"></td>
+ </tr>
+ <%
+ my_row = row_counter.count
+ row_counter.increment()
+ %>
+ %endif
+ <%
+ # TODO: If show_deleted is set to True, then nothing is displayed. Why? This wasn't the case
+ # in the past.
+ if show_deleted:
+ sub_folders, library_datasets = activatable_folders_and_library_datasets( trans, folder )
+ else:
+ sub_folders, library_datasets = active_folders_and_library_datasets( trans, folder )
+ # Render all the subfolders:
+ # TODO: Check permissions first.
+ for sub_folder in sub_folders:
+ render_folder( cntrller, sub_folder, pad, created_ldda_ids, library, [], tracked_datasets, show_deleted=show_deleted, parent=my_row, row_counter=row_counter, root_folder=False )
+
+ # Map LibraryDatasets to LDDAs, then map LDDAs to Datasets.
+ # Then determine which Datasets are accessible and which are not.
+ # For every LibraryDataset, if there's an LDDA for it and it's
+ # accessible then display it.
+ if ( len( library_datasets ) > 0 ):
+ lib_dataset_ldda_map = map_library_datasets_to_lddas( trans, library_datasets )
+ dataset_list = datasets_for_lddas( trans, lib_dataset_ldda_map.values() )
+ #can_access_datasets = trans.app.security_agent.dataset_access_mapping( trans, current_user_roles, dataset_list )
+ can_access_datasets = trans.app.security_agent.dataset_permission_map_for_access( trans, current_user_roles, dataset_list )
+ can_modify_datasets = trans.app.security_agent.item_permission_map_for_modify( trans, current_user_roles, dataset_list )
+ can_manage_datasets = trans.app.security_agent.item_permission_map_for_manage( trans, current_user_roles, dataset_list )
+ for library_dataset in library_datasets:
+ ldda = lib_dataset_ldda_map[ library_dataset.id ]
+ if ldda:
+ # SMTODO: Fix awkard modify/manage permission checks.
+ can_access = is_admin or can_access_datasets[ ldda.dataset_id ]
+ can_modify = is_admin or ( cntrller in ['library', 'requests'] and can_modify_datasets[ ldda.dataset_id ])
+ can_manage = is_admin or ( cntrller in ['library', 'requests'] and can_manage_datasets[ ldda.dataset_id ])
+ selected = created_ldda_ids and str( ldda.id ) in created_ldda_ids
+ if can_access:
+ render_dataset( cntrller, ldda, library_dataset, can_modify, can_manage, selected, library, folder, pad, my_row, row_counter, tracked_datasets, show_deleted=show_deleted )
+ %>
+</%def>
+
+<%def name="render_content(simple=False)">
+ <%
+ from galaxy import util
+ from galaxy.web.controllers.library_common import branch_deleted
+ from time import strftime
+ import logging
+ log = logging.getLogger( __name__ )
+
+ is_admin = trans.user_is_admin() and cntrller == 'library_admin'
+
+ if is_admin:
+ can_add = can_modify = can_manage = True
+ elif cntrller in [ 'library', 'requests' ]:
+ can_add = trans.app.security_agent.can_add_library_item( current_user_roles, library )
+ can_modify = trans.app.security_agent.can_modify_library_item( current_user_roles, library )
+ can_manage = trans.app.security_agent.can_manage_library_item( current_user_roles, library )
+ else:
+ can_add = can_modify = can_manage = False
+
+ info_association, inherited = library.get_info_association()
+ form_type = trans.model.FormDefinition.types.LIBRARY_INFO_TEMPLATE
+
+ # SM: These are mostly display-specific; ignore them for now.
+ # The has_accessible_folders determines if anything can be shown - use it.
+ self.has_accessible_datasets = trans.app.security_agent.has_accessible_library_datasets( trans, library.root_folder, trans.user, current_user_roles )
+ root_folder_has_accessible_library_datasets = trans.app.security_agent.has_accessible_library_datasets( trans, library.root_folder, trans.user, current_user_roles, search_downward=False )
+ has_accessible_folders = is_admin or trans.app.security_agent.has_accessible_folders( trans, library.root_folder, trans.user, current_user_roles )
+
+ tracked_datasets = {}
+
+ class RowCounter( object ):
+ def __init__( self ):
+ self.count = 0
+ def increment( self ):
+ self.count += 1
+ def __str__( self ):
+ return str( self.count )
+ %>
+
+ <h2>Data Library “${library.name}”</h2>
+
+ <ul class="manage-table-actions">
+ %if not library.deleted and ( is_admin or can_add ):
+ <li><a class="action-button" href="${h.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( library.root_folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add datasets</a></li>
+ <li><a class="action-button" href="${h.url_for( controller='library_common', action='create_folder', cntrller=cntrller, parent_id=trans.security.encode_id( library.root_folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add folder</a></li>
+ %endif
+ %if ( ( not library.deleted ) and ( can_modify or can_manage ) ) or ( can_modify and not library.purged ) or ( library.purged ):
+ <li><a class="action-button" id="library-${library.id}-popup" class="menubutton">Library Actions</a></li>
+ <div popupmenu="library-${library.id}-popup">
+ %if not library.deleted:
+ %if can_modify:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='library_info', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit information</a>
+ <a class="action-button" confirm="Click OK to delete the library named '${library.name}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library' )}">Delete this data library</a>
+ %if show_deleted:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=False )}">Hide deleted items</a>
+ %else:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=True )}">Show deleted items</a>
+ %endif
+ %endif
+ %if can_modify and not library.info_association:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='add_template', cntrller=cntrller, item_type='library', form_type=form_type, library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Use template</a>
+ %endif
+ %if can_modify and info_association:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='edit_template', cntrller=cntrller, item_type='library', form_type=form_type, library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit template</a>
+ <a class="action-button" href="${h.url_for( controller='library_common', action='delete_template', cntrller=cntrller, item_type='library', form_type=form_type, library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Unuse template</a>
+ %endif
+ %if can_manage:
+ %if not trans.app.security_agent.library_is_public( library, contents=True ):
+ <a class="action-button" href="${h.url_for( controller='library_common', action='make_library_item_public', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_type='library', id=trans.security.encode_id( library.id ), contents=True, use_panels=use_panels, show_deleted=show_deleted )}">Make public</a>
+ %endif
+ <a class="action-button" href="${h.url_for( controller='library_common', action='library_permissions', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit permissions</a>
+ %endif
+ %if root_folder_has_accessible_library_datasets:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='import_datasets_to_histories', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( library.root_folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Select datasets for import into selected histories</a>
+ %endif
+ %elif can_modify and not library.purged:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='undelete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library', use_panels=use_panels )}">Undelete this data library</a>
+ %elif library.purged:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">This data library has been purged</a>
+ %endif
+ </div>
+ %endif
+ </ul>
+
+ %if message:
+ ${render_msg( message, status )}
+ %endif
+
+ %if library.synopsis not in [ '', 'None', None ]:
+ <div class="libraryItemBody">
+ ${library.synopsis}
+ </div>
+ %endif
+
+ %if self.has_accessible_datasets:
+ <form name="act_on_multiple_datasets" action="${h.url_for( controller='library_common', action='act_on_multiple_datasets', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}" onSubmit="javascript:return checkForm();" method="post">
+ %endif
+ %if has_accessible_folders:
+ <table cellspacing="0" cellpadding="0" border="0" width="100%" class="grid" id="library-grid">
+ <thead>
+ <tr class="libraryTitle">
+ <th>
+ %if self.has_accessible_datasets:
+ <input type="checkbox" id="checkAll" name=select_all_datasets_checkbox value="true" onclick='checkAllFields(1);'/><input type="hidden" name=select_all_datasets_checkbox value="true"/>
+ %endif
+ Name
+ </th>
+ % if not simple:
+ <th>Message</th>
+ <th>Data type</th>
+ % endif
+ <th>Date uploaded</th>
+ <th>File size</th>
+ </tr>
+ </thead>
+ <% row_counter = RowCounter() %>
+ ## SM: Here is where we render the libraries based on admin/non-admin privileges:
+ %if cntrller in [ 'library', 'requests' ]:
+ ${self.render_folder( 'library', library.root_folder, 0, created_ldda_ids, library, hidden_folder_ids, tracked_datasets, show_deleted=show_deleted, parent=None, row_counter=row_counter, root_folder=True, simple=simple )}
+ ## SM: TODO: WTF?
+ %if not library.deleted and self.has_accessible_datasets and not simple:
+ ${render_actions_on_multiple_items()}
+ %endif
+ %elif ( trans.user_is_admin() and cntrller in [ 'library_admin', 'requests_admin' ] ):
+ ${self.render_folder( 'library_admin', library.root_folder, 0, created_ldda_ids, library, [], tracked_datasets, show_deleted=show_deleted, parent=None, row_counter=row_counter, root_folder=True )}
+ ## SM: TODO: WTF?
+ %if not library.deleted and not show_deleted and self.has_accessible_datasets:
+ ${render_actions_on_multiple_items()}
+ %endif
+ %endif
+ </table>
+ %endif
+ %if self.has_accessible_datasets:
+ </form>
+ %endif
+
+ %if tracked_datasets:
+ <script type="text/javascript">
+ // Updater
+ updater({${ ",".join( [ '"%s" : "%s"' % ( k, v ) for k, v in tracked_datasets.iteritems() ] ) }});
+ </script>
+ <!-- running: do not change this comment, used by TwillTestCase.library_wait -->
+ %endif
+
+ %if self.has_accessible_datasets and not simple:
+ ${render_compression_types_help( comptypes )}
+ %endif
+ %if not has_accessible_folders:
+ The data library '${library.name}' does not contain any datasets that you can access.
+ %endif
+</%def>
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/root/alternate_history.mako
--- a/templates/root/alternate_history.mako
+++ b/templates/root/alternate_history.mako
@@ -54,35 +54,18 @@
<%def name="get_urls_for_hda( hda, encoded_data_id, for_editing )"><%
from galaxy.datatypes.metadata import FileParameter
+ #print '\n', hda.name
data_dict = {}
def add_to_data( **kwargs ):
data_dict.update( kwargs )
- # download links (for both main hda and associated meta files)
- if hda.has_data():
- add_to_data( download_url=h.url_for( controller='/dataset', action='display',
- dataset_id=encoded_data_id, to_ext=hda.ext ) )
-
- download_meta_urls = {}
- for file_type in hda.metadata.spec.keys():
- if not isinstance( hda.metadata.spec[ file_type ].param, FileParameter ):
- continue
-
- download_meta_urls[ file_type ] = h.url_for( controller='/dataset', action='get_metadata_file',
- hda_id=encoded_data_id, metadata_name=file_type )
- if download_meta_urls:
- #TODO:?? needed? isn't this the same as download_url?
- add_to_data( download_dataset_url=h.url_for( controller='dataset', action='display',
- dataset_id=encoded_data_id, to_ext=hda.ext ) )
- add_to_data( download_meta_urls=download_meta_urls )
-
#TODO??: better way to do urls (move into js galaxy_paths (decorate) - _not_ dataset specific)
deleted = hda.deleted
purged = hda.purged
+ # ................................................................ link actions
#purged = purged or hda.dataset.purged //??
-
# all of these urls are 'datasets/data_id/<action>
if not ( dataset_purged or purged ) and for_editing:
add_to_data( undelete_url=h.url_for( controller='dataset', action='undelete', dataset_id=encoded_data_id ) )
@@ -93,6 +76,8 @@
if not hda.visible:
add_to_data( unhide_url=h.url_for( controller='dataset', action='unhide', dataset_id=encoded_data_id ) )
+
+ # ................................................................ title actions (display, edit, delete)
display_url = ''
if for_editing:
display_url = h.url_for( controller='dataset', action='display', dataset_id=encoded_data_id, preview=True, filename='' )
@@ -122,11 +107,29 @@
show_deleted_on_refresh=show_deleted
))
+ # ................................................................ primary actions (error, info, download)
+ # download links (hda and associated meta files)
+ if not hda.purged:
+ add_to_data( download_url=h.url_for( controller='/dataset', action='display',
+ dataset_id=encoded_data_id, to_ext=hda.ext ) )
+
+ meta_files = []
+ for k in hda.metadata.spec.keys():
+ if isinstance( hda.metadata.spec[ k ].param, FileParameter ):
+ file_type = k
+ download_url = h.url_for( controller='/dataset', action='get_metadata_file',
+ hda_id=encoded_data_id, metadata_name=file_type )
+ meta_files.append( dict( meta_file_type=file_type, meta_download_url=download_url ) )
+
+ if meta_files:
+ add_to_data( meta_files=meta_files )
+
# error report
if for_editing:
#NOTE: no state == 'error' check
add_to_data( report_error_url=h.url_for( h.url_for( controller='dataset', action='errors', id=encoded_data_id ) ) )
-
+
+ # visualizations
if hda.ext in app.datatypes_registry.get_available_tracks():
# do these need _localized_ dbkeys?
trackster_urls = {}
@@ -140,6 +143,48 @@
trackster_urls[ 'new-url' ] = h.url_for( controller='visualization', action='trackster', dataset_id=encoded_data_id, default_dbkey=hda.dbkey )
add_to_data( trackster_url=trackster_urls )
+ # display apps (igv, uscs)
+ display_types = []
+ display_apps = []
+ if( hda.state in [ 'ok', 'failed_metadata' ]
+ and hda.has_data() ):
+ #NOTE: these two data share structures
+ #TODO: this doesn't seem to get called with the hda I'm using. What would call this? How can I spoof it?
+ for display_type in hda.datatype.get_display_types():
+ display_label = hda.datatype.get_display_label( display_type )
+ target_frame, display_links = hda.datatype.get_display_links( hda, display_type, app, request.base )
+ if display_links:
+ display_links = []
+ for display_name, display_href in display_links:
+ display_type_link = dict(
+ target = target_frame,
+ href = display_href,
+ text = display_name
+ )
+ display_links.append( display_type_link )
+
+ # append the link list to the main map using the display_label
+ display_types.append( dict( label=display_label, links=display_links ) )
+
+ for display_app in hda.get_display_applications( trans ).itervalues():
+ app_links = []
+ for display_app_link in display_app.links.itervalues():
+ app_link = dict(
+ target = display_app_link.url.get( 'target_frame', '_blank' ),
+ href = display_app_link.get_display_url( hda, trans ),
+ text = _( display_app_link.name )
+ )
+ app_links.append( app_link )
+
+ display_apps.append( dict( label=display_app.name, links=app_links ) )
+
+ # attach the display types and apps (if any) to the hda obj
+ #if display_types: print 'display_types:', display_types
+ #if display_apps: print 'display_apps:', display_apps
+ add_to_data( display_types=display_types )
+ add_to_data( display_apps=display_apps )
+
+ # ................................................................ secondary actions (tagging, annotation)
if trans.user:
add_to_data( ajax_get_tag_url=( h.url_for(
controller='tag', action='get_tagging_elt_async',
@@ -154,34 +199,6 @@
add_to_data( ajax_set_annotation_url=( h.url_for(
controller='/dataset', action='annotate_async', id=encoded_data_id ) ) )
- display_type_display_links = {}
- #TODO: this doesn't seem to get called with the hda I'm using. What would call this? How can I spoof it?
- for display_app in hda.datatype.get_display_types():
- # print "display_app:", display_app
- target_frame, display_links = hda.datatype.get_display_links( hda, display_app, app, request.base )
- # print "target_frame:", target_frame, "display_links:", display_links
- #if len( display_links ) > 0:
- # display_type_display_links[ display_app ] = {}
- # for display_name, display_link in display_links:
- #NOTE!: localized name
- #<a target="${target_frame}" href="${display_link}">${_(display_name)}</a>
- #pass
- # <br />
-
- display_apps = {}
- for display_app in hda.get_display_applications( trans ).itervalues():
- display_app_dict = display_apps[ display_app.name ] = {}
- for link_app in display_app.links.itervalues():
- # print link_app.name, link_app.get_display_url( hda, trans )
- #NOTE!: localized name
- display_app_dict[ _( link_app.name ) ] = {
- 'url' : link_app.get_display_url( hda, trans ),
- 'target' : link_app.url.get( 'target_frame', '_blank' )
- }
- if display_apps:
- # print display_apps
- add_to_data( display_apps=display_apps )
-
return data_dict
%></%def>
@@ -302,10 +319,12 @@
"template-history-warning-messages",
"template-history-titleLink",
+ "template-history-failedMetadata",
"template-history-hdaSummary",
- "template-history-failedMetadata",
+ "template-history-downloadLinks",
"template-history-tagArea",
- "template-history-annotationArea"
+ "template-history-annotationArea",
+ "template-history-displayApps"
)}
## if using in-dom templates they need to go here (before the Backbone classes are defined)
@@ -339,15 +358,13 @@
createMockHistoryData();
return;
+ //TODO: handle empty history
} else if ( window.USE_CURR_DATA ){
if( console && console.debug ){ console.debug( '\t using current history data' ); }
glx_history = new History( pageData.history ).loadDatasetsAsHistoryItems( pageData.hdas );
glx_history_view = new HistoryView({ model: glx_history });
glx_history_view.render();
- hi = glx_history.items.at( 0 );
- hi_view = new HistoryItemView({ model: hi });
- $( 'body' ).append( hi_view.render() );
return;
}
@@ -409,3 +426,163 @@
</%def><body class="historyPage"></body>
+
+<script type="text/javascript">
+function createMockHistoryData(){
+ mockHistory = {};
+ mockHistory.data = {
+
+ template : {
+ id : 'a799d38679e985db',
+ name : 'template',
+ data_type : 'fastq',
+ file_size : 226297533,
+ genome_build : '?',
+ metadata_data_lines : 0,
+ metadata_dbkey : '?',
+ metadata_sequences : 0,
+ misc_blurb : '215.8 MB',
+ misc_info : 'uploaded fastq file (misc_info)',
+ model_class : 'HistoryDatasetAssociation',
+ download_url : '',
+ state : 'ok',
+ visible : true,
+ deleted : false,
+ purged : false,
+
+ hid : 0,
+ //TODO: move to history
+ for_editing : true,
+ //for_editing : false,
+
+ //?? not needed
+ //can_edit : true,
+ //can_edit : false,
+
+ accessible : true,
+
+ //TODO: move into model functions (build there (and cache?))
+ //!! be careful with adding these accrd. to permissions
+ //!! IOW, don't send them via template/API if the user doesn't have perms to use
+ //!! (even if they don't show up)
+ undelete_url : '',
+ purge_url : '',
+ unhide_url : '',
+
+ display_url : 'example.com/display',
+ edit_url : 'example.com/edit',
+ delete_url : 'example.com/delete',
+
+ show_params_url : 'example.com/show_params',
+ rerun_url : 'example.com/rerun',
+
+ retag_url : 'example.com/retag',
+ annotate_url : 'example.com/annotate',
+
+ peek : [
+ '<table cellspacing="0" cellpadding="3"><tr><th>1.QNAME</th><th>2.FLAG</th><th>3.RNAME</th><th>4.POS</th><th>5.MAPQ</th><th>6.CIGAR</th><th>7.MRNM</th><th>8.MPOS</th><th>9.ISIZE</th><th>10.SEQ</th><th>11.QUAL</th><th>12.OPT</th></tr>',
+ '<tr><td colspan="100%">@SQ SN:gi|87159884|ref|NC_007793.1| LN:2872769</td></tr>',
+ '<tr><td colspan="100%">@PG ID:bwa PN:bwa VN:0.5.9-r16</td></tr>',
+ '<tr><td colspan="100%">HWUSI-EAS664L:15:64HOJAAXX:1:1:13280:968 73 gi|87159884|ref|NC_007793.1| 2720169 37 101M = 2720169 0 NAATATGACATTATTTTCAAAACAGCTGAAAATTTAGACGTACCGATTTATCTACATCCCGCGCCAGTTAACAGTGACATTTATCAATCATACTATAAAGG !!!!!!!!!!$!!!$!!!!!$!!!!!!$!$!$$$!!$!!$!!!!!!!!!!!$!</td></tr>',
+ '<tr><td colspan="100%">!!!$!$!$$!!$$!!$!!!!!!!!!!!!!!!!!!!!!!!!!!$!!$!! XT:A:U NM:i:1 SM:i:37 AM:i:0 X0:i:1 X1:i:0 XM:i:1 XO:i:0 XG:i:0 MD:Z:0A100</td></tr>',
+ '<tr><td colspan="100%">HWUSI-EAS664L:15:64HOJAAXX:1:1:13280:968 133 gi|87159884|ref|NC_007793.1| 2720169 0 * = 2720169 0 NAAACTGTGGCTTCGTTNNNNNNNNNNNNNNNGTGANNNNNNNNNNNNNNNNNNNGNNNNNNNNNNNNNNNNNNNNCNAANNNNNNNNNNNNNNNNNNNNN !!!!!!!!!!!!$!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</td></tr>',
+ '<tr><td colspan="100%">!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</td></tr>',
+ '</table>'
+ ].join( '' )
+ }
+
+ };
+ _.extend( mockHistory.data, {
+
+ notAccessible :
+ _.extend( _.clone( mockHistory.data.template ),
+ { accessible : false }),
+
+ //deleted, purged, visible
+ deleted :
+ _.extend( _.clone( mockHistory.data.template ),
+ { deleted : true,
+ delete_url : '',
+ purge_url : 'example.com/purge',
+ undelete_url : 'example.com/undelete' }),
+ purgedNotDeleted :
+ _.extend( _.clone( mockHistory.data.template ),
+ { purged : true,
+ delete_url : '' }),
+ notvisible :
+ _.extend( _.clone( mockHistory.data.template ),
+ { visible : false,
+ unhide_url : 'example.com/unhide' }),
+
+ hasDisplayApps :
+ _.extend( _.clone( mockHistory.data.template ),
+ { display_apps : {
+ 'display in IGB' : {
+ Web: "/display_application/63cd3858d057a6d1/igb_bam/Web",
+ Local: "/display_application/63cd3858d057a6d1/igb_bam/Local"
+ }
+ }
+ }
+ ),
+ canTrackster :
+ _.extend( _.clone( mockHistory.data.template ),
+ { trackster_urls : {
+ 'data-url' : "example.com/trackster-data",
+ 'action-url' : "example.com/trackster-action",
+ 'new-url' : "example.com/trackster-new"
+ }
+ }
+ ),
+ zeroSize :
+ _.extend( _.clone( mockHistory.data.template ),
+ { file_size : 0 }),
+
+ hasMetafiles :
+ _.extend( _.clone( mockHistory.data.template ), {
+ download_meta_urls : {
+ 'bam_index' : "example.com/bam-index"
+ }
+ }),
+
+ //states
+ upload :
+ _.extend( _.clone( mockHistory.data.template ),
+ { state : HistoryItem.STATES.UPLOAD }),
+ queued :
+ _.extend( _.clone( mockHistory.data.template ),
+ { state : HistoryItem.STATES.QUEUED }),
+ running :
+ _.extend( _.clone( mockHistory.data.template ),
+ { state : HistoryItem.STATES.RUNNING }),
+ empty :
+ _.extend( _.clone( mockHistory.data.template ),
+ { state : HistoryItem.STATES.EMPTY }),
+ error :
+ _.extend( _.clone( mockHistory.data.template ),
+ { state : HistoryItem.STATES.ERROR,
+ report_error_url: 'example.com/report_err' }),
+ discarded :
+ _.extend( _.clone( mockHistory.data.template ),
+ { state : HistoryItem.STATES.DISCARDED }),
+ setting_metadata :
+ _.extend( _.clone( mockHistory.data.template ),
+ { state : HistoryItem.STATES.SETTING_METADATA }),
+ failed_metadata :
+ _.extend( _.clone( mockHistory.data.template ),
+ { state : HistoryItem.STATES.FAILED_METADATA })
+/*
+*/
+ });
+
+ //mockHistory.views.deleted.logger = console;
+ mockHistory.items = {};
+ mockHistory.views = {};
+ for( key in mockHistory.data ){
+ mockHistory.items[ key ] = new HistoryItem( mockHistory.data[ key ] );
+ mockHistory.items[ key ].set( 'name', key );
+ mockHistory.views[ key ] = new HistoryItemView({ model : mockHistory.items[ key ] });
+ //console.debug( 'view: ', mockHistory.views[ key ] );
+ $( 'body' ).append( mockHistory.views[ key ].render() );
+ }
+}
+</script>
\ No newline at end of file
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/root/index.mako
--- a/templates/root/index.mako
+++ b/templates/root/index.mako
@@ -70,115 +70,6 @@
}
});
- var menu_options = {}; // Holds dictionary of { label: toggle_fn }
-
- SHOW_TOOL = "${_("Show Tool Search")}";
- HIDE_TOOL = "${_("Hide Tool Search")}";
- SHOW_RECENT = "${_("Show Recently Used")}";
- HIDE_RECENT = "${_("Hide Recently Used")}";
-
- var toggle_tool_search_fn = function() {
- // Show/hide menu and update vars, user preferences.
- var menu = $("#galaxy_tools").contents().find('#tool-search'),
- pref_value, menu_option_text, old_text;
- if (menu.is(":visible")) {
- // Hide menu.
- pref_value = "False";
- menu_option_text = SHOW_TOOL;
- old_text = HIDE_TOOL;
-
- // Reset search.
- reset_tool_search(true);
- } else {
- // Show menu.
- pref_value = "True";
- menu_option_text = HIDE_TOOL;
- old_text = SHOW_TOOL;
- }
- menu.toggle();
-
- // Update menu option.
- delete menu_options[old_text];
-
- var new_menu_options = {};
- // Because we always want tool menu to be the first link in the dropdown,
- // we re-create the menu_options dictionary by creating a new
- // dict and then appending the old dict to it
- new_menu_options[menu_option_text] = toggle_tool_search_fn;
- menu_options = $.extend( new_menu_options, menu_options );
- make_popupmenu( $("#tools-options-button"), menu_options );
- galaxy_async.set_user_pref("show_tool_search", pref_value);
- };
-
- var toggle_recently_used_fn = function() {
- // Show/hide menu.
- var ru_menu = $('#galaxy_tools').contents().find('#recently_used_wrapper'),
- ru_menu_body = ru_menu.find(".toolSectionBody"),
- pref_value, old_text, menu_option_text;
- if (ru_menu.hasClass("user_pref_visible")) {
- // Hide menu.
- ru_menu_body.slideUp();
- ru_menu.slideUp();
-
- // Set vars used below and in tool menu frame.
- pref_value = "False";
- old_text = HIDE_RECENT;
- menu_option_text = SHOW_RECENT;
- } else {
- // "Show" menu.
- if (!$('#galaxy_tools').contents().find('#tool-search-query').hasClass("search_active")) {
- // Default.
- ru_menu.slideDown();
- } else {
- // Search active: tf there are matching tools in RU menu, show menu.
- if ( ru_menu.find(".toolTitle.search_match").length !== 0 ) {
- ru_menu.slideDown();
- ru_menu_body.slideDown();
- }
- }
- // Set vars used below and in tool menu frame.
- pref_value = "True";
- old_text = SHOW_RECENT;
- menu_option_text = HIDE_RECENT;
- }
-
- // Update menu class and option.
- ru_menu.toggleClass("user_pref_hidden user_pref_visible");
- delete menu_options[old_text];
- menu_options[menu_option_text] = toggle_recently_used_fn;
- make_popupmenu( $("#tools-options-button"), menu_options );
- galaxy_async.set_user_pref("show_recently_used_menu", pref_value);
- };
-
- // Init tool options.
- ## Search tools menu item.
- %if trans.app.toolbox_search.enabled:
- <%
- show_tool_search = True
- if trans.user:
- show_tool_search = trans.user.preferences.get( "show_tool_search", "False" ) == "True"
-
- if show_tool_search:
- action = "HIDE_TOOL"
- else:
- action = "SHOW_TOOL"
- %>
- menu_options[ ${action} ] = toggle_tool_search_fn;
- %endif
- ## Recently used tools menu.
- %if trans.user:
- <%
- if trans.user.preferences.get( 'show_recently_used_menu', 'False' ) == 'True':
- action = "HIDE_RECENT"
- else:
- action = "SHOW_RECENT"
- %>
- // TODO: make compatible with new tool menu.
- //menu_options[ ${action} ] = toggle_recently_used_fn;
- %endif
-
-
- make_popupmenu( $("#tools-options-button"), menu_options );
});
</script></%def>
@@ -202,9 +93,9 @@
<%def name="left_panel()"><div class="unified-panel-header" unselectable="on"><div class='unified-panel-header-inner'>
- <div style="float: right">
- <a class='panel-header-button' id="tools-options-button" href="#"><span class="ficon large cog"></span></a>
- </div>
+ ## <div style="float: right">
+ ## <a class='panel-header-button' id="tools-options-button" href="#"><span class="ficon large cog"></span></a>
+ ## </div>
${n_('Tools')}
</div></div>
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/root/tool_menu.mako
--- a/templates/root/tool_menu.mako
+++ b/templates/root/tool_menu.mako
@@ -19,12 +19,7 @@
${h.templates( "tool_link", "panel_section", "tool_search" )}
${h.js( "libs/require", "galaxy.autocom_tagging" )}
- <%
- # Set up for creating tool panel.
- tool_search_hidden = "false"
- if trans.user and trans.user.preferences.get( "show_tool_search", "False" ) == "False":
- tool_search_hidden = "true"
-
+ <%
dictified_panel = trans.app.toolbox.to_dict( trans )
%>
@@ -42,11 +37,13 @@
// Init. on document load.
var tool_panel, tool_panel_view, tool_search;
$(function() {
+
// Set up search.
- tool_search = new tools.ToolSearch(
- { spinner_url: "${h.url_for('/static/images/loading_small_white_bg.gif')}",
- search_url: "${h.url_for( controller='root', action='tool_search' )}",
- hidden: ${tool_search_hidden} } );
+ tool_search = new tools.ToolSearch({
+ spinner_url: "${h.url_for('/static/images/loading_small_white_bg.gif')}",
+ search_url: "${h.url_for( controller='root', action='tool_search' )}",
+ hidden: false
+ });
// Set up tool panel.
tool_panel = new tools.ToolPanel( { tool_search: tool_search } );
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/tracks/browser.mako
--- a/templates/tracks/browser.mako
+++ b/templates/tracks/browser.mako
@@ -48,8 +48,10 @@
${render_trackster_js_vars()}
- var view,
- browser_router,
+ // FIXME: deliberate global required for now due to requireJS integration.
+ view = null;
+
+ var browser_router,
ui = new (trackster_ui.TracksterUI)( "${h.url_for('/')}" );
/**
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/user/info.mako
--- a/templates/user/info.mako
+++ b/templates/user/info.mako
@@ -59,16 +59,16 @@
%if not is_admin:
<div class="form-row"><label>Current password:</label>
- <input type="password" name="current" value="${current}" size="40"/>
+ <input type="password" name="current" value="" size="40"/></div>
%endif
<div class="form-row"><label>New password:</label>
- <input type="password" name="password" value="${password}" size="40"/>
+ <input type="password" name="password" value="" size="40"/></div><div class="form-row"><label>Confirm:</label>
- <input type="password" name="confirm" value="${confirm}" size="40"/>
+ <input type="password" name="confirm" value="" size="40"/></div><div class="form-row"><input type="submit" name="change_password_button" value="Save"/>
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/user/login.mako
--- a/templates/user/login.mako
+++ b/templates/user/login.mako
@@ -30,7 +30,7 @@
%if redirect_url:
<script type="text/javascript">
- top.location.href = '${redirect_url}';
+ top.location.href = '${redirect_url | h}';
</script>
%endif
@@ -81,9 +81,9 @@
<form name="login" id="login" action="${form_action}" method="post" ><div class="form-row"><label>Email address:</label>
- <input type="text" name="email" value="${email}" size="40"/>
- <input type="hidden" name="webapp" value="${webapp}" size="40"/>
- <input type="hidden" name="redirect" value="${redirect}" size="40"/>
+ <input type="text" name="email" value="${email | h}" size="40"/>
+ <input type="hidden" name="webapp" value="${webapp | h}" size="40"/>
+ <input type="hidden" name="redirect" value="${redirect | h}" size="40"/></div><div class="form-row"><label>Password:</label>
@@ -107,8 +107,8 @@
<div class="form-row"><label>OpenID URL:</label><input type="text" name="openid_url" size="60" style="background-image:url('${h.url_for( '/static/images/openid-16x16.gif' )}' ); background-repeat: no-repeat; padding-right: 20px; background-position: 99% 50%;"/>
- <input type="hidden" name="webapp" value="${webapp}" size="40"/>
- <input type="hidden" name="redirect" value="${redirect}" size="40"/>
+ <input type="hidden" name="webapp" value="${webapp | h}" size="40"/>
+ <input type="hidden" name="redirect" value="${redirect | h}" size="40"/></div><div class="form-row">
Or, authenticate with your <select name="openid_provider">
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/user/register.mako
--- a/templates/user/register.mako
+++ b/templates/user/register.mako
@@ -3,7 +3,7 @@
%if redirect_url:
<script type="text/javascript">
- top.location.href = '${redirect_url}';
+ top.location.href = '${redirect_url | h}';
</script>
%endif
@@ -42,21 +42,21 @@
<div class="toolFormTitle">Create account</div><div class="form-row"><label>Email address:</label>
- <input type="text" name="email" value="${email}" size="40"/>
- <input type="hidden" name="webapp" value="${webapp}" size="40"/>
- <input type="hidden" name="redirect" value="${redirect}" size="40"/>
+ <input type="text" name="email" value="${email | h}" size="40"/>
+ <input type="hidden" name="webapp" value="${webapp | h}" size="40"/>
+ <input type="hidden" name="redirect" value="${redirect | h}" size="40"/></div><div class="form-row"><label>Password:</label>
- <input type="password" name="password" value="${password}" size="40"/>
+ <input type="password" name="password" value="" size="40"/></div><div class="form-row"><label>Confirm password:</label>
- <input type="password" name="confirm" value="${confirm}" size="40"/>
+ <input type="password" name="confirm" value="" size="40"/></div><div class="form-row"><label>Public name:</label>
- <input type="text" name="username" size="40" value="${username}"/>
+ <input type="text" name="username" size="40" value="${username |h}"/>
%if webapp == 'galaxy':
<div class="toolParamHelp" style="clear: both;">
Your public name is an identifier that will be used to generate addresses for information
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/visualization/circster.mako
--- a/templates/visualization/circster.mako
+++ b/templates/visualization/circster.mako
@@ -11,6 +11,11 @@
<%def name="stylesheets()">
${parent.stylesheets()}
+ <style>
+ text {
+ font-size: 10px;
+ }
+ </style></%def><%def name="javascripts()">
@@ -23,6 +28,7 @@
require.config({
baseUrl: "${h.url_for('/static/scripts')}",
shim: {
+ "libs/underscore": { exports: "_" },
"libs/d3": { exports: "d3" }
}
});
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/visualization/display.mako
--- a/templates/visualization/display.mako
+++ b/templates/visualization/display.mako
@@ -54,44 +54,59 @@
<div id="${trans.security.encode_id( visualization.id )}" class="unified-panel-body" style="overflow:none;top:0px;"></div><script type="text/javascript">
- // TODO: much of this code is copied from browser.mako -- create shared base and use in both places.
- ${render_trackster_js_vars()}
+ require.config({
+ baseUrl: "${h.url_for('/static/scripts') }",
+ shim: {
+ "libs/underscore": { exports: "_" },
+ "libs/backbone/backbone": { exports: "Backbone" },
+ "libs/backbone/backbone-relational": ["libs/backbone/backbone"]
+ }
+ });
+ require( ["base", "viz/visualization", "viz/trackster_ui", "viz/trackster/tracks"],
+ function( base, visualization, trackster_ui, tracks ) {
- var view,
- container_element = $("#${trans.security.encode_id( visualization.id )}");
-
- $(function() {
- var is_embedded = (container_element.parents(".item-content").length > 0);
+ ${render_trackster_js_vars()}
+
+ // FIXME: deliberate global required for now due to requireJS integration.
+ view = null;
+
+ var ui = new (trackster_ui.TracksterUI)( "${h.url_for('/')}" )
+ container_element = $("#${trans.security.encode_id( visualization.id )}");
- // HTML setup.
- if (is_embedded) {
- container_element.css( { "position": "relative" } );
- } else { // Viewing just one shared viz
- $("#right-border").click(function() { view.resize_window(); });
- }
-
- // Create visualization.
- var callback;
- %if 'viewport' in config:
- var callback = function() { view.change_chrom( '${config['viewport']['chrom']}', ${config['viewport']['start']}, ${config['viewport']['end']} ); }
- %endif
- view = create_visualization( {
- container: container_element,
- name: "${config.get('title') | h}",
- vis_id: "${config.get('vis_id')}",
- dbkey: "${config.get('dbkey')}"
- },
- JSON.parse('${ h.to_json_string( config.get( 'viewport', dict() ) ) }'),
- JSON.parse('${ h.to_json_string( config['tracks'] ).replace("'", "\\'") }'),
- JSON.parse('${ h.to_json_string( config.get('bookmarks') ) }')
- );
-
- // Set up keyboard navigation.
- init_keyboard_nav(view);
-
- // HACK: set viewport height because it cannot be set automatically. Currently, max height for embedded
- // elts is 25em, so use 20em.
- view.viewport_container.height("20em");
+ $(function() {
+ var is_embedded = (container_element.parents(".item-content").length > 0);
+
+ // HTML setup.
+ if (is_embedded) {
+ container_element.css( { "position": "relative" } );
+ } else { // Viewing just one shared viz
+ $("#right-border").click(function() { view.resize_window(); });
+ }
+
+ // Create visualization.
+ var callback;
+ %if 'viewport' in config:
+ var callback = function() { view.change_chrom( '${config['viewport']['chrom']}', ${config['viewport']['start']}, ${config['viewport']['end']} ); }
+ %endif
+ view = ui.create_visualization( {
+ container: container_element,
+ name: "${config.get('title') | h}",
+ vis_id: "${config.get('vis_id')}",
+ dbkey: "${config.get('dbkey')}"
+ },
+ JSON.parse('${ h.to_json_string( config.get( 'viewport', dict() ) ) }'),
+ JSON.parse('${ h.to_json_string( config['tracks'] ).replace("'", "\\'") }'),
+ JSON.parse('${ h.to_json_string( config.get('bookmarks') ) }')
+ );
+
+ // Set up keyboard navigation.
+ ui.init_keyboard_nav(view);
+
+ // HACK: set viewport height because it cannot be set automatically. Currently, max height for embedded
+ // elts is 25em, so use 20em.
+ view.viewport_container.height("20em");
+ });
+
});
</script>
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/visualization/scatterplot.mako
--- a/templates/visualization/scatterplot.mako
+++ b/templates/visualization/scatterplot.mako
@@ -6,25 +6,25 @@
<style type="text/css">
.title {
- margin: 0px;
+ margin: 0px;
padding: 8px;
background-color: #ebd9b2;
border: 2px solid #ebd9b2;
}
.subtitle {
- margin: 0px;
+ margin: 0px;
padding: 0px 8px 8px 16px;
background-color: #ebd9b2;
- color: white;
- font-size: small;
+ color: white;
+ font-size: small;
}
#chart-settings-form {
/*from width + margin of chart?*/
- float: right;
+ float: right;
width: 100%;
- margin: 0px;
+ margin: 0px;
padding-top: 1em;
}
@@ -45,80 +45,67 @@
overflow: auto;
}
-.clear {
- clear: both;
- margin: 0px;
-}
-
-
-svg .chart {
- /*shape-rendering: crispEdges;*/
-}
-
svg .grid-line {
- fill: none;
- stroke: lightgrey;
- stroke-opacity: 0.5;
- shape-rendering: crispEdges;
- stroke-dasharray: 3, 3;
+ fill: none;
+ stroke: lightgrey;
+ stroke-opacity: 0.5;
+ shape-rendering: crispEdges;
+ stroke-dasharray: 3, 3;
}
svg .axis path, svg .axis line {
- fill: none;
- stroke: black;
- shape-rendering: crispEdges;
+ fill: none;
+ stroke: black;
+ shape-rendering: crispEdges;
}
svg .axis text {
- font-family: sans-serif;
- font-size: 12px;
+ font-family: sans-serif;
+ font-size: 12px;
}
-
svg .glyph {
- stroke: none;
- fill: black;
- fill-opacity: 0.2;
+ stroke: none;
+ fill: black;
+ fill-opacity: 0.2;
}
-
+
</style>
-
+
</%def><%def name="javascripts()">
${parent.javascripts()}
-${h.js(
- "libs/underscore",
- "libs/backbone/backbone",
- "libs/backbone/backbone-relational",
- "libs/d3",
- "mvc/base-mvc",
- "viz/scatterplot"
-)}
+${h.js( "libs/require" )}
<script type="text/javascript">
-$(function() {
- var hda = ${h.to_json_string( hda )},
- historyID = '${historyID}'
- apiDatasetsURL = "${h.url_for( controller='/api/datasets' )}";
- //?? hmmmm
- //kwargs = ${h.to_json_string( kwargs )};
-
- var settingsForm = new ScatterplotView({
- dataset : hda,
- el : $( '#chart-settings-form' ),
+require.config({ baseUrl : "${h.url_for( '/static/scripts' )}", });
+
+require([ "viz/scatterplot" ], function( scatterplot ){
+
+ var hda = ${h.to_json_string( hda )},
+ historyID = '${historyID}'
+ apiDatasetsURL = "${h.url_for( controller='/api/datasets' )}";
+ //?? hmmmm
+ //kwargs = ${h.to_json_string( kwargs )};
+
+ var settingsForm = new scatterplot.ScatterplotView({
+ dataset : hda,
+ el : $( '#chart-settings-form' ),
apiDatasetsURL : apiDatasetsURL,
+
chartConfig : {
containerSelector : '#chart-holder',
marginTop : 20,
}
- }).render();
+ }).render();
});
+
</script></%def><%def name="body()"><h2 class="title">Scatterplot of '${hda['name']}'</h2><p class="subtitle">${hda['misc_info']}</p>
- <div id="chart-holder"></div>
- <div id="chart-settings-form"></div>
+ <div id="chart-holder"></div>
+ <div id="chart-settings-form"></div></%def>
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/webapps/community/common/common.mako
--- a/templates/webapps/community/common/common.mako
+++ b/templates/webapps/community/common/common.mako
@@ -13,3 +13,46 @@
%>
${html}
</%def>
+
+<%def name="render_readme( readme_text )">
+ <style type="text/css">
+ #readme_table{ table-layout:fixed;
+ width:100%;
+ overflow-wrap:normal;
+ overflow:hidden;
+ border:0px;
+ word-break:keep-all;
+ word-wrap:break-word;
+ line-break:strict; }
+ </style>
+ <div class="toolForm">
+ <div class="toolFormTitle">Repository README file (may contain important installation or license information)</div>
+ <div class="toolFormBody">
+ <div class="form-row">
+ <table id="readme_table">
+ <tr><td>${readme_text}</td></tr>
+ </table>
+ </div>
+ </div>
+ </div>
+</%def>
+
+<%def name="render_long_description( description_text )">
+ <style type="text/css">
+ #description_table{ table-layout:fixed;
+ width:100%;
+ overflow-wrap:normal;
+ overflow:hidden;
+ border:0px;
+ word-break:keep-all;
+ word-wrap:break-word;
+ line-break:strict; }
+ </style>
+ <div class="form-row">
+ <label>Detailed description:</label>
+ <table id="description_table">
+ <tr><td><pre>${description_text}</pre></td></tr>
+ </table>
+ <div style="clear: both"></div>
+ </div>
+</%def>
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/webapps/community/common/view_readme.mako
--- /dev/null
+++ b/templates/webapps/community/common/view_readme.mako
@@ -0,0 +1,88 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+<%namespace file="/webapps/community/common/common.mako" import="render_readme" />
+
+<%
+ if webapp == 'community':
+ is_admin = trans.user_is_admin()
+ is_new = repository.is_new
+ can_contact_owner = trans.user and trans.user != repository.user
+ can_push = trans.app.security_agent.can_push( trans.user, repository )
+ can_rate = not is_new and trans.user and repository.user != trans.user
+ can_upload = can_push
+ can_download = not is_new and ( not is_malicious or can_push )
+ can_browse_contents = not is_new
+ can_view_change_log = not is_new
+ can_manage = is_admin or repository.user == trans.user
+ if can_push:
+ browse_label = 'Browse or delete repository tip files'
+ else:
+ browse_label = 'Browse repository tip files'
+%>
+
+<br/><br/>
+<ul class="manage-table-actions">
+ %if webapp == 'community':
+ <li><a class="action-button" id="repository-${repository.id}-popup" class="menubutton">Repository Actions</a></li>
+ <div popupmenu="repository-${repository.id}-popup">
+ %if can_manage:
+ <a class="action-button" href="${h.url_for( controller='repository', action='manage_repository', id=trans.app.security.encode_id( repository.id ), changeset_revision=repository.tip )}">Manage repository</a>
+ %else:
+ <a class="action-button" href="${h.url_for( controller='repository', action='view_repository', id=trans.app.security.encode_id( repository.id ), changeset_revision=repository.tip, webapp='community' )}">View repository</a>
+ %endif
+ %if can_upload:
+ <a class="action-button" href="${h.url_for( controller='upload', action='upload', repository_id=trans.security.encode_id( repository.id ), webapp=webapp )}">Upload files to repository</a>
+ %endif
+ %if can_view_change_log:
+ <a class="action-button" href="${h.url_for( controller='repository', action='view_changelog', id=trans.app.security.encode_id( repository.id ), webapp=webapp )}">View change log</a>
+ %endif
+ %if can_rate:
+ <a class="action-button" href="${h.url_for( controller='repository', action='rate_repository', id=trans.app.security.encode_id( repository.id ), webapp=webapp )}">Rate repository</a>
+ %endif
+ %if can_browse_contents:
+ <a class="action-button" href="${h.url_for( controller='repository', action='browse_repository', id=trans.app.security.encode_id( repository.id ), webapp=webapp )}">${browse_label}</a>
+ %endif
+ %if can_contact_owner:
+ <a class="action-button" href="${h.url_for( controller='repository', action='contact_owner', id=trans.security.encode_id( repository.id ), webapp=webapp )}">Contact repository owner</a>
+ %endif
+ %if can_download:
+ <a class="action-button" href="${h.url_for( controller='repository', action='download', repository_id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision, file_type='gz', webapp=webapp )}">Download as a .tar.gz file</a>
+ <a class="action-button" href="${h.url_for( controller='repository', action='download', repository_id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision, file_type='bz2', webapp=webapp )}">Download as a .tar.bz2 file</a>
+ <a class="action-button" href="${h.url_for( controller='repository', action='download', repository_id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision, file_type='zip', webapp=webapp )}">Download as a zip file</a>
+ %endif
+ </div>
+ %else:
+ %if cntrller=='repository':
+ <li><a class="action-button" href="${h.url_for( controller='repository', action='browse_valid_repositories', operation='preview_tools_in_changeset', id=trans.security.encode_id( repository.id ), webapp=webapp )}">Preview tools for install</a></li>
+ <li><a class="action-button" id="repository-${repository.id}-popup" class="menubutton">Tool Shed Actions</a></li>
+ <div popupmenu="repository-${repository.id}-popup">
+ <a class="action-button" href="${h.url_for( controller='repository', action='browse_valid_categories', webapp=webapp )}">Browse valid repositories</a>
+ <a class="action-button" href="${h.url_for( controller='repository', action='find_tools', webapp=webapp )}">Search for valid tools</a>
+ <a class="action-button" href="${h.url_for( controller='repository', action='find_workflows', webapp=webapp )}">Search for workflows</a>
+ </div>
+ %else:
+ <li><a class="action-button" id="repository-${repository.id}-popup" class="menubutton">Repository Actions</a></li>
+ <div popupmenu="repository-${repository.id}-popup">
+ <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='manage_repository', id=trans.security.encode_id( repository.id ) )}">Manage repository</a>
+ <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='browse_repository', id=trans.security.encode_id( repository.id ) )}">Browse repository files</a>
+ <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='check_for_updates', id=trans.security.encode_id( repository.id ) )}">Get repository updates</a>
+ %if repository.includes_tools:
+ <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='set_tool_versions', id=trans.security.encode_id( repository.id ) )}">Set tool versions</a>
+ %endif
+ %if repository.tool_dependencies:
+ <% tool_dependency_ids = [ trans.security.encode_id( td.id ) for td in repository.tool_dependencies ] %>
+ <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='manage_tool_dependencies', tool_dependency_ids=tool_dependency_ids )}">Manage tool dependencies</a>
+ %endif
+ <a class="action-button" href="${h.url_for( controller='admin_toolshed', action='deactivate_or_uninstall_repository', id=trans.security.encode_id( repository.id ) )}">Deactivate or uninstall repository</a>
+ </div>
+ %endif
+ %endif
+</ul>
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+%if readme_text:
+ ${render_readme( readme_text )}
+%endif
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/webapps/community/repository/browse_repository.mako
--- a/templates/webapps/community/repository/browse_repository.mako
+++ b/templates/webapps/community/repository/browse_repository.mako
@@ -15,6 +15,7 @@
can_rate = trans.user and repository.user != trans.user
can_manage = is_admin or repository.user == trans.user
can_view_change_log = not is_new
+ has_readme = metadata and 'readme' in metadata
%><%!
@@ -76,6 +77,9 @@
%if can_upload:
<a class="action-button" href="${h.url_for( controller='upload', action='upload', repository_id=trans.security.encode_id( repository.id ), webapp='community' )}">Upload files to repository</a>
%endif
+ %if has_readme:
+ <a class="action-button" href="${h.url_for( controller='repository', action='view_readme', id=trans.app.security.encode_id( repository.id ), changeset_revision=repository.tip, webapp='community' )}">View README</a>
+ %endif
%if can_view_change_log:
<a class="action-button" href="${h.url_for( controller='repository', action='view_changelog', id=trans.app.security.encode_id( repository.id ), webapp='community' )}">View change log</a>
%endif
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/webapps/community/repository/contact_owner.mako
--- a/templates/webapps/community/repository/contact_owner.mako
+++ b/templates/webapps/community/repository/contact_owner.mako
@@ -16,6 +16,7 @@
browse_label = 'Browse or delete repository tip files'
else:
browse_label = 'Browse repository tip files'
+ has_readme = metadata and 'readme' in metadata
%><%!
@@ -42,6 +43,9 @@
%if can_upload:
<a class="action-button" href="${h.url_for( controller='upload', action='upload', repository_id=trans.security.encode_id( repository.id ), webapp='community' )}">Upload files to repository</a>
%endif
+ %if has_readme:
+ <a class="action-button" href="${h.url_for( controller='repository', action='view_readme', id=trans.app.security.encode_id( repository.id ), changeset_revision=repository.tip, webapp='community' )}">View README</a>
+ %endif
%if can_view_change_log:
<a class="action-button" href="${h.url_for( controller='repository', action='view_changelog', id=trans.app.security.encode_id( repository.id ), webapp='community' )}">View change log</a>
%endif
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/webapps/community/repository/manage_repository.mako
--- a/templates/webapps/community/repository/manage_repository.mako
+++ b/templates/webapps/community/repository/manage_repository.mako
@@ -21,6 +21,7 @@
browse_label = 'Browse repository tip files'
can_set_malicious = metadata and can_set_metadata and is_admin and changeset_revision == repository.tip
can_reset_all_metadata = is_admin and len( repo ) > 0
+ has_readme = metadata and 'readme' in metadata
%><%!
@@ -32,35 +33,6 @@
%><%inherit file="${inherit(context)}"/>
-<%def name="stylesheets()">
- ${parent.stylesheets()}
- ${h.css( "jquery.rating" )}
- <style type="text/css">
- ul.fileBrowser,
- ul.toolFile {
- margin-left: 0;
- padding-left: 0;
- list-style: none;
- }
- ul.fileBrowser {
- margin-left: 20px;
- }
- .fileBrowser li,
- .toolFile li {
- padding-left: 20px;
- background-repeat: no-repeat;
- background-position: 0;
- min-height: 20px;
- }
- .toolFile li {
- background-image: url( ${h.url_for( '/static/images/silk/page_white_compressed.png' )} );
- }
- .fileBrowser li {
- background-image: url( ${h.url_for( '/static/images/silk/page_white.png' )} );
- }
- </style>
-</%def>
-
<%def name="javascripts()">
${parent.javascripts()}
${h.js( "libs/jquery/jquery.rating" )}
@@ -77,6 +49,9 @@
%if can_upload:
<a class="action-button" href="${h.url_for( controller='upload', action='upload', repository_id=trans.security.encode_id( repository.id ), webapp='community' )}">Upload files to repository</a>
%endif
+ %if has_readme:
+ <a class="action-button" href="${h.url_for( controller='repository', action='view_readme', id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision, webapp='community' )}">View README</a>
+ %endif
%if can_view_change_log:
<a class="action-button" href="${h.url_for( controller='repository', action='view_changelog', id=trans.app.security.encode_id( repository.id ), webapp='community' )}">View change log</a>
%endif
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/webapps/community/repository/preview_tools_in_changeset.mako
--- a/templates/webapps/community/repository/preview_tools_in_changeset.mako
+++ b/templates/webapps/community/repository/preview_tools_in_changeset.mako
@@ -16,6 +16,7 @@
browse_label = 'Browse or delete repository tip files'
else:
browse_label = 'Browse repository tip files'
+ has_readme = metadata and 'readme' in metadata
%><%!
@@ -27,35 +28,6 @@
%><%inherit file="${inherit(context)}"/>
-<%def name="stylesheets()">
- ${parent.stylesheets()}
- ${h.css( "jquery.rating" )}
- <style type="text/css">
- ul.fileBrowser,
- ul.toolFile {
- margin-left: 0;
- padding-left: 0;
- list-style: none;
- }
- ul.fileBrowser {
- margin-left: 20px;
- }
- .fileBrowser li,
- .toolFile li {
- padding-left: 20px;
- background-repeat: no-repeat;
- background-position: 0;
- min-height: 20px;
- }
- .toolFile li {
- background-image: url( ${h.url_for( '/static/images/silk/page_white_compressed.png' )} );
- }
- .fileBrowser li {
- background-image: url( ${h.url_for( '/static/images/silk/page_white.png' )} );
- }
- </style>
-</%def>
-
<%def name="javascripts()">
${parent.javascripts()}
${h.js( "libs/jquery/jquery.rating" )}
@@ -67,6 +39,9 @@
<li><a class="action-button" href="${h.url_for( controller='repository', action='install_repositories_by_revision', repository_ids=trans.security.encode_id( repository.id ), webapp=webapp, changeset_revisions=changeset_revision )}">Install to local Galaxy</a></li><li><a class="action-button" id="repository-${repository.id}-popup" class="menubutton">Tool Shed Actions</a></li><div popupmenu="repository-${repository.id}-popup">
+ %if has_readme:
+ <a class="action-button" href="${h.url_for( controller='repository', action='view_readme', id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision, webapp=webapp )}">View README</a>
+ %endif
<a class="action-button" href="${h.url_for( controller='repository', action='browse_valid_categories', webapp=webapp )}">Browse valid repositories</a><a class="action-button" href="${h.url_for( controller='repository', action='find_tools', webapp=webapp )}">Search for valid tools</a><a class="action-button" href="${h.url_for( controller='repository', action='find_workflows', webapp=webapp )}">Search for workflows</a>
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/webapps/community/repository/rate_repository.mako
--- a/templates/webapps/community/repository/rate_repository.mako
+++ b/templates/webapps/community/repository/rate_repository.mako
@@ -19,6 +19,7 @@
browse_label = 'Browse or delete repository tip files'
else:
browse_label = 'Browse repository tip files'
+ has_readme = metadata and 'readme' in metadata
%><%!
@@ -83,6 +84,9 @@
%if can_upload:
<a class="action-button" href="${h.url_for( controller='upload', action='upload', repository_id=trans.security.encode_id( repository.id ), webapp='community' )}">Upload files to repository</a>
%endif
+ %if has_readme:
+ <a class="action-button" href="${h.url_for( controller='repository', action='view_readme', id=trans.app.security.encode_id( repository.id ), changeset_revision=repository.tip, webapp='community' )}">View README</a>
+ %endif
%if can_view_change_log:
<a class="action-button" href="${h.url_for( controller='repository', action='view_changelog', id=trans.app.security.encode_id( repository.id ), webapp='community' )}">View change log</a>
%endif
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/webapps/community/repository/tool_form.mako
--- a/templates/webapps/community/repository/tool_form.mako
+++ b/templates/webapps/community/repository/tool_form.mako
@@ -21,6 +21,7 @@
browse_label = 'Browse or delete repository tip files'
else:
browse_label = 'Browse repository tip files'
+ has_readme = metadata and 'readme' in metadata
%><html>
@@ -146,6 +147,9 @@
%if can_upload:
<a class="action-button" href="${h.url_for( controller='upload', action='upload', repository_id=trans.security.encode_id( repository.id ), webapp=webapp )}">Upload files to repository</a>
%endif
+ %if has_readme:
+ <a class="action-button" href="${h.url_for( controller='repository', action='view_readme', id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision, webapp=webapp )}">View README</a>
+ %endif
%if can_view_change_log:
<a class="action-button" href="${h.url_for( controller='repository', action='view_changelog', id=trans.app.security.encode_id( repository.id ), webapp=webapp )}">View change log</a>
%endif
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/webapps/community/repository/view_changelog.mako
--- a/templates/webapps/community/repository/view_changelog.mako
+++ b/templates/webapps/community/repository/view_changelog.mako
@@ -18,6 +18,7 @@
browse_label = 'Browse or delete repository tip files'
else:
browse_label = 'Browse repository tip files'
+ has_readme = metadata and 'readme' in metadata
%><%!
@@ -51,6 +52,9 @@
%else:
<a class="action-button" href="${h.url_for( controller='repository', action='view_repository', id=trans.app.security.encode_id( repository.id ), changeset_revision=repository.tip, webapp='community' )}">View repository</a>
%endif
+ %if has_readme:
+ <a class="action-button" href="${h.url_for( controller='repository', action='view_readme', id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision, webapp='community' )}">View README</a>
+ %endif
%if can_rate:
<a class="action-button" href="${h.url_for( controller='repository', action='rate_repository', id=trans.app.security.encode_id( repository.id ) )}">Rate repository</a>
%endif
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/webapps/community/repository/view_changeset.mako
--- a/templates/webapps/community/repository/view_changeset.mako
+++ b/templates/webapps/community/repository/view_changeset.mako
@@ -19,6 +19,7 @@
browse_label = 'Browse or delete repository tip files'
else:
browse_label = 'Browse repository tip files'
+ has_readme = metadata and 'readme' in metadata
%><%!
@@ -52,6 +53,9 @@
%else:
<a class="action-button" href="${h.url_for( controller='repository', action='view_repository', id=trans.app.security.encode_id( repository.id ), changeset_revision=repository.tip, webapp='community' )}">View repository</a>
%endif
+ %if has_readme:
+ <a class="action-button" href="${h.url_for( controller='repository', action='view_readme', id=trans.app.security.encode_id( repository.id ), changeset_revision=repository.tip, webapp='community' )}">View README</a>
+ %endif
%if can_view_change_log:
<a class="action-button" href="${h.url_for( controller='repository', action='view_changelog', id=trans.app.security.encode_id( repository.id ), webapp='community' )}">View change log</a>
%endif
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/webapps/community/repository/view_repository.mako
--- a/templates/webapps/community/repository/view_repository.mako
+++ b/templates/webapps/community/repository/view_repository.mako
@@ -17,6 +17,7 @@
browse_label = 'Browse or delete repository tip files'
else:
browse_label = 'Browse repository tip files'
+ has_readme = metadata and 'readme' in metadata
%><%!
@@ -28,35 +29,6 @@
%><%inherit file="${inherit(context)}"/>
-<%def name="stylesheets()">
- ${parent.stylesheets()}
- ${h.css( "jquery.rating" )}
- <style type="text/css">
- ul.fileBrowser,
- ul.toolFile {
- margin-left: 0;
- padding-left: 0;
- list-style: none;
- }
- ul.fileBrowser {
- margin-left: 20px;
- }
- .fileBrowser li,
- .toolFile li {
- padding-left: 20px;
- background-repeat: no-repeat;
- background-position: 0;
- min-height: 20px;
- }
- .toolFile li {
- background-image: url( ${h.url_for( '/static/images/silk/page_white_compressed.png' )} );
- }
- .fileBrowser li {
- background-image: url( ${h.url_for( '/static/images/silk/page_white.png' )} );
- }
- </style>
-</%def>
-
<%def name="javascripts()">
${parent.javascripts()}
${h.js( "libs/jquery/jquery.rating" )}
@@ -74,6 +46,9 @@
%if can_upload:
<a class="action-button" href="${h.url_for( controller='upload', action='upload', repository_id=trans.security.encode_id( repository.id ), webapp=webapp )}">Upload files to repository</a>
%endif
+ %if has_readme:
+ <a class="action-button" href="${h.url_for( controller='repository', action='view_readme', id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision, webapp=webapp )}">View README</a>
+ %endif
%if can_view_change_log:
<a class="action-button" href="${h.url_for( controller='repository', action='view_changelog', id=trans.app.security.encode_id( repository.id ), webapp=webapp )}">View change log</a>
%endif
@@ -152,11 +127,7 @@
${repository.description}
</div>
%if repository.long_description:
- <div class="form-row">
- <label>Detailed description:</label>
- <pre>${repository.long_description}</pre>
- <div style="clear: both"></div>
- </div>
+ ${render_long_description( repository.long_description )}
%endif
<div class="form-row"><label>Revision:</label>
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/webapps/community/repository/view_tool_metadata.mako
--- a/templates/webapps/community/repository/view_tool_metadata.mako
+++ b/templates/webapps/community/repository/view_tool_metadata.mako
@@ -20,6 +20,7 @@
browse_label = 'Browse or delete repository tip files'
else:
browse_label = 'Browse repository tip files'
+ has_readme = metadata and 'readme' in metadata
%><%!
@@ -59,6 +60,9 @@
%if can_upload:
<a class="action-button" href="${h.url_for( controller='upload', action='upload', repository_id=trans.security.encode_id( repository.id ), webapp=webapp )}">Upload files to repository</a>
%endif
+ %if has_readme:
+ <a class="action-button" href="${h.url_for( controller='repository', action='view_readme', id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision, webapp=webapp )}">View README</a>
+ %endif
%if can_view_change_log:
<a class="action-button" href="${h.url_for( controller='repository', action='view_changelog', id=trans.app.security.encode_id( repository.id ), webapp=webapp )}">View change log</a>
%endif
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/webapps/community/repository/view_workflow.mako
--- a/templates/webapps/community/repository/view_workflow.mako
+++ b/templates/webapps/community/repository/view_workflow.mako
@@ -22,6 +22,7 @@
browse_label = 'Browse or delete repository tip files'
else:
browse_label = 'Browse repository tip files'
+ has_readme = metadata and 'readme' in metadata
%><%!
@@ -54,6 +55,9 @@
%else:
<a class="action-button" href="${h.url_for( controller='repository', action='view_repository', id=trans.app.security.encode_id( repository.id ), changeset_revision=repository.tip, webapp='community' )}">View repository</a>
%endif
+ %if has_readme:
+ <a class="action-button" href="${h.url_for( controller='repository', action='view_readme', id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision, webapp='community' )}">View README</a>
+ %endif
%if can_view_change_log:
<a class="action-button" href="${h.url_for( controller='repository', action='view_changelog', id=trans.app.security.encode_id( repository.id ), webapp='community' )}">View change log</a>
%endif
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/workflow/editor_tool_form.mako
--- a/templates/workflow/editor_tool_form.mako
+++ b/templates/workflow/editor_tool_form.mako
@@ -98,6 +98,9 @@
<div class="toolForm"><div class="toolFormTitle">Tool: ${tool.name}</div>
+ %if tool.version:
+ <div class="form-row"><div class='titleRow'>Version: ${tool.version}</div></div>
+ %endif
<div class="toolFormBody"><input type="hidden" name="tool_id" value="${tool.id}" />
%for i, inputs in enumerate( tool.inputs_by_page ):
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 templates/workflow/run.mako
--- a/templates/workflow/run.mako
+++ b/templates/workflow/run.mako
@@ -379,6 +379,9 @@
<div class="toolForm"><div class="toolFormTitle"><span class='title_ul_text'>Step ${int(step.order_index)+1}: ${tool.name}</span>
+ %if tool.version:
+ (version ${tool.version})
+ %endif
% if step.annotations:
<div class="step-annotation">${h.to_unicode( step.annotations[0].annotation )}</div>
% endif
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 test-data/cuffdiff_out10.txt
--- a/test-data/cuffdiff_out10.txt
+++ b/test-data/cuffdiff_out10.txt
@@ -1,88 +1,88 @@
test_id gene_id gene locus sample_1 sample_2 status value_1 value_2 sqrt(JS) test_stat p_value q_value significant
-XLOC_000001 XLOC_000001 Xkr4 chr1:3204754-3204833 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000002 XLOC_000002 - chr1:3111449-3111490 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000003 XLOC_000003 - chr1:3111545-3111576 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000004 XLOC_000004 - chr1:3174765-3174792 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000005 XLOC_000005 - chr1:3187401-3187428 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000006 XLOC_000006 - chr1:3188521-3188548 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000007 XLOC_000007 - chr1:3189810-3190789 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000008 XLOC_000008 - chr1:3190858-3191434 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000009 XLOC_000009 - chr1:3191512-3192077 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000010 XLOC_000010 - chr1:3192250-3192336 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000011 XLOC_000011 - chr1:3192441-3192494 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000012 XLOC_000012 - chr1:3192550-3192629 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000013 XLOC_000013 - chr1:3192649-3192676 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000014 XLOC_000014 - chr1:3192731-3192811 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000015 XLOC_000015 - chr1:3192940-3193042 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000016 XLOC_000016 - chr1:3194185-3194226 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000017 XLOC_000017 - chr1:3194302-3194329 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000018 XLOC_000018 - chr1:3194706-3194733 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000019 XLOC_000019 - chr1:3195083-3195110 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000020 XLOC_000020 - chr1:3195450-3195477 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000021 XLOC_000021 - chr1:3197089-3197116 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000022 XLOC_000022 - chr1:3197246-3197273 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000023 XLOC_000023 - chr1:3197346-3197373 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000024 XLOC_000024 - chr1:3197425-3197452 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000025 XLOC_000025 - chr1:3200022-3200191 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000026 XLOC_000026 - chr1:3200325-3200352 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000027 XLOC_000027 - chr1:3200430-3200457 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000028 XLOC_000028 - chr1:3201007-3201039 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000029 XLOC_000029 - chr1:3201077-3201481 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000030 XLOC_000030 - chr1:3201596-3201666 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000031 XLOC_000031 - chr1:3201672-3201699 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000032 XLOC_000032 - chr1:3201725-3201809 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000033 XLOC_000033 Xkr4 chr1:3211521-3211561 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000034 XLOC_000034 Xkr4 chr1:3212213-3212292 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000035 XLOC_000035 Xkr4 chr1:3212367-3212439 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000036 XLOC_000036 Xkr4 chr1:3212717-3212801 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000037 XLOC_000037 Xkr4 chr1:3213095-3213242 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000038 XLOC_000038 Xkr4 chr1:3240606-3240633 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000039 XLOC_000039 Xkr4 chr1:3242479-3242512 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000040 XLOC_000040 Xkr4 chr1:3242633-3242923 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000041 XLOC_000041 Xkr4 chr1:3242924-3243005 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000042 XLOC_000042 Xkr4 chr1:3243018-3243079 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000043 XLOC_000043 Xkr4 chr1:3243108-3243154 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000044 XLOC_000044 Xkr4 chr1:3243347-3243401 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000045 XLOC_000045 Xkr4 chr1:3254079-3254106 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000046 XLOC_000046 Xkr4 chr1:3256974-3257011 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000047 XLOC_000047 Xkr4 chr1:3277155-3277182 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000048 XLOC_000048 Xkr4 chr1:3277190-3277218 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000049 XLOC_000049 Xkr4 chr1:3277913-3278390 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000050 XLOC_000050 Xkr4 chr1:3280117-3280144 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000051 XLOC_000051 Xkr4 chr1:3280498-3280525 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000052 XLOC_000052 Xkr4 chr1:3280686-3280741 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000053 XLOC_000053 Xkr4 chr1:3282504-3282531 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000054 XLOC_000054 Xkr4 chr1:3282650-3282677 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000055 XLOC_000055 Xkr4 chr1:3282760-3282832 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000056 XLOC_000056 Xkr4 chr1:3284966-3284993 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000057 XLOC_000057 Xkr4 chr1:3290488-3290553 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000058 XLOC_000058 Xkr4 chr1:3290798-3290859 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000059 XLOC_000059 Xkr4 chr1:3290919-3291273 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000060 XLOC_000060 Xkr4 chr1:3299443-3299664 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000061 XLOC_000061 Xkr4 chr1:3299691-3299733 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000062 XLOC_000062 Xkr4 chr1:3300051-3300078 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000063 XLOC_000063 Xkr4 chr1:3307748-3307775 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000064 XLOC_000064 Xkr4 chr1:3318620-3318647 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000065 XLOC_000065 Xkr4 chr1:3318999-3319051 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000066 XLOC_000066 Xkr4 chr1:3330527-3330554 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000067 XLOC_000067 Xkr4 chr1:3351240-3351311 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000068 XLOC_000068 Xkr4 chr1:3355887-3356119 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000069 XLOC_000069 Xkr4 chr1:3356180-3356225 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000070 XLOC_000070 Xkr4 chr1:3363076-3363176 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000071 XLOC_000071 Xkr4 chr1:3363214-3363278 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000072 XLOC_000072 Xkr4 chr1:3363387-3363446 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000073 XLOC_000073 Xkr4 chr1:3363753-3363849 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000074 XLOC_000074 Xkr4 chr1:3364871-3364919 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000075 XLOC_000075 Xkr4 chr1:3367135-3367162 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000076 XLOC_000076 Xkr4 chr1:3367210-3367237 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000077 XLOC_000077 Xkr4 chr1:3367333-3367382 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000078 XLOC_000078 Xkr4 chr1:3369580-3369607 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000079 XLOC_000079 Xkr4 chr1:3375001-3375028 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000080 XLOC_000080 Xkr4 chr1:3377211-3377262 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000081 XLOC_000081 Xkr4 chr1:3379888-3379915 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000082 XLOC_000082 Xkr4 chr1:3386739-3386836 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000083 XLOC_000083 Xkr4 chr1:3391325-3391352 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000084 XLOC_000084 Xkr4 chr1:3435841-3435880 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000085 XLOC_000085 Xkr4 chr1:3447761-3447788 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000086 XLOC_000086 Xkr4 chr1:3450906-3450965 q1 q2 NOTEST 0 0 0 0 0 1 no
-XLOC_000087 XLOC_000087 Xkr4 chr1:3451051-3451109 q1 q2 NOTEST 0 0 0 0 0 1 no
+XLOC_000001 XLOC_000001 Xkr4 chr1:3204754-3204833 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000002 XLOC_000002 - chr1:3111449-3111490 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000003 XLOC_000003 - chr1:3111545-3111576 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000004 XLOC_000004 - chr1:3174765-3174792 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000005 XLOC_000005 - chr1:3187401-3187428 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000006 XLOC_000006 - chr1:3188521-3188548 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000007 XLOC_000007 - chr1:3189810-3190789 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000008 XLOC_000008 - chr1:3190858-3191434 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000009 XLOC_000009 - chr1:3191512-3192077 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000010 XLOC_000010 - chr1:3192250-3192336 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000011 XLOC_000011 - chr1:3192441-3192494 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000012 XLOC_000012 - chr1:3192550-3192629 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000013 XLOC_000013 - chr1:3192649-3192676 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000014 XLOC_000014 - chr1:3192731-3192811 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000015 XLOC_000015 - chr1:3192940-3193042 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000016 XLOC_000016 - chr1:3194185-3194226 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000017 XLOC_000017 - chr1:3194302-3194329 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000018 XLOC_000018 - chr1:3194706-3194733 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000019 XLOC_000019 - chr1:3195083-3195110 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000020 XLOC_000020 - chr1:3195450-3195477 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000021 XLOC_000021 - chr1:3197089-3197116 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000022 XLOC_000022 - chr1:3197246-3197273 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000023 XLOC_000023 - chr1:3197346-3197373 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000024 XLOC_000024 - chr1:3197425-3197452 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000025 XLOC_000025 - chr1:3200022-3200191 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000026 XLOC_000026 - chr1:3200325-3200352 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000027 XLOC_000027 - chr1:3200430-3200457 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000028 XLOC_000028 - chr1:3201007-3201039 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000029 XLOC_000029 - chr1:3201077-3201481 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000030 XLOC_000030 - chr1:3201596-3201666 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000031 XLOC_000031 - chr1:3201672-3201699 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000032 XLOC_000032 - chr1:3201725-3201809 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000033 XLOC_000033 Xkr4 chr1:3211521-3211561 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000034 XLOC_000034 Xkr4 chr1:3212213-3212292 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000035 XLOC_000035 Xkr4 chr1:3212367-3212439 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000036 XLOC_000036 Xkr4 chr1:3212717-3212801 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000037 XLOC_000037 Xkr4 chr1:3213095-3213242 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000038 XLOC_000038 Xkr4 chr1:3240606-3240633 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000039 XLOC_000039 Xkr4 chr1:3242479-3242512 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000040 XLOC_000040 Xkr4 chr1:3242633-3242923 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000041 XLOC_000041 Xkr4 chr1:3242924-3243005 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000042 XLOC_000042 Xkr4 chr1:3243018-3243079 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000043 XLOC_000043 Xkr4 chr1:3243108-3243154 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000044 XLOC_000044 Xkr4 chr1:3243347-3243401 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000045 XLOC_000045 Xkr4 chr1:3254079-3254106 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000046 XLOC_000046 Xkr4 chr1:3256974-3257011 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000047 XLOC_000047 Xkr4 chr1:3277155-3277182 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000048 XLOC_000048 Xkr4 chr1:3277190-3277218 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000049 XLOC_000049 Xkr4 chr1:3277913-3278390 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000050 XLOC_000050 Xkr4 chr1:3280117-3280144 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000051 XLOC_000051 Xkr4 chr1:3280498-3280525 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000052 XLOC_000052 Xkr4 chr1:3280686-3280741 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000053 XLOC_000053 Xkr4 chr1:3282504-3282531 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000054 XLOC_000054 Xkr4 chr1:3282650-3282677 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000055 XLOC_000055 Xkr4 chr1:3282760-3282832 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000056 XLOC_000056 Xkr4 chr1:3284966-3284993 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000057 XLOC_000057 Xkr4 chr1:3290488-3290553 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000058 XLOC_000058 Xkr4 chr1:3290798-3290859 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000059 XLOC_000059 Xkr4 chr1:3290919-3291273 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000060 XLOC_000060 Xkr4 chr1:3299443-3299664 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000061 XLOC_000061 Xkr4 chr1:3299691-3299733 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000062 XLOC_000062 Xkr4 chr1:3300051-3300078 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000063 XLOC_000063 Xkr4 chr1:3307748-3307775 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000064 XLOC_000064 Xkr4 chr1:3318620-3318647 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000065 XLOC_000065 Xkr4 chr1:3318999-3319051 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000066 XLOC_000066 Xkr4 chr1:3330527-3330554 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000067 XLOC_000067 Xkr4 chr1:3351240-3351311 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000068 XLOC_000068 Xkr4 chr1:3355887-3356119 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000069 XLOC_000069 Xkr4 chr1:3356180-3356225 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000070 XLOC_000070 Xkr4 chr1:3363076-3363176 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000071 XLOC_000071 Xkr4 chr1:3363214-3363278 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000072 XLOC_000072 Xkr4 chr1:3363387-3363446 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000073 XLOC_000073 Xkr4 chr1:3363753-3363849 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000074 XLOC_000074 Xkr4 chr1:3364871-3364919 q1 q2 NOTEST 0 0 0 0 1 1 no
+XLOC_000075 XLOC_000075 Xkr4 chr1:3367135-3367162 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000076 XLOC_000076 Xkr4 chr1:3367210-3367237 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000077 XLOC_000077 Xkr4 chr1:3367333-3367382 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000078 XLOC_000078 Xkr4 chr1:3369580-3369607 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000079 XLOC_000079 Xkr4 chr1:3375001-3375028 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000080 XLOC_000080 Xkr4 chr1:3377211-3377262 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000081 XLOC_000081 Xkr4 chr1:3379888-3379915 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000082 XLOC_000082 Xkr4 chr1:3386739-3386836 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000083 XLOC_000083 Xkr4 chr1:3391325-3391352 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000084 XLOC_000084 Xkr4 chr1:3435841-3435880 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000085 XLOC_000085 Xkr4 chr1:3447761-3447788 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000086 XLOC_000086 Xkr4 chr1:3450906-3450965 q1 q2 LOWDATA 0 0 0 0 0 1 no
+XLOC_000087 XLOC_000087 Xkr4 chr1:3451051-3451109 q1 q2 LOWDATA 0 0 0 0 0 1 no
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 test-data/cuffdiff_out7.txt
--- a/test-data/cuffdiff_out7.txt
+++ b/test-data/cuffdiff_out7.txt
@@ -1,15 +1,15 @@
tracking_id class_code nearest_ref_id gene_id gene_short_name tss_id locus length coverage q1_FPKM q1_conf_lo q1_conf_hi q1_status q2_FPKM q2_conf_lo q2_conf_hi q2_status
-TSS1 - - XLOC_000001 Xkr4 TSS1 chr1:3204754-3204833 - - 0 0 0 OK 8.3103e+06 0 2.00628e+07 OK
-TSS10 - - XLOC_000008 - TSS10 chr1:3190858-3191434 - - 474092 138279 809906 OK 369273 113039 625507 OK
-TSS11 - - XLOC_000009 - TSS11 chr1:3191512-3192077 - - 502845 149756 855934 OK 739818 487934 991702 OK
-TSS13 - - XLOC_000010 - TSS13 chr1:3192250-3192336 - - 3.994e+06 0 1.1982e+07 OK 2.93812e+06 0 8.81435e+06 OK
-TSS14 - - XLOC_000011 - TSS14 chr1:3192441-3192494 - - 0 0 0 OK 8.52143e+07 0 1.83611e+08 OK
-TSS15 - - XLOC_000012 - TSS15 chr1:3192550-3192629 - - 0 0 0 OK 4.15515e+06 0 1.24654e+07 OK
+TSS1 - - XLOC_000001 Xkr4 TSS1 chr1:3204754-3204833 - - 0 0 0 OK 5.42247e+06 0 1.83844e+07 OK
+TSS10 - - XLOC_000008 - TSS10 chr1:3190858-3191434 - - 227564 0 483427 OK 240951 0 505030 OK
+TSS11 - - XLOC_000009 - TSS11 chr1:3191512-3192077 - - 241366 0 509259 OK 482731 109420 856042 OK
+TSS13 - - XLOC_000010 - TSS13 chr1:3192250-3192336 - - 1.91712e+06 0 7.66198e+06 OK 1.91712e+06 0 7.66198e+06 OK
+TSS14 - - XLOC_000011 - TSS14 chr1:3192441-3192494 - - 0 0 0 OK 5.56023e+07 0 1.86077e+08 OK
+TSS15 - - XLOC_000012 - TSS15 chr1:3192550-3192629 - - 0 0 0 OK 2.71123e+06 0 1.08357e+07 OK
TSS16 - - XLOC_000013 - TSS16 chr1:3192649-3192676 - - 0 0 0 OK 0 0 0 OK
TSS17 - - XLOC_000014 - TSS17 chr1:3192731-3192811 - - 0 0 0 OK 0 0 0 OK
-TSS18 - - XLOC_000015 - TSS18 chr1:3192940-3193042 - - 0 0 0 OK 6.02976e+06 0 1.34147e+07 OK
-TSS19 - - XLOC_000016 - TSS19 chr1:3194185-3194226 - - 0 0 0 OK 2.1403e+08 0 6.42089e+08 OK
-TSS2 - - XLOC_000002 - TSS2 chr1:3111449-3111490 - - 0 0 0 OK 2.1403e+08 0 6.42089e+08 OK
+TSS18 - - XLOC_000015 - TSS18 chr1:3192940-3193042 - - 0 0 0 OK 3.93442e+06 0 1.25021e+07 OK
+TSS19 - - XLOC_000016 - TSS19 chr1:3194185-3194226 - - 0 0 0 OK 1.39654e+08 0 5.58144e+08 OK
+TSS2 - - XLOC_000002 - TSS2 chr1:3111449-3111490 - - 0 0 0 OK 1.39654e+08 0 5.58144e+08 OK
TSS20 - - XLOC_000017 - TSS20 chr1:3194302-3194329 - - 0 0 0 OK 0 0 0 OK
TSS21 - - XLOC_000018 - TSS21 chr1:3194706-3194733 - - 0 0 0 OK 0 0 0 OK
TSS22 - - XLOC_000019 - TSS22 chr1:3195083-3195110 - - 0 0 0 OK 0 0 0 OK
@@ -18,62 +18,62 @@
TSS25 - - XLOC_000022 - TSS25 chr1:3197246-3197273 - - 0 0 0 OK 0 0 0 OK
TSS26 - - XLOC_000023 - TSS26 chr1:3197346-3197373 - - 0 0 0 OK 0 0 0 OK
TSS27 - - XLOC_000024 - TSS27 chr1:3197425-3197452 - - 0 0 0 OK 0 0 0 OK
-TSS28 - - XLOC_000025 - TSS28 chr1:3200022-3200191 - - 959058 0 2.06648e+06 OK 705514 0 1.52017e+06 OK
+TSS28 - - XLOC_000025 - TSS28 chr1:3200022-3200191 - - 460348 0 1.54058e+06 OK 460348 0 1.54058e+06 OK
TSS29 - - XLOC_000026 - TSS29 chr1:3200325-3200352 - - 0 0 0 OK 0 0 0 OK
TSS3 - - XLOC_000003 - TSS3 chr1:3111545-3111576 - - 0 0 0 OK 0 0 0 OK
TSS30 - - XLOC_000027 - TSS30 chr1:3200430-3200457 - - 0 0 0 OK 0 0 0 OK
TSS31 - - XLOC_000028 - TSS31 chr1:3201007-3201039 - - 0 0 0 OK 0 0 0 OK
-TSS32 - - XLOC_000029 - TSS32 chr1:3201077-3201481 - - 77513.9 0 167019 OK 285108 21736.5 548480 OK
-TSS33 - - XLOC_000030 - TSS33 chr1:3201596-3201666 - - 1.89853e+07 0 4.58345e+07 OK 0 0 0 OK
+TSS32 - - XLOC_000029 - TSS32 chr1:3201077-3201481 - - 37206.6 0 124514 OK 186033 0 476912 OK
+TSS33 - - XLOC_000030 - TSS33 chr1:3201596-3201666 - - 9.11292e+06 0 3.08965e+07 OK 0 0 0 OK
TSS34 - - XLOC_000031 - TSS34 chr1:3201672-3201699 - - 0 0 0 OK 0 0 0 OK
-TSS35 - - XLOC_000032 - TSS35 chr1:3201725-3201809 - - 1.75659e+07 0 3.90796e+07 OK 0 0 0 OK
+TSS35 - - XLOC_000032 - TSS35 chr1:3201725-3201809 - - 8.43162e+06 0 2.67925e+07 OK 0 0 0 OK
TSS36 - - XLOC_000033 Xkr4 TSS36 chr1:3211521-3211561 - - 0 0 0 OK 0 0 0 OK
-TSS37 - - XLOC_000034 Xkr4 TSS37 chr1:3212213-3212292 - - 0 0 0 OK 8.3103e+06 0 2.00628e+07 OK
-TSS38 - - XLOC_000035 Xkr4 TSS38 chr1:3212367-3212439 - - 0 0 0 OK 2.4671e+07 0 5.48867e+07 OK
-TSS39 - - XLOC_000036 Xkr4 TSS39 chr1:3212717-3212801 - - 4.39147e+06 0 1.31744e+07 OK 0 0 0 OK
+TSS37 - - XLOC_000034 Xkr4 TSS37 chr1:3212213-3212292 - - 0 0 0 OK 5.42247e+06 0 1.83844e+07 OK
+TSS38 - - XLOC_000035 Xkr4 TSS38 chr1:3212367-3212439 - - 0 0 0 OK 1.60978e+07 0 5.11527e+07 OK
+TSS39 - - XLOC_000036 Xkr4 TSS39 chr1:3212717-3212801 - - 2.10791e+06 0 8.42447e+06 OK 0 0 0 OK
TSS4 - - XLOC_000004 - TSS4 chr1:3174765-3174792 - - 0 0 0 OK 0 0 0 OK
-TSS40 - - XLOC_000037 Xkr4 TSS40 chr1:3213095-3213242 - - 8.89174e+06 0 2.05036e+07 OK 1.82908e+06 0 5.48723e+06 OK
+TSS40 - - XLOC_000037 Xkr4 TSS40 chr1:3213095-3213242 - - 2.84535e+06 0 1.07397e+07 OK 1.19347e+06 0 5.96736e+06 OK
TSS41 - - XLOC_000038 Xkr4 TSS41 chr1:3240606-3240633 - - 0 0 0 OK 0 0 0 OK
TSS42 - - XLOC_000039 Xkr4 TSS42 chr1:3242479-3242512 - - 0 0 0 OK 0 0 0 OK
-TSS43 - - XLOC_000040 Xkr4 TSS43 chr1:3242633-3242923 - - 56312.4 0 168937 OK 372827 0 761430 OK
+TSS43 - - XLOC_000040 Xkr4 TSS43 chr1:3242633-3242923 - - 27029.9 0 108028 OK 243269 0 681799 OK
TSS44 - - XLOC_000041 Xkr4 TSS44 chr1:3242924-3243005 - - 0 0 0 OK 0 0 0 OK
-TSS45 - - XLOC_000042 Xkr4 TSS45 chr1:3243018-3243079 - - 0 0 0 OK 2.66226e+07 0 6.42725e+07 OK
-TSS46 - - XLOC_000043 Xkr4 TSS46 chr1:3243108-3243154 - - 9.99919e+07 0 2.99976e+08 OK 0 0 0 OK
-TSS47 - - XLOC_000044 Xkr4 TSS47 chr1:3243347-3243401 - - 0 0 0 OK 5.0951e+07 0 1.23007e+08 OK
+TSS45 - - XLOC_000042 Xkr4 TSS45 chr1:3243018-3243079 - - 0 0 0 OK 1.73712e+07 0 5.88955e+07 OK
+TSS46 - - XLOC_000043 Xkr4 TSS46 chr1:3243108-3243154 - - 4.79961e+07 0 1.91822e+08 OK 0 0 0 OK
+TSS47 - - XLOC_000044 Xkr4 TSS47 chr1:3243347-3243401 - - 0 0 0 OK 3.32455e+07 0 1.12716e+08 OK
TSS48 - - XLOC_000045 Xkr4 TSS48 chr1:3254079-3254106 - - 0 0 0 OK 0 0 0 OK
-TSS49 - - XLOC_000046 Xkr4 TSS49 chr1:3256974-3257011 - - 0 0 0 OK 2.06814e+09 0 4.99293e+09 OK
+TSS49 - - XLOC_000046 Xkr4 TSS49 chr1:3256974-3257011 - - 0 0 0 OK 1.34946e+09 0 4.57522e+09 OK
TSS5 - - XLOC_000005 - TSS5 chr1:3187401-3187428 - - 0 0 0 OK 0 0 0 OK
TSS50 - - XLOC_000047 Xkr4 TSS50 chr1:3277155-3277182 - - 0 0 0 OK 0 0 0 OK
TSS51 - - XLOC_000048 Xkr4 TSS51 chr1:3277190-3277218 - - 0 0 0 OK 0 0 0 OK
-TSS52 - - XLOC_000049 Xkr4 TSS52 chr1:3277913-3278390 - - 265614 16793 514436 OK 41870.3 0 90217.9 OK
+TSS52 - - XLOC_000049 Xkr4 TSS52 chr1:3277913-3278390 - - 127495 0 331348 OK 27320.3 0 91429.2 OK
TSS54 - - XLOC_000050 Xkr4 TSS54 chr1:3280117-3280144 - - 0 0 0 OK 0 0 0 OK
TSS55 - - XLOC_000051 Xkr4 TSS55 chr1:3280498-3280525 - - 0 0 0 OK 0 0 0 OK
-TSS56 - - XLOC_000052 Xkr4 TSS56 chr1:3280686-3280741 - - 0 0 0 OK 2.29576e+07 0 6.88728e+07 OK
+TSS56 - - XLOC_000052 Xkr4 TSS56 chr1:3280686-3280741 - - 0 0 0 OK 1.49798e+07 0 5.98686e+07 OK
TSS57 - - XLOC_000053 Xkr4 TSS57 chr1:3282504-3282531 - - 0 0 0 OK 0 0 0 OK
TSS58 - - XLOC_000054 Xkr4 TSS58 chr1:3282650-3282677 - - 0 0 0 OK 0 0 0 OK
TSS59 - - XLOC_000055 Xkr4 TSS59 chr1:3282760-3282832 - - 0 0 0 OK 0 0 0 OK
TSS6 - - XLOC_000006 - TSS6 chr1:3188521-3188548 - - 0 0 0 OK 0 0 0 OK
TSS60 - - XLOC_000056 Xkr4 TSS60 chr1:3284966-3284993 - - 0 0 0 OK 0 0 0 OK
-TSS61 - - XLOC_000057 Xkr4 TSS61 chr1:3290488-3290553 - - 0 0 0 OK 9.79535e+06 0 2.9386e+07 OK
-TSS62 - - XLOC_000058 Xkr4 TSS62 chr1:3290798-3290859 - - 1.8095e+07 0 5.4285e+07 OK 0 0 0 OK
-TSS63 - - XLOC_000059 Xkr4 TSS63 chr1:3290919-3291273 - - 342218 0 691214 OK 75524 0 162732 OK
-TSS65 - - XLOC_000060 Xkr4 TSS65 chr1:3299443-3299664 - - 937403 0 1.99795e+06 OK 137917 0 413751 OK
+TSS61 - - XLOC_000057 Xkr4 TSS61 chr1:3290488-3290553 - - 0 0 0 OK 6.39146e+06 0 2.55442e+07 OK
+TSS62 - - XLOC_000058 Xkr4 TSS62 chr1:3290798-3290859 - - 8.68561e+06 0 3.4713e+07 OK 0 0 0 OK
+TSS63 - - XLOC_000059 Xkr4 TSS63 chr1:3290919-3291273 - - 164265 0 453103 OK 49279.4 0 164917 OK
+TSS65 - - XLOC_000060 Xkr4 TSS65 chr1:3299443-3299664 - - 449954 0 1.56567e+06 OK 89990.7 0 449954 OK
TSS66 - - XLOC_000060 Xkr4 TSS66 chr1:3299443-3299664 - - 0 0 0 OK 0 0 0 OK
TSS67 - - XLOC_000061 Xkr4 TSS67 chr1:3299691-3299733 - - 0 0 0 OK 0 0 0 OK
TSS68 - - XLOC_000062 Xkr4 TSS68 chr1:3300051-3300078 - - 0 0 0 OK 0 0 0 OK
TSS69 - - XLOC_000063 Xkr4 TSS69 chr1:3307748-3307775 - - 0 0 0 OK 0 0 0 OK
-TSS7 - - XLOC_000007 - TSS7 chr1:3189810-3190789 - - 415851 231920 599782 OK 458870 363705 554035 OK
+TSS7 - - XLOC_000007 - TSS7 chr1:3189810-3190789 - - 199608 33901.5 365315 OK 299412 183619 415206 OK
TSS70 - - XLOC_000064 Xkr4 TSS70 chr1:3318620-3318647 - - 0 0 0 OK 0 0 0 OK
TSS71 - - XLOC_000065 Xkr4 TSS71 chr1:3318999-3319051 - - 0 0 0 OK 0 0 0 OK
TSS72 - - XLOC_000066 Xkr4 TSS72 chr1:3330527-3330554 - - 0 0 0 OK 0 0 0 OK
-TSS73 - - XLOC_000067 Xkr4 TSS73 chr1:3351240-3351311 - - 8.91489e+06 0 2.67447e+07 OK 0 0 0 OK
-TSS74 - - XLOC_000068 Xkr4 TSS74 chr1:3355887-3356119 - - 585828 0 1.30332e+06 OK 0 0 0 OK
-TSS75 - - XLOC_000069 Xkr4 TSS75 chr1:3356180-3356225 - - 1.19208e+08 0 3.57623e+08 OK 0 0 0 OK
-TSS76 - - XLOC_000070 Xkr4 TSS76 chr1:3363076-3363176 - - 4.42166e+06 0 1.06748e+07 OK 0 0 0 OK
+TSS73 - - XLOC_000067 Xkr4 TSS73 chr1:3351240-3351311 - - 4.27915e+06 0 1.71021e+07 OK 0 0 0 OK
+TSS74 - - XLOC_000068 Xkr4 TSS74 chr1:3355887-3356119 - - 281197 0 1.06014e+06 OK 0 0 0 OK
+TSS75 - - XLOC_000069 Xkr4 TSS75 chr1:3356180-3356225 - - 5.72196e+07 0 2.28684e+08 OK 0 0 0 OK
+TSS76 - - XLOC_000070 Xkr4 TSS76 chr1:3363076-3363176 - - 2.1224e+06 0 7.19579e+06 OK 0 0 0 OK
TSS77 - - XLOC_000071 Xkr4 TSS77 chr1:3363214-3363278 - - 0 0 0 OK 0 0 0 OK
-TSS78 - - XLOC_000072 Xkr4 TSS78 chr1:3363387-3363446 - - 6.42536e+07 0 1.38447e+08 OK 0 0 0 OK
+TSS78 - - XLOC_000072 Xkr4 TSS78 chr1:3363387-3363446 - - 3.08417e+07 0 1.03214e+08 OK 0 0 0 OK
TSS79 - - XLOC_000073 Xkr4 TSS79 chr1:3363753-3363849 - - 0 0 0 OK 0 0 0 OK
-TSS80 - - XLOC_000074 Xkr4 TSS80 chr1:3364871-3364919 - - 7.29939e+07 0 2.18982e+08 OK 0 0 0 OK
+TSS80 - - XLOC_000074 Xkr4 TSS80 chr1:3364871-3364919 - - 3.50371e+07 0 1.40029e+08 OK 0 0 0 OK
TSS81 - - XLOC_000075 Xkr4 TSS81 chr1:3367135-3367162 - - 0 0 0 OK 0 0 0 OK
TSS82 - - XLOC_000076 Xkr4 TSS82 chr1:3367210-3367237 - - 0 0 0 OK 0 0 0 OK
TSS83 - - XLOC_000077 Xkr4 TSS83 chr1:3367333-3367382 - - 0 0 0 OK 0 0 0 OK
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 test-data/cuffdiff_out9.txt
--- a/test-data/cuffdiff_out9.txt
+++ b/test-data/cuffdiff_out9.txt
@@ -1,89 +1,89 @@
test_id gene_id gene locus sample_1 sample_2 status value_1 value_2 sqrt(JS) test_stat p_value q_value significant
-TSS1 XLOC_000001 Xkr4 chr1:3204754-3204833 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS10 XLOC_000008 - chr1:3190858-3191434 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS11 XLOC_000009 - chr1:3191512-3192077 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS13 XLOC_000010 - chr1:3192250-3192336 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS14 XLOC_000011 - chr1:3192441-3192494 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS15 XLOC_000012 - chr1:3192550-3192629 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS16 XLOC_000013 - chr1:3192649-3192676 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS17 XLOC_000014 - chr1:3192731-3192811 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS18 XLOC_000015 - chr1:3192940-3193042 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS19 XLOC_000016 - chr1:3194185-3194226 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS2 XLOC_000002 - chr1:3111449-3111490 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS20 XLOC_000017 - chr1:3194302-3194329 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS21 XLOC_000018 - chr1:3194706-3194733 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS22 XLOC_000019 - chr1:3195083-3195110 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS23 XLOC_000020 - chr1:3195450-3195477 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS24 XLOC_000021 - chr1:3197089-3197116 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS25 XLOC_000022 - chr1:3197246-3197273 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS26 XLOC_000023 - chr1:3197346-3197373 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS27 XLOC_000024 - chr1:3197425-3197452 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS28 XLOC_000025 - chr1:3200022-3200191 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS29 XLOC_000026 - chr1:3200325-3200352 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS3 XLOC_000003 - chr1:3111545-3111576 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS30 XLOC_000027 - chr1:3200430-3200457 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS31 XLOC_000028 - chr1:3201007-3201039 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS32 XLOC_000029 - chr1:3201077-3201481 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS33 XLOC_000030 - chr1:3201596-3201666 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS34 XLOC_000031 - chr1:3201672-3201699 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS35 XLOC_000032 - chr1:3201725-3201809 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS36 XLOC_000033 Xkr4 chr1:3211521-3211561 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS37 XLOC_000034 Xkr4 chr1:3212213-3212292 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS38 XLOC_000035 Xkr4 chr1:3212367-3212439 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS39 XLOC_000036 Xkr4 chr1:3212717-3212801 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS4 XLOC_000004 - chr1:3174765-3174792 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS40 XLOC_000037 Xkr4 chr1:3213095-3213242 q1 q2 OK 0 0 0.249945 0.143447 0.135175 0.135175 no
-TSS41 XLOC_000038 Xkr4 chr1:3240606-3240633 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS42 XLOC_000039 Xkr4 chr1:3242479-3242512 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS43 XLOC_000040 Xkr4 chr1:3242633-3242923 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS44 XLOC_000041 Xkr4 chr1:3242924-3243005 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS45 XLOC_000042 Xkr4 chr1:3243018-3243079 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS46 XLOC_000043 Xkr4 chr1:3243108-3243154 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS47 XLOC_000044 Xkr4 chr1:3243347-3243401 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS48 XLOC_000045 Xkr4 chr1:3254079-3254106 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS49 XLOC_000046 Xkr4 chr1:3256974-3257011 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS5 XLOC_000005 - chr1:3187401-3187428 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS50 XLOC_000047 Xkr4 chr1:3277155-3277182 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS51 XLOC_000048 Xkr4 chr1:3277190-3277218 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS52 XLOC_000049 Xkr4 chr1:3277913-3278390 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS54 XLOC_000050 Xkr4 chr1:3280117-3280144 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS55 XLOC_000051 Xkr4 chr1:3280498-3280525 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS56 XLOC_000052 Xkr4 chr1:3280686-3280741 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS57 XLOC_000053 Xkr4 chr1:3282504-3282531 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS58 XLOC_000054 Xkr4 chr1:3282650-3282677 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS59 XLOC_000055 Xkr4 chr1:3282760-3282832 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS6 XLOC_000006 - chr1:3188521-3188548 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS60 XLOC_000056 Xkr4 chr1:3284966-3284993 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS61 XLOC_000057 Xkr4 chr1:3290488-3290553 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS62 XLOC_000058 Xkr4 chr1:3290798-3290859 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS63 XLOC_000059 Xkr4 chr1:3290919-3291273 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS65 XLOC_000060 Xkr4 chr1:3299443-3299664 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS66 XLOC_000060 Xkr4 chr1:3299443-3299664 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS67 XLOC_000061 Xkr4 chr1:3299691-3299733 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS68 XLOC_000062 Xkr4 chr1:3300051-3300078 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS69 XLOC_000063 Xkr4 chr1:3307748-3307775 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS7 XLOC_000007 - chr1:3189810-3190789 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS70 XLOC_000064 Xkr4 chr1:3318620-3318647 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS71 XLOC_000065 Xkr4 chr1:3318999-3319051 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS72 XLOC_000066 Xkr4 chr1:3330527-3330554 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS73 XLOC_000067 Xkr4 chr1:3351240-3351311 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS74 XLOC_000068 Xkr4 chr1:3355887-3356119 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS75 XLOC_000069 Xkr4 chr1:3356180-3356225 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS76 XLOC_000070 Xkr4 chr1:3363076-3363176 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS77 XLOC_000071 Xkr4 chr1:3363214-3363278 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS78 XLOC_000072 Xkr4 chr1:3363387-3363446 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS79 XLOC_000073 Xkr4 chr1:3363753-3363849 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS80 XLOC_000074 Xkr4 chr1:3364871-3364919 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS81 XLOC_000075 Xkr4 chr1:3367135-3367162 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS82 XLOC_000076 Xkr4 chr1:3367210-3367237 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS83 XLOC_000077 Xkr4 chr1:3367333-3367382 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS84 XLOC_000078 Xkr4 chr1:3369580-3369607 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS85 XLOC_000079 Xkr4 chr1:3375001-3375028 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS86 XLOC_000080 Xkr4 chr1:3377211-3377262 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS87 XLOC_000081 Xkr4 chr1:3379888-3379915 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS88 XLOC_000082 Xkr4 chr1:3386739-3386836 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS89 XLOC_000083 Xkr4 chr1:3391325-3391352 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS90 XLOC_000084 Xkr4 chr1:3435841-3435880 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS91 XLOC_000085 Xkr4 chr1:3447761-3447788 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS92 XLOC_000086 Xkr4 chr1:3450906-3450965 q1 q2 NOTEST 0 0 0 0 0 1 no
-TSS93 XLOC_000087 Xkr4 chr1:3451051-3451109 q1 q2 NOTEST 0 0 0 0 0 1 no
+TSS1 XLOC_000001 Xkr4 chr1:3204754-3204833 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS10 XLOC_000008 - chr1:3190858-3191434 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS11 XLOC_000009 - chr1:3191512-3192077 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS13 XLOC_000010 - chr1:3192250-3192336 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS14 XLOC_000011 - chr1:3192441-3192494 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS15 XLOC_000012 - chr1:3192550-3192629 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS16 XLOC_000013 - chr1:3192649-3192676 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS17 XLOC_000014 - chr1:3192731-3192811 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS18 XLOC_000015 - chr1:3192940-3193042 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS19 XLOC_000016 - chr1:3194185-3194226 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS2 XLOC_000002 - chr1:3111449-3111490 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS20 XLOC_000017 - chr1:3194302-3194329 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS21 XLOC_000018 - chr1:3194706-3194733 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS22 XLOC_000019 - chr1:3195083-3195110 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS23 XLOC_000020 - chr1:3195450-3195477 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS24 XLOC_000021 - chr1:3197089-3197116 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS25 XLOC_000022 - chr1:3197246-3197273 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS26 XLOC_000023 - chr1:3197346-3197373 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS27 XLOC_000024 - chr1:3197425-3197452 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS28 XLOC_000025 - chr1:3200022-3200191 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS29 XLOC_000026 - chr1:3200325-3200352 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS3 XLOC_000003 - chr1:3111545-3111576 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS30 XLOC_000027 - chr1:3200430-3200457 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS31 XLOC_000028 - chr1:3201007-3201039 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS32 XLOC_000029 - chr1:3201077-3201481 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS33 XLOC_000030 - chr1:3201596-3201666 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS34 XLOC_000031 - chr1:3201672-3201699 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS35 XLOC_000032 - chr1:3201725-3201809 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS36 XLOC_000033 Xkr4 chr1:3211521-3211561 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS37 XLOC_000034 Xkr4 chr1:3212213-3212292 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS38 XLOC_000035 Xkr4 chr1:3212367-3212439 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS39 XLOC_000036 Xkr4 chr1:3212717-3212801 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS4 XLOC_000004 - chr1:3174765-3174792 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS40 XLOC_000037 Xkr4 chr1:3213095-3213242 q1 q2 NOTEST 0 0 0.249947 0 0.557805 1 no
+TSS41 XLOC_000038 Xkr4 chr1:3240606-3240633 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS42 XLOC_000039 Xkr4 chr1:3242479-3242512 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS43 XLOC_000040 Xkr4 chr1:3242633-3242923 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS44 XLOC_000041 Xkr4 chr1:3242924-3243005 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS45 XLOC_000042 Xkr4 chr1:3243018-3243079 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS46 XLOC_000043 Xkr4 chr1:3243108-3243154 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS47 XLOC_000044 Xkr4 chr1:3243347-3243401 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS48 XLOC_000045 Xkr4 chr1:3254079-3254106 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS49 XLOC_000046 Xkr4 chr1:3256974-3257011 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS5 XLOC_000005 - chr1:3187401-3187428 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS50 XLOC_000047 Xkr4 chr1:3277155-3277182 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS51 XLOC_000048 Xkr4 chr1:3277190-3277218 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS52 XLOC_000049 Xkr4 chr1:3277913-3278390 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS54 XLOC_000050 Xkr4 chr1:3280117-3280144 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS55 XLOC_000051 Xkr4 chr1:3280498-3280525 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS56 XLOC_000052 Xkr4 chr1:3280686-3280741 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS57 XLOC_000053 Xkr4 chr1:3282504-3282531 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS58 XLOC_000054 Xkr4 chr1:3282650-3282677 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS59 XLOC_000055 Xkr4 chr1:3282760-3282832 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS6 XLOC_000006 - chr1:3188521-3188548 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS60 XLOC_000056 Xkr4 chr1:3284966-3284993 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS61 XLOC_000057 Xkr4 chr1:3290488-3290553 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS62 XLOC_000058 Xkr4 chr1:3290798-3290859 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS63 XLOC_000059 Xkr4 chr1:3290919-3291273 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS65 XLOC_000060 Xkr4 chr1:3299443-3299664 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS66 XLOC_000060 Xkr4 chr1:3299443-3299664 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS67 XLOC_000061 Xkr4 chr1:3299691-3299733 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS68 XLOC_000062 Xkr4 chr1:3300051-3300078 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS69 XLOC_000063 Xkr4 chr1:3307748-3307775 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS7 XLOC_000007 - chr1:3189810-3190789 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS70 XLOC_000064 Xkr4 chr1:3318620-3318647 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS71 XLOC_000065 Xkr4 chr1:3318999-3319051 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS72 XLOC_000066 Xkr4 chr1:3330527-3330554 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS73 XLOC_000067 Xkr4 chr1:3351240-3351311 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS74 XLOC_000068 Xkr4 chr1:3355887-3356119 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS75 XLOC_000069 Xkr4 chr1:3356180-3356225 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS76 XLOC_000070 Xkr4 chr1:3363076-3363176 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS77 XLOC_000071 Xkr4 chr1:3363214-3363278 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS78 XLOC_000072 Xkr4 chr1:3363387-3363446 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS79 XLOC_000073 Xkr4 chr1:3363753-3363849 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS80 XLOC_000074 Xkr4 chr1:3364871-3364919 q1 q2 NOTEST 0 0 0 0 1 1 no
+TSS81 XLOC_000075 Xkr4 chr1:3367135-3367162 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS82 XLOC_000076 Xkr4 chr1:3367210-3367237 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS83 XLOC_000077 Xkr4 chr1:3367333-3367382 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS84 XLOC_000078 Xkr4 chr1:3369580-3369607 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS85 XLOC_000079 Xkr4 chr1:3375001-3375028 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS86 XLOC_000080 Xkr4 chr1:3377211-3377262 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS87 XLOC_000081 Xkr4 chr1:3379888-3379915 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS88 XLOC_000082 Xkr4 chr1:3386739-3386836 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS89 XLOC_000083 Xkr4 chr1:3391325-3391352 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS90 XLOC_000084 Xkr4 chr1:3435841-3435880 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS91 XLOC_000085 Xkr4 chr1:3447761-3447788 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS92 XLOC_000086 Xkr4 chr1:3450906-3450965 q1 q2 LOWDATA 0 0 0 0 0 1 no
+TSS93 XLOC_000087 Xkr4 chr1:3451051-3451109 q1 q2 LOWDATA 0 0 0 0 0 1 no
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 tools/ngs_rna/cuffcompare_wrapper.py
--- a/tools/ngs_rna/cuffcompare_wrapper.py
+++ b/tools/ngs_rna/cuffcompare_wrapper.py
@@ -1,5 +1,7 @@
#!/usr/bin/env python
+# Supports Cuffcompare versions v1.3.0 and newer.
+
import optparse, os, shutil, subprocess, sys, tempfile
def stop_err( msg ):
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 tools/ngs_rna/cuffcompare_wrapper.xml
--- a/tools/ngs_rna/cuffcompare_wrapper.xml
+++ b/tools/ngs_rna/cuffcompare_wrapper.xml
@@ -1,5 +1,5 @@
<tool id="cuffcompare" name="Cuffcompare" version="0.0.5">
- <!-- Wrapper supports Cuffcompare versions v1.0.0-v1.3.0 -->
+ <!-- Wrapper supports Cuffcompare versions v1.3.0 and newer --><description>compare assembled transcripts to a reference annotation and track Cufflinks transcripts across multiple experiments</description><requirements><requirement type="package">cufflinks</requirement>
@@ -80,13 +80,18 @@
from_work_dir="cc_output.stats" /><data format="tabular" name="input1_tmap" label="${tool.name} on ${on_string}: ${first_input.hid} data tmap file"
from_work_dir="cc_output.input1.tmap" />
- <data format="tabular" name="input1_refmap" label="${tool.name} on ${on_string}: data ${first_input.hid} refmap file"
- from_work_dir="cc_output.input1.refmap"/>
+ <data format="tabular" name="input1_refmap"
+ label="${tool.name} on ${on_string}: data ${first_input.hid} refmap file"
+ from_work_dir="cc_output.input1.refmap">
+ <filter>annotation['use_ref_annotation'] == 'Yes'</filter>
+ </data><data format="tabular" name="input2_tmap" label="${tool.name} on ${on_string}: data ${input_files[0]['additional_input'].hid} tmap file" from_work_dir="cc_output.input2.tmap">
- <filter>len( input_files ) > 0</filter>
+ <filter>len( input_files ) > 1</filter></data>
- <data format="tabular" name="input2_refmap" label="${tool.name} on ${on_string}: data ${input_files[0]['additional_input'].hid} refmap file" from_work_dir="cc_output.input2.refmap">
- <filter>len( input_files ) > 0</filter>
+ <data format="tabular" name="input2_refmap"
+ label="${tool.name} on ${on_string}: data ${input_files[0]['additional_input'].hid} refmap file"
+ from_work_dir="cc_output.input2.refmap">
+ <filter>annotation['use_ref_annotation'] == 'Yes' and len( input_files ) > 1</filter></data><data format="tabular" name="transcripts_tracking" label="${tool.name} on ${on_string}: transcript tracking" from_work_dir="cc_output.tracking"><filter>len( input_files ) > 0</filter>
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 tools/ngs_rna/cuffdiff_wrapper.py
--- a/tools/ngs_rna/cuffdiff_wrapper.py
+++ b/tools/ngs_rna/cuffdiff_wrapper.py
@@ -1,5 +1,7 @@
#!/usr/bin/env python
+# Wrapper supports Cuffdiff versions v1.3.0-v2.0
+
import optparse, os, shutil, subprocess, sys, tempfile
def group_callback( option, op_str, value, parser ):
@@ -59,6 +61,7 @@
where each end is 50bp, you should set -r to be 200. The default is 45bp.')
parser.add_option( '-c', '--min-alignment-count', dest='min_alignment_count', help='The minimum number of alignments in a locus for needed to conduct significance testing on changes in that locus observed between samples. If no testing is performed, changes in the locus are deemed not signficant, and the locus\' observed changes don\'t contribute to correction for multiple testing. The default is 1,000 fragment alignments (up to 2,000 paired reads).' )
parser.add_option( '--FDR', dest='FDR', help='The allowed false discovery rate. The default is 0.05.' )
+ parser.add_option( '-u', '--multi-read-correct', dest='multi_read_correct', action="store_true", help='Tells Cufflinks to do an initial estimation procedure to more accurately weight reads mapping to multiple locations in the genome')
# Advanced Options:
parser.add_option( '--num-importance-samples', dest='num_importance_samples', help='Sets the number of importance samples generated for each locus during abundance estimation. Default: 1000' )
@@ -153,6 +156,8 @@
cmd += ( " -c %i" % int ( options.min_alignment_count ) )
if options.FDR:
cmd += ( " --FDR %f" % float( options.FDR ) )
+ if options.multi_read_correct:
+ cmd += ( " -u" )
if options.num_importance_samples:
cmd += ( " --num-importance-samples %i" % int ( options.num_importance_samples ) )
if options.max_mle_iterations:
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 tools/ngs_rna/cuffdiff_wrapper.xml
--- a/tools/ngs_rna/cuffdiff_wrapper.xml
+++ b/tools/ngs_rna/cuffdiff_wrapper.xml
@@ -1,5 +1,5 @@
<tool id="cuffdiff" name="Cuffdiff" version="0.0.5">
- <!-- Wrapper supports Cuffdiff versions v1.0.0-v1.3.0 -->
+ <!-- Wrapper supports Cuffdiff versions v1.3.0-v2.0 --><description>find significant changes in transcript expression, splicing, and promoter use</description><requirements><requirement type="package">cufflinks</requirement>
@@ -33,7 +33,11 @@
-N
#end if
-
+ ## Multi-read correct?
+ #if str($multiread_correct) == "Yes":
+ -u
+ #end if
+
## Bias correction?
#if $bias_correction.do_bias_correction == "Yes":
-b
@@ -89,11 +93,19 @@
</conditional><param name="fdr" type="float" value="0.05" label="False Discovery Rate" help="The allowed false discovery rate."/>
+
<param name="min_alignment_count" type="integer" value="10" label="Min Alignment Count" help="The minimum number of alignments in a locus for needed to conduct significance testing on changes in that locus observed between samples."/>
+
<param name="do_normalization" type="select" label="Perform quartile normalization" help="Removes top 25% of genes from FPKM denominator to improve accuracy of differential expression calls for low abundance transcripts."><option value="No">No</option><option value="Yes">Yes</option></param>
+
+ <param name="multiread_correct" type="select" label="Use multi-read correct" help="Tells Cufflinks to do an initial estimation procedure to more accurately weight reads mapping to multiple locations in the genome.">
+ <option value="No" selected="true">No</option>
+ <option value="Yes">Yes</option>
+ </param>
+
<conditional name="bias_correction"><param name="do_bias_correction" type="select" label="Perform Bias Correction" help="Bias detection and correction can significantly improve accuracy of transcript abundance estimates."><option value="No">No</option>
@@ -113,6 +125,7 @@
</when><when value="No"></when></conditional>
+
<conditional name="additional"><param name="sAdditional" type="select" label="Set Additional Parameters? (not recommended)"><option value="No">No</option>
@@ -154,6 +167,7 @@
<param name="min_alignment_count" value="0" /><param name="do_bias_correction" value="No" /><param name="do_normalization" value="No" />
+ <param name="multiread_correct" value="No"/><param name="sAdditional" value="No"/><!--
Line diffs are needed because cuffdiff does not produce deterministic output.
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 tools/ngs_rna/cufflinks_wrapper.py
--- a/tools/ngs_rna/cufflinks_wrapper.py
+++ b/tools/ngs_rna/cufflinks_wrapper.py
@@ -1,5 +1,7 @@
#!/usr/bin/env python
+# Supports Cufflinks versions 1.3 and newer.
+
import optparse, os, shutil, subprocess, sys, tempfile
from galaxy import eggs
from galaxy.datatypes.util.gff_util import parse_gff_attributes, gff_attributes_to_str
@@ -36,6 +38,7 @@
where each end is 50bp, you should set -r to be 200. The default is 45bp.')
parser.add_option( '-G', '--GTF', dest='GTF', help='Tells Cufflinks to use the supplied reference annotation to estimate isoform expression. It will not assemble novel transcripts, and the program will ignore alignments not structurally compatible with any reference transcript.' )
parser.add_option( '-g', '--GTF-guide', dest='GTFguide', help='use reference transcript annotation to guide assembly' )
+ parser.add_option( '-u', '--multi-read-correct', dest='multi_read_correct', action="store_true", help='Tells Cufflinks to do an initial estimation procedure to more accurately weight reads mapping to multiple locations in the genome')
# Normalization options.
parser.add_option( "-N", "--quartile-normalization", dest="do_normalization", action="store_true" )
@@ -116,7 +119,9 @@
if options.GTF:
cmd += ( " -G %s" % options.GTF )
if options.GTFguide:
- cmd += ( " -g %s" % options.GTFguide )
+ cmd += ( " -g %s" % options.GTFguide )
+ if options.multi_read_correct:
+ cmd += ( " -u" )
if options.num_importance_samples:
cmd += ( " --num-importance-samples %i" % int ( options.num_importance_samples ) )
if options.max_mle_iterations:
@@ -157,7 +162,7 @@
total_map_mass = -1
tmp_stderr = open( tmp_name, 'r' )
for line in tmp_stderr:
- if line.lower().find( "total map mass" ) >= 0 or line.lower().find( "upper quartile" ) >= 0:
+ if line.lower().find( "map mass" ) >= 0 or line.lower().find( "upper quartile" ) >= 0:
total_map_mass = float( line.split(":")[1].strip() )
break
tmp_stderr.close()
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 tools/ngs_rna/cufflinks_wrapper.xml
--- a/tools/ngs_rna/cufflinks_wrapper.xml
+++ b/tools/ngs_rna/cufflinks_wrapper.xml
@@ -1,5 +1,5 @@
<tool id="cufflinks" name="Cufflinks" version="0.0.5">
- <!-- Wrapper supports Cufflinks versions v1.0.0-v1.3.0 -->
+ <!-- Wrapper supports Cufflinks versions v1.3.0 and newer --><description>transcript assembly and FPKM (RPKM) estimates for RNA-Seq data</description><requirements><requirement type="package">cufflinks</requirement>
@@ -20,12 +20,6 @@
#if $reference_annotation.use_ref == "Use reference annotation guide":
-g $reference_annotation.reference_annotation_guide_file
#end if
-
- ## Set paired-end parameters?
- #if $singlePaired.sPaired == "Yes":
- -m $singlePaired.mean_inner_distance
- -s $singlePaired.inner_distance_std_dev
- #end if
## Normalization?
#if str($do_normalization) == "Yes":
@@ -34,7 +28,7 @@
## Bias correction?
#if $bias_correction.do_bias_correction == "Yes":
- -b
+ -b
#if $bias_correction.seq_source.index_source == "history":
--ref_file=$bias_correction.seq_source.ref_file
#else:
@@ -44,6 +38,11 @@
--index_dir=${GALAXY_DATA_INDEX_DIR}
#end if
+ ## Multi-read correct?
+ #if str($multiread_correct) == "Yes":
+ -u
+ #end if
+
## Include global model if available.
#if $global_model:
--global_model=$global_model
@@ -55,12 +54,12 @@
<param name="min_isoform_fraction" type="float" value="0.10" min="0" max="1" label="Min Isoform Fraction" help=""/><param name="pre_mrna_fraction" type="float" value="0.15" min="0" max="1" label="Pre MRNA Fraction" help=""/><param name="do_normalization" type="select" label="Perform quartile normalization" help="Removes top 25% of genes from FPKM denominator to improve accuracy of differential expression calls for low abundance transcripts.">
- <option value="No">No</option>
+ <option value="No" selected="true">No</option><option value="Yes">Yes</option></param><conditional name="reference_annotation"><param name="use_ref" type="select" label="Use Reference Annotation">
- <option value="No">No</option>
+ <option value="No" selected="true">No</option><option value="Use reference annotation">Use reference annotation</option><option value="Use reference annotation guide">Use reference annotation as guide</option></param>
@@ -74,13 +73,13 @@
</conditional><conditional name="bias_correction"><param name="do_bias_correction" type="select" label="Perform Bias Correction" help="Bias detection and correction can significantly improve accuracy of transcript abundance estimates.">
- <option value="No">No</option>
- <option value="Yes">Yes</option>
+ <option value="No" selected="true">No</option>
+ <option value="Yes">Yes</option></param><when value="Yes"><conditional name="seq_source"><param name="index_source" type="select" label="Reference sequence data">
- <option value="cached">Locally cached</option>
+ <option value="cached" selected="true">Locally cached</option><option value="history">History</option></param><when value="cached"></when>
@@ -91,17 +90,12 @@
</when><when value="No"></when></conditional>
- <conditional name="singlePaired">
- <param name="sPaired" type="select" label="Set Parameters for Paired-end Reads? (not recommended)">
- <option value="No">No</option>
- <option value="Yes">Yes</option>
- </param>
- <when value="No"></when>
- <when value="Yes">
- <param name="mean_inner_distance" type="integer" value="20" label="Mean Inner Distance between Mate Pairs"/>
- <param name="inner_distance_std_dev" type="integer" value="20" label="Standard Deviation for Inner Distance between Mate Pairs"/>
- </when>
- </conditional>
+
+ <param name="multiread_correct" type="select" label="Use multi-read correct" help="Tells Cufflinks to do an initial estimation procedure to more accurately weight reads mapping to multiple locations in the genome.">
+ <option value="No" selected="true">No</option>
+ <option value="Yes">Yes</option>
+ </param>
+
<param name="global_model" type="hidden_data" label="Global model (for use in Trackster)" optional="True"/></inputs>
@@ -121,7 +115,6 @@
Simple test that uses test data included with cufflinks.
--><test>
- <param name="sPaired" value="No"/><param name="input" value="cufflinks_in.bam"/><param name="max_intron_len" value="300000"/><param name="min_isoform_fraction" value="0.05"/>
@@ -129,6 +122,7 @@
<param name="use_ref" value="No"/><param name="do_normalization" value="No" /><param name="do_bias_correction" value="No"/>
+ <param name="multiread_correct" value="No"/><output name="genes_expression" format="tabular" lines_diff="2" file="cufflinks_out3.fpkm_tracking"/><output name="transcripts_expression" format="tabular" lines_diff="2" file="cufflinks_out2.fpkm_tracking"/><output name="assembled_isoforms" file="cufflinks_out1.gtf"/>
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 tools/ngs_rna/cuffmerge_wrapper.py
--- a/tools/ngs_rna/cuffmerge_wrapper.py
+++ b/tools/ngs_rna/cuffmerge_wrapper.py
@@ -1,5 +1,7 @@
#!/usr/bin/env python
+# Supports Cuffmerge versions 1.3 and newer.
+
import optparse, os, shutil, subprocess, sys, tempfile
def stop_err( msg ):
diff -r 20e1f9fb8da9975b130047aece6d3ee3d26482fd -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 tools/ngs_rna/cuffmerge_wrapper.xml
--- a/tools/ngs_rna/cuffmerge_wrapper.xml
+++ b/tools/ngs_rna/cuffmerge_wrapper.xml
@@ -1,5 +1,5 @@
<tool id="cuffmerge" name="Cuffmerge" version="0.0.5">
- <!-- Wrapper supports Cuffmerge version v1.0.0 -->
+ <!-- Wrapper supports Cuffmerge versions 1.3 and newer --><description>merge together several Cufflinks assemblies</description><requirements><requirement type="package">cufflinks</requirement>
@@ -88,7 +88,7 @@
<param name="reference_annotation" value="cuffcompare_in3.gtf" ftype="gtf"/><param name="use_seq_data" value="No"/><!-- oId assignment differ/are non-deterministic -->
- <output name="merged_transcripts" file="cuffmerge_out1.gtf" lines_diff="8"/>
+ <output name="merged_transcripts" file="cuffmerge_out1.gtf" lines_diff="50"/></test></tests>
https://bitbucket.org/galaxy/galaxy-central/changeset/f3705f69d822/
changeset: f3705f69d822
user: james_taylor
date: 2012-09-28 21:13:07
summary: User inheritence to extend user controller in community webapp
affected #: 2 files
diff -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 -r f3705f69d8227fe08c56076528bf2c9356808d71 lib/galaxy/webapps/community/buildapp.py
--- a/lib/galaxy/webapps/community/buildapp.py
+++ b/lib/galaxy/webapps/community/buildapp.py
@@ -42,21 +42,6 @@
T = getattr( module, key )
if isclass( T ) and T is not BaseUIController and issubclass( T, BaseUIController ):
webapp.add_ui_controller( name, T( app ) )
- import galaxy.web.controllers
- controller_dir = galaxy.web.controllers.__path__[0]
- for fname in os.listdir( controller_dir ):
- # TODO: fix this if we decide to use, we don't need to inspect all controllers...
- if fname.startswith( 'user' ) and fname.endswith( ".py" ):
- name = fname[:-3]
- module_name = "galaxy.web.controllers." + name
- module = __import__( module_name )
- for comp in module_name.split( "." )[1:]:
- module = getattr( module, comp )
- # Look for a controller inside the modules
- for key in dir( module ):
- T = getattr( module, key )
- if isclass( T ) and T is not BaseUIController and issubclass( T, BaseUIController ):
- webapp.add_ui_controller( name, T( app ) )
def app_factory( global_conf, **kwargs ):
"""Return a wsgi application serving the root object"""
diff -r 08dbb6b88aeb89bb37b0dfa7641a463bd2a4fa52 -r f3705f69d8227fe08c56076528bf2c9356808d71 lib/galaxy/webapps/community/controllers/user.py
--- /dev/null
+++ b/lib/galaxy/webapps/community/controllers/user.py
@@ -0,0 +1,1 @@
+from galaxy.webapps.main.controllers.user import *
\ No newline at end of file
https://bitbucket.org/galaxy/galaxy-central/changeset/974f6b70c570/
changeset: 974f6b70c570
user: james_taylor
date: 2012-09-28 21:15:28
summary: Main webapp needs to be called 'galaxy' since that is what is used in templates currently
affected #: 106 files
Diff too large to display.
https://bitbucket.org/galaxy/galaxy-central/changeset/ac3980d88740/
changeset: ac3980d88740
user: james_taylor
date: 2012-09-28 21:17:18
summary: Use name galaxy when initializing controllers
affected #: 1 file
diff -r 974f6b70c570c866b98553dccff5af2a57c6ad4b -r ac3980d88740001c2291a6fc2d198aa5dee5bfbc lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -38,7 +38,7 @@
atexit.register( app.shutdown )
# Create the universe WSGI application
webapp = galaxy.web.framework.WebApplication( app, session_cookie='galaxysession' )
- webapp.add_ui_controllers( 'galaxy.webapps.main.controllers', app )
+ webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app )
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
# These two routes handle our simple needs at the moment
@@ -56,7 +56,7 @@
webapp.add_route( '/u/:username/v/:slug', controller='visualization', action='display_by_username_and_slug' )
# Add the web API
- webapp.add_api_controllers( 'galaxy.webapps.main.api', app )
+ webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
webapp.api_mapper.resource( 'content',
'contents',
controller='library_contents',
https://bitbucket.org/galaxy/galaxy-central/changeset/b5fe81cc70be/
changeset: b5fe81cc70be
user: james_taylor
date: 2012-09-28 21:20:59
summary: Subclasses of WebApplication for different apps
affected #: 3 files
diff -r ac3980d88740001c2291a6fc2d198aa5dee5bfbc -r b5fe81cc70be18f720f2180bfe9c221995d0530e lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py
+++ b/lib/galaxy/web/framework/__init__.py
@@ -215,7 +215,8 @@
class WebApplication( base.WebApplication ):
- def __init__( self, galaxy_app, session_cookie='galaxysession' ):
+ def __init__( self, galaxy_app, session_cookie='galaxysession', name=None ):
+ self.name = name
base.WebApplication.__init__( self )
self.set_transaction_factory( lambda e: self.transaction_chooser( e, galaxy_app, session_cookie ) )
# Mako support
diff -r ac3980d88740001c2291a6fc2d198aa5dee5bfbc -r b5fe81cc70be18f720f2180bfe9c221995d0530e lib/galaxy/webapps/community/buildapp.py
--- a/lib/galaxy/webapps/community/buildapp.py
+++ b/lib/galaxy/webapps/community/buildapp.py
@@ -21,6 +21,9 @@
log = logging.getLogger( __name__ )
+class CommunityWebApplication( galaxy.web.framework.WebApplication ):
+ pass
+
def add_ui_controllers( webapp, app ):
"""
Search for controllers in the 'galaxy.webapps.controllers' module and add
@@ -58,7 +61,7 @@
sys.exit( 1 )
atexit.register( app.shutdown )
# Create the universe WSGI application
- webapp = galaxy.web.framework.WebApplication( app, session_cookie='galaxycommunitysession' )
+ webapp = CommunityWebApplication( app, session_cookie='galaxycommunitysession', name="community" )
add_ui_controllers( webapp, app )
webapp.add_route( '/:controller/:action', action='index' )
webapp.add_route( '/:action', controller='repository', action='index' )
diff -r ac3980d88740001c2291a6fc2d198aa5dee5bfbc -r b5fe81cc70be18f720f2180bfe9c221995d0530e lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -20,6 +20,9 @@
import galaxy.datatypes.registry
import galaxy.web.framework
+class GalaxyWebApplication( galaxy.web.framework.WebApplication ):
+ pass
+
def app_factory( global_conf, **kwargs ):
"""
Return a wsgi application serving the root object
@@ -37,7 +40,7 @@
sys.exit( 1 )
atexit.register( app.shutdown )
# Create the universe WSGI application
- webapp = galaxy.web.framework.WebApplication( app, session_cookie='galaxysession' )
+ webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' )
webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app )
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
https://bitbucket.org/galaxy/galaxy-central/changeset/170b9bda24b0/
changeset: 170b9bda24b0
user: james_taylor
date: 2012-09-28 21:30:18
summary: Subclass of WebApplication for reports
affected #: 1 file
diff -r b5fe81cc70be18f720f2180bfe9c221995d0530e -r 170b9bda24b04b95cbce81a9d2fb4aafc898c1f2 lib/galaxy/webapps/reports/buildapp.py
--- a/lib/galaxy/webapps/reports/buildapp.py
+++ b/lib/galaxy/webapps/reports/buildapp.py
@@ -20,6 +20,9 @@
import galaxy.model.mapping
import galaxy.web.framework
+class ReportsWebApplication( galaxy.web.framework.WebApplication ):
+ pass
+
def add_ui_controllers( webapp, app ):
"""
Search for controllers in the 'galaxy.webapps.controllers' module and add
@@ -52,7 +55,7 @@
app = UniverseApplication( global_conf = global_conf, **kwargs )
atexit.register( app.shutdown )
# Create the universe WSGI application
- webapp = galaxy.web.framework.WebApplication( app, session_cookie='galaxyreportssession' )
+ webapp = ReportsWebApplication( app, session_cookie='galaxyreportssession', name="reports" )
add_ui_controllers( webapp, app )
# These two routes handle our simple needs at the moment
webapp.add_route( '/:controller/:action', controller="root", action='index' )
https://bitbucket.org/galaxy/galaxy-central/changeset/a37d82881a49/
changeset: a37d82881a49
user: james_taylor
date: 2012-09-28 21:30:25
summary: Automated merge with https://bitbucket.org/galaxy/galaxy-central
affected #: 111 files
Diff too large to display.
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.
1
0
commit/galaxy-central: natefoo: Allow Postgres users to update user disk usage with a fast wCTE rather than a slower computation in Python.
by Bitbucket 28 Sep '12
by Bitbucket 28 Sep '12
28 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/c8293bc4a964/
changeset: c8293bc4a964
user: natefoo
date: 2012-09-28 20:30:10
summary: Allow Postgres users to update user disk usage with a fast wCTE rather than a slower computation in Python.
affected #: 1 file
diff -r 8759493589092eb06138a75cf020f9cd8348106f -r c8293bc4a964863237fc069b4c1419a4674045e7 scripts/set_user_disk_usage.py
--- a/scripts/set_user_disk_usage.py
+++ b/scripts/set_user_disk_usage.py
@@ -43,31 +43,58 @@
from galaxy.model import mapping
- return mapping.init( config.file_path, config.database_connection, create_tables = False, object_store = object_store ), object_store
+ return mapping.init( config.file_path, config.database_connection, create_tables = False, object_store = object_store ), object_store, config.database_connection.split(':')[0]
-def quotacheck( sa_session, users ):
+def pgcalc( sa_session, id ):
+ sql = """
+ UPDATE galaxy_user
+ SET disk_usage = (SELECT SUM(total_size)
+ FROM ( SELECT d.total_size
+ FROM history_dataset_association hda
+ JOIN history h ON h.id = hda.history_id
+ JOIN dataset d ON hda.dataset_id = d.id
+ WHERE h.user_id = :id
+ AND h.purged = false
+ AND hda.purged = false
+ AND d.purged = false
+ AND d.id NOT IN (SELECT dataset_id
+ FROM library_dataset_dataset_association)
+ GROUP BY d.id) sizes)
+ WHERE id = :id
+ RETURNING disk_usage;
+ """
+ r = sa_session.execute(sql, {'id':id})
+ new = r.fetchone()[0]
+ if options.dryrun:
+ sa_session.rollback()
+ return new
+
+def quotacheck( sa_session, users, engine ):
sa_session.refresh( user )
current = user.get_disk_usage()
print user.username, '<' + user.email + '> old usage:', str( current ) + ',',
- new = user.calculate_disk_usage()
- sa_session.refresh( user )
- # usage changed while calculating, do it again
- if user.get_disk_usage() != current:
- print 'usage changed while calculating, trying again...'
- return quotacheck( sa_session, user )
+ if engine != 'postgres':
+ new = user.calculate_disk_usage()
+ sa_session.refresh( user )
+ # usage changed while calculating, do it again
+ if user.get_disk_usage() != current:
+ print 'usage changed while calculating, trying again...'
+ return quotacheck( sa_session, user, engine )
+ else:
+ new = pgcalc( sa_session, user.id )
# yes, still a small race condition between here and the flush
if new == current:
print 'no change'
else:
print 'new usage:', new
- if not options.dryrun:
+ if not options.dryrun and engine != 'postgres':
user.set_disk_usage( new )
sa_session.add( user )
sa_session.flush()
if __name__ == '__main__':
print 'Loading Galaxy model...'
- model, object_store = init()
+ model, object_store, engine = init()
sa_session = model.context.current
if not options.username and not options.email:
@@ -75,7 +102,7 @@
print 'Processing %i users...' % user_count
for i, user in enumerate( sa_session.query( model.User ).enable_eagerloads( False ).yield_per( 1000 ) ):
print '%3i%%' % int( float(i) / user_count * 100 ),
- quotacheck( sa_session, user )
+ quotacheck( sa_session, user, engine )
print '100% complete'
object_store.shutdown()
sys.exit( 0 )
@@ -87,4 +114,4 @@
print 'User not found'
sys.exit( 1 )
object_store.shutdown()
- quotacheck( sa_session, user )
+ quotacheck( sa_session, user, engine )
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.
1
0
commit/galaxy-central: greg: Handle files moved from one directory location to another in a change set in a tool shed repository.
by Bitbucket 28 Sep '12
by Bitbucket 28 Sep '12
28 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/875949358909/
changeset: 875949358909
user: greg
date: 2012-09-28 20:25:57
summary: Handle files moved from one directory location to another in a change set in a tool shed repository.
affected #: 1 file
diff -r 485932c40a2e1b1ebcf786c0ffaf288a243d3d11 -r 8759493589092eb06138a75cf020f9cd8348106f lib/galaxy/webapps/community/controllers/common.py
--- a/lib/galaxy/webapps/community/controllers/common.py
+++ b/lib/galaxy/webapps/community/controllers/common.py
@@ -401,7 +401,12 @@
for ctx_file in manifest_ctx.files():
ctx_file_name = strip_path( ctx_file )
if ctx_file_name == stripped_filename:
- fctx = manifest_ctx[ ctx_file ]
+ try:
+ fctx = manifest_ctx[ ctx_file ]
+ except LookupError:
+ # The ctx_file may have been moved in the change set. For example, 'ncbi_blastp_wrapper.xml' was moved to
+ # 'tools/ncbi_blast_plus/ncbi_blastp_wrapper.xml', so keep looking for the file until we find the new location.
+ continue
fh = tempfile.NamedTemporaryFile( 'wb' )
tmp_filename = fh.name
fh.close()
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.
1
0