merge with master

This commit is contained in:
Bjoern Gruening
2017-08-07 14:52:27 +02:00
1454 changed files with 50896 additions and 21718 deletions
+1
View File
@@ -5,3 +5,4 @@ database/
doc/source/conf.py
eggs/
lib/galaxy/util/jstree.py
lib/galaxy/web/proxy/js/node_modules/
+1
View File
@@ -1,2 +1,3 @@
lib/galaxy/jobs/metrics
lib/galaxy/exceptions
lib/galaxy/containers
+19 -255
View File
@@ -5,73 +5,19 @@ cron/cleanup_datasets.py
cron/parse_builds_3_sites.py
cron/parse_builds.py
doc/parse_gx_xsd.py
doc/patch.py
lib/galaxy/actions/
lib/galaxy/auth/
lib/galaxy/config.py
lib/galaxy/dataset_collections/builder.py
lib/galaxy/dataset_collections/__init__.py
lib/galaxy/dataset_collections/structure.py
lib/galaxy/dataset_collections/subcollections.py
lib/galaxy/dataset_collections/type_description.py
lib/galaxy/datatypes/assembly.py
lib/galaxy/datatypes/binary.py
lib/galaxy/datatypes/checkers.py
lib/galaxy/datatypes/constructive_solid_geometry.py
lib/galaxy/datatypes/converters/bcf_bgzip_to_bcf_converter.py
lib/galaxy/datatypes/converters/bcf_to_bcf_bgzip_converter.py
lib/galaxy/datatypes/converters/bedgraph_to_array_tree_converter.py
lib/galaxy/datatypes/converters/bed_to_gff_converter.py
lib/galaxy/datatypes/converters/bgzip.py
lib/galaxy/datatypes/converters/fasta_to_len.py
lib/galaxy/datatypes/converters/fastqsolexa_to_fasta_converter.py
lib/galaxy/datatypes/converters/fastqsolexa_to_qual_converter.py
lib/galaxy/datatypes/converters/fastq_to_fqtoc.py
lib/galaxy/datatypes/converters/gff_to_bed_converter.py
lib/galaxy/datatypes/converters/gff_to_interval_index_converter.py
lib/galaxy/datatypes/converters/__init__.py
lib/galaxy/datatypes/converters/interval_to_bed_converter.py
lib/galaxy/datatypes/converters/interval_to_bedstrict_converter.py
lib/galaxy/datatypes/converters/interval_to_coverage.py
lib/galaxy/datatypes/converters/interval_to_fli.py
lib/galaxy/datatypes/converters/interval_to_interval_index_converter.py
lib/galaxy/datatypes/converters/interval_to_tabix_converter.py
lib/galaxy/datatypes/converters/lped_to_fped_converter.py
lib/galaxy/datatypes/converters/lped_to_pbed_converter.py
lib/galaxy/datatypes/converters/maf_to_fasta_converter.py
lib/galaxy/datatypes/converters/maf_to_interval_converter.py
lib/galaxy/datatypes/converters/pbed_to_lped_converter.py
lib/galaxy/datatypes/converters/picard_interval_list_to_bed6_converter.py
lib/galaxy/datatypes/converters/pileup_to_interval_index_converter.py
lib/galaxy/datatypes/converters/tabular_to_dbnsfp.py
lib/galaxy/datatypes/converters/vcf_to_vcf_bgzip.py
lib/galaxy/datatypes/converters/wiggle_to_array_tree_converter.py
lib/galaxy/datatypes/coverage.py
lib/galaxy/datatypes/dataproviders/base.py
lib/galaxy/datatypes/dataproviders/exceptions.py
lib/galaxy/datatypes/dataproviders/__init__.py
lib/galaxy/datatypes/data.py
lib/galaxy/datatypes/display_applications/__init__.py
lib/galaxy/datatypes/display_applications/util.py
lib/galaxy/datatypes/images.py
lib/galaxy/datatypes/__init__.py
lib/galaxy/datatypes/metadata.py
lib/galaxy/datatypes/msa.py
lib/galaxy/datatypes/ngsindex.py
lib/galaxy/datatypes/proteomics.py
lib/galaxy/datatypes/sequence.py
lib/galaxy/datatypes/tabular.py
lib/galaxy/datatypes/text.py
lib/galaxy/datatypes/tracks.py
lib/galaxy/datatypes/util/generic_util.py
lib/galaxy/datatypes/util/__init__.py
lib/galaxy/containers/
lib/galaxy/dataset_collections/
lib/galaxy/datatypes/
lib/galaxy/dependencies/
lib/galaxy/eggs/
lib/galaxy/exceptions/__init__.py
lib/galaxy/exceptions/
lib/galaxy/external_services/__init__.py
lib/galaxy/external_services/parameters.py
lib/galaxy/external_services/result_handlers/basic.py
lib/galaxy/external_services/result_handlers/__init__.py
lib/galaxy_ext/
lib/galaxy/forms/__init__.py
lib/galaxy/jobs/actions/
lib/galaxy/jobs/command_factory.py
@@ -93,16 +39,7 @@ lib/galaxy/jobs/runners/pulsar.py
lib/galaxy/jobs/runners/slurm.py
lib/galaxy/jobs/runners/state_handlers/
lib/galaxy/jobs/runners/tasks.py
lib/galaxy/jobs/runners/util/cli/factory.py
lib/galaxy/jobs/runners/util/cli/job/__init__.py
lib/galaxy/jobs/runners/util/cli/job/slurm_torque.py
lib/galaxy/jobs/runners/util/cli/job/torque.py
lib/galaxy/jobs/runners/util/cli/shell/__init__.py
lib/galaxy/jobs/runners/util/drmaa/
lib/galaxy/jobs/runners/util/env.py
lib/galaxy/jobs/runners/util/external.py
lib/galaxy/jobs/runners/util/__init__.py
lib/galaxy/jobs/runners/util/job_script/
lib/galaxy/jobs/runners/util/
lib/galaxy/jobs/splitters/basic.py
lib/galaxy/jobs/splitters/__init__.py
lib/galaxy/jobs/stock_rules.py
@@ -119,142 +56,7 @@ lib/galaxy/managers/lddas.py
lib/galaxy/managers/libraries.py
lib/galaxy/managers/secured.py
lib/galaxy/managers/taggable.py
lib/galaxy/model/__init__.py
lib/galaxy/model/item_attrs.py
lib/galaxy/model/mapping.py
lib/galaxy/model/metadata.py
lib/galaxy/model/migrate/__init__.py
lib/galaxy/model/migrate/versions/0001_initial_tables.py
lib/galaxy/model/migrate/versions/0002_metadata_file_table.py
lib/galaxy/model/migrate/versions/0003_security_and_libraries.py
lib/galaxy/model/migrate/versions/0004_indexes_and_defaults.py
lib/galaxy/model/migrate/versions/0006_change_qual_datatype.py
lib/galaxy/model/migrate/versions/0007_sharing_histories.py
lib/galaxy/model/migrate/versions/0008_galaxy_forms.py
lib/galaxy/model/migrate/versions/0009_request_table.py
lib/galaxy/model/migrate/versions/0010_hda_display_at_authz_table.py
lib/galaxy/model/migrate/versions/0011_v0010_mysql_index_fix.py
lib/galaxy/model/migrate/versions/0012_user_address.py
lib/galaxy/model/migrate/versions/0013_change_lib_item_templates_to_forms.py
lib/galaxy/model/migrate/versions/0014_pages.py
lib/galaxy/model/migrate/versions/0015_tagging.py
lib/galaxy/model/migrate/versions/0016_v0015_mysql_index_fix.py
lib/galaxy/model/migrate/versions/0017_library_item_indexes.py
lib/galaxy/model/migrate/versions/0018_ordered_tags_and_page_tags.py
lib/galaxy/model/migrate/versions/0019_request_library_folder.py
lib/galaxy/model/migrate/versions/0020_library_upload_job.py
lib/galaxy/model/migrate/versions/0021_user_prefs.py
lib/galaxy/model/migrate/versions/0022_visualization_tables.py
lib/galaxy/model/migrate/versions/0023_page_published_and_deleted_columns.py
lib/galaxy/model/migrate/versions/0024_page_slug_unique_constraint.py
lib/galaxy/model/migrate/versions/0025_user_info.py
lib/galaxy/model/migrate/versions/0026_cloud_tables.py
lib/galaxy/model/migrate/versions/0027_request_events.py
lib/galaxy/model/migrate/versions/0028_external_metadata_file_override.py
lib/galaxy/model/migrate/versions/0029_user_actions.py
lib/galaxy/model/migrate/versions/0030_history_slug_column.py
lib/galaxy/model/migrate/versions/0031_community_and_workflow_tags.py
lib/galaxy/model/migrate/versions/0032_stored_workflow_slug_column.py
lib/galaxy/model/migrate/versions/0033_published_cols_for_histories_and_workflows.py
lib/galaxy/model/migrate/versions/0034_page_user_share_association.py
lib/galaxy/model/migrate/versions/0035_item_annotations_and_workflow_step_tags.py
lib/galaxy/model/migrate/versions/0036_add_deleted_column_to_library_template_assoc_tables.py
lib/galaxy/model/migrate/versions/0037_samples_library.py
lib/galaxy/model/migrate/versions/0038_add_inheritable_column_to_library_template_assoc_tables.py
lib/galaxy/model/migrate/versions/0039_add_synopsis_column_to_library_table.py
lib/galaxy/model/migrate/versions/0040_page_annotations.py
lib/galaxy/model/migrate/versions/0041_workflow_invocation.py
lib/galaxy/model/migrate/versions/0042_workflow_invocation_fix.py
lib/galaxy/model/migrate/versions/0043_visualization_sharing_tagging_annotating.py
lib/galaxy/model/migrate/versions/0044_add_notify_column_to_request_table.py
lib/galaxy/model/migrate/versions/0045_request_type_permissions_table.py
lib/galaxy/model/migrate/versions/0046_post_job_actions.py
lib/galaxy/model/migrate/versions/0047_job_table_user_id_column.py
lib/galaxy/model/migrate/versions/0048_dataset_instance_state_column.py
lib/galaxy/model/migrate/versions/0049_api_keys_table.py
lib/galaxy/model/migrate/versions/0050_drop_cloud_tables.py
lib/galaxy/model/migrate/versions/0051_imported_col_for_jobs_table.py
lib/galaxy/model/migrate/versions/0052_sample_dataset_table.py
lib/galaxy/model/migrate/versions/0053_item_ratings.py
lib/galaxy/model/migrate/versions/0054_visualization_dbkey.py
lib/galaxy/model/migrate/versions/0055_add_pja_assoc_for_jobs.py
lib/galaxy/model/migrate/versions/0056_workflow_outputs.py
lib/galaxy/model/migrate/versions/0057_request_notify.py
lib/galaxy/model/migrate/versions/0058_history_import_export.py
lib/galaxy/model/migrate/versions/0059_sample_dataset_file_path.py
lib/galaxy/model/migrate/versions/0060_history_archive_import.py
lib/galaxy/model/migrate/versions/0061_tasks.py
lib/galaxy/model/migrate/versions/0062_user_openid_table.py
lib/galaxy/model/migrate/versions/0063_sequencer_table.py
lib/galaxy/model/migrate/versions/0064_add_run_and_sample_run_association_tables.py
lib/galaxy/model/migrate/versions/0066_deferred_job_and_transfer_job_tables.py
lib/galaxy/model/migrate/versions/0067_populate_sequencer_table.py
lib/galaxy/model/migrate/versions/0068_rename_sequencer_to_external_services.py
lib/galaxy/model/migrate/versions/0069_rename_sequencer_form_type.py
lib/galaxy/model/migrate/versions/0070_add_info_column_to_deferred_job_table.py
lib/galaxy/model/migrate/versions/0071_add_history_and_workflow_to_sample.py
lib/galaxy/model/migrate/versions/0072_add_pid_and_socket_columns_to_transfer_job_table.py
lib/galaxy/model/migrate/versions/0073_add_ldda_to_implicit_conversion_table.py
lib/galaxy/model/migrate/versions/0074_add_purged_column_to_library_dataset_table.py
lib/galaxy/model/migrate/versions/0075_add_subindex_column_to_run_table.py
lib/galaxy/model/migrate/versions/0076_fix_form_values_data_corruption.py
lib/galaxy/model/migrate/versions/0077_create_tool_tag_association_table.py
lib/galaxy/model/migrate/versions/0078_add_columns_for_disk_usage_accounting.py
lib/galaxy/model/migrate/versions/0079_input_library_to_job_table.py
lib/galaxy/model/migrate/versions/0080_quota_tables.py
lib/galaxy/model/migrate/versions/0081_add_tool_version_to_hda_ldda.py
lib/galaxy/model/migrate/versions/0082_add_tool_shed_repository_table.py
lib/galaxy/model/migrate/versions/0083_add_prepare_files_to_task.py
lib/galaxy/model/migrate/versions/0084_add_ldda_id_to_implicit_conversion_table.py
lib/galaxy/model/migrate/versions/0085_add_task_info.py
lib/galaxy/model/migrate/versions/0086_add_tool_shed_repository_table_columns.py
lib/galaxy/model/migrate/versions/0087_tool_id_guid_map_table.py
lib/galaxy/model/migrate/versions/0088_add_installed_changeset_revison_column.py
lib/galaxy/model/migrate/versions/0089_add_object_store_id_columns.py
lib/galaxy/model/migrate/versions/0090_add_tool_shed_repository_table_columns.py
lib/galaxy/model/migrate/versions/0091_add_tool_version_tables.py
lib/galaxy/model/migrate/versions/0092_add_migrate_tools_table.py
lib/galaxy/model/migrate/versions/0093_add_job_params_col.py
lib/galaxy/model/migrate/versions/0094_add_job_handler_col.py
lib/galaxy/model/migrate/versions/0095_hda_subsets.py
lib/galaxy/model/migrate/versions/0096_openid_provider.py
lib/galaxy/model/migrate/versions/0097_add_ctx_rev_column.py
lib/galaxy/model/migrate/versions/0098_genome_index_tool_data_table.py
lib/galaxy/model/migrate/versions/0099_add_tool_dependency_table.py
lib/galaxy/model/migrate/versions/0100_alter_tool_dependency_table_version_column.py
lib/galaxy/model/migrate/versions/0101_drop_installed_changeset_revision_column.py
lib/galaxy/model/migrate/versions/0102_add_tool_dependency_status_columns.py
lib/galaxy/model/migrate/versions/0103_add_tool_shed_repository_status_columns.py
lib/galaxy/model/migrate/versions/0104_update_genome_downloader_job_parameters.py
lib/galaxy/model/migrate/versions/0105_add_cleanup_event_table.py
lib/galaxy/model/migrate/versions/0106_add_missing_indexes.py
lib/galaxy/model/migrate/versions/0107_add_exit_code_to_job_and_task.py
lib/galaxy/model/migrate/versions/0108_add_extended_metadata.py
lib/galaxy/model/migrate/versions/0109_add_repository_dependency_tables.py
lib/galaxy/model/migrate/versions/0110_add_dataset_uuid.py
lib/galaxy/model/migrate/versions/0111_add_job_destinations.py
lib/galaxy/model/migrate/versions/0112_add_data_manager_history_association_and_data_manager_job_association_tables.py
lib/galaxy/model/migrate/versions/0113_update_migrate_tools_table.py
lib/galaxy/model/migrate/versions/0114_update_migrate_tools_table_again.py
lib/galaxy/model/migrate/versions/0115_longer_user_password_field.py
lib/galaxy/model/migrate/versions/0116_drop_update_available_col_add_tool_shed_status_col.py
lib/galaxy/model/migrate/versions/0117_add_user_activation.py
lib/galaxy/model/migrate/versions/0118_add_hda_extended_metadata.py
lib/galaxy/model/migrate/versions/0119_job_metrics.py
lib/galaxy/model/migrate/versions/0120_dataset_collections.py
lib/galaxy/model/migrate/versions/0121_workflow_uuids.py
lib/galaxy/model/migrate/versions/0123_add_workflow_request_tables.py
lib/galaxy/model/migrate/versions/0124_job_state_history.py
lib/galaxy/model/migrate/versions/0125_workflow_step_tracking.py
lib/galaxy/model/migrate/versions/0126_password_reset.py
lib/galaxy/model/migrate/versions/0127_output_collection_adjustments.py
lib/galaxy/model/migrate/versions/0128_session_timeout.py
lib/galaxy/model/migrate/versions/0129_job_external_output_metadata_validity.py
lib/galaxy/model/migrate/versions/0130_change_pref_datatype.py
lib/galaxy/model/orm/
lib/galaxy/model/tool_shed_install/migrate/__init__.py
lib/galaxy/model/tool_shed_install/migrate/versions/
lib/galaxy/model/util.py
lib/galaxy/model/
lib/galaxy/objectstore/pulsar.py
lib/galaxy/objectstore/s3_multipart_upload.py
lib/galaxy/openid/__init__.py
@@ -262,48 +64,10 @@ lib/galaxy/quota/
lib/galaxy/sample_tracking/data_transfer.py
lib/galaxy/sample_tracking/__init__.py
lib/galaxy/sample_tracking/sample.py
lib/galaxy/security/validate_user_input.py
lib/galaxy/security/
lib/galaxy/tags/
lib/galaxy/tools/actions/
lib/galaxy/tools/cwl/
lib/galaxy/tools/data_manager/__init__.py
lib/galaxy/tools/deps/
lib/galaxy/tools/exception_handling.py
lib/galaxy/tools/execute.py
lib/galaxy/tools/filters/
lib/galaxy/tools/imp_exp/export_history.py
lib/galaxy/tools/imp_exp/__init__.py
lib/galaxy/tools/linters/
lib/galaxy/tools/lint.py
lib/galaxy/tools/lint_util.py
lib/galaxy/tools/loader_directory.py
lib/galaxy/tools/loader.py
lib/galaxy/tools/parameters/dataset_matcher.py
lib/galaxy/tools/parameters/history_query.py
lib/galaxy/tools/parameters/__init__.py
lib/galaxy/tools/parameters/input_translation.py
lib/galaxy/tools/parameters/sanitize.py
lib/galaxy/tools/parameters/validation.py
lib/galaxy/tools/parameters/wrapped_json.py
lib/galaxy/tools/parameters/wrapped.py
lib/galaxy/tools/parser/
lib/galaxy/tools/special_tools.py
lib/galaxy/tools/test.py
lib/galaxy/tools/toolbox/
lib/galaxy/tools/util/galaxyops/
lib/galaxy/tools/util/__init__.py
lib/galaxy/tools/verify/
lib/galaxy/tools/
lib/galaxy/util/
lib/galaxy_utils/__init__.py
lib/galaxy/util/sleeper.py
lib/galaxy/util/specs.py
lib/galaxy_utils/sequence/fasta.py
lib/galaxy_utils/sequence/fastq.py
lib/galaxy_utils/sequence/__init__.py
lib/galaxy_utils/sequence/transform.py
lib/galaxy_utils/sequence/vcf.py
lib/galaxy/util/template.py
lib/galaxy/util/ucsc.py
lib/galaxy/version.py
lib/galaxy/visualization/data_providers/basic.py
lib/galaxy/visualization/data_providers/cigar.py
@@ -317,6 +81,7 @@ lib/galaxy/visualization/plugins/utils.py
lib/galaxy/visualization/tracks/
lib/galaxy/web/base/controllers/__init__.py
lib/galaxy/web/base/__init__.py
lib/galaxy/web/base/interactive_environments.py
lib/galaxy/web/buildapp.py
lib/galaxy/web/formatting.py
lib/galaxy/web/framework/base.py
@@ -334,6 +99,7 @@ lib/galaxy/web/framework/middleware/xforwardedhost.py
lib/galaxy/web/__init__.py
lib/galaxy/web/params.py
lib/galaxy/webapps/galaxy/api/genomes.py
lib/galaxy/webapps/galaxy/api/histories.py
lib/galaxy/webapps/galaxy/api/__init__.py
lib/galaxy/webapps/galaxy/api/jobs.py
lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -343,6 +109,7 @@ lib/galaxy/webapps/galaxy/api/samples.py
lib/galaxy/webapps/galaxy/api/tools.py
lib/galaxy/webapps/galaxy/api/tours.py
lib/galaxy/webapps/galaxy/api/workflows.py
lib/galaxy/webapps/galaxy/config_watchers.py
lib/galaxy/webapps/galaxy/controllers/async.py
lib/galaxy/webapps/galaxy/controllers/data_manager.py
lib/galaxy/webapps/galaxy/controllers/error.py
@@ -377,13 +144,11 @@ lib/galaxy/webapps/tool_shed/util/__init__.py
lib/galaxy/webapps/tool_shed/util/ratings_util.py
lib/galaxy/webapps/tool_shed/util/shed_statistics.py
lib/galaxy/webapps/util.py
lib/galaxy/workflow/extract.py
lib/galaxy/workflow/__init__.py
lib/galaxy/workflow/render.py
lib/galaxy/workflow/run.py
lib/galaxy/workflow/schedulers/
lib/galaxy/workflow/steps.py
lib/galaxy/work/__init__.py
lib/galaxy/webhooks/
lib/galaxy/work/
lib/galaxy/workflow/
lib/galaxy_ext/
lib/log_tempfile.py
lib/mimeparse.py
lib/psyco_full.py
lib/tool_shed/
@@ -472,7 +237,6 @@ scripts/tools/re_escape_output.py
test/api/__init__.py
test/api/test_dataset_collections.py
test/api/test_datasets.py
test/api/test_datatypes.py
test/api/test_framework.py
test/api/test_history_contents_provenance.py
test/api/test_jobs.py
@@ -483,7 +247,6 @@ test/api/test_tool_data.py
test/api/test_tours.py
test/api/test_workflow_extraction.py
test/api/test_workflows_from_yaml.py
test/api/workflows_format_2/
test/base/api_asserts.py
test/base/api.py
test/base/api_util.py
@@ -495,6 +258,7 @@ test/base/nose_util.py
test/base/test_logging.py
test/base/tool_shed_util.py
test/base/twilltestcase.py
test/base/workflows_format_2/
test/casperjs/
test/functional/database_contexts.py
test/functional/__init__.py
@@ -535,6 +299,7 @@ test/unit/shed_unit/test_fabric_util.py
test/unit/shed_unit/test_td_common_util.py
test/unit/test_galaxy_transactions.py
test/unit/test_lazy_process.py
test/unit/test_objectstore.py
test/unit/test_routes.py
test/unit/test_security_helper.py
test/unit/test_sockets.py
@@ -562,6 +327,5 @@ test/unit/web/base/__init__.py
test/unit/web/framework/__init__.py
test/unit/web/__init__.py
test/unit/workflows/
test/unit/test_objectstore.py
tool_list.py
tools/
+1 -1
View File
@@ -7,7 +7,7 @@ set -e
# D3XX - Quoting issues.
# D401 - First line should be in imperative mood
# D403 - First word of the first line should be properly capitalized
args="--ignore=D --select=D100,D201,D202,D206,D207,D208,D209,D211,D3,D401,D403"
args="--ignore=D --select=D100,D201,D202,D206,D207,D208,D209,D211,D3,D403"
# If the first argument is --include, lint the modules expected to pass. If
# the first argument is --exclude, lint all modules the full Galaxy linter lints
+6 -3
View File
@@ -2,9 +2,12 @@
set -e
sleep 30 # TODO: wait on something instead of just sleeping...
echo `df`
echo "Waiting for postgres to become available"
while ! nc -z postgres 5432;
do
sleep 1
printf "."
done
echo "Creating postgres database for Galaxy"
createdb -w -U postgres -h postgres galaxy
+12 -3
View File
@@ -36,6 +36,12 @@ docker-compose down | true
docker-compose build galaxy
docker-compose up -d
function tear_down {
docker-compose down
}
trap tear_down EXIT
for service_name in postgres galaxy selenium
do
echo "Waiting on service ${service_name}"
@@ -79,7 +85,6 @@ do
docker logs "${container_id}"
echo "---"
done
docker-compose down | true
exit 1
fi
done
@@ -92,6 +97,12 @@ done;
export GALAXY_TEST_SELENIUM_REMOTE=1
export GALAXY_TEST_SELENIUM_REMOTE_PORT="${SELENIUM_PORT}"
# Retry all failed Selenium tests a second time to deal
# with transiently failing tests. Failure information for
# first tests is still populated in database/test_errors
# and available at the top of the Jenkins test report.
export GALAXY_TEST_SELENIUM_RETRIES=1
# Access Galaxy on localhost via port $GALAXY_PORT
export GALAXY_TEST_PORT="${GALAXY_PORT}"
@@ -105,6 +116,4 @@ exit_code=$?
cd $TEST_DIRECTORY
docker-compose down
exit $exit_code
+9 -45
View File
@@ -6,21 +6,9 @@ doc/patch.py
lib/galaxy/actions/
lib/galaxy/auth/
lib/galaxy/config.py
lib/galaxy/containers/
lib/galaxy/dataset_collections/
lib/galaxy/datatypes/assembly.py
lib/galaxy/datatypes/binary.py
lib/galaxy/datatypes/constructive_solid_geometry.py
lib/galaxy/datatypes/converters/
lib/galaxy/datatypes/dataproviders/
lib/galaxy/datatypes/data.py
lib/galaxy/datatypes/images.py
lib/galaxy/datatypes/msa.py
lib/galaxy/datatypes/ngsindex.py
lib/galaxy/datatypes/proteomics.py
lib/galaxy/datatypes/sequence.py
lib/galaxy/datatypes/sniff.py
lib/galaxy/datatypes/tabular.py
lib/galaxy/datatypes/tracks.py
lib/galaxy/datatypes/
lib/galaxy/dependencies/
lib/galaxy/eggs/
lib/galaxy/exceptions/
@@ -28,47 +16,26 @@ lib/galaxy/external_services/
lib/galaxy/forms/
lib/galaxy/jobs/
lib/galaxy/managers/
lib/galaxy/model/__init__.py
lib/galaxy/model/item_attrs.py
lib/galaxy/model/mapping.py
lib/galaxy/model/metadata.py
lib/galaxy/model/migrate/
lib/galaxy/model/orm/now.py
lib/galaxy/model/
lib/galaxy/objectstore/
lib/galaxy/openid/
lib/galaxy/quota/
lib/galaxy/sample_tracking/
lib/galaxy/security/
lib/galaxy/tags/
lib/galaxy/tools/actions/
lib/galaxy/tools/cwl/
lib/galaxy/tools/deps/
lib/galaxy/tools/exception_handling.py
lib/galaxy/tools/execute.py
lib/galaxy/tools/lint.py
lib/galaxy/tools/lint_util.py
lib/galaxy/tools/linters/
lib/galaxy/tools/loader.py
lib/galaxy/tools/loader_directory.py
lib/galaxy/tools/parameters/dataset_matcher.py
lib/galaxy/tools/parameters/__init__.py
lib/galaxy/tools/parameters/input_translation.py
lib/galaxy/tools/parameters/sanitize.py
lib/galaxy/tools/parameters/validation.py
lib/galaxy/tools/parameters/wrapped_json.py
lib/galaxy/tools/parameters/wrapped.py
lib/galaxy/tools/parser/
lib/galaxy/tools/test.py
lib/galaxy/tools/toolbox/
lib/galaxy/tools/
lib/galaxy/tours/
lib/galaxy/util/
lib/galaxy/visualization/
lib/galaxy/web/buildapp.py
lib/galaxy/web/framework/base.py
lib/galaxy/web/framework/decorators.py
lib/galaxy/web/framework/__init__.py
lib/galaxy/web/framework/middleware/error.py
lib/galaxy/web/framework/middleware/static.py
lib/galaxy/web/framework/middleware/statsd.py
lib/galaxy/web/__init__.py
lib/galaxy/webapps/galaxy/api/histories.py
lib/galaxy/webapps/galaxy/api/tours.py
lib/galaxy/webapps/galaxy/api/workflows.py
lib/galaxy/webapps/galaxy/controllers/external_services.py
@@ -81,13 +48,10 @@ lib/galaxy/webapps/tool_shed/controllers/user.py
lib/galaxy/webapps/tool_shed/framework/middleware/remoteuser.py
lib/galaxy/webapps/tool_shed/__init__.py
lib/galaxy/webapps/tool_shed/util/ratings_util.py
lib/galaxy/webhooks/
lib/galaxy/work/
lib/galaxy/workflow/extract.py
lib/galaxy/workflow/run.py
lib/galaxy/workflow/schedulers/core.py
lib/galaxy/workflow/steps.py
lib/galaxy/workflow/
lib/galaxy_ext/
lib/galaxy_utils/
lib/log_tempfile.py
lib/psyco_full.py
lib/tool_shed/
+4 -3
View File
@@ -17,6 +17,7 @@ database/files
database/jobs_directory
database/job_working_directory
database/pbs
database/test_errors
database/tmp
database/*.sqlite
database/openid_consumer_cache
@@ -93,7 +94,7 @@ tool-data/genome/*
tool-data/*.sample
tool-data/testtoolshed.g2.bx.psu.edu/
tool-data/toolshed.g2.bx.psu.edu/
tool-data/**/*.fa
tool-data/**/*.fa
# Test output
test-data-cache
@@ -121,7 +122,7 @@ tool-data/shared/jars/
sprite-*.less
# JS, Local node_modules, and bower_components directories
static/scripts/bundled
static/scripts/bundled/[0-9]*
node_modules
bower_components
@@ -135,4 +136,4 @@ doc/source/dev/schema.rst
.DS_Store
*.rej
*~
.idea/
.idea
+6 -1
View File
@@ -1,3 +1,4 @@
sudo: false
language: python
python: 2.7
os:
@@ -24,9 +25,13 @@ matrix:
allow_failures:
- env: TOX_ENV=py27-lint-imports
addons:
apt:
packages:
- libxml2-utils
install:
- pip install tox
- if [ "$TOX_ENV" == "validate-test-tools" ]; then sudo apt-get install libxml2-utils; fi
- if [ "$TOX_ENV" == "qunit" ]; then bash -c 'cd test/qunit && npm install'; fi
- if [ "$TOX_ENV" == "first_startup" ]; then bash -c "bash scripts/common_startup.sh && wget -q https://github.com/jmchilton/galaxy-downloads/raw/master/db_gx_rev_0127.sqlite && mv db_gx_rev_0127.sqlite database/universe.sqlite && bash manage_db.sh -c ./config/galaxy.ini.sample upgrade"; fi
+1 -1
View File
@@ -16,7 +16,7 @@ following publications:
a platform for interactive large-scale genome analysis."
Genome Research. 2005 Oct; 15(10):1451-5.
See also: http://wiki.galaxyproject.org/CitingGalaxy
See also: https://galaxyproject.org/citing-galaxy
+16 -21
View File
@@ -6,26 +6,25 @@ galaxy project](https://github.com/galaxyproject/galaxy) -
also checkout our 2013 Galaxy Community
Conference presentation on the topic
([video](https://vimeo.com/channels/581875/73486255),
[presentation](https://wiki.galaxyproject.org/Documents/Presentations/GCC2013?action=AttachFile&do=view&target=BakerContribute.pdf)). For
[presentation](https://depot.galaxyproject.org/hub/attachments/documents/presentations/gcc2013/BakerContribute.pdf)). For
information on contributing more broadly to the Galaxy ecosystem and a
deeper discussion of some of these points - please see the
[Develop](https://wiki.galaxyproject.org/Develop/) section of the
[Galaxy Wiki](https://wiki.galaxyproject.org/).
[Develop](https://galaxyproject.org/develop) section of the
[Galaxy Community Hub](https://galaxyproject.org).
## Before you Begin
If you have an idea for a feature to add or an approach for a bugfix,
it is best to communicate with Galaxy developers early. The most
common venues for this are
[GitHub issues](https://github.com/galaxyproject/galaxy/issues) and the
[Galaxy and Tool Shed Trello boards](https://wiki.galaxyproject.org/Issues).
[GitHub issues](https://github.com/galaxyproject/galaxy/issues).
Browse through existing GitHub issues and Trello cards and if one seems related,
comment on it. We also maintain a [card](https://trello.com/c/eFdPIdIB) with
links to smaller issues we believe would make the best entry points for new
comment on it. We also maintain a [tag](https://github.com/galaxyproject/galaxy/issues?q=is%3Aissue+is%3Aopen+label%3Afriendliness%2Ffriendly) on Github for
smaller issues we believe would make the best entry points for new
developers.
Galaxy developers are generally available via
[IRC](https://wiki.galaxyproject.org/GetInvolved#IRC_Channel) and on
the [development mailing list](http://dev.list.galaxyproject.org/).
[IRC](https://galaxyproject.org/get-involved), [Gitter](https://gitter.im/galaxyproject/Lobby)
and on the [development mailing list](http://dev.list.galaxyproject.org).
## Reporting a new issue
@@ -67,21 +66,18 @@ opened using [this form](https://github.com/galaxyproject/galaxy/issues/new).
should be made against the recent `release_XX.XX` branch (`git checkout release_XX.XX`).
* Serious security problems should not be fixed via pull request - please
responsibly disclose these by e-mailing them (with or without patches) to
galaxy-committers@lists.galaxyproject.org . The Galaxy core development team will
issue patches to public servers before announcing the issue to ensure there
is time to patch and highlight these fixes widely. We will provide you
credit for the discovery when publicly disclosing the issue.
see [the Galaxy security policies](SECURITY_POLICY.md) for information
about responsibly disclosing security issues.
* If your changes modify code - please ensure the resulting files
conform to Galaxy [style
guidelines](https://wiki.galaxyproject.org/Develop/BestPractices).
guidelines](https://galaxyproject.org/develop/best-practices).
* Galaxy contains hundreds of tests of different types and complexity
and running each is difficult and probably not reasonable at this
time (someday we will provide a holistic test procedure to make this
possible). For now, please just review the [running tests
documentation](https://wiki.galaxyproject.org/Admin/RunningTests)
documentation](https://galaxyproject.org/admin/running-tests)
and run any that seem relevant. Developers reviewing your pull
request will be happy to help guide you to running the most relevant
tests as part of the pull request review process and may request the
@@ -110,11 +106,10 @@ opened using [this form](https://github.com/galaxyproject/galaxy/issues/new).
## A Quick Note about Tools
For the most part, Galaxy tools should be published to the
[Tool Shed](https://wiki.galaxyproject.org/ToolShed) and not in this
repository directly. If you are looking to supply fixes for migrated
[Tool Shed](https://galaxyproject.org/toolshed) and not in this
repository directly. If you are looking for migrated
core tools that used to exist in this repository - please checkout
the [tools-devteam](https://github.com/galaxyproject/tools-devteam)
the [tools-iuc](https://github.com/galaxyproject/tools-iuc)
repository on GitHub.
More information about tool development can be found [on the
wiki](https://wiki.galaxyproject.org/Develop).
More information about tool development can be found [on the comunity hub](https://galaxyproject.org/develop).
+38
View File
@@ -6,14 +6,23 @@ The following individuals have contributed code to Galaxy:
* Istvan Albert <istvan.albert@gmail.com>
* Renato Alves <alves.rjc@gmail.com> <rjalves@igc.gulbenkian.pt>
* Guruprasad Ananda <gua110@bx.psu.edu>
* Evgeny Anatskiy <evgeny.anatskiy@gmail.com>
* Florent Angly <florent.angly@gmail.com>
* Raj Ayyampalayam <raj76@uga.edu>
* Abdulrahman Azab <eng.azab@gmail.com>
* Finn Bacall <finn.bacall@cs.man.ac.uk>
* Dannon Baker <dannon.baker@gmail.com>
* balto <balto_59@hotmail.fr>
* Christopher Bare <christopherbare@gmail.com>
* Bérénice Batut <berenice.batut@gmail.com>
* Marius van den Beek <m.vandenbeek@gmail.com>
* Maria Bernard <maria.bernard@jouy.inra.fr>
* Jean-Frédéric Berthelot <jean-frederic.berthelot@lifl.fr>
* Léo Biscassi <leo.biscassi@gmail.com>
* Dan Blanchard <dan.blanchard@gmail.com>
* Clemens Blank <blankclemens@gmail.com>
* Daniel Blankenberg <dan.blankenberg@gmail.com> <dan@bx.psu.edu>
* Jorrit Boekel <jorrit.boekel@scilifelab.se>
* James Boocock <sfk2001@gmail.com>
* Carlos Borroto <carlos.borroto@gmail.com>
* Daniel Bouchard <dbouchard@corefacility.ca> <daniel.bouchard@phac-aspc.gc.ca>
@@ -26,6 +35,7 @@ The following individuals have contributed code to Galaxy:
* Jennifer Cabral <jencabral@gmail.com>
* Martin Čech <marten@bx.psu.edu>
* Ramkrishna Chakrabarty <rc@bx.psu.edu>
* Matt Chambers <matt.chambers42@gmail.com>
* Brad Chapman <chapmanb@50mail.com>
* John Chilton <jmchilton@gmail.com>
* Saket Choudhary <saketkc@gmail.com>
@@ -43,13 +53,17 @@ The following individuals have contributed code to Galaxy:
* Shane Dowling <shane@shanedowling.com>
* John Duddy <jduddy@illumina.com>
* Carl Eberhard <carlfeberhard@gmail.com>
* Ignacio Eguinoa <ignacio.eguinoa@gmail.com>
* Mark Einon <mark.einon@gmail.com>
* Kyle Ellrott <kellrott@gmail.com> <kellrott@soe.ucsc.edu>
* Eric Enns <eric.enns@gmail.com>
* fescudie <fescudie@toulouse.inra.fr>
* Anne Fouilloux <annefou@uio.no>
* Dorine Francheteau <dorine@bx.psu.edu>
* Jean-Frédéric (@JeanFred on Github)
* Maximilian Friedersdorff <max@friedersdorff.com>
* Jaime Frey <jfrey@cs.wisc.edu>
* Ben Fulton <benmarkfulton@gmail.com>
* Carrie Ganote <cganote@iu.edu>
* Ryan Golhar <ngsbioinformatics@gmail.com>
* Jeremy Goecks <jeremy.goecks@emory.edu> <jgoecks@gwu.edu>
@@ -63,29 +77,41 @@ The following individuals have contributed code to Galaxy:
* Saskia Hiltemann <zazkia@gmail.com>
* Rob Hooft <rob.hooft@nbic.nl>
* Y. Hoogstrate <y.hoogstrate@erasmusmc.nl>
* Hans-Rudolf Hotz <hrhotz@gmail.com>
* Jian-Long Huang <jlh@pyhub.org>
* Gert Hulselmans <gert.hulselmans@med.kuleuven.be>
* Manabu Ishii <manabu.ishii.rb@gmail.com>
* Jennifer Jackson <jen@bx.psu.edu>
* Joachim Jacob <joachim.jacob@gmail.com>
* Xiaoqian Jiang <jxq198409@hotmail.com>
* Jim Johnson <jj@umn.edu> <jj@msi.umn.edu>
* Radhesh Kamath <radhesh@bx.psu.edu>
* Iyad Kandalaft <ik@iyadk.com>
* Jan Kanis <jan.code@jankanis.nl>
* David King <dcking@bx.psu.edu>
* Rory Kirchner <roryk@mit.edu>
* Edward Kirton <eskirton@lbl.gov>
* Jasper Koehorst <jasperkoehorst@gmail.com>
* Anup Kumar <anup.rulez@gmail.com>
* Brad Langhorst <langhorst@neb.com>
* Delphine Lariviere <lariviere.delphine@gmail.com>
* Ross Lazarus <ross.lazarus@gmail.com> <rossl@bx.psu.edu>
* Yvan Le Bras <yvan.le_bras@irisa.fr>
* Gildas Le Corguillé @lecorguille
* Alexander Lenail <alexander.lenail@tufts.edu>
* Simone Leo <simone.leo@gmail.com>
* Kanwei Li <kanwei@gmail.com>
* Michael Li <michael.li@uwaterloo.ca>
* Pierre Lindenbaum <plindenbaum@yahoo.fr>
* Mikael Loaec <mikael.loaec@versailles.inra.fr>
* Thoba Lose <lose.thoba@gmail.com>
* Philip Mabon <philipmabon@gmail.com>
* Remi Marenco <remi.marenco@gmail.com> <remimarenco@gmail.com>
* Zipho Mashologu <zipho@trustpay.biz>
* Thomas McGowan <mcgo0092@msi.umn.edu>
* Scott McManus <scottmcmanus@emory.edu> <scottmcmanus@gatech.edu>
* Hervé Ménager <herve.menager@pasteur.fr>
* Pablo Moreno <pablo.a.moreno@gmail.com>
* Hunter Moseley <hunter.moseley@louisville.edu>
* Takao Nakaguchi <takao.nakaguchi@gmail.com>
* Arjun Nath <arjun@bx.psu.edu>
@@ -93,36 +119,48 @@ The following individuals have contributed code to Galaxy:
* Eric Paniagua <paniagua.cshl@gmail.com>
* Richard Park <rpark@bu.edu>
* Lance Parsons <lparsons@princeton.edu>
* Balthazar Pavot <balthazar.pavot@hotmail.fr>
* Chinmay Rao <chinmay@bx.psu.edu>
* Matt Ralston <mrals89@gmail.com>
* ramezrawas <ramezrawas@gmail.com>
* Eric Rasche <esr@tamu.edu> <rasche.eric@gmail.com> <rasche.eric@yandex.ru>
* Athos Ribeiro <athoscr@fedoraproject.org>
* Andrew Robinson <Andrew.Robinson@latrobe.edu.au>
* Devon Ryan <dpryan79@gmail.com>
* Michael Sauria <crockopotamus@gmail.com>
* Andrea Sbardellati <andrea.sbardellati@crs4.it>
* Ian Schenck <ian@bx.psu.edu>
* Nick Semenkovich <semenko@alum.mit.edu>
* Varun Shankar <varunshankar55@gmail.com>
* Matthew Shirley <mdshw5@gmail.com>
* Timur Shtatland <shtatland@neb.com>
* Sourav Singh <ssouravsingh12@gmail.com>
* Clare Sloggett <sloc@unimelb.edu.au>
* Eteri Sokhoyan @Sokhoyan
* Nicola Soranzo <nicola.soranzo@tgac.ac.uk> <nsoranzo@tiscali.it> <soranzo@crs4.it>
* Nick Stoler <nick@nstoler.com>
* Roy Storey <kiwiroy@gmail.com>
* Hanfei Sun <ad9075@gmail.com>
* Ilya Sytchev <hackdna@gmail.com>
* Scott Szakonyi <sszakony@nd.edu>
* James Taylor <james@jamestaylor.org>
* Tomithy Too <tomithy.too@gmail.com>
* David Trudgian <dave@trudgian.net> <david.trudgian@utsouthwestern.edu>
* Nitesh Turaga <nitesh.turaga@gmail.com>
* Clayton Turner <clayclay911@gmail.com>
* Jesse c j van Dam <jesse.vandam@wur.nl>
* Ashok Varadharajan <ashvark@gmail.com>
* Marek Vavruša <marek@vavrusa.com>
* Martijn Vermaat <m.vermaat.hg@lumc.nl>
* Kelly Vincent <kpvincent@bx.psu.edu>
* Jeremy Volkening <jdv@base2bio.com>
* Greg Von Kuster <greg@bx.psu.edu>
* Pavan Videm <videmp@informatik.uni-freiburg.de>
* Hiral Vora <hvora1@uncc.edu>
* Junzhou Wang <junzhouwang@gmail.com>
* Andrew Warren <anwarren@vbi.vt.edu>
* Trevor Wennblom <trevor@well.com>
* Joachim Wolff <wolffj@informatik.uni-freiburg.de>
* Thomas Wollmann <thomas.s.wollmann@gmail.com> <thomas.wollmann@bioquant.uni-heidelberg.de>
* Jay Young <xiaojay@gmail.com>
* Yi Zhang <yizhang@bx.psu.edu>
+5 -7
View File
@@ -22,14 +22,14 @@ OPEN_RESOURCE=bash -c 'open $$0 || xdg-open $$0'
SLIDESHOW_TO_PDF?=bash -c 'docker run --rm -v `pwd`:/cwd astefanutti/decktape /cwd/$$0 /cwd/`dirname $$0`/`basename -s .html $$0`.pdf'
all: help
@echo "This makefile is primarily used for building Galaxy's JS client. A sensible all target is not yet implemented."
@echo "This makefile is used for building Galaxy's JS client, documentation, and drive the release process. A sensible all target is not implemented."
# Building docs requires sphinx and utilities be installed (see issue 3166) as well as pandoc.
# Run following commands to setup the Python portion of these requirements:
docs: ## Generate HTML documentation.
# Run following commands to setup the Python portion of the requirements:
# $ ./scripts/common_startup.sh
# $ . .venv/bin/activate
# $ pip install sphinx sphinx_rtd_theme lxml recommonmark
docs: ## generate Sphinx HTML documentation, including API docs
# $ pip install -r lib/galaxy/dependencies/dev-requirements.txt
# You also need to install pandoc separately.
$(IN_VENV) $(MAKE) -C doc clean
$(IN_VENV) $(MAKE) -C doc html
@@ -148,8 +148,6 @@ release-create-rc: release-ensure-upstream ## Create a release-candidate branch
git push $(MY_UPSTREAM) version-$(RELEASE_CURR):version-$(RELEASE_CURR)
git push $(MY_UPSTREAM) version-$(RELEASE_NEXT).dev:version-$(RELEASE_NEXT).dev
git checkout dev
git branch -d version-$(RELEASE_CURR)
git branch -d version-$(RELEASE_NEXT).dev
# TODO: Use hub to automate these PR creations or push directly.
@echo "Open a PR from version-$(RELEASE_CURR) of your fork to release_$(RELEASE_CURR)"
@echo "Open a PR from version-$(RELEASE_NEXT).dev of your fork to dev"
+8 -4
View File
@@ -1,4 +1,4 @@
.. figure:: https://wiki.galaxyproject.org/Images/GalaxyLogo?action=AttachFile&do=get&target=galaxy_project_logo.jpg
.. figure:: https://galaxyproject.org/images/galaxy-logos/galaxy_project_logo.jpg
:alt: Galaxy Logo
The latest information about Galaxy is available via `https://galaxyproject.org/ <https://galaxyproject.org/>`__
@@ -9,9 +9,13 @@ The latest information about Galaxy is available via `https://galaxyproject.org/
.. image:: https://img.shields.io/badge/chat-irc.freenode.net%23galaxyproject-blue.svg
:target: https://webchat.freenode.net/?channels=galaxyproject
:alt: Chat with us
:alt: Chat on irc
.. image:: https://img.shields.io/badge/chat-gitter-blue.svg
:target: https://gitter.im/galaxyproject/Lobby
:alt: Chat on gitter
.. image:: https://img.shields.io/badge/docs-release-green.svg
.. image:: https://img.shields.io/badge/release-documentation-blue.svg
:target: https://docs.galaxyproject.org/en/master/
:alt: Release Documentation
@@ -45,7 +49,7 @@ done in the ``config/galaxy.ini`` file. Tools can be either installed
from the Tool Shed or added manually. For details please see the Galaxy
wiki:
https://wiki.galaxyproject.org/Admin/Tools/AddToolFromToolShedTutorial
https://galaxyproject.org/admin/tools/add-tool-from-toolshed-tutorial/
Not all dependencies are included for the tools provided in the sample
``tool_conf.xml``. A full list of external dependencies is available at:
+62
View File
@@ -0,0 +1,62 @@
# Security
The Galaxy project is strongly committed to security and responsible disclosure. We have adopted and published a set of policies specifying how we will act in response to reported security issues, in order to ensure timely updates are made available to all affected parties.
## Reporting Security Issues
If you believe you have discovered a security issue, please email [galaxy-committers@lists.galaxyproject.org](galaxy-committers@lists.galaxyproject.org). Please use `[SECURITY]` in the email title. Someone on that list will acknowledge your email within 2 US business days. We ask that you not disclose the issues on the public issue tracker. We will provide you credit for the discovery when publicly disclosing the issue.
Security issues which *only* affect a pre-release version of Galaxy (i.e. the `dev` branch in GitHub) do not need to go through this process, so you may open issues and pull requests publicly.
## Supported versions
The following branches or releases receive security support:
- Development on the `dev` branch, hosted on GitHub, which will become the next release of Galaxy
- Releases within the past 12 months.
- E.g. 16.04 will receive support until 2017-04. As the month changes to 2017-05 it will become unsupported.
- There are currently no plans for Long Term Support (LTS) releases.
For unsupported branches:
- Older versions of Galaxy may be affected by security issues.
- Security patches *may* apply
- The security team does not commit to investigating issues that pertain to unsupported releases.
- The security team does not commit to issuing patches or new releases of unsupported versions.
## Issue Severity
Galaxy takes a very conservative stance on issue severity as individual Galaxy instances often install tools and make customizations that might increase their risk in the face of otherwise less-serious vulnerabilities. As a result, issues that would be considered less-severe in other projects may be treated as higher risk here.
### Issue Classification
Severity | Examples
------------ | ---------
High | Remote code execution (RCE), SQL Injection, Cross-site scripting (XSS), and *any issue allowing user impersonation*.
Medium / Low | Unvalidated redirects/forwards, Issues due to uncommon configuration options.
These are only examples. The security team will provide a severity classification based on its impact on the average Galaxy instance. However, Galaxy administrators should take it upon themselves to evaluate the impact for their instance(s).
## Notification of Vulnerabilities
For high severity issues, we will notify [the list of public Galaxy owners](https://lists.galaxyproject.org/listinfo/galaxy-public-servers) with:
- A description of the issue
- List of supported versions that are affected
- Steps to update or patch your Galaxy
The issue will then be embargoed for three (3) days. For medium and low
severity issues, or for publicly announcing high severity issues after the
embargo, we will:
- Patch the oldest release within the 12 month support window, and merge that fix forward.
- Updates will be available on the `release_XX.YY` branches.
- Update each release branch
- Post a notice to the [galaxy-announce mailing list](https://lists.galaxyproject.org/listinfo/galaxy-announce) with:
- A description of the issue
- List of supported versions that are affected
- Steps to update or patch your Galaxy
If an issue is deemed to be time-sensitive – e.g. due to active and ongoing exploits in the wild – the embargo may be shortened considerably.
If we believe that the reported issue affects other Galaxy Project components or projects outside of the Galaxy ecosystem, we may discuss the issue with those projects and coordinate disclosure and resolution with them.
+128
View File
@@ -0,0 +1,128 @@
define(["mvc/toolshed/shed-list-view",
"mvc/toolshed/categories-view",
"mvc/toolshed/repositories-view",
"mvc/toolshed/repository-view",
"mvc/toolshed/repository-queue-view",
"mvc/toolshed/repo-status-view",
"mvc/toolshed/workflows-view"],
function(mod_shed_list_view,
mod_categories_view,
mod_repositories_view,
mod_repository_view,
mod_repoqueue_view,
mod_repo_status_view,
mod_workflows_view) {
var AdminToolshedRouter = Backbone.Router.extend({
initialize: function() {
this.routesHit = 0;
// keep count of number of routes handled by the application
Backbone.history.on( 'route', function() { this.routesHit++; }, this );
this.bind( 'route', this.trackPageview );
},
routes: {
"" : "toolsheds",
"sheds" : "toolsheds",
"queue" : "queue",
"workflows" : "workflows",
"status/r/:repositories" : "status",
"status" : "status",
"categories/s/:tool_shed" : "categories",
"category/s/:tool_shed/c/:cateory_id" : "repositories",
"repository/s/:tool_shed/r/:repository_id" : "repository",
},
/**
* If more than one route has been hit the user did not land on current
* page directly so we can go back safely. Otherwise go to the home page.
* Use replaceState if available so the navigation doesn't create an
* extra history entry
*/
back: function() {
if( this.routesHit > 1 ) {
window.history.back();
} else {
this.navigate( '#', { trigger:true, replace:true } );
}
}
});
var GalaxyAdminToolshedApp = Backbone.View.extend({
app_config: {
known_views: ['toolsheds', 'queue', 'status', 'categories', 'repositories', 'repoository']
},
initialize: function() {
Galaxy.admintoolshedapp = this;
this.admin_toolshed_router = new AdminToolshedRouter();
this.admin_toolshed_router.on('route:queue', function() {
if (Galaxy.admintoolshedapp.adminRepoQueueView) {
Galaxy.admintoolshedapp.adminRepoQueueView.reDraw();
}
else {
Galaxy.admintoolshedapp.adminRepoQueueView = new mod_repoqueue_view.RepoQueueView();
}
});
this.admin_toolshed_router.on('route:toolsheds', function() {
if (Galaxy.admintoolshedapp.adminShedListView) {
Galaxy.admintoolshedapp.adminShedListView.reDraw();
}
else {
Galaxy.admintoolshedapp.adminShedListView = new mod_shed_list_view.ShedListView();
}
});
this.admin_toolshed_router.on('route:categories', function(tool_shed) {
if (Galaxy.admintoolshedapp.adminShedCategoriesView) {
Galaxy.admintoolshedapp.adminShedCategoriesView.reDraw({tool_shed: tool_shed.replace(/\//g, '%2f')});
}
else {
Galaxy.admintoolshedapp.adminShedCategoriesView = new mod_categories_view.CategoryView({tool_shed: tool_shed.replace(/\//g, '%2f')});
}
});
this.admin_toolshed_router.on('route:repositories', function(tool_shed, category_id) {
if (Galaxy.admintoolshedapp.adminShedCategoryView) {
Galaxy.admintoolshedapp.adminShedCategoryView.reDraw({tool_shed: tool_shed.replace(/\//g, '%2f'), category_id: category_id});
}
else {
Galaxy.admintoolshedapp.adminShedCategoryView = new mod_repositories_view.Category({tool_shed: tool_shed.replace(/\//g, '%2f'), category_id: category_id});
}
});
this.admin_toolshed_router.on('route:repository', function(tool_shed, repository_id) {
if (Galaxy.admintoolshedapp.adminRepositoryView) {
Galaxy.admintoolshedapp.adminRepositoryView.reDraw({tool_shed: tool_shed.replace(/\//g, '%2f'), repository_id: repository_id});
}
else {
Galaxy.admintoolshedapp.adminRepositoryView = new mod_repository_view.RepoDetails({tool_shed: tool_shed.replace(/\//g, '%2f'), repository_id: repository_id});
}
});
this.admin_toolshed_router.on('route:status', function(repositories) {
if (Galaxy.admintoolshedapp.adminRepoStatusView) {
Galaxy.admintoolshedapp.adminRepoStatusView.reDraw({repositories: repositories.split('|')});
}
else {
Galaxy.admintoolshedapp.adminRepoStatusView = new mod_repo_status_view.RepoStatus({repositories: repositories.split('|')});
}
});
this.admin_toolshed_router.on('route:workflows', function() {
if (Galaxy.admintoolshedapp.adminWorkflowsView) {
Galaxy.admintoolshedapp.adminWorkflowsView.reDraw();
}
else {
Galaxy.admintoolshedapp.adminWorkflowsView = new mod_workflows_view.Workflows();
}
});
Backbone.history.start({pushState: false});
},
});
return {
GalaxyApp: GalaxyAdminToolshedApp
};
});
+17
View File
@@ -0,0 +1,17 @@
var jQuery = require( 'jquery' ),
$ = jQuery,
GalaxyApp = require( 'galaxy' ).GalaxyApp,
AdminPanel = require( './panels/admin-panel' ),
Page = require( 'layout/page' );
window.app = function app( options, bootstrapped ){
window.Galaxy = new GalaxyApp( options, bootstrapped );
Galaxy.debug( 'admin app' );
Galaxy.params = Galaxy.config.params;
$(function(){
_.extend( options.config, { active_view : 'admin' } );
Galaxy.page = new Page.View( _.extend( options, {
Left : AdminPanel
} ) );
});
};
+150 -84
View File
@@ -1,15 +1,22 @@
var jQuery = require( 'jquery' ),
$ = jQuery,
GalaxyApp = require( 'galaxy' ).GalaxyApp,
QUERY_STRING = require( 'utils/query-string-parsing' ),
PANEL = require( 'layout/panel' ),
ToolPanel = require( './tool-panel' ),
HistoryPanel = require( './history-panel' ),
PAGE = require( 'layout/page' ),
ToolPanel = require( './panels/tool-panel' ),
HistoryPanel = require( './panels/history-panel' ),
Page = require( 'layout/page' ),
ToolForm = require( 'mvc/tool/tool-form' ),
UserPreferences = require( 'mvc/user/user-preferences' );
Tours = require( 'mvc/tours' );
UserPreferences = require( 'mvc/user/user-preferences' ),
CustomBuilds = require( 'mvc/user/user-custom-builds' ),
Tours = require( 'mvc/tours' ),
GridView = require( 'mvc/grid/grid-view' ),
GridShared = require( 'mvc/grid/grid-shared' ),
Workflows = require( 'mvc/workflow/workflow' ),
HistoryList = require( 'mvc/history/history-list' ),
ToolFormComposite = require( 'mvc/tool/tool-form-composite' ),
Utils = require( 'utils/utils' ),
Ui = require( 'mvc/ui/ui-misc' ),
DatasetEditAttributes = require('mvc/dataset/dataset-edit-attributes');
/** define the 'Analyze Data'/analysis/main/home page for Galaxy
* * has a masthead
@@ -25,95 +32,145 @@ var jQuery = require( 'jquery' ),
window.app = function app( options, bootstrapped ){
window.Galaxy = new GalaxyApp( options, bootstrapped );
Galaxy.debug( 'analysis app' );
// TODO: use router as App base (combining with Galaxy)
// .................................................... panels and page
var config = options.config,
toolPanel = new ToolPanel({
el : '#left',
userIsAnonymous : Galaxy.user.isAnonymous(),
toolbox : config.toolbox,
toolbox_in_panel : config.toolbox_in_panel,
stored_workflow_menu_entries : config.stored_workflow_menu_entries,
nginx_upload_path : config.nginx_upload_path,
ftp_upload_site : config.ftp_upload_site,
default_genome : config.default_genome,
default_extension : config.default_extension,
}),
centerPanel = new PANEL.CenterPanel({
el : '#center'
}),
historyPanel = new HistoryPanel({
el : '#right',
galaxyRoot : Galaxy.root,
userIsAnonymous : Galaxy.user.isAnonymous(),
allow_user_dataset_purge: config.allow_user_dataset_purge,
}),
analysisPage = new PAGE.PageLayoutView( _.extend( options, {
el : 'body',
left : toolPanel,
center : centerPanel,
right : historyPanel,
}));
// .................................................... decorate the galaxy object
// TODO: most of this is becoming unnecessary as we move to apps
Galaxy.page = analysisPage;
Galaxy.params = Galaxy.config.params;
// add tool panel to Galaxy object
Galaxy.toolPanel = toolPanel.tool_panel;
Galaxy.upload = toolPanel.uploadButton;
var routingMessage = Backbone.View.extend({
initialize: function(options) {
this.message = options.message || "Undefined Message";
this.msg_status = options.type || 'info';
this.render();
},
render: function(){
this.$el.html(_.escape(this.message)).addClass(this.msg_status + "message");
}
});
Galaxy.currHistoryPanel = historyPanel.historyView;
Galaxy.currHistoryPanel.listenToGalaxy( Galaxy );
//HACK: move there
Galaxy.app = {
display : function( view, target ){
// TODO: Remove this line after select2 update
$( '.select2-hidden-accessible' ).remove();
centerPanel.display( view );
},
};
// .................................................... routes
/** */
Galaxy.router = new ( Backbone.Router.extend({
/** Routes */
var Router = Backbone.Router.extend({
// TODO: not many client routes at this point - fill and remove from server.
// since we're at root here, this may be the last to be routed entirely on the client.
initialize : function( options ){
initialize : function( page, options ){
this.page = page;
this.options = options;
},
/** helper to push a new navigation state */
push: function( url, data ) {
data = data || {};
data.__identifer = Math.random().toString( 36 ).substr( 2 );
if ( !$.isEmptyObject( data ) ) {
url += url.indexOf( '?' ) == -1 ? '?' : '&';
url += $.param( data , true );
}
this.navigate( url, { 'trigger': true } );
},
/** override to parse query string into obj and send to each route */
execute: function( callback, args, name ){
Galaxy.debug( 'router execute:', callback, args, name );
var queryObj = QUERY_STRING.parse( args.pop() );
args.push( queryObj );
if( callback ){
callback.apply( this, args );
if ( this.authenticate( args, name ) ) {
callback.apply( this, args );
} else {
this.loginRequired();
}
}
},
routes : {
'(/)' : 'home',
// TODO: remove annoying 'root' from root urls
'(/)root*' : 'home',
'(/)tours(/)(:tour_id)' : 'show_tours',
'(/)users(/)' : 'show_users',
'(/)user(/)' : 'show_user',
'(/)user(/)(:form_id)' : 'show_user_form',
'(/)workflow(/)' : 'show_workflows',
'(/)workflow/run(/)' : 'show_run',
'(/)pages(/)(:action_id)' : 'show_pages',
'(/)visualizations/(:action_id)' : 'show_visualizations',
'(/)workflows/list_published(/)' : 'show_workflows_published',
'(/)histories(/)(:action_id)' : 'show_histories',
'(/)datasets(/)list(/)' : 'show_datasets',
'(/)workflow/import_workflow' : 'show_import_workflow',
'(/)custom_builds' : 'show_custom_builds',
'(/)datasets/edit': 'show_dataset_edit_attributes'
},
require_login: [
'show_user',
'show_user_form',
'show_workflows'
],
loginRequired: function() {
this.page.display( new routingMessage({type: 'error', message: "You must be logged in to make this request."}) );
},
authenticate: function( args, name ) {
return ( Galaxy.user && Galaxy.user.id ) || this.require_login.indexOf( name ) == -1;
},
show_tours : function( tour_id ){
if ( tour_id ){
Tours.giveTour( tour_id );
} else {
centerPanel.display( new Tours.ToursView() );
this.page.display( new Tours.ToursView() );
}
},
show_users : function(){
centerPanel.display( new UserPreferences.View() );
show_user : function(){
this.page.display( new UserPreferences.View() );
},
show_user_form : function( form_id ) {
this.page.display( new UserPreferences.Forms( { form_id: form_id, user_id: Galaxy.params.id } ) );
},
show_visualizations : function( action_id ) {
this.page.display( new GridShared.View( { action_id: action_id, plural: 'Visualizations', item: 'visualization' } ) );
},
show_workflows_published : function() {
this.page.display( new GridView( { url_base: Galaxy.root + 'workflow/list_published', dict_format: true } ) );
},
show_histories : function( action_id ) {
this.page.display( new HistoryList.View( { action_id: action_id } ) );
},
show_datasets : function() {
this.page.display( new GridView( { url_base: Galaxy.root + 'dataset/list', dict_format: true } ) );
},
show_pages : function( action_id ) {
this.page.display( new GridShared.View( { action_id: action_id, plural: 'Pages', item: 'page' } ) );
},
show_workflows : function(){
this.page.display( new Workflows.View() );
},
show_run : function() {
this._loadWorkflow();
},
show_import_workflow : function() {
this.page.display( new Workflows.ImportWorkflowView() );
},
show_custom_builds : function() {
var self = this;
var historyPanel = this.page.historyPanel.historyView;
if ( !historyPanel || !historyPanel.model || !historyPanel.model.id ) {
window.setTimeout(function() { self.show_custom_builds() }, 500)
return;
}
this.page.display( new CustomBuilds.View() );
},
show_dataset_edit_attributes : function() {
this.page.display( new DatasetEditAttributes.View() );
},
/** */
@@ -122,7 +179,7 @@ window.app = function app( options, bootstrapped ){
// load a tool by id (tool_id) or rerun a previous tool execution (job_id)
if( params.tool_id || params.job_id ) {
if ( params.tool_id === 'upload1' ) {
Galaxy.upload.show();
this.page.toolPanel.upload.show();
this._loadCenterIframe( 'welcome' );
} else {
this._loadToolForm( params );
@@ -130,7 +187,7 @@ window.app = function app( options, bootstrapped ){
} else {
// show the workflow run form
if( params.workflow_id ){
this._loadCenterIframe( 'workflow/run?id=' + params.workflow_id );
this._loadWorkflow();
// load the center iframe with controller.action: galaxy.org/?m_c=history&m_a=list -> history/list
} else if( params.m_c ){
this._loadCenterIframe( params.m_c + '/' + params.m_a );
@@ -144,34 +201,43 @@ window.app = function app( options, bootstrapped ){
/** load the center panel with a tool form described by the given params obj */
_loadToolForm : function( params ){
//TODO: load tool form code async
params.id = params.tool_id;
centerPanel.display( new ToolForm.View( params ) );
params.id = decodeURIComponent( params.tool_id );
this.page.display( new ToolForm.View( params ) );
},
/** load the center panel iframe using the given url */
_loadCenterIframe : function( url, root ){
root = root || Galaxy.root;
url = root + url;
centerPanel.$( '#galaxy_main' ).prop( 'src', url );
this.page.$( '#galaxy_main' ).prop( 'src', url );
},
}))( options );
/** load workflow by its url in run mode */
_loadWorkflow: function() {
var self = this;
Utils.get({
url: Galaxy.root + 'api/workflows/' + Utils.getQueryString( 'id' ) + '/download',
data: { 'style': 'run' },
success: function( response ) {
self.page.display( new ToolFormComposite.View( response ) );
},
error: function( response ) {
var error_msg = "Error occurred while loading the resource.",
options = { 'message': error_msg, 'status': 'error', 'persistent': true, 'cls': 'errormessage' };
self.page.display( new Ui.Message( options ) );
}
});
}
});
// .................................................... when the page is ready
// render and start the router
$(function(){
analysisPage.render();
analysisPage.right.historyView.loadCurrentHistory();
// use galaxy to listen to history size changes and then re-fetch the user's total size (to update the quota meter)
// TODO: we have to do this here (and after every page.render()) because the masthead is re-created on each
// page render. It's re-created each time because there is no render function and can't be re-rendered without
// re-creating it.
Galaxy.listenTo( analysisPage.right.historyView, 'history-size-change', function(){
// fetch to update the quota meter adding 'current' for any anon-user's id
Galaxy.user.fetch({ url: Galaxy.user.urlRoot() + '/' + ( Galaxy.user.id || 'current' ) });
});
analysisPage.right.historyView.connectToQuotaMeter( analysisPage.masthead.quotaMeter );
Galaxy.page = new Page.View( _.extend( options, {
Left : ToolPanel,
Right : HistoryPanel,
Router : Router
} ) );
// start the router - which will call any of the routes above
Backbone.history.start({
@@ -1,95 +0,0 @@
var RightPanel = require( 'layout/panel' ).RightPanel,
Ui = require( 'mvc/ui/ui-misc' ),
historyOptionsMenu = require( 'mvc/history/options-menu' );
CurrentHistoryView = require( 'mvc/history/history-view-edit-current' ).CurrentHistoryView,
_l = require( 'utils/localization' );
/** the right hand panel in the analysis page that shows the current history */
var HistoryPanel = RightPanel.extend({
title : _l( 'History' ),
initialize : function( options ){
RightPanel.prototype.initialize.call( this, options );
this.options = _.pick( options, 'userIsAnonymous', 'allow_user_dataset_purge', 'galaxyRoot' );
// view of the current history
this.historyView = new CurrentHistoryView({
className : CurrentHistoryView.prototype.className + ' middle',
purgeAllowed : options.allow_user_dataset_purge,
linkTarget : 'galaxy_main'
});
},
/** override to change footer selector */
$toggleButton : function(){
return this.$( '.footer > .panel-collapse' );
},
render : function(){
RightPanel.prototype.render.call( this );
this.optionsMenu = historyOptionsMenu( this.$( '#history-options-button' ), {
anonymous : this.options.userIsAnonymous,
purgeAllowed : this.options.allow_user_dataset_purge,
root : this.options.galaxyRoot
});
this.$( '> .header .buttons [title]' ).tooltip({ placement: 'bottom' });
this.historyView.setElement( this.$( '.history-panel' ) );
this.$el.attr( 'class', 'history-right-panel' );
},
/** override to add buttons */
_templateHeader: function( data ){
var historyUrl = this.options.galaxyRoot + 'history';
var multiUrl = this.options.galaxyRoot + 'history/view_multiple';
return [
'<div class="header">',
'<div class="buttons">',
// this button re-fetches the history and contents and re-renders the history panel
'<a id="history-refresh-button" title="', _l( 'Refresh history' ), '" ',
'class="" href="', historyUrl, '"><span class="fa fa-refresh"></span></a>',
// opens a drop down menu with history related functions (like view all, delete, share, etc.)
'<a id="history-options-button" title="', _l( 'History options' ), '" ',
'class="" href="javascript:void(0)"><span class="fa fa-cog"></span></a>',
!this.options.userIsAnonymous?
[ '<a id="history-view-multi-button" title="', _l( 'View all histories' ), '" ',
'class="" href="', multiUrl, '"><span class="fa fa-columns"></span></a>' ].join('') : '',
'</div>',
'<div class="title">', _.escape( this.title ), '</div>',
'</div>',
].join('');
},
/** add history view div */
_templateBody : function( data ){
return [
'<div id="current-history-panel" class="history-panel middle"/>',
].join('');
},
/** override to use simplified selector */
_templateFooter: function( data ){
return [
'<div class="footer">',
'<div class="panel-collapse ', _.escape( this.id ), '"/>',
'<div class="drag"/>',
'</div>',
].join('');
},
events : {
'click #history-refresh-button' : '_clickRefresh',
// override to change footer selector
'mousedown .footer > .drag' : '_mousedownDragHandler',
'click .footer > .panel-collapse' : 'toggle'
},
_clickRefresh : function( ev ){
ev.preventDefault();
this.historyView.loadCurrentHistory();
},
toString : function(){ return 'HistoryPanel'; }
});
module.exports = HistoryPanel;
+18 -22
View File
@@ -1,10 +1,8 @@
var jQuery = require( 'jquery' ),
$ = jQuery,
GalaxyApp = require( 'galaxy' ).GalaxyApp,
PANEL = require( 'layout/panel' ),
_l = require( 'utils/localization' ),
PAGE = require( 'layout/page' );
Page = require( 'layout/page' );
window.app = function app( options, bootstrapped ){
window.Galaxy = new GalaxyApp( options, bootstrapped );
@@ -19,26 +17,24 @@ window.app = function app( options, bootstrapped ){
return;
}
var loginPage = new PAGE.PageLayoutView( _.extend( options, {
el : 'body',
center : new PANEL.CenterPanel({ el : '#center' }),
right : new PANEL.RightPanel({
title : _l( 'Login required' ),
el : '#right'
}),
}));
var LoginPage = Backbone.View.extend({
initialize: function( page ) {
this.page = page;
this.model = new Backbone.Model({ title : _l( 'Login required' ) } );
this.setElement( this._template() );
},
render: function() {
this.page.$( '#galaxy_main' ).prop( 'src', options.welcome_url );
},
_template : function() {
var login_url = options.root + 'user/login?' + $.param( { redirect : redirect } );
return '<iframe src="' + login_url + '" frameborder="0" style="width: 100%; height: 100%;"/>';
}
});
$(function(){
// TODO: incorporate *actual* referrer/redirect info as the original page does
var params = jQuery.param({ redirect : redirect }),
loginUrl = Galaxy.root + 'user/login?' + params;
loginPage.render();
// welcome page (probably) needs to remain sandboxed
loginPage.center.$( '#galaxy_main' ).prop( 'src', options.welcome_url );
loginPage.right.$( '.unified-panel-body' )
.css( 'overflow', 'hidden' )
.html( '<iframe src="' + loginUrl + '" frameborder="0" style="width: 100%; height: 100%;"/>' );
Galaxy.page = new Page.View( _.extend( options, {
Right : LoginPage
} ) );
});
};
@@ -0,0 +1,169 @@
var _l = require( 'utils/localization' );
var AdminPanel = Backbone.View.extend({
initialize: function( page, options ) {
var self = this;
this.page = page;
this.root = options.root;
this.config = options.config;
this.settings = options.settings;
this.message = options.message;
this.status = options.status;
this.model = new Backbone.Model({
title : _l( 'Administration' )
});
this.categories = new Backbone.Collection([{
title : 'Server',
items : [ {
title : 'Data types registry',
url : 'admin/view_datatypes_registry'
},{
title : 'Data tables registry',
url : 'admin/view_tool_data_tables'
},{
title : 'Display applications',
url : 'admin/display_applications'
},{
title : 'Manage jobs',
url : 'admin/jobs'
} ]
},{
title : 'Tools and Tool Shed',
items : [ {
title : 'Search Tool Shed',
url : 'admin_toolshed/browse_tool_sheds',
enabled : self.settings.is_tool_shed_installed
},{
title : 'Search Tool Shed (Beta)',
url : 'admin_toolshed/browse_toolsheds',
enabled : self.settings.is_tool_shed_installed && self.config.enable_beta_ts_api_install
},{
title : 'Monitor installing repositories',
url : 'admin_toolshed/monitor_repository_installation?installing_repository_ids=' + self.settings.installing_repository_ids,
enabled : self.settings.installing_repository_ids
},{
title : 'Manage installed tools',
url : 'admin_toolshed/browse_repositories',
enabled : self.settings.is_repo_installed
},{
title : 'Reset metadata',
url : 'admin_toolshed/reset_metadata_on_selected_installed_repositories',
enabled : self.settings.is_repo_installed
},{
title : 'Download local tool',
url : 'admin/package_tool'
},{
title : 'Tool lineage',
url : 'admin/tool_versions'
},{
title : 'Reload a tool\'s configuration',
url : 'admin/reload_tool'
},{
title : 'Review tool migration stages',
url : 'admin/review_tool_migration_stages'
},{
title : 'View Tool Error Logs',
url : 'admin/tool_errors'
},{
title : 'Manage Display Whitelist',
url : 'admin/sanitize_whitelist'
},{
title : 'Manage Tool Dependencies',
url : 'admin/manage_tool_dependencies'
} ]
},{
title : 'User Management',
items : [ {
title : 'Users',
url : 'admin/users'
},{
title : 'Groups',
url : 'admin/groups'
},{
title : 'Roles',
url : 'admin/roles'
},{
title : 'API keys',
url : 'userskeys/all_users'
},{
title : 'Impersonate a user',
url : 'admin/impersonate',
enabled : self.config.allow_user_impersonation
} ]
},{
title : 'Data',
items : [ {
title : 'Quotas',
url : 'admin/quotas',
enabled : self.config.enable_quotas
},{
title : 'Data libraries',
url : 'library_admin/browse_libraries'
},{
title : 'Roles',
url : 'admin/roles'
},{
title : 'Local data',
url : 'data_manager'
} ]
},{
title : 'Form Definitions',
items : [ {
title : 'Form definitions',
url : 'forms/browse_form_definitions'
} ]
},{
title : 'Sample Tracking',
items : [ {
title : 'Sequencers and external services',
url : 'external_service/browse_external_services'
},{
title : 'Request types',
url : 'request_type/browse_request_types'
},{
title : 'Sequencing requests',
url : 'requests_admin/browse_requests'
},{
title : 'Find samples',
url : 'requests_common/find_samples?cntrller=requests_admin'
} ]
}]);
this.setElement( this._template() );
},
render : function() {
var self = this;
this.$el.empty();
this.categories.each( function( category ) {
var $section = $( self._templateSection( category.attributes ) );
var $entries = $section.find( '.ui-side-section-body' );
_.each( category.get( 'items' ), function( item ) {
if ( item.enabled === undefined || item.enabled ) {
$entries.append( $( '<div/>' ).addClass( 'ui-side-section-body-title' )
.append( $( '<a/>' ).attr({
href : self.root + item.url,
target : 'galaxy_main' }).text( _l( item.title ) ) ) );
}
});
self.$el.append( $section );
});
this.page.$( '#galaxy_main' ).prop( 'src', this.root + 'admin/center?message=' + this.message + '&status=' + this.status );
},
_templateSection : function( options ) {
return [
'<div>',
'<div class="ui-side-section-title">' + _l( options.title ) + '</div>',
'<div class="ui-side-section-body"/>',
'</div>'
].join('');
},
_template : function() {
return '<div class="ui-side-panel"/>';
},
toString : function() { return 'adminPanel' }
});
module.exports = AdminPanel;
@@ -0,0 +1,90 @@
var Ui = require( 'mvc/ui/ui-misc' ),
historyOptionsMenu = require( 'mvc/history/options-menu' );
CurrentHistoryView = require( 'mvc/history/history-view-edit-current' ).CurrentHistoryView,
_l = require( 'utils/localization' );
/** the right hand panel in the analysis page that shows the current history */
var HistoryPanel = Backbone.View.extend({
initialize : function( page, options ) {
var self = this;
this.userIsAnonymous = Galaxy.user.isAnonymous();
this.allow_user_dataset_purge = options.config.allow_user_dataset_purge;
this.root = options.root;
// view of the current history
this.historyView = new CurrentHistoryView({
className : CurrentHistoryView.prototype.className + ' middle',
purgeAllowed : this.allow_user_dataset_purge,
linkTarget : 'galaxy_main'
});
// add history panel to Galaxy object
Galaxy.currHistoryPanel = this.historyView;
Galaxy.currHistoryPanel.listenToGalaxy( Galaxy );
// build buttons
this.buttonRefresh = new Ui.ButtonLink({
id : 'history-refresh-button',
title : 'Refresh history',
cls : 'panel-header-button',
icon : 'fa fa-refresh',
onclick : function() {
self.historyView.loadCurrentHistory();
}
});
this.buttonOptions = new Ui.ButtonLink({
id : 'history-options-button',
title : 'History options',
cls : 'panel-header-button',
target : 'galaxy_main',
icon : 'fa fa-cog',
href : this.root + 'root/history_options'
});
this.buttonViewMulti = new Ui.ButtonLink({
id : 'history-view-multi-button',
title : 'View all histories',
cls : 'panel-header-button',
icon : 'fa fa-columns',
href : this.root + 'history/view_multiple'
});
// define components
this.model = new Backbone.Model({
cls : 'history-right-panel',
title : _l( 'History' ),
buttons : [ this.buttonRefresh, this.buttonOptions, this.buttonViewMulti ]
});
// build body template and connect history view
this.setElement( this._template() );
this.historyView.setElement( this.$el );
this.historyView.connectToQuotaMeter( Galaxy.quotaMeter );
this.historyView.loadCurrentHistory();
// fetch to update the quota meter adding 'current' for any anon-user's id
Galaxy.listenTo( this.historyView, 'history-size-change', function(){
Galaxy.user.fetch({ url: Galaxy.user.urlRoot() + '/' + ( Galaxy.user.id || 'current' ) });
});
},
render : function() {
this.optionsMenu = historyOptionsMenu( this.buttonOptions.$el, {
anonymous : this.userIsAnonymous,
purgeAllowed : this.allow_user_dataset_purge,
root : this.root
});
this.buttonViewMulti.$el[ !this.userIsAnonymous ? 'show' : 'hide' ]();
},
/** add history view div */
_template : function( data ){
return [
'<div id="current-history-panel" class="history-panel middle"/>',
].join('');
},
toString : function() { return 'historyPanel' }
});
module.exports = HistoryPanel;
@@ -0,0 +1,121 @@
var Tools = require( 'mvc/tool/tools' ),
Upload = require( 'mvc/upload/upload-view' ),
_l = require( 'utils/localization' ),
ToolForm = require( 'mvc/tool/tool-form-composite' );
var ToolPanel = Backbone.View.extend({
initialize: function( page, options ) {
// access configuration options
var config = options.config;
this.root = options.root;
/** @type {Object[]} descriptions of user's workflows to be shown in the tool menu */
this.stored_workflow_menu_entries = config.stored_workflow_menu_entries || [];
// create tool search, tool panel, and tool panel view.
var tool_search = new Tools.ToolSearch({
hidden : false
});
var tools = new Tools.ToolCollection( config.toolbox );
this.tool_panel = new Tools.ToolPanel({
tool_search : tool_search,
tools : tools,
layout : config.toolbox_in_panel
});
this.tool_panel_view = new Tools.ToolPanelView({ model: this.tool_panel });
// add upload modal
this.upload_button = new Upload({
nginx_upload_path : config.nginx_upload_path,
ftp_upload_site : config.ftp_upload_site,
default_genome : config.default_genome,
default_extension : config.default_extension,
});
// add uploader button to Galaxy object
Galaxy.upload = this.upload_button;
// components for panel definition
this.model = new Backbone.Model({
title : _l( 'Tools' ),
buttons : [ this.upload_button ]
});
// build body template
this.setElement( this._template() );
},
render : function(){
// if there are tools, render panel and display everything
var self = this;
if ( this.tool_panel.get( 'layout' ).size() > 0 ) {
this.$el.prepend( this.tool_panel_view.$el );
this.tool_panel_view.render();
}
// build the dom for the workflow portion of the tool menu
// add internal workflow list
self.$( '#internal-workflows' ).append( self._templateAllWorkflow({
title : _l( 'All workflows' ),
href : 'workflow'
}));
_.each( this.stored_workflow_menu_entries, function( menu_entry ){
self.$( '#internal-workflows' ).append( self._templateWorkflowLink({
title : menu_entry.stored_workflow.name,
href : 'workflow/run?id=' + menu_entry.encoded_stored_workflow_id
}));
});
},
/** build a link to one tool */
_templateTool: function( tool ) {
return [
'<div class="toolTitle">',
'<a href="', this.root, tool.href, '" target="galaxy_main">', tool.title, '</a>',
'</div>'
].join('');
},
/** build a link to 'All Workflows' */
_templateAllWorkflow: function( tool ) {
return [
'<div class="toolTitle">',
// global
'<a href="', Galaxy.root, tool.href, '">', tool.title, '</a>',
'</div>'
].join('');
},
/** build links to workflows in toolpanel */
_templateWorkflowLink: function( wf ) {
return [
'<div class="toolTitle">',
'<a class="'+ wf.cls +' " href="', Galaxy.root, wf.href, '">', wf.title, '</a>',
'</div>'
].join('');
},
/** override to include inital menu dom and workflow section */
_template : function() {
return [
'<div class="toolMenuContainer">',
'<div class="toolMenu" style="display: none">',
'<div id="search-no-results" style="display: none; padding-top: 5px">',
'<em><strong>', _l( 'Search did not match any tools.' ), '</strong></em>',
'</div>',
'</div>',
'<div class="toolSectionPad"/>',
'<div class="toolSectionPad"/>',
'<div class="toolSectionTitle" id="title_XXinternalXXworkflow">',
'<span>', _l( 'Workflows' ), '</span>',
'</div>',
'<div id="internal-workflows" class="toolSectionBody">',
'<div class="toolSectionBg"/>',
'</div>',
'</div>'
].join('');
},
toString : function() { return 'toolPanel' }
});
module.exports = ToolPanel;
-114
View File
@@ -1,114 +0,0 @@
var LeftPanel = require( 'layout/panel' ).LeftPanel,
Tools = require( 'mvc/tool/tools' ),
Upload = require( 'mvc/upload/upload-view' ),
_l = require( 'utils/localization' );
/* Builds the tool menu panel on the left of the analysis page */
var ToolPanel = LeftPanel.extend({
title : _l( 'Tools' ),
initialize: function( options ){
LeftPanel.prototype.initialize.call( this, options );
this.log( this + '.initialize:', options );
/** @type {Object[]} descriptions of user's workflows to be shown in the tool menu */
this.stored_workflow_menu_entries = options.stored_workflow_menu_entries || [];
// create tool search, tool panel, and tool panel view.
var tool_search = new Tools.ToolSearch({
hidden : false
});
var tools = new Tools.ToolCollection( options.toolbox );
this.tool_panel = new Tools.ToolPanel({
tool_search : tool_search,
tools : tools,
layout : options.toolbox_in_panel
});
this.tool_panel_view = new Tools.ToolPanelView({ model: this.tool_panel });
// add upload modal
this.uploadButton = new Upload({
nginx_upload_path : options.nginx_upload_path,
ftp_upload_site : options.ftp_upload_site,
default_genome : options.default_genome,
default_extension : options.default_extension,
});
},
render : function(){
var self = this;
LeftPanel.prototype.render.call( self );
self.$( '.panel-header-buttons' ).append( self.uploadButton.$el );
// if there are tools, render panel and display everything
if (self.tool_panel.get( 'layout' ).size() > 0) {
self.tool_panel_view.render();
//TODO: why the hide/show?
self.$( '.toolMenu' ).show();
}
self.$( '.toolMenuContainer' ).prepend( self.tool_panel_view.$el );
self._renderWorkflowMenu();
// if a tool link has the minsizehint attribute, handle it here (gen. by hiding the tool panel)
self.$( 'a[minsizehint]' ).click( function() {
if ( parent.handle_minwidth_hint ) {
parent.handle_minwidth_hint( $( self ).attr( 'minsizehint' ) );
}
});
},
/** build the dom for the workflow portion of the tool menu */
_renderWorkflowMenu : function(){
var self = this;
// add internal workflow list
self.$( '#internal-workflows' ).append( self._templateTool({
title : _l( 'All workflows' ),
href : 'workflow/list_for_run'
}));
_.each( self.stored_workflow_menu_entries, function( menu_entry ){
self.$( '#internal-workflows' ).append( self._templateTool({
title : menu_entry.stored_workflow.name,
href : 'workflow/run?id=' + menu_entry.encoded_stored_workflow_id
}));
});
},
/** build a link to one tool */
_templateTool: function( tool ) {
return [
'<div class="toolTitle">',
// global
'<a href="', Galaxy.root, tool.href, '" target="galaxy_main">', tool.title, '</a>',
'</div>'
].join('');
},
/** override to include inital menu dom and workflow section */
_templateBody : function(){
return [
'<div class="unified-panel-body unified-panel-body-background">',
'<div class="toolMenuContainer">',
'<div class="toolMenu" style="display: none">',
'<div id="search-no-results" style="display: none; padding-top: 5px">',
'<em><strong>', _l( 'Search did not match any tools.' ), '</strong></em>',
'</div>',
'</div>',
'<div class="toolSectionPad"/>',
'<div class="toolSectionPad"/>',
'<div class="toolSectionTitle" id="title_XXinternalXXworkflow">',
'<span>', _l( 'Workflows' ), '</span>',
'</div>',
'<div id="internal-workflows" class="toolSectionBody">',
'<div class="toolSectionBg"/>',
'</div>',
'</div>',
'</div>'
].join('');
},
toString : function(){ return 'ToolPanel'; }
});
module.exports = ToolPanel;
@@ -17,6 +17,81 @@ function display_spinner(){
$('#main').append('<img id="spinner" src="' + galaxy_root + 'static/style/largespinner.gif" style="position:absolute;margin:auto;top:0;left:0;right:0;bottom:0;">');
}
function not_ready(timeout, timeout_values, timeout_time_max, timeout_time_step) {
if(timeout_values.count == 0){
display_spinner();
toastr.info(
"Galaxy is launching a container in which to run this interactive environment. Please wait...",
{'closeButton': true, 'tapToDismiss': false}
);
}
timeout_values.count++;
if(timeout_values.time < timeout_time_max){
timeout_values.time += timeout_time_step;
}
console.log("Readiness request " + timeout_values.count + " sleeping " + timeout_values.time / 1000 + "s");
window.setTimeout(timeout, timeout_values.time)
}
/**
* Check a URL for a boolean true/false and call a callback when done.
*/
function load_when_ready(url, success_callback){
var ajax_timeout = 500;
var ajax_timeout_max = 10000;
var ajax_timeout_step = 250;
var timeout_values = { time: 1000, count: 0 };
var timeout_time_max = 15000;
var timeout_time_step = 1000;
var timeout = function(){
$.ajax({
url: url,
xhrFields: {
withCredentials: true
},
type: "GET",
timeout: ajax_timeout,
dataType: "json",
success: function(data){
if(data == true){
console.log("Galaxy reports IE container ready, returning");
clear_main_area();
toastr.clear();
success_callback();
}else if(data == false){
not_ready(timeout, timeout_values, timeout_time_max, timeout_time_step);
}else{
clear_main_area();
toastr.clear();
toastr.error(
"Galaxy failed to launch a container in which to run this interactive environment, contact your administrator.",
"Error",
{'closeButton': true, 'tapToDismiss': false}
);
}
},
error: function(jqXHR, textStatus, errorThrown){
if(textStatus == "timeout"){
if(ajax_timeout < ajax_timeout_max){
ajax_timeout += ajax_timeout_step;
}
not_ready(timeout, timeout_values, timeout_time_max, timeout_time_step);
}else{
clear_main_area();
toastr.clear();
toastr.error(
"Galaxy encountered an error while attempting to determine the readiness of this interactive environment, contact your administrator.",
"Error",
{'closeButton': true, 'tapToDismiss': false}
);
}
}
});
}
window.setTimeout(timeout, timeout_values.time);
}
/**
* Test availability of a URL, and call a callback when done.
@@ -43,7 +118,7 @@ function test_ie_availability(url, success_callback){
},
error: function(jqxhr, status, error){
request_count++;
console.log("Request " + request_count);
console.log("Availability request " + request_count);
if(request_count > 30){
clearInterval(interval);
clear_main_area();
+4 -2
View File
@@ -48,6 +48,7 @@ GalaxyApp.prototype._init = function __init( options, bootstrapped ){
self._processOptions( options );
// special case for root
self.root = options.root || '/';
self.session_csrf_token = options.session_csrf_token || null;
self._initConfig( options.config || {} );
self._patchGalaxy( window.Galaxy );
@@ -76,9 +77,10 @@ GalaxyApp.prototype._init = function __init( options, bootstrapped ){
/** default options */
GalaxyApp.prototype.defaultOptions = {
/** monkey patch attributes from existing window.Galaxy object? */
patchExisting : true,
patchExisting : true,
/** root url of this app */
root : '/'
root : '/',
session_csrf_token : null,
};
/** filter to options present in defaultOptions (and default to them) */
+1
View File
@@ -93,6 +93,7 @@ var LibraryRouter = Backbone.Router.extend({
var LibraryPrefs = mod_baseMVC.SessionStorageModel.extend({
defaults : {
with_deleted : false,
without_restricted: false,
sort_order : 'asc',
sort_by : 'name',
library_page_size : 20,
+95 -114
View File
@@ -322,71 +322,63 @@ WYMeditor.editor.prototype.dialog = function( dialogType, dialogFeatures, bodyHt
break;
}
$.ajax(
{
url: item_info.list_ajax_url,
data: {},
error: function() { alert( "Failed to list " + item_info.plural.toLowerCase() + " for selection"); },
success: function(table_html)
{
show_modal(
"Insert Link to " + item_info.singular,
table_html +
"<div><input id='make-importable' type='checkbox' checked/>" +
"Make the selected " + item_info.plural.toLowerCase() + " accessible so that they can viewed by everyone.</div>"
,
{
"Insert": function()
{
// Make selected items accessible (importable) ?
var make_importable = false;
if ( $('#make-importable:checked').val() !== null )
make_importable = true;
require( [ 'mvc/grid/grid-view' ], function( GridView ) {
var grid = new GridView( { url_base: item_info.list_ajax_url, dict_format: true, embedded: true } );
Galaxy.modal.show({
title : 'Insert Link to ' + item_info.singular,
body : $( '<div/>' ).append( grid.$el )
.append( $( '<div/>' ).append( '<input id="make-importable" type="checkbox" checked/>' )
.append( 'Make the selected ' + item_info.plural.toLowerCase() + ' accessible so that they can viewed by everyone.' ) ),
closing_events : true,
buttons : {
'Insert': function() {
// Make selected items accessible (importable) ?
var make_importable = false;
if ( $('#make-importable:checked').val() != null )
make_importable = true;
// Insert links to history for each checked item.
var item_ids = new Array();
$('input[name=id]:checked').each(function() {
var item_id = $(this).val();
// Insert links to history for each checked item.
var item_ids = new Array();
grid.$('input[name=id]:checked').each(function() {
var item_id = $(this).val();
// Make item importable?
if (make_importable)
make_item_importable(item_info.controller, item_id, item_info.singular);
// Make item importable?
if (make_importable)
make_item_importable(item_info.controller, item_id, item_info.singular);
// Insert link(s) to item(s). This is done by getting item info and then manipulating wym.
url_template = get_name_and_link_url + item_id;
ajax_url = url_template.replace( "ITEM_CONTROLLER", item_info.controller);
$.getJSON( ajax_url, function( returned_item_info ) {
// Get link text.
wym._exec(WYMeditor.CREATE_LINK, sStamp);
var link_text = $("a[href=" + sStamp + "]", wym._doc.body).text();
// Insert link(s) to item(s). This is done by getting item info and then manipulating wym.
url_template = get_name_and_link_url + item_id;
ajax_url = url_template.replace( "ITEM_CONTROLLER", item_info.controller);
$.getJSON( ajax_url, function( returned_item_info ) {
// Get link text.
wym._exec(WYMeditor.CREATE_LINK, sStamp);
var link_text = $("a[href=" + sStamp + "]", wym._doc.body).text();
// Insert link: need to do different actions depending on link text.
if (
link_text == "" // Firefox.
||
link_text == sStamp // Safari
)
{
// User selected no text; create link from scratch and use default text.
wym.insert("<a href='" + returned_item_info.link + "'>" + item_info.singular + " '" + returned_item_info.name + "'</a>");
}
else
{
// Link created from selected text; add href and title.
$("a[href=" + sStamp + "]", wym._doc.body).attr(WYMeditor.HREF, returned_item_info.link).attr(WYMeditor.TITLE, item_info.singular + item_id);
}
});
// Insert link: need to do different actions depending on link text.
if (
link_text == "" // Firefox.
||
link_text == sStamp // Safari
)
{
// User selected no text; create link from scratch and use default text.
wym.insert("<a href='" + returned_item_info.link + "'>" + item_info.singular + " '" + returned_item_info.name + "'</a>");
}
else
{
// Link created from selected text; add href and title.
$("a[href=" + sStamp + "]", wym._doc.body).attr(WYMeditor.HREF, returned_item_info.link).attr(WYMeditor.TITLE, item_info.singular + item_id);
}
});
});
Galaxy.modal.hide();
hide_modal();
},
"Cancel": function()
{
hide_modal();
}
},
'Close' : function() {
Galaxy.modal.hide();
}
);
}
}
});
});
}
// EMBED GALAXY OBJECT DIALOGS
@@ -412,67 +404,56 @@ WYMeditor.editor.prototype.dialog = function( dialogType, dialogFeatures, bodyHt
break;
}
$.ajax(
{
url: item_info.list_ajax_url,
data: {},
error: function() { alert( "Failed to list " + item_info.plural.toLowerCase() + " for selection"); },
success: function(list_html)
{
// Can make histories, workflows importable; cannot make datasets importable.
if (dialogType == CONTROLS.DIALOG_EMBED_HISTORY || dialogType == CONTROLS.DIALOG_EMBED_WORKFLOW
|| dialogType == CONTROLS.DIALOG_EMBED_VISUALIZATION)
list_html = list_html + "<div><input id='make-importable' type='checkbox' checked/>" +
"Make the selected " + item_info.plural.toLowerCase() + " accessible so that they can viewed by everyone.</div>";
show_modal(
"Embed " + item_info.plural,
list_html,
{
"Embed": function()
{
// Make selected items accessible (importable) ?
var make_importable = false;
if ( $('#make-importable:checked').val() != null )
make_importable = true;
require( [ 'mvc/grid/grid-view' ], function( GridView ) {
var grid = new GridView( { url_base: item_info.list_ajax_url, dict_format: true, embedded: true } );
Galaxy.modal.show({
title : 'Insert Link to ' + item_info.singular,
body : $( '<div/>' ).append( grid.$el )
.append( $( '<div/>' ).append( '<input id="make-importable" type="checkbox" checked/>' )
.append( 'Make the selected ' + item_info.plural.toLowerCase() + ' accessible so that they can viewed by everyone.' ) ),
closing_events : true,
buttons : {
'Embed': function() {
// Make selected items accessible (importable) ?
var make_importable = false;
if ( $('#make-importable:checked').val() != null )
make_importable = true;
$('input[name=id]:checked').each(function() {
// Get item ID and name.
var item_id = $(this).val();
// Use ':first' because there are many labels in table; the first one is the item name.
var item_name = $("label[for='" + item_id + "']:first").text();
grid.$('input[name=id]:checked').each(function() {
// Get item ID and name.
var item_id = $(this).val();
// Use ':first' because there are many labels in table; the first one is the item name.
var item_name = $("label[for='" + item_id + "']:first").text();
if (make_importable)
make_item_importable(item_info.controller, item_id, item_info.singular);
if (make_importable)
make_item_importable(item_info.controller, item_id, item_info.singular);
// Embedded item HTML; item class is embedded in div container classes; this is necessary because the editor strips
// all non-standard attributes when it returns its content (e.g. it will not return an element attribute of the form
// item_class='History').
var item_elt_id = item_info.iclass + "-" + item_id;
var item_embed_html = [
"<div id='", item_elt_id, "' class='embedded-item ",
item_info.singular.toLowerCase(), " placeholder'>",
"<p class='title'>",
"Embedded Galaxy ", item_info.singular, " '", item_name, "'",
"</p>",
"<p class='content'>",
"[Do not edit this block; Galaxy will fill it in with the annotated ",
item_info.singular.toLowerCase(), " when it is displayed.]",
"</p>",
"</div>" ].join( '' );
// Embedded item HTML; item class is embedded in div container classes; this is necessary because the editor strips
// all non-standard attributes when it returns its content (e.g. it will not return an element attribute of the form
// item_class='History').
var item_elt_id = item_info.iclass + "-" + item_id;
var item_embed_html = [
"<div id='", item_elt_id, "' class='embedded-item ",
item_info.singular.toLowerCase(), " placeholder'>",
"<p class='title'>",
"Embedded Galaxy ", item_info.singular, " '", item_name, "'",
"</p>",
"<p class='content'>",
"[Do not edit this block; Galaxy will fill it in with the annotated ",
item_info.singular.toLowerCase(), " when it is displayed.]",
"</p>",
"</div>" ].join( '' );
// Insert embedded item into document.
wym.insert(item_embed_html);
});
hide_modal();
},
"Cancel": function()
{
hide_modal();
}
// Insert embedded item into document.
wym.insert(item_embed_html);
});
Galaxy.modal.hide();
},
'Close' : function() {
Galaxy.modal.hide();
}
);
}
}
});
});
}
};
+121 -94
View File
@@ -1,5 +1,5 @@
/** Masthead Collection **/
define(['layout/generic-nav-view', 'mvc/webhooks'], function( GenericNav, Webhooks ) {
define(['layout/generic-nav-view', 'mvc/webhooks', 'utils/localization', 'utils/utils'], function( GenericNav, Webhooks, _l, Utils ) {
var Collection = Backbone.Collection.extend({
model: Backbone.Model.extend({
defaults: {
@@ -8,6 +8,7 @@ var Collection = Backbone.Collection.extend({
}
}),
fetch: function( options ){
var self = this;
options = options || {};
this.reset();
@@ -22,9 +23,9 @@ var Collection = Backbone.Collection.extend({
//
this.add({
id : 'analysis',
title : 'Analyze Data',
title : _l('Analyze Data'),
url : '',
tooltip : 'Analysis home view'
tooltip : _l('Analysis home view')
});
//
@@ -32,10 +33,14 @@ var Collection = Backbone.Collection.extend({
//
this.add({
id : 'workflow',
title : 'Workflow',
title : _l('Workflow'),
tooltip : _l('Chain tools into workflows'),
disabled : !Galaxy.user.id,
url : 'workflow',
tooltip : 'Chain tools into workflows',
disabled : !Galaxy.user.id
target : 'galaxy_main',
onclick : function() {
window.location = Galaxy.root + 'workflow';
}
});
//
@@ -43,24 +48,24 @@ var Collection = Backbone.Collection.extend({
//
this.add({
id : 'shared',
title : 'Shared Data',
title : _l('Shared Data'),
url : 'library/index',
tooltip : 'Access published resources',
tooltip : _l('Access published resources'),
menu : [{
title : 'Data Libraries',
title : _l('Data Libraries'),
url : 'library/list'
},{
title : 'Histories',
url : 'history/list_published'
title : _l('Histories'),
url : 'histories/list_published'
},{
title : 'Workflows',
url : 'workflow/list_published'
title : _l('Workflows'),
url : 'workflows/list_published'
},{
title : 'Visualizations',
url : 'visualization/list_published'
title : _l('Visualizations'),
url : 'visualizations/list_published'
},{
title : 'Pages',
url : 'page/list_published'
title : _l('Pages'),
url : 'pages/list_published'
}]
});
@@ -87,20 +92,20 @@ var Collection = Backbone.Collection.extend({
//
this.add({
id : 'visualization',
title : 'Visualization',
url : 'visualization/list',
tooltip : 'Visualize datasets',
title : _l('Visualization'),
url : 'visualizations/list',
tooltip : _l('Visualize datasets'),
disabled : !Galaxy.user.id,
menu : [{
title : 'New Track Browser',
title : _l('New Track Browser'),
url : 'visualization/trackster',
target : '_frame'
},{
title : 'Saved Visualizations',
url : 'visualization/list',
title : _l('Saved Visualizations'),
url : 'visualizations/list',
target : '_frame'
},{
title : 'Interactive Environments',
title : _l('Interactive Environments'),
url : 'visualization/gie_list',
target : 'galaxy_main'
}
@@ -117,25 +122,24 @@ var Collection = Backbone.Collection.extend({
$.each(webhooks.models, function(index, model) {
var webhook = model.toJSON();
if (webhook.activate) {
var obj = {
id : webhook.name,
icon : webhook.config.icon,
url : webhook.config.url,
tooltip : webhook.config.tooltip,
onclick : webhook.config.function && new Function(webhook.config.function),
};
// Galaxy.page is undefined for data libraries, workflows pages
if( Galaxy.page ) {
Galaxy.page.masthead.collection.add({
id : webhook.name,
icon : webhook.config.icon,
url : webhook.config.url,
tooltip : webhook.config.tooltip,
onclick : webhook.config.function && new Function(webhook.config.function),
});
Galaxy.page.masthead.collection.add(obj);
}
else if( Galaxy.masthead ) {
Galaxy.masthead.collection.add({
id : webhook.name,
icon : webhook.config.icon,
url : webhook.config.url,
tooltip : webhook.config.tooltip,
onclick : webhook.config.function && new Function(webhook.config.function),
});
Galaxy.masthead.collection.add(obj);
}
// Append masthead script and styles to Galaxy main
Utils.appendScriptStyle( webhook );
}
});
});
@@ -147,9 +151,9 @@ var Collection = Backbone.Collection.extend({
//
Galaxy.user.get( 'is_admin' ) && this.add({
id : 'admin',
title : 'Admin',
title : _l('Admin'),
url : 'admin',
tooltip : 'Administer this Galaxy',
tooltip : _l('Administer this Galaxy'),
cls : 'admin-only'
});
@@ -158,34 +162,34 @@ var Collection = Backbone.Collection.extend({
//
var helpTab = {
id : 'help',
title : 'Help',
tooltip : 'Support, contact, and community',
title : _l('Help'),
tooltip : _l('Support, contact, and community'),
menu : [{
title : 'Support',
title : _l('Support'),
url : options.support_url,
target : '_blank'
},{
title : 'Search',
title : _l('Search'),
url : options.search_url,
target : '_blank'
},{
title : 'Mailing Lists',
title : _l('Mailing Lists'),
url : options.mailing_lists,
target : '_blank'
},{
title : 'Videos',
title : _l('Videos'),
url : options.screencasts_url,
target : '_blank'
},{
title : 'Wiki',
title : _l('Wiki'),
url : options.wiki_url,
target : '_blank'
},{
title : 'How to Cite Galaxy',
title : _l('How to Cite Galaxy'),
url : options.citation_url,
target : '_blank'
},{
title : 'Interactive Tours',
title : _l('Interactive Tours'),
url : 'tours',
onclick : function(){
if (Galaxy.router){
@@ -198,17 +202,17 @@ var Collection = Backbone.Collection.extend({
}]
};
options.terms_url && helpTab.menu.push({
title : 'Terms and Conditions',
title : _l('Terms and Conditions'),
url : options.terms_url,
target : '_blank'
});
options.biostar_url && helpTab.menu.unshift({
title : 'Ask a question',
title : _l('Ask a question'),
url : 'biostar/biostar_question_redirect',
target : '_blank'
});
options.biostar_url && helpTab.menu.unshift({
title : 'Galaxy Biostar',
title : _l('Galaxy Biostar'),
url : options.biostar_url_redirect,
target : '_blank'
});
@@ -217,66 +221,89 @@ var Collection = Backbone.Collection.extend({
//
// User tab.
//
var userTab = {};
if ( !Galaxy.user.id ){
var userTab = {
id : 'user',
title : 'User',
cls : 'loggedout-only',
tooltip : 'Account registration or login',
menu : [{
title : 'Login',
url : 'user/login',
target : 'galaxy_main',
noscratchbook : true
}]
};
options.allow_user_creation && userTab.menu.push({
title : 'Register',
url : 'user/create',
target : 'galaxy_main',
noscratchbook : true
});
this.add( userTab );
if ( options.allow_user_creation ) {
userTab = {
id : 'user',
title : _l('Login or Register'),
cls : 'loggedout-only',
tooltip : _l('Account registration or login'),
menu : [{
title : _l('Login'),
url : 'user/login',
target : 'galaxy_main',
noscratchbook : true
}, {
title: _l('Register'),
url: 'user/create',
target: 'galaxy_main',
noscratchbook: true
}
]
};
} else {
userTab = {
id: 'user',
title: _l('Login'),
cls: 'loggedout-only',
tooltip: _l('Login'),
url: 'user/login',
target: 'galaxy_main',
noscratchbook: true
};
}
} else {
var userTab = {
userTab = {
id : 'user',
title : 'User',
title : _l('User'),
cls : 'loggedin-only',
tooltip : 'Account and saved data',
tooltip : _l('Account and saved data'),
menu : [{
title : 'Logged in as ' + Galaxy.user.get( 'email' )
title : _l('Logged in as') + ' ' + Galaxy.user.get( 'email' )
},{
title : 'Preferences',
url : 'users',
title : _l('Preferences'),
url : 'user',
target : 'galaxy_main',
onclick : function() {
window.location = Galaxy.root + 'users';
if ( Galaxy.router ) {
Galaxy.router.push( 'user' );
} else {
window.location = Galaxy.root + 'user';
}
}
},{
title : 'Custom Builds',
url : 'user/dbkeys',
target : 'galaxy_main'
title : _l('Custom Builds'),
url : 'custom_builds',
target : 'galaxy_main',
onclick : function() {
if ( Galaxy.router ) {
Galaxy.router.push( 'custom_builds' );
} else {
window.location = Galaxy.root + 'custom_builds';
}
}
},{
title : 'Logout',
url : 'user/logout',
title : _l('Logout'),
url : 'user/logout?session_csrf_token=' + Galaxy.session_csrf_token,
target : '_top',
divider : true
},{
title : 'Saved Histories',
url : 'history/list',
target : 'galaxy_main'
title : _l('Saved Histories'),
url : 'histories/list',
target : '_top'
},{
title : 'Saved Datasets',
url : 'dataset/list',
target : 'galaxy_main'
title : _l('Saved Datasets'),
url : 'datasets/list',
target : '_top'
},{
title : 'Saved Pages',
url : 'page/list',
title : _l('Saved Pages'),
url : 'pages/list',
target : '_top'
}]
};
this.add( userTab );
}
this.add( userTab );
var activeView = this.get( options.active_view );
activeView && activeView.set( 'active', true );
return new jQuery.Deferred().resolve().promise();
@@ -308,7 +335,7 @@ var Tab = Backbone.View.extend({
this.$note.html( this.model.get( 'note' ) || '' )
.removeClass().addClass( 'dropdown-note' )
.addClass( this.model.get( 'note_cls' ) )
.css( { 'display' : this.model.get( 'show_note' ) && 'block' || 'none' } )
.css( { 'display' : this.model.get( 'show_note' ) && 'block' || 'none' } );
this.$toggle.html( this.model.get( 'title' ) || '' )
.removeClass().addClass( 'dropdown-toggle' )
.addClass( this.model.get( 'cls' ) )
@@ -387,7 +414,7 @@ var Tab = Backbone.View.extend({
}
} else {
function buildLink( label, url ) {
return $( '<div/>' ).append( $( '<a/>' ).attr( 'href', Galaxy.root + url ).html( label ) ).html()
return $( '<div/>' ).append( $( '<a/>' ).attr( 'href', Galaxy.root + url ).html( label ) ).html();
}
this.$toggle.popover && this.$toggle.popover( 'destroy' );
this.$toggle.popover({
+160 -168
View File
@@ -1,171 +1,163 @@
define([
'layout/masthead',
'layout/panel',
'mvc/ui/ui-modal',
'mvc/base-mvc'
], function( Masthead, Panel, Modal, BaseMVC ) {
define( [ 'layout/masthead', 'layout/panel', 'mvc/ui/ui-modal' ], function( Masthead, Panel, Modal ) {
var View = Backbone.View.extend({
el : 'body',
className : 'full-content',
_panelids : [ 'left', 'right' ],
// ============================================================================
var PageLayoutView = Backbone.View.extend( BaseMVC.LoggableMixin ).extend({
_logNamespace : 'layout',
el : 'body',
className : 'full-content',
_panelIds : [
'left', 'center', 'right'
],
defaultOptions : {
message_box_visible : false,
message_box_content : '',
message_box_class : 'info',
show_inactivity_warning : false,
inactivity_box_content : ''
},
initialize : function( options ) {
// TODO: remove globals
this.log( this + '.initialize:', options );
_.extend( this, _.pick( options, this._panelIds ) );
this.options = _.defaults( _.omit( options.config, this._panelIds ), this.defaultOptions );
Galaxy.modal = this.modal = new Modal.View();
this.masthead = new Masthead.View( this.options );
this.$el.attr( 'scroll', 'no' );
this.$el.html( this._template() );
this.$el.append( this.masthead.frame.$el );
this.$( '#masthead' ).replaceWith( this.masthead.$el );
this.$el.append( this.modal.$el );
this.$messagebox = this.$( '#messagebox' );
this.$inactivebox = this.$( '#inactivebox' );
},
render : function() {
// TODO: Remove this line after select2 update
$( '.select2-hidden-accessible' ).remove();
this.log( this + '.render:' );
this.masthead.render();
this.renderMessageBox();
this.renderInactivityBox();
this.renderPanels();
this._checkCommunicationServerOnline();
return this;
},
/** Render message box */
renderMessageBox : function() {
if ( this.options.message_box_visible ){
var content = this.options.message_box_content || '';
var level = this.options.message_box_class || 'info';
this.$el.addClass( 'has-message-box' );
this.$messagebox
.attr( 'class', 'panel-' + level + '-message' )
.html( content )
.toggle( !!content )
.show();
} else {
this.$el.removeClass( 'has-message-box' );
this.$messagebox.hide();
}
return this;
},
/** Render inactivity warning */
renderInactivityBox : function() {
if( this.options.show_inactivity_warning ){
var content = this.options.inactivity_box_content || '';
var verificationLink = $( '<a/>' ).attr( 'href', Galaxy.root + 'user/resend_verification' ).text( 'Resend verification' );
this.$el.addClass( 'has-inactivity-box' );
this.$inactivebox
.html( content + ' ' )
.append( verificationLink )
.toggle( !!content )
.show();
} else {
this.$el.removeClass( 'has-inactivity-box' );
this.$inactivebox.hide();
}
return this;
},
/** Render panels */
renderPanels : function() {
var page = this;
this._panelIds.forEach( function( panelId ){
if( _.has( page, panelId ) ){
page[ panelId ].setElement( '#' + panelId );
page[ panelId ].render();
}
});
if( !this.left ){
this.center.$el.css( 'left', 0 );
}
if( !this.right ){
this.center.$el.css( 'right', 0 );
}
return this;
},
/** body template */
_template: function() {
return [
'<div id="everything">',
'<div id="background"/>',
'<div id="masthead"/>',
'<div id="messagebox"/>',
'<div id="inactivebox" class="panel-warning-message" />',
this.left? '<div id="left" />' : '',
this.center? '<div id="center" class="inbound" />' : '',
this.right? '<div id="right" />' : '',
'</div>',
'<div id="dd-helper" />',
].join('');
},
/** hide both side panels if previously shown */
hideSidePanels : function(){
if( this.left ){
this.left.hide();
}
if( this.right ){
this.right.hide();
}
},
toString : function() { return 'PageLayoutView'; },
/** Check if the communication server is online and show the icon otherwise hide the icon */
_checkCommunicationServerOnline: function(){
var host = window.Galaxy.config.communication_server_host,
port = window.Galaxy.config.communication_server_port,
preferences = window.Galaxy.user.attributes.preferences,
$chat_icon_element = $( "#show-chat-online" );
/** Check if the user has deactivated the communication in it's personal settings */
if ( preferences && [ '1', 'true' ].indexOf( preferences.communication_server ) != -1 ) {
// See if the configured communication server is available
$.ajax({
url: host + ":" + port,
})
.success( function( data ) {
// enable communication only when a user is logged in
if( window.Galaxy.user.id !== null ) {
if( $chat_icon_element.css( "visibility") === "hidden" ) {
$chat_icon_element.css( "visibility", "visible" );
}
}
})
.error( function( data ) {
// hide the communication icon if the communication server is not available
$chat_icon_element.css( "visibility", "hidden" );
initialize : function( options ) {
var self = this;
this.config = _.defaults( options.config || {}, {
message_box_visible : false,
message_box_content : '',
message_box_class : 'info',
show_inactivity_warning : false,
inactivity_box_content : ''
});
} else {
$chat_icon_element.css( "visibility", "hidden" );
}
},
});
// ============================================================================
return {
PageLayoutView: PageLayoutView
};
});
// attach global objects, build mastheads
Galaxy.modal = this.modal = new Modal.View();
Galaxy.display = this.display = function( view ) { self.center.display( view ) };
Galaxy.router = this.router = options.Router && new options.Router( self, options );
this.masthead = new Masthead.View( this.config );
this.center = new Panel.CenterPanel();
// build page template
this.$el.attr( 'scroll', 'no' );
this.$el.html( this._template() );
this.$( '#masthead' ).replaceWith( this.masthead.$el );
this.$( '#center' ).append( this.center.$el );
this.$el.append( this.masthead.frame.$el );
this.$el.append( this.modal.$el );
this.$messagebox = this.$( '#messagebox' );
this.$inactivebox = this.$( '#inactivebox' );
// build panels
this.panels = {};
_.each( this._panelids, function( panel_id ) {
var panel_class_name = panel_id.charAt( 0 ).toUpperCase() + panel_id.slice( 1 );
var panel_class = options[ panel_class_name ];
if ( panel_class ) {
var panel_instance = new panel_class( self, options );
self[ panel_instance.toString() ] = panel_instance;
self.panels[ panel_id ] = new Panel.SidePanel({
id : panel_id,
el : self.$( '#' + panel_id ),
view : panel_instance
});
}
});
this.render();
},
render : function() {
// TODO: Remove this line after select2 update
$( '.select2-hidden-accessible' ).remove();
this.masthead.render();
this.renderMessageBox();
this.renderInactivityBox();
this.renderPanels();
this._checkCommunicationServerOnline();
return this;
},
/** Render message box */
renderMessageBox : function() {
if ( this.config.message_box_visible ){
var content = this.config.message_box_content || '';
var level = this.config.message_box_class || 'info';
this.$el.addClass( 'has-message-box' );
this.$messagebox
.attr( 'class', 'panel-' + level + '-message' )
.html( content )
.toggle( !!content )
.show();
} else {
this.$el.removeClass( 'has-message-box' );
this.$messagebox.hide();
}
return this;
},
/** Render inactivity warning */
renderInactivityBox : function() {
if( this.config.show_inactivity_warning ){
var content = this.config.inactivity_box_content || '';
var verificationLink = $( '<a/>' ).attr( 'href', Galaxy.root + 'user/resend_verification' ).text( 'Resend verification' );
this.$el.addClass( 'has-inactivity-box' );
this.$inactivebox
.html( content + ' ' )
.append( verificationLink )
.toggle( !!content )
.show();
} else {
this.$el.removeClass( 'has-inactivity-box' );
this.$inactivebox.hide();
}
return this;
},
/** Render panels */
renderPanels : function() {
var self = this;
_.each( this._panelids, function( panel_id ) {
var panel = self.panels[ panel_id ];
if ( panel ) {
panel.render();
} else {
self.$( '#center' ).css( panel_id, 0 );
self.$( '#' + panel_id ).hide();
}
});
return this;
},
/** body template */
_template: function() {
return [
'<div id="everything">',
'<div id="background"/>',
'<div id="masthead"/>',
'<div id="messagebox"/>',
'<div id="inactivebox" class="panel-warning-message" />',
'<div id="left" />',
'<div id="center" class="inbound" />',
'<div id="right" />',
'</div>',
'<div id="dd-helper" />',
].join('');
},
toString : function() { return 'PageLayoutView' },
/** Check if the communication server is online and show the icon otherwise hide the icon */
_checkCommunicationServerOnline: function(){
var host = window.Galaxy.config.communication_server_host,
port = window.Galaxy.config.communication_server_port,
preferences = window.Galaxy.user.attributes.preferences,
$chat_icon_element = $( "#show-chat-online" );
/** Check if the user has deactivated the communication in it's personal settings */
if ( preferences && [ '1', 'true' ].indexOf( preferences.communication_server ) != -1 ) {
// See if the configured communication server is available
$.ajax({
url: host + ":" + port,
})
.success( function( data ) {
// enable communication only when a user is logged in
if( window.Galaxy.user.id !== null ) {
if( $chat_icon_element.css( "visibility") === "hidden" ) {
$chat_icon_element.css( "visibility", "visible" );
}
}
})
.error( function( data ) {
// hide the communication icon if the communication server is not available
$chat_icon_element.css( "visibility", "hidden" );
});
} else {
$chat_icon_element.css( "visibility", "hidden" );
}
},
});
return { View: View }
});
+209 -260
View File
@@ -1,293 +1,242 @@
define([
'jquery',
'libs/underscore',
'libs/backbone',
'mvc/base-mvc',
], function( jQuery, _, Backbone, BASE_MVC ){
define( [ 'jquery', 'libs/underscore', 'libs/backbone' ], function( $, _, Backbone ) {
"use strict";
"use strict";
// ============================================================================
var $ = jQuery;
var MIN_PANEL_WIDTH = 160,
MAX_PANEL_WIDTH = 800;
var MIN_PANEL_WIDTH = 160,
MAX_PANEL_WIDTH = 800;
/** View for left/right panels used by Page view */
var SidePanel = Backbone.View.extend({
// ----------------------------------------------------------------------------
/**
*
*/
var SidePanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend({
_logNamespace : 'layout',
initialize: function( options ){
this.view = options.view;
this.hidden = false;
this.saved_size = null;
this.hiddenByTool = false;
},
initialize: function( attributes ){
this.log( this + '.initialize:', attributes );
this.title = attributes.title || this.title || '';
$center : function() {
return this.$el.siblings( '#center' );
},
this.hidden = false;
this.savedSize = null;
this.hiddenByTool = false;
},
$toggleButton : function() {
return this.$( '.unified-panel-footer > .panel-collapse' );
},
$center : function(){
return this.$el.siblings( '#center' );
},
render: function() {
var self = this;
var panel = this.view;
var components = this.view.model.attributes || {};
this.$el.html( this._templatePanel( this.id ) );
_.each( components.buttons, function( button ) {
self.$( '.panel-header-buttons' ).append( button.$el );
});
this.$el.addClass( components.cls );
this.$( '.panel-header-text' ).html( _.escape( components.title ) );
this.$( '.unified-panel-body' ).append( panel.$el );
panel.render();
},
$toggleButton : function(){
return this.$( '.unified-panel-footer > .panel-collapse' );
},
/** panel dom template. id is 'right' or 'left' */
_templatePanel: function() {
return [
this._templateHeader(),
this._templateBody(),
this._templateFooter(),
].join('');
},
render: function(){
this.log( this + '.render:' );
this.$el.html( this.template( this.id ) );
},
/** panel dom template. id is 'right' or 'left' */
template: function(){
return [
this._templateHeader(),
this._templateBody(),
this._templateFooter(),
].join('');
},
/** panel dom template. id is 'right' or 'left' */
_templateHeader: function( data ){
return [
'<div class="unified-panel-header" unselectable="on">',
'<div class="unified-panel-header-inner">',
'<div class="panel-header-buttons" style="float: right"/>',
'<div class="panel-header-text">', _.escape( this.title ), '</div>',
/** panel dom template. id is 'right' or 'left' */
_templateHeader: function( data ) {
return [
'<div class="unified-panel-header" unselectable="on">',
'<div class="unified-panel-header-inner">',
'<div class="panel-header-buttons" style="float: right"/>',
'<div class="panel-header-text"/>',
'</div>',
'</div>',
'</div>',
].join('');
},
].join('');
},
/** panel dom template. id is 'right' or 'left' */
_templateBody: function( data ){
return '<div class="unified-panel-body"/>';
},
/** panel dom template. id is 'right' or 'left' */
_templateBody: function( data ) {
return '<div class="unified-panel-body"/>';
},
/** panel dom template. id is 'right' or 'left' */
_templateFooter: function( data ){
return [
'<div class="unified-panel-footer">',
'<div class="panel-collapse ', _.escape( this.id ), '"/>',
'<div class="drag"/>',
'</div>',
].join('');
},
/** panel dom template. id is 'right' or 'left' */
_templateFooter: function( data ) {
return [
'<div class="unified-panel-footer">',
'<div class="panel-collapse ', _.escape( this.id ), '"/>',
'<div class="drag"/>',
'</div>',
].join('');
},
// ..............................................................
events : {
'mousedown .unified-panel-footer > .drag' : '_mousedownDragHandler',
'click .unified-panel-footer > .panel-collapse' : 'toggle'
},
events : {
'mousedown .unified-panel-footer > .drag' : '_mousedownDragHandler',
'click .unified-panel-footer > .panel-collapse' : 'toggle'
},
_mousedownDragHandler : function( ev ){
var self = this,
draggingLeft = this.id === 'left',
prevX = ev.pageX;
_mousedownDragHandler : function( ev ) {
var self = this,
draggingLeft = this.id === 'left',
prevX = ev.pageX;
function move( e ){
var delta = e.pageX - prevX;
prevX = e.pageX;
function move( e ){
var delta = e.pageX - prevX;
prevX = e.pageX;
var oldWidth = self.$el.width(),
newWidth = draggingLeft?( oldWidth + delta ):( oldWidth - delta );
// Limit range
newWidth = Math.min( MAX_PANEL_WIDTH, Math.max( MIN_PANEL_WIDTH, newWidth ) );
self.resize( newWidth );
}
var oldWidth = self.$el.width(),
newWidth = draggingLeft?( oldWidth + delta ):( oldWidth - delta );
// Limit range
newWidth = Math.min( MAX_PANEL_WIDTH, Math.max( MIN_PANEL_WIDTH, newWidth ) );
self.resize( newWidth );
}
// this is a page wide overlay that assists in capturing the move and release of the mouse
// if not provided, progress and end wouldn't fire if the mouse moved out of the drag button area
$( '#dd-helper' )
.show()
.on( 'mousemove', move )
.one( 'mouseup', function( e ){
$( this ).hide().off( 'mousemove', move );
});
},
// this is a page wide overlay that assists in capturing the move and release of the mouse
// if not provided, progress and end wouldn't fire if the mouse moved out of the drag button area
$( '#dd-helper' )
.show()
.on( 'mousemove', move )
.one( 'mouseup', function( e ){
$( this ).hide().off( 'mousemove', move );
});
},
//TODO: the following three could be simplified I think
resize : function( newSize ){
this.$el.css( 'width', newSize );
// TODO: the following three could be simplified I think
// if panel is 'right' (this.id), move center right newSize number of pixels
this.$center().css( this.id, newSize );
return self;
},
resize : function( newSize ) {
this.$el.css( 'width', newSize );
this.$center().css( this.id, newSize );
return this;
},
show : function(){
if( !this.hidden ){ return; }
var self = this,
animation = {},
whichSide = this.id;
show : function(){
if( !this.hidden ) { return }
var self = this,
animation = {},
whichSide = this.id;
animation[ whichSide ] = 0;
self.$el
.css( whichSide, -this.saved_size )
.show()
.animate( animation, "fast", function(){
self.resize( self.saved_size );
});
self.hidden = false;
self.$toggleButton().removeClass( "hidden" );
return this;
},
animation[ whichSide ] = 0;
self.$el
.css( whichSide, -this.savedSize )
.show()
.animate( animation, "fast", function(){
self.resize( self.savedSize );
});
hide : function(){
if( this.hidden ) { return }
var animation = {},
whichSide = this.id;
this.saved_size = this.$el.width();
animation[ whichSide ] = -this.saved_size;
this.$el.animate( animation, "fast" );
this.$center().css( whichSide, 0 );
this.hidden = true;
this.$toggleButton().addClass( "hidden" );
return this;
},
self.hidden = false;
self.$toggleButton().removeClass( "hidden" );
return self;
},
toggle: function( ev ) {
this.hidden ? this.show() : this.hide();
this.hiddenByTool = false;
return this;
},
hide : function(){
if( this.hidden ){ return; }
var self = this,
animation = {},
whichSide = this.id;
self.savedSize = this.$el.width();
animation[ whichSide ] = -this.savedSize;
this.$el.animate( animation, "fast" );
this.$center().css( whichSide, 0 );
self.hidden = true;
self.$toggleButton().addClass( "hidden" );
return self;
},
toggle: function( ev ){
var self = this;
if( self.hidden ){
self.show();
} else {
self.hide();
}
self.hiddenByTool = false;
return self;
},
// ..............................................................
//TODO: only used in message.mako?
/** */
handle_minwidth_hint: function( hint ){
var space = this.$center().width() - ( this.hidden ? this.savedSize : 0 );
if( space < hint ){
if( !this.hidden ){
this.toggle();
this.hiddenByTool = true;
// ..............................................................
//TODO: only used in message.mako?
/** */
handle_minwidth_hint: function( hint ){
var space = this.$center().width() - ( this.hidden ? this.saved_size : 0 );
if( space < hint ){
if( !this.hidden ){
this.toggle();
this.hiddenByTool = true;
}
} else {
if( this.hiddenByTool ){
this.toggle();
this.hiddenByTool = false;
}
}
} else {
if( this.hiddenByTool ){
this.toggle();
this.hiddenByTool = false;
return self;
},
/** */
force_panel : function( op ){
if( op == 'show' ){ return this.show(); }
if( op == 'hide' ){ return this.hide(); }
return self;
},
toString : function(){ return 'SidePanel(' + this.id + ')' }
});
// ----------------------------------------------------------------------------
// TODO: side should be defined by page - not here
var LeftPanel = SidePanel.extend({
id : 'left',
});
var RightPanel = SidePanel.extend({
id : 'right',
});
/** Center panel with the ability to switch between iframe and view */
var CenterPanel = Backbone.View.extend({
initialize : function( options ){
this.setElement( $( this.template() ) );
this.$frame = this.$( '.center-frame' );
this.$panel = this.$( '.center-panel' );
this.$frame.on( 'load', _.bind( this._iframeChangeHandler, this ) );
},
/** Display iframe if its target url changes, hide center panel */
_iframeChangeHandler : function( ev ) {
var iframe = ev.currentTarget;
var location = iframe.contentWindow && iframe.contentWindow.location;
if( location && location.host ) {
$( iframe ).show();
this.$panel.empty().hide();
Galaxy.trigger( 'center-frame:load', {
fullpath: location.pathname + location.search + location.hash,
pathname: location.pathname,
search : location.search,
hash : location.hash
});
}
}
return self;
},
},
/** */
force_panel : function( op ){
if( op == 'show' ){ return this.show(); }
if( op == 'hide' ){ return this.hide(); }
return self;
},
toString : function(){ return 'SidePanel(' + this.id + ')'; }
});
// ----------------------------------------------------------------------------
// TODO: side should be defined by page - not here
var LeftPanel = SidePanel.extend({
id : 'left',
});
var RightPanel = SidePanel.extend({
id : 'right',
});
// ----------------------------------------------------------------------------
/**
*
*/
var CenterPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend({
_logNamespace : 'layout',
initialize : function( options ){
this.log( this + '.initialize:', options );
/** previous view contained in the center panel - cached for removal later */
this.prev = null;
},
render : function(){
this.log( this + '.render:' );
this.$el.html( this.template() );
// ?: doesn't work/listen in events map
this.$( '#galaxy_main' ).on( 'load', _.bind( this._iframeChangeHandler, this ) );
},
/** */
_iframeChangeHandler : function( ev ){
var iframe = ev.currentTarget;
var location = iframe.contentWindow && iframe.contentWindow.location;
if( location && location.host ){
// show the iframe and hide MVCview div, remove any views in the MVCview div
$( iframe ).show();
if( this.prev ){
this.prev.remove();
/** Display a view in the center panel, hide iframe */
display: function( view ) {
var contentWindow = this.$frame[ 0 ].contentWindow || {};
var message = contentWindow.onbeforeunload && contentWindow.onbeforeunload();
if ( !message || confirm( message ) ) {
contentWindow.onbeforeunload = undefined;
this.$frame.attr( 'src', 'about:blank' ).hide();
this.$panel.empty().scrollTop( 0 ).append( view.$el ).show();
Galaxy.trigger( 'center-panel:load', view );
}
this.$( '#center-panel' ).hide();
// TODO: move to Galaxy
Galaxy.trigger( 'galaxy_main:load', {
fullpath: location.pathname + location.search + location.hash,
pathname: location.pathname,
search : location.search,
hash : location.hash
});
}
},
},
/** */
display: function( view ){
// we need to display an MVC view: hide the iframe and show the other center panel
// first checking for any onbeforeunload handlers on the iframe
var contentWindow = this.$( '#galaxy_main' )[ 0 ].contentWindow || {};
var message = contentWindow.onbeforeunload && contentWindow.onbeforeunload();
if ( !message || confirm( message ) ) {
contentWindow.onbeforeunload = undefined;
// remove any previous views
if( this.prev ){
this.prev.remove();
}
this.prev = view;
this.$( '#galaxy_main' ).attr( 'src', 'about:blank' ).hide();
this.$( '#center-panel' ).scrollTop( 0 ).append( view.$el ).show();
Galaxy.trigger( 'center-panel:load', view );
template: function() {
return '<div class="center-container">' +
'<iframe id="galaxy_main" name="galaxy_main" frameborder="0" class="center-frame" />' +
'<div class="center-panel" />' +
'</div>';
},
} else {
if( view ){
view.remove();
}
}
},
toString : function() { return 'CenterPanel' }
});
template: function(){
return [
//TODO: remove inline styling
'<div style="position: absolute; width: 100%; height: 100%">',
'<iframe name="galaxy_main" id="galaxy_main" frameborder="0" ',
'style="position: absolute; width: 100%; height: 100%;"/>',
'<div id="center-panel" ',
'style="display: none; position: absolute; width: 100%; height: 100%; padding: 10px; overflow: auto;"/>',
'</div>'
].join('');
},
toString : function(){ return 'CenterPanel'; }
});
// ============================================================================
return {
SidePanel : SidePanel,
LeftPanel : LeftPanel,
RightPanel : RightPanel,
CenterPanel : CenterPanel
};
});
});
+3 -3
View File
@@ -9,7 +9,7 @@ return Backbone.View.extend({
this.buttonActive = options.collection.add({
id : 'enable-scratchbook',
icon : 'fa-th',
tooltip : 'Enable/Disable Scratchbook',
tooltip : _l( 'Enable/Disable Scratchbook' ),
onclick : function() {
self.active = !self.active;
self.buttonActive.set({
@@ -28,7 +28,7 @@ return Backbone.View.extend({
this.buttonLoad = options.collection.add({
id : 'show-scratchbook',
icon : 'fa-eye',
tooltip : 'Show/Hide Scratchbook',
tooltip : _l( 'Show/Hide Scratchbook' ),
show_note : true,
visible : false,
onclick : function( e ) {
@@ -187,4 +187,4 @@ return Backbone.View.extend({
}
});
});
});
@@ -14,7 +14,7 @@
* @param {object|string} options
* @returns (object} jQuery object
*/
$.fn.autocomplete = function(options) {
$.fn.autocomplete_verheul = function(options) {
var url;
if (arguments.length > 1) {
url = options;
@@ -24,7 +24,7 @@
url = options;
options = { url: url };
}
var opts = $.extend({}, $.fn.autocomplete.defaults, options);
var opts = $.extend({}, $.fn.autocomplete_verheul.defaults, options);
return this.each(function() {
var $this = $(this);
$this.data('autocompleter', new $.Autocompleter(
@@ -38,7 +38,7 @@
* Store default options
* @type {object}
*/
$.fn.autocomplete.defaults = {
$.fn.autocomplete_verheul.defaults = {
inputClass: 'acInput',
loadingClass: 'acLoading',
resultsClass: 'acResults',
@@ -289,10 +289,10 @@
/**
* Sanitize options
*/
this.options.minChars = sanitizeInteger(this.options.minChars, $.fn.autocomplete.defaults.minChars, { min: 0 });
this.options.maxItemsToShow = sanitizeInteger(this.options.maxItemsToShow, $.fn.autocomplete.defaults.maxItemsToShow, { min: 0 });
this.options.maxCacheLength = sanitizeInteger(this.options.maxCacheLength, $.fn.autocomplete.defaults.maxCacheLength, { min: 1 });
this.options.delay = sanitizeInteger(this.options.delay, $.fn.autocomplete.defaults.delay, { min: 0 });
this.options.minChars = sanitizeInteger(this.options.minChars, $.fn.autocomplete_verheul.defaults.minChars, { min: 0 });
this.options.maxItemsToShow = sanitizeInteger(this.options.maxItemsToShow, $.fn.autocomplete_verheul.defaults.maxItemsToShow, { min: 0 });
this.options.maxCacheLength = sanitizeInteger(this.options.maxCacheLength, $.fn.autocomplete_verheul.defaults.maxCacheLength, { min: 1 });
this.options.delay = sanitizeInteger(this.options.delay, $.fn.autocomplete_verheul.defaults.delay, { min: 0 });
if (this.options.preventDefaultReturn != 2) {
this.options.preventDefaultReturn = this.options.preventDefaultReturn ? 1 : 0;
}
@@ -151,7 +151,7 @@ var CitationListView = Backbone.View.extend({
' citations explicitly at this time. When writing up your analysis, please manually',
' review your histories and find all references',
' that should be cited in order to completely describe your work. Also, please remember to',
' <a href="https://wiki.galaxyproject.org/CitingGalaxy">cite Galaxy</a>.',
' <a href="https://galaxyproject.org/citing-galaxy">cite Galaxy</a>.',
'</div>',
].join('');
} else {
@@ -0,0 +1,127 @@
define([
], function( ){
/* For presentation-related functionality shared across collection creators.
Particularily overlapping functionality related to name processing and help.
*/
var CollectionCreatorMixin = {
/** add (or clear if clear is truthy) a validation warning to the DOM element described in what */
_validationWarning : function( what, clear ){
var VALIDATION_CLASS = 'validation-warning';
if( what === 'name' ){
what = this.$( '.collection-name' ).add( this.$( '.collection-name-prompt' ) );
this.$( '.collection-name' ).focus().select();
}
if( clear ){
what = what || this.$( '.' + VALIDATION_CLASS );
what.removeClass( VALIDATION_CLASS );
} else {
what.addClass( VALIDATION_CLASS );
}
},
_changeHideOriginals: function( ev ) {
this.hideOriginals = this.$( '.hide-originals' ).prop("checked");
},
// ........................................................................ footer
/** handle a collection name change */
_changeName : function( ev ){
this._validationWarning( 'name', !!this._getName() );
},
/** check for enter key press when in the collection name and submit */
_nameCheckForEnter : function( ev ){
if( ev.keyCode === 13 && !this.blocking ){
this._clickCreate();
}
},
/** get the current collection name */
_getName : function(){
return _.escape( this.$( '.collection-name' ).val() );
},
// ........................................................................ header
/** expand help */
_clickMoreHelp : function( ev ){
ev.stopPropagation();
this.$( '.main-help' ).addClass( 'expanded' );
this.$( '.more-help' ).hide();
},
/** collapse help */
_clickLessHelp : function( ev ){
ev.stopPropagation();
this.$( '.main-help' ).removeClass( 'expanded' );
this.$( '.more-help' ).show();
},
/** toggle help */
_toggleHelp : function( ev ){
ev.stopPropagation();
this.$( '.main-help' ).toggleClass( 'expanded' );
this.$( '.more-help' ).toggle();
},
/** show an alert on the top of the interface containing message (alertClass is bootstrap's alert-*) */
_showAlert : function( message, alertClass ){
alertClass = alertClass || 'alert-danger';
this.$( '.main-help' ).hide();
this.$( '.header .alert' )
.attr( 'class', 'alert alert-dismissable' ).addClass( alertClass ).show()
.find( '.alert-message' ).html( message );
},
/** hide the alerts at the top */
_hideAlert : function( message ){
this.$( '.main-help' ).show();
this.$( '.header .alert' ).hide();
},
_cancelCreate: function( ev ){
if( typeof this.oncancel === 'function' ){
this.oncancel.call( this );
}
},
/** attempt to create the current collection */
_clickCreate : function( ev ){
var name = this._getName();
if( !name ){
this._validationWarning( 'name' );
} else if( !this.blocking ){
this.createList( name );
}
},
_setUpCommonSettings : function( attributes ) {
this.hideOriginals = attributes.defaultHideSourceItems || false;
},
/** render the footer, completion controls, and cancel controls */
_renderFooter : function( speed, callback ){
var instance = this;
var $footer = this.$( '.footer' ).empty().html( this.templates.footer() );
_.each( this.footerSettings, function( property, selector ) {
this.$( selector ).prop( 'checked', instance[ property ] );
});
if( typeof this.oncancel === 'function' ){
this.$( '.cancel-create.btn' ).show();
}
return $footer;
},
_creatorTemplates: {
main : _.template([
'<div class="header flex-row no-flex"></div>',
'<div class="middle flex-row flex-row-container"></div>',
'<div class="footer flex-row no-flex"></div>'
].join('')),
}
}
//==============================================================================
return {
CollectionCreatorMixin: CollectionCreatorMixin
};
});
@@ -130,6 +130,10 @@ var DatasetDCE = DATASET_MODEL.DatasetAssociation.extend( BASE_MVC.mixin( Datase
DatasetCollectionElementMixin.defaults
),
_downloadQueryParameters : function() {
return '?to_ext=' + this.get( 'file_ext' ) + '&hdca_id=' + this.get( 'parent_hdca_id' ) + '&element_identifier=' + this.get( 'element_identifier' );
},
// because all objects have constructors (as this hashmap would even if this next line wasn't present)
// the constructor in hcontentMixin won't be attached by BASE_MVC.mixin to this model
// - re-apply manually for now
@@ -210,6 +214,10 @@ var DatasetCollection = Backbone.Model
//TODO: same patterns as DatasetCollectionElement _createObjectModel - refactor to BASE_MVC.hasSubModel?
var elements = this.get( 'elements' ) || [];
this.unset( 'elements', { silent: true });
var self = this;
_.each(elements, function(element, index) {
_.extend(element, {"parent_hdca_id": self.get("id")});
});
this.elements = new this.collectionClass( elements );
//this.debug( 'collectionClass:', this.collectionClass + '', this.elements );
return this.elements;
@@ -277,7 +285,7 @@ var DatasetCollection = Backbone.Model
// ........................................................................ searchable
/** searchable attributes for collections */
searchAttributes : [
'name'
'name', 'tags'
],
// ........................................................................ misc
@@ -3,9 +3,11 @@ define([
"mvc/collection/collection-model",
"mvc/collection/collection-li-edit",
"mvc/base-mvc",
"mvc/tag",
"ui/fa-icon-button",
"utils/localization",
"ui/editable-text",
], function( DC_VIEW, DC_MODEL, DC_EDIT, BASE_MVC, _l ){
], function( DC_VIEW, DC_MODEL, DC_EDIT, BASE_MVC, TAGS, faIconButton, _l ){
'use strict';
/* =============================================================================
@@ -47,6 +49,8 @@ var CollectionViewEdit = _super.extend(
return;
}
this.tagsEditorShown = true;
//TODO: extract
var panel = this,
nameSelector = '> .controls .name';
@@ -67,6 +71,13 @@ var CollectionViewEdit = _super.extend(
}
}
});
this.tagsEditor = new TAGS.TagsEditor({
model : this.model,
el : $where.find( '.tags-display' ),
onshowFirstTime : function(){ this.render(); },
usePrompt : false
});
this.tagsEditor.toggle( true );
},
// ........................................................................ misc
@@ -45,6 +45,7 @@ var CollectionView = _super.extend(
this.parentName = attributes.parentName;
/** foldout or drilldown */
this.foldoutStyle = attributes.foldoutStyle || 'foldout';
this.downloadUrl = this.model.attributes.url + '/download';
},
_queueNewRender : function( $newRender, speed ) {
@@ -169,7 +170,16 @@ CollectionView.prototype.templates = (function(){
'<% } %>',
'</div>',
'</div>',
'</div>'
'<div class="tags-display"></div>',
'<div class="actions">',
'<a class="download-btn icon-btn" ',
'href="<%- view.downloadUrl %>', '" title="" download="" data-original-title="Download Collection">',
'<span class="fa fa-floppy-o"></span>',
'</a>',
'</div>',
'</div>',
], 'collection' );
return _.extend( _.clone( _super.prototype.templates ), {
@@ -3,15 +3,17 @@ define([
"mvc/history/hdca-model",
"mvc/dataset/states",
"mvc/base-mvc",
"mvc/collection/base-creator",
"mvc/ui/ui-modal",
"utils/natural-sort",
"utils/localization",
"ui/hoverhighlight"
], function( HDCA, STATES, BASE_MVC, UI_MODAL, naturalSort, _l ){
], function( HDCA, STATES, BASE_MVC, baseCreator, UI_MODAL, naturalSort, _l ){
'use strict';
var logNamespace = 'collections';
/*==============================================================================
TODO:
use proper Element model and not just json
@@ -151,7 +153,7 @@ var DatasetCollectionElementView = Backbone.View.extend( BASE_MVC.LoggableMixin
// ============================================================================
/** An interface for building collections.
*/
var ListCollectionCreator = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend({
var ListCollectionCreator = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( baseCreator.CollectionCreatorMixin ).extend({
_logNamespace : logNamespace,
/** the class used to display individual elements */
@@ -177,6 +179,10 @@ var ListCollectionCreator = Backbone.View.extend( BASE_MVC.LoggableMixin ).exten
highlightClr : 'rgba( 64, 255, 255, 1.0 )'
},
footerSettings : {
'.hide-originals': 'hideOriginals'
},
/** set up initial options, instance vars, behaviors */
initialize : function( attributes ){
this.metric( 'ListCollectionCreator.initialize', attributes );
@@ -189,6 +195,7 @@ var ListCollectionCreator = Backbone.View.extend( BASE_MVC.LoggableMixin ).exten
/** unordered, original list - cache to allow reversal */
creator.initialElements = attributes.elements || [];
this._setUpCommonSettings( attributes );
this._instanceSetUp();
this._elementsSetUp();
this._setUpBehaviors();
@@ -258,10 +265,8 @@ var ListCollectionCreator = Backbone.View.extend( BASE_MVC.LoggableMixin ).exten
if( element.history_content_type !== 'dataset' ){
return _l( "is not a dataset" );
}
if( element.state !== STATES.OK ){
if( _.contains( STATES.NOT_READY_STATES, element.state ) ){
return _l( "hasn't finished running yet" );
}
var validState = element.state === STATES.OK || _.contains( STATES.NOT_READY_STATES, element.state );
if( ! validState ){
return _l( "has errored, is paused, or is not accessible" );
}
if( element.deleted || element.purged ){
@@ -348,15 +353,6 @@ var ListCollectionCreator = Backbone.View.extend( BASE_MVC.LoggableMixin ).exten
return $middle;
},
/** render the footer, completion controls, and cancel controls */
_renderFooter : function( speed, callback ){
var $footer = this.$( '.footer' ).empty().html( this.templates.footer() );
if( typeof this.oncancel === 'function' ){
this.$( '.cancel-create.btn' ).show();
}
return $footer;
},
/** add any jQuery/bootstrap/custom plugins to elements rendered */
_addPluginComponents : function(){
this.$( '.help-content i' ).hoverhighlight( '.collection-creator', this.highlightClr );
@@ -367,21 +363,6 @@ var ListCollectionCreator = Backbone.View.extend( BASE_MVC.LoggableMixin ).exten
this._showAlert( this.templates.invalidElements({ problems: this.invalidElements }), 'alert-warning' );
},
/** add (or clear if clear is truthy) a validation warning to the DOM element described in what */
_validationWarning : function( what, clear ){
var VALIDATION_CLASS = 'validation-warning';
if( what === 'name' ){
what = this.$( '.collection-name' ).add( this.$( '.collection-name-prompt' ) );
this.$( '.collection-name' ).focus().select();
}
if( clear ){
what = what || this.$( '.' + VALIDATION_CLASS );
what.removeClass( VALIDATION_CLASS );
} else {
what.addClass( VALIDATION_CLASS );
}
},
_disableNameAndCreate : function( disable ){
disable = !_.isUndefined( disable )? disable : true;
if( disable ){
@@ -528,7 +509,7 @@ var ListCollectionCreator = Backbone.View.extend( BASE_MVC.LoggableMixin ).exten
});
creator.blocking = true;
return creator.creationFn( elements, name )
return creator.creationFn( elements, name, creator.hideOriginals )
.always( function(){
creator.blocking = false;
})
@@ -624,48 +605,11 @@ var ListCollectionCreator = Backbone.View.extend( BASE_MVC.LoggableMixin ).exten
// footer
'change .collection-name' : '_changeName',
'keydown .collection-name' : '_nameCheckForEnter',
'click .cancel-create' : function( ev ){
if( typeof this.oncancel === 'function' ){
this.oncancel.call( this );
}
},
'change .hide-originals' : '_changeHideOriginals',
'click .cancel-create' : '_cancelCreate',
'click .create-collection' : '_clickCreate'//,
},
// ........................................................................ header
/** expand help */
_clickMoreHelp : function( ev ){
ev.stopPropagation();
this.$( '.main-help' ).addClass( 'expanded' );
this.$( '.more-help' ).hide();
},
/** collapse help */
_clickLessHelp : function( ev ){
ev.stopPropagation();
this.$( '.main-help' ).removeClass( 'expanded' );
this.$( '.more-help' ).show();
},
/** toggle help */
_toggleHelp : function( ev ){
ev.stopPropagation();
this.$( '.main-help' ).toggleClass( 'expanded' );
this.$( '.more-help' ).toggle();
},
/** show an alert on the top of the interface containing message (alertClass is bootstrap's alert-*) */
_showAlert : function( message, alertClass ){
alertClass = alertClass || 'alert-danger';
this.$( '.main-help' ).hide();
this.$( '.header .alert' )
.attr( 'class', 'alert alert-dismissable' ).addClass( alertClass ).show()
.find( '.alert-message' ).html( message );
},
/** hide the alerts at the top */
_hideAlert : function( message ){
this.$( '.main-help' ).show();
this.$( '.header .alert' ).hide();
},
// ........................................................................ elements
/** reset all data to the initial state */
reset : function(){
@@ -789,46 +733,12 @@ var ListCollectionCreator = Backbone.View.extend( BASE_MVC.LoggableMixin ).exten
this.$dragging = null;
},
// ........................................................................ footer
/** handle a collection name change */
_changeName : function( ev ){
this._validationWarning( 'name', !!this._getName() );
},
/** check for enter key press when in the collection name and submit */
_nameCheckForEnter : function( ev ){
if( ev.keyCode === 13 && !this.blocking ){
this._clickCreate();
}
},
/** get the current collection name */
_getName : function(){
return _.escape( this.$( '.collection-name' ).val() );
},
/** attempt to create the current collection */
_clickCreate : function( ev ){
var name = this._getName();
if( !name ){
this._validationWarning( 'name' );
} else if( !this.blocking ){
this.createList( name );
}
},
// ------------------------------------------------------------------------ templates
//TODO: move to require text plugin and load these as text
//TODO: underscore currently unnecc. bc no vars are used
//TODO: better way of localizing text-nodes in long strings
/** underscore template fns attached to class */
templates : {
/** the skeleton */
main : _.template([
'<div class="header flex-row no-flex"></div>',
'<div class="middle flex-row flex-row-container"></div>',
'<div class="footer flex-row no-flex"></div>'
].join('')),
templates : _.extend({}, baseCreator.CollectionCreatorMixin._creatorTemplates, {
/** the header (not including help text) */
header : _.template([
@@ -864,6 +774,12 @@ var ListCollectionCreator = Backbone.View.extend( BASE_MVC.LoggableMixin ).exten
/** creation and cancel controls */
footer : _.template([
'<div class="attributes clear">',
'<div class="clear">',
'<label class="setting-prompt pull-right">',
_l( 'Hide original elements' ), '?',
'<input class="hide-originals pull-right" type="checkbox" />',
'</label>',
'</div>',
'<div class="clear">',
'<input class="collection-name form-control pull-right" ',
'placeholder="', _l( 'Enter a name for your new collection' ), '" />',
@@ -974,7 +890,7 @@ var ListCollectionCreator = Backbone.View.extend( BASE_MVC.LoggableMixin ).exten
'</div>',
'</div>'
].join('')),
},
}),
// ------------------------------------------------------------------------ misc
/** string rep */
@@ -1032,10 +948,11 @@ var listCollectionCreatorModal = function _listCollectionCreatorModal( elements,
/** Use a modal to create a list collection, then add it to the given history contents.
* @returns {Deferred} resolved when the collection is added to the history.
*/
function createListCollection( contents ){
function createListCollection( contents, defaultHideSourceItems ){
var elements = contents.toJSON(),
promise = listCollectionCreatorModal( elements, {
creationFn : function( elements, name ){
defaultHideSourceItems: defaultHideSourceItems,
creationFn : function( elements, name, hideSourceItems ){
elements = elements.map( function( element ){
return {
id : element.id,
@@ -1044,7 +961,7 @@ function createListCollection( contents ){
src : ( element.history_content_type === 'dataset'? 'hda' : 'hdca' )
};
});
return contents.createHDCA( elements, 'list', name );
return contents.createHDCA( elements, 'list', name, hideSourceItems );
}
});
return promise;
@@ -1,11 +1,11 @@
define([
"utils/levenshtein",
"utils/natural-sort",
"mvc/collection/list-collection-creator",
"mvc/collection/base-creator",
"mvc/base-mvc",
"utils/localization",
"ui/hoverhighlight"
], function( levenshteinDistance, naturalSort, LIST_COLLECTION_CREATOR, baseMVC, _l ){
], function( levenshteinDistance, naturalSort, baseCreator, baseMVC, _l ){
'use strict';
@@ -190,7 +190,7 @@ function autoPairFnBuilder( options ){
// ============================================================================
/** An interface for building collections of paired datasets.
*/
var PairedCollectionCreator = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
var PairedCollectionCreator = Backbone.View.extend( baseMVC.LoggableMixin ).extend( baseCreator.CollectionCreatorMixin ).extend({
_logNamespace : logNamespace,
className: 'list-of-pairs-collection-creator collection-creator flex-row-container',
@@ -259,6 +259,7 @@ var PairedCollectionCreator = Backbone.View.extend( baseMVC.LoggableMixin ).exte
/** Used for blocking UI events during ajax/operations (don't post twice) */
this.blocking = false;
this._setUpCommonSettings( attributes );
this._setUpBehaviors();
this._dataSetUp();
},
@@ -581,6 +582,7 @@ var PairedCollectionCreator = Backbone.View.extend( baseMVC.LoggableMixin ).exte
var ajaxData = {
type : 'dataset_collection',
collection_type : 'list:paired',
hide_source_items : creator.hideOriginals || false,
name : _.escape( name || creator.$( '.collection-name' ).val() ),
element_identifiers : creator.paired.map( function( pair ){
return creator._pairToJSON( pair );
@@ -632,8 +634,8 @@ var PairedCollectionCreator = Backbone.View.extend( baseMVC.LoggableMixin ).exte
/** render the entire interface */
render : function( speed, callback ){
//this.debug( '-- _render' );
//this.$el.empty().html( PairedCollectionCreator.templates.main() );
this.$el.empty().html( PairedCollectionCreator.templates.main() );
//this.$el.empty().html( this.templates.main() );
this.$el.empty().html( this.templates.main() );
this._renderHeader( speed );
this._renderMiddle( speed );
this._renderFooter( speed );
@@ -645,8 +647,8 @@ var PairedCollectionCreator = Backbone.View.extend( baseMVC.LoggableMixin ).exte
/** render the header section */
_renderHeader : function( speed, callback ){
//this.debug( '-- _renderHeader' );
var $header = this.$( '.header' ).empty().html( PairedCollectionCreator.templates.header() )
.find( '.help-content' ).prepend( $( PairedCollectionCreator.templates.helpContent() ) );
var $header = this.$( '.header' ).empty().html( this.templates.header() )
.find( '.help-content' ).prepend( $( this.templates.helpContent() ) );
this._renderFilters();
return $header;
@@ -659,7 +661,7 @@ var PairedCollectionCreator = Backbone.View.extend( baseMVC.LoggableMixin ).exte
/** render the middle including unpaired and paired sections (which may be hidden) */
_renderMiddle : function( speed, callback ){
var $middle = this.$( '.middle' ).empty().html( PairedCollectionCreator.templates.middle() );
var $middle = this.$( '.middle' ).empty().html( this.templates.middle() );
// (re-) hide the un/paired panels based on instance vars
if( this.unpairedPanelHidden ){
@@ -810,14 +812,9 @@ var PairedCollectionCreator = Backbone.View.extend( baseMVC.LoggableMixin ).exte
return $msg;
},
/** render the footer, completion controls, and cancel controls */
_renderFooter : function( speed, callback ){
var $footer = this.$( '.footer' ).empty().html( PairedCollectionCreator.templates.footer() );
this.$( '.remove-extensions' ).prop( 'checked', this.removeExtensions );
if( typeof this.oncancel === 'function' ){
this.$( '.cancel-create.btn' ).show();
}
return $footer;
footerSettings : {
'.hide-originals': 'hideOriginals',
'.remove-extensions': 'removeExtensions'
},
/** add any jQuery/bootstrap/custom plugins to elements rendered */
@@ -942,6 +939,7 @@ var PairedCollectionCreator = Backbone.View.extend( baseMVC.LoggableMixin ).exte
// header
'click .more-help' : '_clickMoreHelp',
'click .less-help' : '_clickLessHelp',
'click .main-help' : '_toggleHelp',
'click .header .alert button' : '_hideAlert',
'click .forward-column .column-title' : '_clickShowOnlyUnpaired',
'click .reverse-column .column-title' : '_clickShowOnlyUnpaired',
@@ -981,39 +979,11 @@ var PairedCollectionCreator = Backbone.View.extend( baseMVC.LoggableMixin ).exte
'change .remove-extensions' : function( ev ){ this.toggleExtensions(); },
'change .collection-name' : '_changeName',
'keydown .collection-name' : '_nameCheckForEnter',
'click .cancel-create' : function( ev ){
if( typeof this.oncancel === 'function' ){
this.oncancel.call( this );
}
},
'change .hide-originals' : '_changeHideOriginals',
'click .cancel-create' : '_cancelCreate',
'click .create-collection' : '_clickCreate'//,
},
// ........................................................................ header
/** expand help */
_clickMoreHelp : function( ev ){
this.$( '.main-help' ).addClass( 'expanded' );
this.$( '.more-help' ).hide();
},
/** collapse help */
_clickLessHelp : function( ev ){
this.$( '.main-help' ).removeClass( 'expanded' );
this.$( '.more-help' ).show();
},
/** show an alert on the top of the interface containing message (alertClass is bootstrap's alert-*)*/
_showAlert : function( message, alertClass ){
alertClass = alertClass || 'alert-danger';
this.$( '.main-help' ).hide();
this.$( '.header .alert' ).attr( 'class', 'alert alert-dismissable' ).addClass( alertClass ).show()
.find( '.alert-message' ).html( message );
},
/** hide the alerts at the top */
_hideAlert : function( message ){
this.$( '.main-help' ).show();
this.$( '.header .alert' ).hide();
},
/** toggle between showing only unpaired and split view */
_clickShowOnlyUnpaired : function( ev ){
//this.debug( 'click unpaired', ev.currentTarget );
@@ -1419,33 +1389,6 @@ var PairedCollectionCreator = Backbone.View.extend( baseMVC.LoggableMixin ).exte
creator._renderFooter();
},
/** handle a collection name change */
_changeName : function( ev ){
this._validationWarning( 'name', !!this._getName() );
},
/** check for enter key press when in the collection name and submit */
_nameCheckForEnter : function( ev ){
if( ev.keyCode === 13 && !this.blocking ){
this._clickCreate();
}
},
/** get the current collection name */
_getName : function(){
return _.escape( this.$( '.collection-name' ).val() );
},
/** attempt to create the current collection */
_clickCreate : function( ev ){
var name = this._getName();
if( !name ){
this._validationWarning( 'name' );
} else if( !this.blocking ){
this.createList();
}
},
// ------------------------------------------------------------------------ misc
/** debug a dataset list */
_printList : function( list ){
@@ -1465,209 +1408,200 @@ var PairedCollectionCreator = Backbone.View.extend( baseMVC.LoggableMixin ).exte
},
/** string rep */
toString : function(){ return 'PairedCollectionCreator'; }
toString : function(){ return 'PairedCollectionCreator'; },
templates: _.extend({}, baseCreator.CollectionCreatorMixin._creatorTemplates, {
/** the header (not including help text) */
header : _.template([
'<div class="main-help well clear">',
'<a class="more-help" href="javascript:void(0);">', _l( 'More help' ), '</a>',
'<div class="help-content">',
'<a class="less-help" href="javascript:void(0);">', _l( 'Less' ), '</a>',
'</div>',
'</div>',
'<div class="alert alert-dismissable">',
'<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>',
'<span class="alert-message"></span>',
'</div>',
'<div class="column-headers vertically-spaced flex-column-container">',
'<div class="forward-column flex-column column">',
'<div class="column-header">',
'<div class="column-title">',
'<span class="title">', _l( 'Unpaired forward' ), '</span>',
'<span class="title-info unpaired-info"></span>',
'</div>',
'<div class="unpaired-filter forward-unpaired-filter pull-left">',
'<input class="search-query" placeholder="', _l( 'Filter this list' ), '" />',
'</div>',
'</div>',
'</div>',
'<div class="paired-column flex-column no-flex column">',
'<div class="column-header">',
'<a class="choose-filters-link" href="javascript:void(0)">',
_l( 'Choose filters' ),
'</a>',
'<a class="clear-filters-link" href="javascript:void(0);">',
_l( 'Clear filters' ),
'</a><br />',
'<a class="autopair-link" href="javascript:void(0);">',
_l( 'Auto-pair' ),
'</a>',
'</div>',
'</div>',
'<div class="reverse-column flex-column column">',
'<div class="column-header">',
'<div class="column-title">',
'<span class="title">', _l( 'Unpaired reverse' ), '</span>',
'<span class="title-info unpaired-info"></span>',
'</div>',
'<div class="unpaired-filter reverse-unpaired-filter pull-left">',
'<input class="search-query" placeholder="', _l( 'Filter this list' ), '" />',
'</div>',
'</div>',
'</div>',
'</div>'
].join('')),
/** the middle: unpaired, divider, and paired */
middle : _.template([
// contains two flex rows (rows that fill available space) and a divider btwn
'<div class="unpaired-columns flex-column-container scroll-container flex-row">',
'<div class="forward-column flex-column column">',
'<ol class="column-datasets"></ol>',
'</div>',
'<div class="paired-column flex-column no-flex column">',
'<ol class="column-datasets"></ol>',
'</div>',
'<div class="reverse-column flex-column column">',
'<ol class="column-datasets"></ol>',
'</div>',
'</div>',
'<div class="flexible-partition">',
'<div class="flexible-partition-drag" title="', _l( 'Drag to change' ), '"></div>',
'<div class="column-header">',
'<div class="column-title paired-column-title">',
'<span class="title"></span>',
'</div>',
'<a class="unpair-all-link" href="javascript:void(0);">',
_l( 'Unpair all' ),
'</a>',
'</div>',
'</div>',
'<div class="paired-columns flex-column-container scroll-container flex-row">',
'<ol class="column-datasets"></ol>',
'</div>'
].join('')),
/** creation and cancel controls */
footer : _.template([
'<div class="attributes clear">',
'<div class="clear">',
'<label class="setting-prompt pull-right">',
_l( 'Hide original elements' ), '?',
'<input class="hide-originals pull-right" type="checkbox" />',
'</label>',
'<label class="setting-prompt pull-right">',
_l( 'Remove file extensions from pair names' ), '?',
'<input class="remove-extensions pull-right" type="checkbox" />',
'</label>',
'</div>',
'<div class="clear">',
'<input class="collection-name form-control pull-right" ',
'placeholder="', _l( 'Enter a name for your new list' ), '" />',
'<div class="collection-name-prompt pull-right">', _l( 'Name' ), ':</div>',
'</div>',
'</div>',
'<div class="actions clear vertically-spaced">',
'<div class="other-options pull-left">',
'<button class="cancel-create btn" tabindex="-1">', _l( 'Cancel' ), '</button>',
'<div class="create-other btn-group dropup">',
'<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">',
_l( 'Create a different kind of collection' ),
' <span class="caret"></span>',
'</button>',
'<ul class="dropdown-menu" role="menu">',
'<li><a href="#">', _l( 'Create a <i>single</i> pair' ), '</a></li>',
'<li><a href="#">', _l( 'Create a list of <i>unpaired</i> datasets' ), '</a></li>',
'</ul>',
'</div>',
'</div>',
'<div class="main-options pull-right">',
'<button class="create-collection btn btn-primary">', _l( 'Create list' ), '</button>',
'</div>',
'</div>'
].join('')),
/** help content */
helpContent : _.template([
'<p>', _l([
'Collections of paired datasets are ordered lists of dataset pairs (often forward and reverse reads). ',
'These collections can be passed to tools and workflows in order to have analyses done on each member of ',
'the entire group. This interface allows you to create a collection, choose which datasets are paired, ',
'and re-order the final collection.'
].join( '' )), '</p>',
'<p>', _l([
'Unpaired datasets are shown in the <i data-target=".unpaired-columns">unpaired section</i> ',
'(hover over the underlined words to highlight below). ',
'Paired datasets are shown in the <i data-target=".paired-columns">paired section</i>.',
'<ul>To pair datasets, you can:',
'<li>Click a dataset in the ',
'<i data-target=".unpaired-columns .forward-column .column-datasets,',
'.unpaired-columns .forward-column">forward column</i> ',
'to select it then click a dataset in the ',
'<i data-target=".unpaired-columns .reverse-column .column-datasets,',
'.unpaired-columns .reverse-column">reverse column</i>.',
'</li>',
'<li>Click one of the "Pair these datasets" buttons in the ',
'<i data-target=".unpaired-columns .paired-column .column-datasets,',
'.unpaired-columns .paired-column">middle column</i> ',
'to pair the datasets in a particular row.',
'</li>',
'<li>Click <i data-target=".autopair-link">"Auto-pair"</i> ',
'to have your datasets automatically paired based on name.',
'</li>',
'</ul>'
].join( '' )), '</p>',
'<p>', _l([
'<ul>You can filter what is shown in the unpaired sections by:',
'<li>Entering partial dataset names in either the ',
'<i data-target=".forward-unpaired-filter input">forward filter</i> or ',
'<i data-target=".reverse-unpaired-filter input">reverse filter</i>.',
'</li>',
'<li>Choosing from a list of preset filters by clicking the ',
'<i data-target=".choose-filters-link">"Choose filters" link</i>.',
'</li>',
'<li>Entering regular expressions to match dataset names. See: ',
'<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions"',
' target="_blank">MDN\'s JavaScript Regular Expression Tutorial</a>. ',
'Note: forward slashes (\\) are not needed.',
'</li>',
'<li>Clearing the filters by clicking the ',
'<i data-target=".clear-filters-link">"Clear filters" link</i>.',
'</li>',
'</ul>'
].join( '' )), '</p>',
'<p>', _l([
'To unpair individual dataset pairs, click the ',
'<i data-target=".unpair-btn">unpair buttons ( <span class="fa fa-unlink"></span> )</i>. ',
'Click the <i data-target=".unpair-all-link">"Unpair all" link</i> to unpair all pairs.'
].join( '' )), '</p>',
'<p>', _l([
'You can include or remove the file extensions (e.g. ".fastq") from your pair names by toggling the ',
'<i data-target=".remove-extensions-prompt">"Remove file extensions from pair names?"</i> control.'
].join( '' )), '</p>',
'<p>', _l([
'Once your collection is complete, enter a <i data-target=".collection-name">name</i> and ',
'click <i data-target=".create-collection">"Create list"</i>. ',
'(Note: you do not have to pair all unpaired datasets to finish.)'
].join( '' )), '</p>'
].join(''))
})
});
//TODO: move to require text plugin and load these as text
//TODO: underscore currently unnecc. bc no vars are used
//TODO: better way of localizing text-nodes in long strings
/** underscore template fns attached to class */
PairedCollectionCreator.templates = PairedCollectionCreator.templates || {
/** the skeleton */
main : _.template([
'<div class="header flex-row no-flex"></div>',
'<div class="middle flex-row flex-row-container"></div>',
'<div class="footer flex-row no-flex">'
].join('')),
/** the header (not including help text) */
header : _.template([
'<div class="main-help well clear">',
'<a class="more-help" href="javascript:void(0);">', _l( 'More help' ), '</a>',
'<div class="help-content">',
'<a class="less-help" href="javascript:void(0);">', _l( 'Less' ), '</a>',
'</div>',
'</div>',
'<div class="alert alert-dismissable">',
'<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>',
'<span class="alert-message"></span>',
'</div>',
'<div class="column-headers vertically-spaced flex-column-container">',
'<div class="forward-column flex-column column">',
'<div class="column-header">',
'<div class="column-title">',
'<span class="title">', _l( 'Unpaired forward' ), '</span>',
'<span class="title-info unpaired-info"></span>',
'</div>',
'<div class="unpaired-filter forward-unpaired-filter pull-left">',
'<input class="search-query" placeholder="', _l( 'Filter this list' ), '" />',
'</div>',
'</div>',
'</div>',
'<div class="paired-column flex-column no-flex column">',
'<div class="column-header">',
'<a class="choose-filters-link" href="javascript:void(0)">',
_l( 'Choose filters' ),
'</a>',
'<a class="clear-filters-link" href="javascript:void(0);">',
_l( 'Clear filters' ),
'</a><br />',
'<a class="autopair-link" href="javascript:void(0);">',
_l( 'Auto-pair' ),
'</a>',
'</div>',
'</div>',
'<div class="reverse-column flex-column column">',
'<div class="column-header">',
'<div class="column-title">',
'<span class="title">', _l( 'Unpaired reverse' ), '</span>',
'<span class="title-info unpaired-info"></span>',
'</div>',
'<div class="unpaired-filter reverse-unpaired-filter pull-left">',
'<input class="search-query" placeholder="', _l( 'Filter this list' ), '" />',
'</div>',
'</div>',
'</div>',
'</div>'
].join('')),
/** the middle: unpaired, divider, and paired */
middle : _.template([
// contains two flex rows (rows that fill available space) and a divider btwn
'<div class="unpaired-columns flex-column-container scroll-container flex-row">',
'<div class="forward-column flex-column column">',
'<ol class="column-datasets"></ol>',
'</div>',
'<div class="paired-column flex-column no-flex column">',
'<ol class="column-datasets"></ol>',
'</div>',
'<div class="reverse-column flex-column column">',
'<ol class="column-datasets"></ol>',
'</div>',
'</div>',
'<div class="flexible-partition">',
'<div class="flexible-partition-drag" title="', _l( 'Drag to change' ), '"></div>',
'<div class="column-header">',
'<div class="column-title paired-column-title">',
'<span class="title"></span>',
'</div>',
'<a class="unpair-all-link" href="javascript:void(0);">',
_l( 'Unpair all' ),
'</a>',
'</div>',
'</div>',
'<div class="paired-columns flex-column-container scroll-container flex-row">',
'<ol class="column-datasets"></ol>',
'</div>'
].join('')),
/** creation and cancel controls */
footer : _.template([
'<div class="attributes clear">',
'<div class="clear">',
'<label class="remove-extensions-prompt pull-right">',
_l( 'Remove file extensions from pair names' ), '?',
'<input class="remove-extensions pull-right" type="checkbox" />',
'</label>',
'</div>',
'<div class="clear">',
'<input class="collection-name form-control pull-right" ',
'placeholder="', _l( 'Enter a name for your new list' ), '" />',
'<div class="collection-name-prompt pull-right">', _l( 'Name' ), ':</div>',
'</div>',
'</div>',
'<div class="actions clear vertically-spaced">',
'<div class="other-options pull-left">',
'<button class="cancel-create btn" tabindex="-1">', _l( 'Cancel' ), '</button>',
'<div class="create-other btn-group dropup">',
'<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">',
_l( 'Create a different kind of collection' ),
' <span class="caret"></span>',
'</button>',
'<ul class="dropdown-menu" role="menu">',
'<li><a href="#">', _l( 'Create a <i>single</i> pair' ), '</a></li>',
'<li><a href="#">', _l( 'Create a list of <i>unpaired</i> datasets' ), '</a></li>',
'</ul>',
'</div>',
'</div>',
'<div class="main-options pull-right">',
'<button class="create-collection btn btn-primary">', _l( 'Create list' ), '</button>',
'</div>',
'</div>'
].join('')),
/** help content */
helpContent : _.template([
'<p>', _l([
'Collections of paired datasets are ordered lists of dataset pairs (often forward and reverse reads). ',
'These collections can be passed to tools and workflows in order to have analyses done on each member of ',
'the entire group. This interface allows you to create a collection, choose which datasets are paired, ',
'and re-order the final collection.'
].join( '' )), '</p>',
'<p>', _l([
'Unpaired datasets are shown in the <i data-target=".unpaired-columns">unpaired section</i> ',
'(hover over the underlined words to highlight below). ',
'Paired datasets are shown in the <i data-target=".paired-columns">paired section</i>.',
'<ul>To pair datasets, you can:',
'<li>Click a dataset in the ',
'<i data-target=".unpaired-columns .forward-column .column-datasets,',
'.unpaired-columns .forward-column">forward column</i> ',
'to select it then click a dataset in the ',
'<i data-target=".unpaired-columns .reverse-column .column-datasets,',
'.unpaired-columns .reverse-column">reverse column</i>.',
'</li>',
'<li>Click one of the "Pair these datasets" buttons in the ',
'<i data-target=".unpaired-columns .paired-column .column-datasets,',
'.unpaired-columns .paired-column">middle column</i> ',
'to pair the datasets in a particular row.',
'</li>',
'<li>Click <i data-target=".autopair-link">"Auto-pair"</i> ',
'to have your datasets automatically paired based on name.',
'</li>',
'</ul>'
].join( '' )), '</p>',
'<p>', _l([
'<ul>You can filter what is shown in the unpaired sections by:',
'<li>Entering partial dataset names in either the ',
'<i data-target=".forward-unpaired-filter input">forward filter</i> or ',
'<i data-target=".reverse-unpaired-filter input">reverse filter</i>.',
'</li>',
'<li>Choosing from a list of preset filters by clicking the ',
'<i data-target=".choose-filters-link">"Choose filters" link</i>.',
'</li>',
'<li>Entering regular expressions to match dataset names. See: ',
'<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions"',
' target="_blank">MDN\'s JavaScript Regular Expression Tutorial</a>. ',
'Note: forward slashes (\\) are not needed.',
'</li>',
'<li>Clearing the filters by clicking the ',
'<i data-target=".clear-filters-link">"Clear filters" link</i>.',
'</li>',
'</ul>'
].join( '' )), '</p>',
'<p>', _l([
'To unpair individual dataset pairs, click the ',
'<i data-target=".unpair-btn">unpair buttons ( <span class="fa fa-unlink"></span> )</i>. ',
'Click the <i data-target=".unpair-all-link">"Unpair all" link</i> to unpair all pairs.'
].join( '' )), '</p>',
'<p>', _l([
'You can include or remove the file extensions (e.g. ".fastq") from your pair names by toggling the ',
'<i data-target=".remove-extensions-prompt">"Remove file extensions from pair names?"</i> control.'
].join( '' )), '</p>',
'<p>', _l([
'Once your collection is complete, enter a <i data-target=".collection-name">name</i> and ',
'click <i data-target=".create-collection">"Create list"</i>. ',
'(Note: you do not have to pair all unpaired datasets to finish.)'
].join( '' )), '</p>'
].join(''))
};
//=============================================================================
/** a modal version of the paired collection creator */
var pairedCollectionCreatorModal = function _pairedCollectionCreatorModal( datasets, options ){
@@ -1708,11 +1642,12 @@ var pairedCollectionCreatorModal = function _pairedCollectionCreatorModal( datas
//=============================================================================
function createListOfPairsCollection( collection ){
function createListOfPairsCollection( collection, defaultHideSourceItems ){
var elements = collection.toJSON();
//TODO: validate elements
return pairedCollectionCreatorModal( elements, {
historyId : collection.historyId
historyId : collection.historyId,
defaultHideSourceItems: defaultHideSourceItems
});
}
@@ -230,15 +230,16 @@ var pairCollectionCreatorModal = function _pairCollectionCreatorModal( elements,
/** Use a modal to create a pair collection, then add it to the given history contents.
* @returns {Deferred} resolved when the collection is added to the history.
*/
function createPairCollection( contents ){
function createPairCollection( contents, defaultHideSourceItems ){
var elements = contents.toJSON(),
promise = pairCollectionCreatorModal( elements, {
creationFn : function( elements, name ){
defaultHideSourceItems: defaultHideSourceItems,
creationFn : function( elements, name, hideSourceItems ){
elements = [
{ name: "forward", src: "hda", id: elements[0].id },
{ name: "reverse", src: "hda", id: elements[1].id }
];
return contents.createHDCA( elements, 'paired', name );
return contents.createHDCA( elements, 'paired', name, hideSourceItems );
}
});
return promise;
+16 -96
View File
@@ -336,7 +336,7 @@ var EmbeddedTabularDatasetChunkedView = TabularDatasetChunkedView.extend({
});
// button for trackster visualization
/** Button for trackster visualization */
var TabularButtonTracksterView = Backbone.View.extend({
// gene region columns
@@ -391,11 +391,9 @@ var TabularButtonTracksterView = Backbone.View.extend({
this.file_ext = model.get('file_ext');
// check for bed-file format
if (this.file_ext == 'bed')
{
if (this.file_ext == 'bed') {
// verify that metadata exists
if (metadata.get('chromCol') && metadata.get('startCol') && metadata.get('endCol'))
{
if (metadata.get('chromCol') && metadata.get('startCol') && metadata.get('endCol')) {
// read in columns
this.col.chrom = metadata.get('chromCol') - 1;
this.col.start = metadata.get('startCol') - 1;
@@ -407,8 +405,7 @@ var TabularButtonTracksterView = Backbone.View.extend({
}
// check for vcf-file format
if (this.file_ext == 'vcf')
{
if (this.file_ext == 'vcf') {
// search array
function search (str, array) {
for (var j = 0; j < array.length; j++)
@@ -473,15 +470,16 @@ var TabularButtonTracksterView = Backbone.View.extend({
this.hide();
},
// backbone events
events:
{
/** Add event handlers */
events: {
'mouseover tr' : 'show',
'mouseleave' : 'hide'
},
// show button
show: function (e) {
var self = this;
// is numeric
function is_numeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
@@ -518,7 +516,12 @@ var TabularButtonTracksterView = Backbone.View.extend({
// update css
$('#btn_viz').css({'position': 'fixed', 'top': top + 'px', 'left': left + 'px'});
$('#btn_viz').off('click');
$('#btn_viz').click(this.create_trackster_action(this.url_viz, btn_viz_pars, this.genome_build));
$('#btn_viz').click( function() {
self.frame.add({
title : 'Trackster',
url : self.url_viz + '/trackster?' + $.param(btn_viz_pars)
});
});
// show the button
$('#btn_viz').show();
@@ -528,92 +531,9 @@ var TabularButtonTracksterView = Backbone.View.extend({
}
},
// hide button
/** hide button */
hide: function () {
this.$el.find('#btn_viz').hide();
},
// create action
create_trackster_action : function (vis_url, dataset_params, dbkey) {
// link this
var self = this;
// create function
return function() {
var listTracksParams = {};
if (dbkey) {
listTracksParams[ 'f-dbkey' ] = dbkey;
}
$.ajax({
url: vis_url + '/list_tracks?' + $.param( listTracksParams ),
dataType: 'html',
error: function() {
// show error message
self.modal.show({
title : 'Something went wrong!',
body : 'Unfortunately we could not add this dataset to the track browser. Please try again or contact us.',
buttons : {
'Cancel': function(){
self.modal.hide();
}
}
});
},
success: function(table_html) {
self.modal.show({
title : 'View Data in a New or Saved Visualization',
buttons :{
'Cancel': function(){
self.modal.hide();
},
'View in saved visualization': function(){
// show modal with saved visualizations
self.modal.show(
{
title : 'Add Data to Saved Visualization',
body : table_html,
buttons : {
'Cancel': function(){
self.modal.hide();
},
'Add to visualization': function(){
// hide
self.modal.hide();
// search selected fields
self.modal.$el.find('input[name=id]:checked').each(function(){
// get visualization id
var vis_id = $(this).val();
dataset_params.id = vis_id;
// add widget
self.frame.add({
title : 'Trackster',
type : 'url',
content : vis_url + '/trackster?' + $.param(dataset_params)
});
});
}
}
});
},
'View in new visualization': function(){
// hide
self.modal.hide();
// add widget
self.frame.add({
title : 'Trackster',
type : 'url',
content : vis_url + '/trackster?' + $.param(dataset_params)
});
}
}
});
}
});
return false;
};
this.$('#btn_viz').hide();
}
});
@@ -0,0 +1,258 @@
define( [ 'utils/utils', 'mvc/ui/ui-tabs', 'mvc/ui/ui-misc', 'mvc/form/form-view' ], function( Utils, Tabs, Ui, Form ) {
/** Dataset edit attributes view */
var View = Backbone.View.extend({
initialize: function() {
this.setElement( '<div/>' );
this.model = new Backbone.Model( { 'dataset_id': Galaxy.params.dataset_id } );
this.render();
},
// Fetch data for the selected dataset and
// build tabs for editing its attributes
render: function() {
var url = Galaxy.root + 'dataset/edit',
self = this;
Utils.get({
url : url,
data : { 'dataset_id' : self.model.get( 'dataset_id' ) },
success : function( response ) {
self.render_attribute_page( self, response );
},
error : function( response ) {
var error_response = {
'status': 'error',
'message': 'Error occured while loading the dataset.',
'persistent': true,
'cls': 'errormessage'
};
self.display_message( error_response, self.$( '.response-message' ) );
}
});
},
/** Render all the tabs view */
render_attribute_page: function( self, response ) {
var message = {
'message' : response.message,
'status' : response.status,
'persistent' : true,
'cls' : response.status + 'message'
};
self.$el.empty().append( self._templateHeader() );
self.display_message( message, self.$( '.response-message' ) );
// Create all tabs
self.create_tabs( response, self.$( '.edit-attr' ) );
},
/** Perform AJAX post call */
call_ajax: function( self, data, tab_name ) {
var post_url = Galaxy.root + 'dataset/edit';
$.ajax({
type: "PUT",
url: post_url,
data: data,
success: function( response ) {
self.render_attribute_page( self, response );
self.reload_history();
},
error : function( response ) {
var error_response = {
'status': 'error',
'message': 'Error occured while saving. Please fill all the required fields and try again.',
'persistent': true,
'cls': 'errormessage'
};
self.display_message( error_response, self.$( '.response-message' ) );
}
});
},
/** Display actions messages */
display_message: function( response, $el ) {
$el.empty().html( new Ui.Message( response ).$el );
},
/** Create tabs for different attributes of dataset*/
create_tabs: function( response, $el_edit_attr ) {
var self = this;
self.tabs = new Tabs.View();
self.tabs.add({
id : 'attributes',
title : 'Attributes',
icon : 'fa fa-bars',
tooltip : 'Edit dataset attributes',
$el : self._getAttributesFormTemplate( response )
});
self.tabs.add({
id : 'convert',
title : 'Convert',
icon : 'fa-gear',
tooltip : 'Convert to new format',
$el : self._getConvertFormTemplate( response )
});
self.tabs.add({
id : 'datatype',
title : 'Datatypes',
icon : 'fa-database',
tooltip : 'Change data type',
$el : self._getChangeDataTypeFormTemplate( response )
});
self.tabs.add({
id : 'permissions',
title : 'Permissions',
icon : 'fa-user',
tooltip : 'Permissions',
$el : self._getPermissionsFormTemplate( response )
});
$el_edit_attr.append( self.tabs.$el );
self.tabs.showTab( 'attributes' );
},
/** Main template */
_templateHeader: function() {
return '<div class="page-container edit-attr">' +
'<div class="response-message"></div>' +
'<h3>Edit Dataset Attributes</h3>' +
'</div>';
},
/** Attributes tab template */
_getAttributesFormTemplate: function( response ) {
var self = this;
var form = new Form({
title : 'Edit attributes',
inputs : response.edit_attributes_inputs,
operations: {
'submit_editattr' : new Ui.ButtonIcon({
tooltip : 'Save attributes of the dataset.',
icon : 'fa-floppy-o ',
title : 'Save attributes',
onclick : function() { self._submit( self, form, response, "edit_attributes" ) }
}),
'submit_autocorrect' : new Ui.ButtonIcon({
tooltip : 'This will inspect the dataset and attempt to correct the values of fields if they are not accurate.',
icon : 'fa-undo ',
title : 'Auto-detect',
onclick : function() { self._submit( self, form, response, "auto-detect" ) }
})
}
});
return form.$el;
},
/** Convert tab template */
_getConvertFormTemplate: function( response ) {
var self = this;
var form = new Form({
title : 'Convert to new format',
inputs : response.convert_inputs,
operations: {
'submit' : new Ui.ButtonIcon({
tooltip : 'Convert the datatype to a new format.',
title : 'Convert datatype',
icon : 'fa-exchange ',
onclick : function() { self._submit( self, form, response, "convert" ) }
})
}
});
return form.$el;
},
/** Change datatype template */
_getChangeDataTypeFormTemplate: function( response ) {
var self = this;
var form = new Form({
title : 'Change datatype',
inputs : response.convert_datatype_inputs,
operations: {
'submit' : new Ui.ButtonIcon({
tooltip : 'Change the datatype to a new type.',
title : 'Change datatype',
icon : 'fa-exchange ',
onclick : function() { self._submit( self, form, response, "change" ) }
})
}
});
return form.$el;
},
/** Permissions template */
_getPermissionsFormTemplate: function( response ) {
var template = "",
self = this;
if( response.can_manage_dataset ) {
var form = new Form({
title : 'Manage dataset permissions on ' + response.display_name,
inputs : response.permission_inputs,
operations: {
'submit': new Ui.ButtonIcon({
tooltip : 'Save permissions.',
title : 'Save permissions',
icon : 'fa-floppy-o ',
onclick : function() { self._submit( self, form, response, "permissions" ) }
})
}
});
return form.$el;
}
else {
var form = new Form({
title : 'View permissions',
inputs : response.permission_inputs
});
return form.$el;
}
},
/** Submit action */
_submit: function( self, form, response, type ) {
var form_data = form.data.create();
form_data.dataset_id = response.dataset_id;
switch( type ) {
case "edit_attributes":
form_data.save = 'Save';
break;
case "auto-detect":
form_data.detect = 'Auto-detect';
break;
case "convert":
if ( form_data.target_type !== null && form_data.target_type ) {
form_data.dataset_id = response.dataset_id;
form_data.convert_data = 'Convert';
}
break;
case "change":
form_data.change = 'Save';
break;
case "permissions":
var post_data = {};
post_data.permissions = JSON.stringify( form_data );
post_data.update_roles_button = "Save";
post_data.dataset_id = response.dataset_id;
form_data = post_data;
break;
}
self.call_ajax( self, form_data );
},
/** Reload Galaxy's history after updating dataset's attributes */
reload_history: function() {
if ( window.Galaxy ) {
window.Galaxy.currHistoryPanel.loadCurrentHistory();
}
}
});
return {
View : View
};
});
@@ -60,12 +60,10 @@ var DatasetListItemEdit = _super.extend(
deleted = this.model.get( 'deleted' ),
editBtnData = {
title : _l( 'Edit attributes' ),
href : this.model.urls.edit,
target : this.linkTarget,
href : Galaxy.root + 'datasets/edit?dataset_id=' + this.model.attributes.id,
faIcon : 'fa-pencil',
classes : 'edit-btn'
};
// disable if purged or deleted and explain why in the tooltip
if( deleted || purged ){
editBtnData.disabled = true;
@@ -155,7 +153,7 @@ var DatasetListItemEdit = _super.extend(
return null
}
return faIconButton({
title: 'Tool Help',
title: _l('Tool Help'),
classes: 'icon-btn',
href: '#',
faIcon: 'fa-question',
@@ -218,14 +216,7 @@ var DatasetListItemEdit = _super.extend(
faIcon : 'fa-refresh',
onclick : function( ev ) {
ev.preventDefault();
// create webpack split point in order to load the tool form async
// TODO: split not working (tool loads fine)
require([ 'mvc/tool/tool-form' ], function( ToolForm ){
var form = new ToolForm.View({ 'job_id' : creating_job });
form.deferred.execute( function(){
Galaxy.app.display( form );
});
});
Galaxy.router.push( '/', { job_id : creating_job } );
}
});
}
+26 -10
View File
@@ -46,20 +46,26 @@ var DatasetListItemView = _super.extend(
// re-rendering on any model changes
return self.listenTo( self.model, {
'change': function( model, options ){
'change': function( model ){
// if the model moved into the ready state and is expanded without details, fetch those details now
if( self.model.changedAttributes().state
&& self.model.inReadyState()
&& self.expanded
&& !self.model.hasDetails() ){
if( self.model.changedAttributes().state &&
self.model.inReadyState() &&
self.expanded &&
!self.model.hasDetails() ){
// normally, will render automatically (due to fetch -> change),
// but! setting_metadata sometimes doesn't cause any other changes besides state
// so, not rendering causes it to seem frozen in setting_metadata state
self.model.fetch({ silent : true })
.done( function(){ self.render(); });
} else {
self.render();
if( _.has( model.changed, 'tags' ) && _.keys( model.changed ).length === 1 ){
// If only the tags have changed, rerender specifically
// the titlebar region. Otherwise default to the full
// render.
self.$( '.nametags' ).html( self._renderNametags() );
} else {
self.render();
}
}
}
});
@@ -120,9 +126,9 @@ var DatasetListItemView = _super.extend(
_renderDisplayButton : function(){
// don't show display if not viewable or not accessible
var state = this.model.get( 'state' );
if( ( state === STATES.NOT_VIEWABLE )
|| ( state === STATES.DISCARDED )
|| ( !this.model.get( 'accessible' ) ) ){
if( ( state === STATES.NOT_VIEWABLE ) ||
( state === STATES.DISCARDED ) ||
( !this.model.get( 'accessible' ) ) ){
return null;
}
@@ -299,6 +305,16 @@ var DatasetListItemView = _super.extend(
].join( '\n' ));
},
_renderNametags : function(){
var tpl = _.template([
'<% _.each(_.sortBy(_.uniq(tags), function(x) { return x }), function(tag){ %>',
'<% if (tag.indexOf("name:") == 0){ %>',
'<span class="label label-info"><%- tag.slice(5) %></span>',
'<% } %>',
'<% }); %>'].join(''));
return tpl({tags: this.model.get('tags')});
},
// ......................................................................... misc
events : _.extend( _.clone( _super.prototype.events ), {
'click .display-btn' : function( ev ){ this.trigger( 'display', this, ev ); },
@@ -64,7 +64,7 @@ var DatasetAssociation = Backbone.Model
'purge' : 'datasets/' + id + '/purge_async',
'display' : 'datasets/' + id + '/display/?preview=True',
'edit' : 'datasets/' + id + '/edit',
'download' : 'datasets/' + id + '/display?to_ext=' + this.get( 'file_ext' ),
'download' : 'datasets/' + id + '/display' + this._downloadQueryParameters(),
'report_error' : 'dataset/errors?id=' + id,
'rerun' : 'tool_runner/rerun?id=' + id,
'show_params' : 'datasets/' + id + '/show_params',
@@ -78,6 +78,10 @@ var DatasetAssociation = Backbone.Model
return urls;
},
_downloadQueryParameters : function() {
return '?to_ext=' + this.get( 'file_ext' );
},
/** set up any event listeners
* event: state:ready fired when this DA moves into/is already in a ready state
*/
+12 -9
View File
@@ -5,13 +5,14 @@ define([], function() {
return Backbone.View.extend({
initialize: function( app, options ) {
this.app = app;
this.app_options = app.options || {};
this.app_options = app.model ? app.model.attributes : {};
this.field = options && options.field || new Backbone.View();
this.model = options && options.model || new Backbone.Model({
text_enable : this.app_options.text_enable || 'Enable',
text_disable : this.app_options.text_disable || 'Disable',
cls_enable : this.app_options.cls_enable || 'fa fa-caret-square-o-down',
cls_disable : this.app_options.cls_disable || 'fa fa-caret-square-o-up'
cls_disable : this.app_options.cls_disable || 'fa fa-caret-square-o-up',
always_refresh : this.app_options.always_refresh
}).set( options );
// set element and link components
@@ -44,24 +45,26 @@ define([], function() {
app.trigger && app.trigger( 'change' );
self.render();
});
// hide error on value change
if ( this.field.model && !this.model.get( 'always_refresh' ) ) {
this.listenTo( this.field.model, 'change:value', function() { self.reset() } );
}
},
/** Set backdrop for input element
*/
/** Set backdrop for input element */
backdrop: function() {
this.model.set( 'backdrop', true );
},
/** Set error text
*/
/** Set error text */
error: function( text ) {
this.model.set( 'error_text', text );
},
/** Reset this view
*/
/** Reset this view */
reset: function() {
this.model.set( 'error_text', null );
!this.model.get( 'fixed' ) && this.model.set( 'error_text', null );
},
render: function() {
@@ -29,7 +29,8 @@ define(['utils/utils',
'hidden_data' : '_fieldHidden',
'baseurl' : '_fieldHidden',
'library_data' : '_fieldLibrary',
'ftpfile' : '_fieldFtp'
'ftpfile' : '_fieldFtp',
'upload' : '_fieldUpload'
},
/** Returns an input field for a given field type */
@@ -151,6 +152,7 @@ define(['utils/utils',
area : input_def.area,
readonly : input_def.readonly,
placeholder : input_def.placeholder,
datalist : input_def.datalist,
onchange : input_def.onchange
});
},
@@ -211,6 +213,14 @@ define(['utils/utils',
multiple : input_def.multiple,
onchange : input_def.onchange
});
},
/** Upload file field */
_fieldUpload: function( input_def ) {
return new Ui.Upload({
id : 'field-' + input_def.id,
onchange : input_def.onchange
});
}
});
@@ -43,7 +43,7 @@ function( Utils, Ui, Portlet, Repeat, InputElement, Parameters ) {
_addConditional: function( input_def ) {
var self = this;
input_def.test_param.id = input_def.id;
this.app.options.sustain_conditionals && ( input_def.test_param.disabled = true );
this.app.model.get( 'sustain_conditionals' ) && ( input_def.test_param.disabled = true );
var field = this._addRow( input_def.test_param );
// set onchange event for test parameter
@@ -109,7 +109,7 @@ function( Utils, Ui, Portlet, Repeat, InputElement, Parameters ) {
}
// hide options
this.app.options.sustain_repeats && repeat.hideOptions();
this.app.model.get( 'sustain_repeats' ) && repeat.hideOptions();
// create input field wrapper
var input_element = new InputElement( this.app, {
@@ -144,7 +144,7 @@ function( Utils, Ui, Portlet, Repeat, InputElement, Parameters ) {
this.app.field_list[ id ] = field;
var input_element = new InputElement( this.app, {
name : input_def.name,
label : input_def.label || input_def.name,
label : input_def.hide_label ? '' : input_def.label || input_def.name,
value : input_def.value,
text_value : input_def.text_value,
collapsible_value : input_def.collapsible_value,
@@ -156,6 +156,7 @@ function( Utils, Ui, Portlet, Repeat, InputElement, Parameters ) {
style : input_def.style,
backdrop : input_def.backdrop,
hidden : input_def.hidden,
fixed : input_def.fixed,
field : field
});
this.app.element_list[ id ] = input_element;
+22 -19
View File
@@ -5,12 +5,13 @@ define( [ 'utils/utils', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc', 'mvc/form/form-s
function( Utils, Portlet, Ui, FormSection, FormData ) {
return Backbone.View.extend({
initialize: function( options ) {
this.options = Utils.merge( options, {
this.model = new Backbone.Model({
initial_errors : false,
cls : 'ui-portlet-limited',
icon : null,
always_refresh : true
});
always_refresh : true,
message_status : 'warning'
}).set( options );
this.setElement( '<div/>' );
this.render();
},
@@ -101,16 +102,16 @@ function( Utils, Portlet, Ui, FormSection, FormData ) {
this.data = new FormData.Manager( this );
this._renderForm();
this.data.create();
this.options.initial_errors && this.errors( this.options );
this.model.get( 'initial_errors' ) && this.errors( this.model.attributes );
// add listener which triggers on checksum change, and reset the form input wrappers
var current_check = this.data.checksum();
this.on('change', function( input_id ) {
this.on( 'change', function( input_id ) {
var input = self.input_list[ input_id ];
if ( !input || input.refresh_on_change || self.options.always_refresh ) {
if ( !input || input.refresh_on_change || self.model.get( 'always_refresh' ) ) {
var new_check = self.data.checksum();
if ( new_check != current_check ) {
current_check = new_check;
self.options.onchange && self.options.onchange();
self.model.get( 'onchange' ) && self.model.get( 'onchange' )();
}
}
});
@@ -123,23 +124,25 @@ function( Utils, Portlet, Ui, FormSection, FormData ) {
/** Renders/appends dom elements of the form */
_renderForm: function() {
$( '.tooltip' ).remove();
this.message = new Ui.Message();
this.section = new FormSection.View( this, { inputs: this.options.inputs } );
var options = this.model.attributes;
this.message = new Ui.UnescapedMessage();
this.section = new FormSection.View( this, { inputs: options.inputs } );
this.portlet = new Portlet.View({
icon : this.options.icon,
title : this.options.title,
cls : this.options.cls,
operations : this.options.operations,
buttons : this.options.buttons,
collapsible : this.options.collapsible,
collapsed : this.options.collapsed
icon : options.icon,
title : options.title,
cls : options.cls,
operations : options.operations,
buttons : options.buttons,
collapsible : options.collapsible,
collapsed : options.collapsed,
onchange_title : options.onchange_title
});
this.portlet.append( this.message.$el );
this.portlet.append( this.section.$el );
this.$el.empty();
this.options.inputs && this.$el.append( this.portlet.$el );
this.options.message && this.message.update( { persistent: true, status: 'warning', message: this.options.message } );
options.inputs && this.$el.append( this.portlet.$el );
options.message && this.message.update( { persistent: true, status: options.message_status, message: options.message } );
Galaxy.emit.debug( 'form-view::initialize()', 'Completed' );
}
});
});
});
+1 -2
View File
@@ -107,13 +107,12 @@ return Backbone.Model.extend({
_.each(_.pairs(self.attributes.filters), function(k) {
url_data['f-' + k[0]] = k[1];
});
return url_data;
},
// Return URL for obtaining a new grid
get_url: function (args) {
return this.get('url_base') + "?" + $.param(this.get_url_data()) + '&' + $.param(args);
return this.get('url_base') + '?' + $.param(this.get_url_data()) + '&' + $.param(args);
}
});
@@ -0,0 +1,58 @@
/** This class renders the grid list with shared section. */
define( [ 'mvc/grid/grid-view' ], function( GridView ) {
var View = Backbone.View.extend({
initialize: function( options ) {
var self = this;
this.setElement( $( '<div/>' ) );
this.model = new Backbone.Model( options );
this.item = this.model.get( 'item' );
$.ajax({
url : Galaxy.root + this.item + '/' + this.model.get( 'action_id' ),
success : function( response ) {
response[ 'dict_format' ] = true;
self.model.set( response );
self.render();
}
});
},
render: function() {
var grid = new GridView( this.model.attributes );
this.$el.empty().append( grid.$el );
this.$el.append( this._templateShared() );
},
_templateShared: function() {
var self = this;
var $tmpl = $( '<div>' +
'<h2>' + this.model.get( 'plural' ) + ' shared with you by others</h2>' +
'</div>' );
var options = this.model.attributes;
if ( options.shared_by_others && options.shared_by_others.length > 0 ) {
var $table = $( '<table class="colored" border="0" cellspacing="0" cellpadding="0" width="100%">' +
'<tr class="header">' +
'<th>Title</th>' +
'<th>Owner</th>' +
'</tr>' +
'</table>' );
_.each( options.shared_by_others, function( it, index ) {
var display_url = Galaxy.root + self.item + '/display_by_username_and_slug?username=' + it.username + '&slug=' + it.slug;
$table.append( '<tr>' +
'<td>' +
'<a href="' + display_url + '">' + _.escape( it.title ) + '</a>' +
'</td>' +
'<td>' + _.escape( it.username ) + '</td>' +
'</tr>' );
});
$tmpl.append( $table );
} else {
$tmpl.append( 'No ' + this.model.get( 'plural' ).toLowerCase() + ' have been shared with you.' );
}
return $tmpl;
}
});
return {
View: View
}
});
@@ -62,14 +62,8 @@ return {
}
for (i in options.global_actions) {
var action = options.global_actions[i];
var label_cls = '';
if (action.inbound) {
label_cls = 'use-inbound'
} else {
label_cls = 'use-outbound'
}
tmpl += '<li>' +
'<a class="action-button ' + label_cls + '" href="' + action.url_args + '" onclick="return false;">' + action.label + '</a>' +
'<a class="action-button use-target" target="' + action.target + '" href="' + action.url_args + '" onclick="return false;" >' + action.label + '</a>' +
'</li>';
}
if (show_popup) {
@@ -179,7 +173,7 @@ return {
// load attributes
var link = column_settings.link;
var value = column_settings.value;
var inbound = column_settings.inbound;
var target = column_settings.target;
// unescape value
if (jQuery.type( value ) === 'string') {
@@ -206,14 +200,7 @@ return {
if (options.operations.length != 0) {
tmpl += '<div id="' + id + '" class="' + cls + '" style="float: left;">';
}
var label_class = '';
if (inbound) {
label_class = 'use-inbound';
} else {
label_class = 'use-outbound';
}
tmpl += '<a class="menubutton-label ' + label_class + '" href="' + link + '" onclick="return false;">' + value + '</a>';
tmpl += '<a class="menubutton-label use-target" target="' + target + '" href="' + link + '" onclick="return false;">' + value + '</a>';
if (options.operations.length != 0) {
tmpl += '</div>';
}
@@ -228,7 +215,7 @@ return {
}
return tmpl;
},
// template
footer: function(options) {
@@ -317,7 +304,7 @@ return {
'<input type="hidden" id="operation" name="operation" value="">' +
'<td></td>' +
'<td colspan="100">' +
'For <span class="grid-selected-count"></span> selected ' + options.get_class_plural + ': ';
'For <span class="grid-selected-count"></span> selected items: ';
// configure buttons for operations
for (i in options.operations) {
+99 -121
View File
@@ -4,10 +4,11 @@ jQuery.ajaxSettings.traditional = true;
// dependencies
define([
'utils/utils',
'mvc/grid/grid-model',
'mvc/grid/grid-template',
"mvc/ui/popup-menu"
], function(GridModel, Templates, PopupMenu) {
'mvc/ui/popup-menu'
], function(Utils, GridModel, Templates, PopupMenu) {
// grid view
return Backbone.View.extend({
@@ -16,10 +17,43 @@ return Backbone.View.extend({
grid: null,
// Initialize
initialize: function(grid_config)
{
initialize: function(grid_config) {
this.dict_format = grid_config.dict_format;
var self = this;
window.add_tag_to_grid_filter = function( tag_name, tag_value ){
// Put tag name and value together.
var tag = tag_name + ( tag_value !== undefined && tag_value !== "" ? ":" + tag_value : "" );
var advanced_search = $( '#advanced-search').is(":visible" );
if( !advanced_search ){
$('#standard-search').slideToggle('fast');
$('#advanced-search').slideToggle('fast');
}
self.add_filter_condition( "tags", tag );
};
// set element
this.setElement('#grid-container');
if ( this.dict_format ) {
this.setElement('<div/>');
if ( grid_config.url_base && !grid_config.items ) {
var url_data = {};
_.each(grid_config.filters, function(v, k) {
url_data['f-' + k] = v;
});
$.ajax({
url : grid_config.url_base + '?' + $.param( url_data ),
success : function( response ) {
response.embedded = grid_config.embedded;
response.filters = grid_config.filters;
self.init_grid( response );
}
});
} else {
this.init_grid(grid_config);
}
} else {
this.setElement('#grid-container');
this.init_grid(grid_config);
}
// fix padding
if (grid_config.use_panels) {
@@ -28,9 +62,6 @@ return Backbone.View.extend({
overflow : 'auto'
});
}
// initialize controls
this.init_grid(grid_config);
},
// refresh frames
@@ -45,8 +76,7 @@ return Backbone.View.extend({
},
// Initialize
init_grid: function(grid_config)
{
init_grid: function(grid_config) {
// link grid model
this.grid = new GridModel(grid_config);
@@ -155,18 +185,6 @@ return Backbone.View.extend({
});
});
// Initialize autocomplete for text inputs in search UI.
var t1 = this.$el.find('#input-tags-filter');
if (t1.length) {
t1.autocomplete(this.grid.history_tag_autocomplete_url,
{ selectFirst: false, autoFill: false, highlight: false, mustMatch: false });
}
var t2 = this.$el.find('#input-name-filter');
if (t2.length) {
t2.autocomplete(this.grid.history_name_autocomplete_url,
{ selectFirst: false, autoFill: false, highlight: false, mustMatch: false });
}
// Initialize standard, advanced search toggles.
this.$el.find('.advanced-search-toggle').each( function() {
$(this).off();
@@ -221,80 +239,53 @@ return Backbone.View.extend({
//
// add inbound/outbound events
//
this.$el.find('.use-inbound').each( function() {
this.$el.find('.use-target').each( function() {
$(this).click( function(e) {
self.execute({
href : $(this).attr('href'),
inbound : true
target : $(this).attr('target')
});
return false;
});
});
this.$el.find('.use-outbound').each( function() {
$(this).click( function(e) {
self.execute({
href : $(this).attr('href')
});
return false;
});
});
// empty grid?
var items_length = options.items.length;
if (items_length == 0) {
return;
}
//
// add operation popup menus
//
for (var i in options.items)
{
// get items
var item = options.items[i];
_.each( options.items, function( item, index ) {
var button = self.$( '#grid-' + index + '-popup' ).off();
var popup = new PopupMenu( button );
_.each( options[ 'operations' ], function( operation ) {
self._add_operation( popup, operation, item );
});
});
},
// get identifiers
var button = this.$el.find('#grid-' + i + '-popup');
button.off();
var popup = new PopupMenu(button);
// load details
for (var j in options['operations'])
{
// get operation details
var operation = options['operations'][j];
var operation_id = operation['label'];
var operation_settings = item['operation_config'][operation_id];
var encode_id = item['encode_id'];
// check
if (operation_settings['allowed'] && operation['allow_popup'])
{
// popup configuration
var popupConfig =
{
html : operation['label'],
href : operation_settings['url_args'],
target : operation_settings['target'],
confirmation_text : operation['confirm'],
inbound : operation['inbound']
};
// add popup function
popupConfig.func = function(e)
{
e.preventDefault();
var label = $(e.target).html();
var options = this.findItemByHtml(label);
self.execute(options);
};
// add item
popup.addItem(popupConfig);
/** Add an operation to the items menu */
_add_operation: function( popup, operation, item ) {
var self = this;
var settings = item.operation_config[ operation.label ];
if ( settings.allowed && operation.allow_popup ) {
popup.addItem({
html : operation.label,
href : settings.url_args,
target : settings.target,
confirmation_text : operation.confirm,
func : function( e ) {
e.preventDefault();
var label = $( e.target ).html();
if ( operation.onclick ) {
operation.onclick( item.encode_id );
} else {
self.execute( this.findItemByHtml( label ) );
}
}
}
});
}
},
@@ -442,8 +433,7 @@ return Backbone.View.extend({
},
// confirmation/submission of operation request
submit_operation: function (operation_button, confirmation_text)
{
submit_operation: function (operation_button, confirmation_text) {
// identify operation
var operation_name = $(operation_button).val();
@@ -477,26 +467,11 @@ return Backbone.View.extend({
},
check_all_items: function () {
var chk_all = document.getElementById('check_all'),
checks = document.getElementsByTagName('input'),
total = 0,
i;
if ( chk_all.checked === true ) {
for ( i=0; i < checks.length; i++ ) {
if ( checks[i].name.indexOf( 'id' ) !== -1) {
checks[i].checked = true;
total++;
}
}
}
else {
for ( i=0; i < checks.length; i++ ) {
if ( checks[i].name.indexOf( 'id' ) !== -1) {
checks[i].checked = false;
}
}
}
var check = this.$('.grid-row-select-checkbox');
var state = this.$( '#check_all' ).prop( 'checked' );
_.each( check, function( c ) {
$( c ).prop( 'checked', state );
});
this.init_grid_elements();
},
@@ -518,17 +493,16 @@ return Backbone.View.extend({
var href = null;
var operation = null;
var confirmation_text = null;
var inbound = null;
var target = null;
// check for options
if (options)
{
if (options) {
// get options
href = options.href;
operation = options.operation;
id = options.id;
confirmation_text = options.confirmation_text;
inbound = options.inbound;
target = options.target;
// check if input contains the operation tag
if (href !== undefined && href.indexOf('operation=') != -1) {
@@ -568,10 +542,10 @@ return Backbone.View.extend({
});
// Do operation. If operation cannot be performed asynchronously, redirect to location.
if (this.grid.can_async_op(operation)) {
if (this.grid.can_async_op(operation) || this.dict_format) {
this.update_grid();
} else {
this.go_to(inbound, href);
this.go_to(target, href);
}
// done
@@ -580,15 +554,15 @@ return Backbone.View.extend({
// refresh grid
if (href) {
this.go_to(inbound, href);
this.go_to(target, href);
return false;
}
// refresh grid
if (this.grid.get('async')) {
if (this.grid.get('async') || this.dict_format) {
this.update_grid();
} else {
this.go_to(inbound, href);
this.go_to(target, href);
}
// done
@@ -596,7 +570,7 @@ return Backbone.View.extend({
},
// go to url
go_to: function (inbound, href) {
go_to: function (target, href) {
// get aysnc status
var async = this.grid.get('async');
this.grid.set('async', false);
@@ -616,17 +590,21 @@ return Backbone.View.extend({
item_ids: undefined,
async: async
});
if (inbound) {
// this currently assumes that there is only a single grid shown at a time
var $div = $('.grid-header').closest('.inbound');
if ($div.length !== 0) {
$div.load(href);
return;
}
switch (target) {
case 'inbound':
// this currently assumes that there is only a single grid shown at a time
var $div = $('.grid-header').closest('.inbound');
if ($div.length !== 0) {
$div.load(href);
return;
}
break;
case 'top':
window.top.location = href;
break;
default:
window.location = href;
}
window.location = href;
},
// Update grid.
@@ -649,7 +627,7 @@ return Backbone.View.extend({
var insert = self.grid.get('insert');
// request new configuration
var json = $.parseJSON(response_text);
var json = self.dict_format ? response_text : $.parseJSON(response_text);
// update
json.embedded = embedded;
+9 -1
View File
@@ -16,7 +16,7 @@ var _super = DATASET_LI.DatasetListItemView;
var HDAListItemView = _super.extend(
/** @lends HDAListItemView.prototype */{
className : _super.prototype.className + " history-content",
className : _super.prototype.className + " history-content",
initialize : function( attributes, options ){
_super.prototype.initialize.call( this, attributes, options );
@@ -43,6 +43,14 @@ HDAListItemView.prototype.templates = (function(){
'<span class="hid"><%- dataset.hid %></span> ',
'<span class="name"><%- dataset.name %></span>',
'</div>',
'</br>',
'<span class="nametags">',
'<% _.each(_.sortBy(_.uniq(dataset.tags), function(x) { return x }), function(tag){ %>',
'<% if (tag.indexOf("name:") == 0){ %>',
'<span class="label label-info"><%- tag.slice(5) %></span>',
'<% } %>',
'<% }); %>',
'</span>',
'</div>'
], 'dataset' );
+8 -2
View File
@@ -20,9 +20,8 @@ var HDCAListItemView = _super.extend(
/** event listeners */
_setUpListeners : function(){
_super.prototype._setUpListeners.call( this );
this.listenTo( this.model, {
'change:populated change:visible' : function( model, options ){ this.render(); },
'change:tags change:populated change:visible' : function( model, options ){ this.render(); },
});
},
@@ -85,6 +84,13 @@ HDCAListItemView.prototype.templates = (function(){
'<span class="name"><%- collection.name %></span>',
'</div>',
'<div class="subtitle"></div>',
'<span class="nametags">',
'<% _.each(_.sortBy(_.uniq(collection.tags), function(x) { return x }), function(tag){ %>',
'<% if (tag.indexOf("name:") == 0){ %>',
'<span class="label label-info"><%- tag.slice(5) %></span>',
'<% } %>',
'<% }); %>',
'</span>',
'</div>'
], 'collection' );
@@ -421,7 +421,7 @@ var HistoryContents = _super.extend( BASE_MVC.LoggableMixin ).extend({
},
/** create a new HDCA in this collection */
createHDCA : function( elementIdentifiers, collectionType, name, options ){
createHDCA : function( elementIdentifiers, collectionType, name, hideSourceItems, options ){
// normally collection.create returns the new model, but we need the promise from the ajax, so we fake create
//precondition: elementIdentifiers is an array of plain js objects
// in the proper form to create the collectionType
@@ -430,6 +430,7 @@ var HistoryContents = _super.extend( BASE_MVC.LoggableMixin ).extend({
collection_type : collectionType,
history_id : this.historyId,
name : name,
hide_source_items : hideSourceItems || false,
// should probably be able to just send in a bunch of json here and restruct per class
// note: element_identifiers is now (incorrectly) an attribute
element_identifiers : elementIdentifiers
@@ -0,0 +1,49 @@
/** This class renders the grid list. */
define( [ 'utils/utils', 'mvc/grid/grid-view', 'mvc/history/history-model', 'mvc/history/copy-dialog' ], function( Utils, GridView, HistoryModel, historyCopyDialog ) {
var View = Backbone.View.extend({
initialize: function( options ) {
var self = this;
this.setElement( $( '<div/>' ) );
this.model = new Backbone.Model();
Utils.get({
url : Galaxy.root + 'history/' + options.action_id,
success : function( response ) {
response[ 'dict_format' ] = true;
_.each( response[ 'operations' ], function( operation ) {
if ( operation.label == 'Copy' ) {
operation.onclick = function( id ) { self._showCopyDialog( id ) };
}
});
self.model.set( response );
self.render();
}
});
},
render: function() {
var grid = new GridView( this.model.attributes );
this.$el.empty().append( grid.$el );
},
_showCopyDialog: function( id ) {
var history = new HistoryModel.History( { id : id } );
history.fetch()
.fail( function() {
alert( 'History could not be fetched. Please contact an administrator' );
})
.done( function(){
historyCopyDialog( history, {} ).done( function() {
if( window.parent && window.parent.Galaxy && window.parent.Galaxy.currHistoryPanel ) {
window.parent.Galaxy.currHistoryPanel.loadCurrentHistory();
}
window.location.reload( true );
} );
});
}
});
return {
View: View
}
});
@@ -177,7 +177,7 @@ var History = Backbone.Model
// if we don't flip this, then a fully-fetched list will not be re-checked via fetch
this.contents.allFetched = false;
var fetchFn = self.contents.currentPage !== 0
? function(){ return self.contents.fetchPage( 0 ); }
? function(){ return self.contents.fetchPage( self.contents.currentPage ); }
: function(){ return self.contents.fetchUpdated( lastUpdateTime ); };
// note: if there was no previous update time, all summary contents will be fetched
return fetchFn()
@@ -338,7 +338,7 @@ var CurrentHistoryView = _super.extend(/** @lends CurrentHistoryView.prototype *
// compare the url to the following list and if there's a match
// pull the id from url and indicate in the history view that
// the dataset with that id is the 'current'ly active dataset
'galaxy_main:load': function( data ){
'center-frame:load': function( data ){
var pathToMatch = data.fullpath;
var hdaId = null;
var useToURLRegexMap = {
@@ -68,6 +68,10 @@ var HistoryViewEdit = _super.extend(
// ---- set up instance vars
/** editor for tags - sub-view */
this.tagsEditor = null;
/** enable drag and drop - sub-view */
this.dragItems = true;
/** editor for annotations - sub-view */
this.annotationEditor = null;
@@ -285,25 +289,33 @@ var HistoryViewEdit = _super.extend(
_collectionActions : function(){
var panel = this;
return [
{ html: _l( 'Build Dataset List' ), func: function() {
LIST_COLLECTION_CREATOR.createListCollection( panel.getSelectedModels() )
.done( function(){ panel.model.refresh(); });
}
{ html: _l( 'Build Dataset List' ), func: function() { panel.buildCollection( "list") }
},
// TODO: Only show quick pair if two things selected.
{ html: _l( 'Build Dataset Pair' ), func: function() {
PAIR_COLLECTION_CREATOR.createPairCollection( panel.getSelectedModels() )
.done( function(){ panel.model.refresh(); });
}
{ html: _l( 'Build Dataset Pair' ), func: function() { panel.buildCollection( "paired") }
},
{ html: _l( 'Build List of Dataset Pairs' ), func: function() {
LIST_OF_PAIRS_COLLECTION_CREATOR.createListOfPairsCollection( panel.getSelectedModels() )
.done( function(){ panel.model.refresh(); });
}
{ html: _l( 'Build List of Dataset Pairs' ), func: function() { panel.buildCollection( "list:paired" ) }
},
];
},
buildCollection : function( collectionType, selection, hideSourceItems ) {
var panel = this;
var selection = selection || panel.getSelectedModels();
var hideSourceItems = hideSourceItems || false;
var createFunc;
if( collectionType == "list" ) {
createFunc = LIST_COLLECTION_CREATOR.createListCollection;
} else if( collectionType == "paired" ) {
createFunc = PAIR_COLLECTION_CREATOR.createPairCollection;
} else if( collectionType == "list:paired" ) {
createFunc = LIST_OF_PAIRS_COLLECTION_CREATOR.createListOfPairsCollection;
} else {
console.warn( "Unknown collectionType encountered " + collectionType );
}
createFunc( selection, hideSourceItems ).done( function() { panel.model.refresh() } );
},
// ------------------------------------------------------------------------ sub-views
/** In this override, add purgeAllowed and whether tags/annotation editors should be shown */
_getItemViewOptions : function( model ){
@@ -369,14 +369,17 @@ var HistoryView = _super.extend(
}),
_clickPrevPage : function( ev ){
this.model.clearUpdateTimeout();
this.model.contents.fetchPrevPage();
},
_clickNextPage : function( ev ){
this.model.clearUpdateTimeout();
this.model.contents.fetchNextPage();
},
_changePageSelect : function( ev ){
this.model.clearUpdateTimeout();
var page = $( ev.currentTarget ).val();
this.model.contents.fetchPage( page );
},
@@ -16,13 +16,15 @@ var menu = [
},
{
html : _l( 'Saved Histories' ),
href : 'history/list',
href : 'histories/list',
target : '_top'
},
{
html : _l( 'Histories Shared with Me' ),
href : 'history/list_shared'
href : 'histories/list_shared',
target : '_top'
},
{
html : _l( 'Current History' ),
header : true,
@@ -110,17 +112,11 @@ var menu = [
html : _l( 'Unhide Hidden Datasets' ),
anon : true,
func : function() {
// TODO: Deprecate this functionality and replace with group dataset selector and action combination
if( Galaxy && Galaxy.currHistoryPanel && confirm( _l( 'Really unhide all hidden datasets?' ) ) ){
var filtered = Galaxy.currHistoryPanel.model.contents.hidden();
//TODO: batch
filtered.ajaxQueue( Backbone.Model.prototype.save, { visible : true })
.done( function(){
Galaxy.currHistoryPanel.renderItems();
})
.fail( function(){
alert( 'There was an error unhiding the datasets' );
console.error( arguments );
});
$.post(Galaxy.root + "history/adjust_hidden",
{ 'user_action' : 'unhide' },
function(){Galaxy.currHistoryPanel.loadCurrentHistory();});
}
},
},
@@ -128,18 +124,11 @@ var menu = [
html : _l( 'Delete Hidden Datasets' ),
anon : true,
func : function() {
// TODO: Deprecate this functionality and replace with group dataset selector and action combination
if( Galaxy && Galaxy.currHistoryPanel && confirm( _l( 'Really delete all hidden datasets?' ) ) ){
var filtered = Galaxy.currHistoryPanel.model.contents.hidden();
//TODO: batch
// both delete *and* unhide them
filtered.ajaxQueue( Backbone.Model.prototype.save, { deleted : true, visible: true })
.done( function(){
Galaxy.currHistoryPanel.renderItems();
})
.fail( function(){
alert( 'There was an error deleting the datasets' );
console.error( arguments );
});
$.post(Galaxy.root + "history/adjust_hidden",
{ 'user_action' : 'delete' },
function(){Galaxy.currHistoryPanel.loadCurrentHistory();});
}
},
},
@@ -218,7 +207,7 @@ function buildMenu( isAnon, purgeAllowed, urlRoot ){
//TODO:?? hard-coded galaxy_main
if( menuOption.href ){
menuOption.href = urlRoot + menuOption.href;
menuOption.target = 'galaxy_main';
menuOption.target = menuOption.target || 'galaxy_main';
}
if( menuOption.confirm ){
@@ -386,7 +386,7 @@ var FolderListView = Backbone.View.extend({
'</table>',
'<div class="empty-folder-message" style="display:none;">',
'This folder is either empty or you do not have proper access permissions to see the contents. If you expected something to show up',
' please consult the <a href="https://wiki.galaxyproject.org/Admin/DataLibraries/LibrarySecurity" target="_blank">library security wikipage</a>',
' please consult the <a href="https://galaxyproject.org/data-libraries/#permissions" target="_blank">library security wikipage</a>',
' or visit the <a href="https://biostar.usegalaxy.org/" target="_blank">Galaxy support site</a>.',
'</div>'
].join(''));
@@ -729,27 +729,34 @@ var FolderToolbarView = Backbone.View.extend({
*/
addAllDatasetsFromHistory : function (){
var checked_hdas = this.modal.$el.find( '#selected_history_content' ).find( ':checked' );
var history_dataset_ids = [];
var hdas_to_add = [];
var history_item_ids = []; // can be hda or hdca
var history_item_types = [];
var items_to_add = [];
if ( checked_hdas.length < 1 ){
mod_toastr.info( 'You must select some datasets first.' );
} else {
this.modal.disableButton( 'Add' );
checked_hdas.each(function(){
var hid = $( this.parentElement ).data( 'id' );
if ( hid ) {
history_dataset_ids.push( hid );
}
if ( hid ) {
var item_type = $( this.parentElement ).data( 'name' );
history_item_ids.push( hid );
history_item_types.push( item_type );
}
});
for ( var i = history_dataset_ids.length - 1; i >= 0; i-- ) {
history_dataset_id = history_dataset_ids[i];
for ( var i = history_item_ids.length - 1; i >= 0; i-- ) {
history_item_id = history_item_ids[i];
var folder_item = new mod_library_model.Item();
folder_item.url = Galaxy.root + 'api/folders/' + this.options.id + '/contents';
folder_item.set( { 'from_hda_id':history_dataset_id } );
hdas_to_add.push( folder_item );
if (history_item_types[i] === 'collection') {
folder_item.set({'from_hdca_id': history_item_id});
} else {
folder_item.set({'from_hda_id': history_item_id});
}
items_to_add.push(folder_item);
}
this.initChainCallControl( { length: hdas_to_add.length, action: 'adding_datasets' } );
this.chainCallAddingHdas( hdas_to_add );
this.initChainCallControl( { length: items_to_add.length, action: 'adding_datasets' } );
this.chainCallAddingHdas( items_to_add );
}
},
@@ -1144,7 +1151,7 @@ var FolderToolbarView = Backbone.View.extend({
'&nbsp;Details',
'</button>',
'<span class="help-button" data-toggle="tooltip" data-placement="top" title="Visit Libraries Wiki">',
'<a href="https://wiki.galaxyproject.org/DataLibraries/screen/FolderContents" target="_blank">',
'<a href="https://galaxyproject.org/data-libraries/screen/folder-contents/" target="_blank">',
'<button class="primary-button" type="button">',
'<span class="fa fa-question-circle"></span>',
'&nbsp;Help',
@@ -1387,9 +1394,26 @@ var FolderToolbarView = Backbone.View.extend({
'<strong>Choose the datasets to import:</strong>',
'<ul>',
'<% _.each(history_contents, function(history_item) { %>',
'<li data-id="<%= _.escape(history_item.get("id")) %>">',
'<input style="margin: 0;" type="checkbox"> <%= _.escape(history_item.get("hid")) %>: <%= _.escape(history_item.get("name")) %>',
'</li>',
'<% if (history_item.get("deleted") != true ) { %>',
'<% if (history_item.get("type") === "collection") { %>',
'<% var collection_type = history_item.get("collection_type") %>',
'<% if (collection_type === "list") { %>',
'<li data-id="<%= _.escape(history_item.get("id")) %>" data-name="<%= _.escape(history_item.get("type")) %>">',
'<input style="margin: 0;" type="checkbox"> <%= _.escape(history_item.get("hid")) %>: <%= _.escape(history_item.get("name")) %> (Dataset Collection)',
'</li>',
'<% } else { %>',
'<li><input style="margin: 0;" type="checkbox" onclick="return false;" disabled="disabled">',
'<span title="You can convert this collection into a collection of type list using the Collection Tools">',
' <%= _.escape(history_item.get("hid")) %>: <%= _.escape(history_item.get("name")) %> (Dataset Collection of type <%= _.escape(collection_type) %> not supported.)',
'</span>',
'</li>',
'<% } %>',
'<% } else if (history_item.get("visible") === true && history_item.get("state") === "ok") { %>',
'<li data-id="<%= _.escape(history_item.get("id")) %>" data-name="<%= _.escape(history_item.get("type")) %>">',
'<input style="margin: 0;" type="checkbox"> <%= _.escape(history_item.get("hid")) %>: <%= _.escape(history_item.get("name")) %>',
'</li>',
'<% } %>',
'<% } %>',
'<% }); %>',
'</ul>'
].join(''));
@@ -66,6 +66,7 @@ var LibraryListView = Backbone.View.extend({
var template = this.templateLibraryList();
var libraries_to_render = null;
var models = null;
var is_public = function(model){ return model.get('public') === true; }
$( ".tooltip" ).hide();
if ( typeof options !== 'undefined' ){
models = typeof options.models !== 'undefined' ? options.models : null;
@@ -77,12 +78,18 @@ var LibraryListView = Backbone.View.extend({
} else {
libraries_to_render = this.collection.where( { deleted: false } );
}
if ( Galaxy.libraries.preferences.get( 'without_restricted' ) ){
libraries_to_render = _.filter( libraries_to_render, is_public );
}
} else if ( models !== null ){
if ( Galaxy.libraries.preferences.get( 'with_deleted' ) ){
libraries_to_render = models;
} else {
var is_deleted = function(model){ return model.get('deleted') === false; }
libraries_to_render = _.filter(models, is_deleted );
libraries_to_render = _.filter( models, is_deleted );
}
if ( Galaxy.libraries.preferences.get( 'without_restricted' ) ) {
libraries_to_render = _.filter( libraries_to_render, is_public );
}
} else {
libraries_to_render = [];
@@ -232,7 +239,7 @@ var LibraryListView = Backbone.View.extend({
'<% } else{ %>',
'<div>',
'There are no libraries visible to you here. If you expected some to show up please consult the',
' <a href="https://wiki.galaxyproject.org/Admin/DataLibraries/LibrarySecurity" target="_blank">library security wikipage</a>',
' <a href="https://galaxyproject.org/data-libraries/#permissions" target="_blank">library security wikipage</a>',
' or visit the <a href="https://biostar.usegalaxy.org/" target="_blank">Galaxy support site</a>.',
'</div>',
'<% }%>',
@@ -17,6 +17,7 @@ var LibraryToolbarView = Backbone.View.extend({
events: {
'click #create_new_library_btn' : 'createLibraryFromModal',
'click #include_deleted_chk' : 'includeDeletedChecked',
'click #exclude_restricted_chk' : 'excludeRestrictedChecked',
'click #lib_page_size_prompt' : 'showPageSizePrompt',
'keyup .library-search-input' : 'searchLibraries'
},
@@ -37,6 +38,7 @@ var LibraryToolbarView = Backbone.View.extend({
this.$el.html(toolbar_template( { admin_user: is_admin, anon_user: is_anonym } ) );
if ( is_admin ){
this.$el.find( '#include_deleted_chk' )[0].checked = Galaxy.libraries.preferences.get( 'with_deleted' );
this.$el.find( '#exclude_restricted_chk' )[0].checked = Galaxy.libraries.preferences.get( 'without_restricted' );
}
},
@@ -155,6 +157,18 @@ var LibraryToolbarView = Backbone.View.extend({
}
},
/**
* Include or exclude restricted libraries in the view.
*/
excludeRestrictedChecked: function( event ) {
if (event.target.checked){
Galaxy.libraries.preferences.set( { 'without_restricted': true } );
} else {
Galaxy.libraries.preferences.set( { 'without_restricted': false });
}
Galaxy.libraries.libraryListView.render();
},
/**
* Take the contents of the search field and send it to the list view
* to query the collection of libraries.
@@ -183,7 +197,12 @@ var LibraryToolbarView = Backbone.View.extend({
'<div class="checkbox toolbar-item" style="height: 20px;">',
'<label>',
'<input id="include_deleted_chk" type="checkbox">',
'&nbsp;include deleted ',
'include deleted ',
'</input>',
'</label>',
'<label>',
'<input id="exclude_restricted_chk" type="checkbox">',
'exclude restricted',
'</input>',
'</label>',
'</div>',
@@ -192,7 +211,7 @@ var LibraryToolbarView = Backbone.View.extend({
'</span>',
'<% } %>',
'<span class="help-button" data-toggle="tooltip" data-placement="top" title="Visit Libraries Wiki">',
'<a href="https://wiki.galaxyproject.org/DataLibraries/screen/ListOfLibraries" target="_blank">',
'<a href="https://galaxyproject.org/data-libraries/screen/list-of-libraries/" target="_blank">',
'<button class="primary-button" type="button"><span class="fa fa-question-circle"></span> Help</button>',
'</a>',
'</span>',
+31 -12
View File
@@ -18,15 +18,17 @@ var TagsEditor = Backbone.View
initialize : function( options ){
//console.debug( this, options );
// only listen to the model only for changes to tags - re-render
this.listenTo( this.model, 'change:tags', function(){
this.render();
});
if (options.usePrompt === false) {
this.label = '';
} else {
this.label = '<label class="prompt">' + _l( 'Tags' ) + '</label>';
}
this.hiddenUntilActivated( options.$activator, options );
},
/** Build the DOM elements, call select to on the created input, and set up behaviors */
render : function(){
var view = this;
var self = this;
this.$el.html( this._template() );
this.$input().select2({
@@ -34,7 +36,7 @@ var TagsEditor = Backbone.View
width : '100%',
tags : function(){
// initialize possible tags in the dropdown based on all the tags the user has used so far
return view._getTagsUsed();
return self._getTagsUsed();
}
});
@@ -42,11 +44,24 @@ var TagsEditor = Backbone.View
return this;
},
_hashToName: function(tag){
if (tag.startsWith('#')){
return 'name:' + tag.slice(1);
}
return tag;
},
_nameToHash: function(tag){
if (tag.startsWith('name:')){
tag = '#' + tag.slice(5);
}
return tag;
},
/** @returns {String} the html text used to build the view's DOM */
_template : function(){
return [
//TODO: make prompt optional
'<label class="prompt">', _l( 'Tags' ), '</label>',
this.label,
// set up initial tags by adding as CSV to input vals (necc. to init select2)
'<input class="tags-input" value="', this.tagsToCSV(), '" />'
].join( '' );
@@ -54,12 +69,13 @@ var TagsEditor = Backbone.View
/** @returns {String} the sorted, comma-separated tags from the model */
tagsToCSV : function(){
var self = this;
var tagsArray = this.model.get( 'tags' );
if( !_.isArray( tagsArray ) || _.isEmpty( tagsArray ) ){
return '';
}
return tagsArray.map( function( tag ){
return _.escape( tag );
return _.escape( self._nameToHash( tag ) );
}).sort().join( ',' );
},
@@ -71,20 +87,23 @@ var TagsEditor = Backbone.View
/** @returns {String[]} all tags used by the current user */
_getTagsUsed : function(){
//TODO: global
return Galaxy.user.get( 'tags_used' );
var self = this;
return _.map(Galaxy.user.get( 'tags_used' ), self._nameToHash);
},
/** set up any event listeners on the view's DOM (mostly handled by select2) */
_setUpBehaviors : function(){
var view = this;
var self = this;
this.$input().on( 'change', function( event ){
// Modify any 'hashtag' 'nametags'
event.val = _.map(event.val, self._hashToName);
// save the model's tags in either remove or added event
view.model.save({ tags: event.val }, { silent: true });
self.model.save({ tags: event.val });
// if it's new, add the tag to the users tags
if( event.added ){
//??: solve weird behavior in FF on test.galaxyproject.org where
// event.added.text is string object: 'String{ 0="o", 1="n", 2="e" }'
view._addNewTagToTagsUsed( event.added.text + '' );
self._addNewTagToTagsUsed( event.added.text + '' );
}
});
},
+107 -151
View File
@@ -1,119 +1,118 @@
/**
This is the base class of the tool form plugin. This class is e.g. inherited by the regular and the workflow tool form.
*/
define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view',
'mvc/citation/citation-model', 'mvc/citation/citation-view'],
function(Utils, Deferred, Ui, FormBase, CitationModel, CitationView) {
define( [ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view', 'mvc/citation/citation-model', 'mvc/citation/citation-view' ],
function( Utils, Deferred, Ui, FormBase, CitationModel, CitationView ) {
return FormBase.extend({
initialize: function(options) {
initialize: function( options ) {
var self = this;
FormBase.prototype.initialize.call(this, options);
this.deferred = new Deferred();
if (options.inputs) {
this._buildForm(options);
FormBase.prototype.initialize.call( this, options );
if ( this.model.get( 'inputs' ) ) {
this._buildForm( this.model.attributes );
} else {
this.deferred.execute(function(process) {
self._buildModel(process, options, true);
this.deferred.execute( function( process ) {
self._buildModel( process, self.model.attributes, true );
});
}
// listen to history panel
if ( options.listen_to_history && parent.Galaxy && parent.Galaxy.currHistoryPanel ) {
if ( this.model.get( 'listen_to_history' ) && parent.Galaxy && parent.Galaxy.currHistoryPanel ) {
this.listenTo( parent.Galaxy.currHistoryPanel.collection, 'change', function() {
this.refresh();
self.model.get( 'onchange' )();
});
}
// destroy dom elements
this.$el.on( 'remove', function() { self.remove() } );
},
/** Listen to history panel changes and update the tool form */
refresh: function() {
var self = this;
self.deferred.reset();
this.deferred.execute( function (process){
self._updateModel( process)
});
this.$el.on( 'remove', function() { self._destroy() } );
},
/** Wait for deferred build processes before removal */
remove: function() {
_destroy: function() {
var self = this;
this.$el.hide();
this.deferred.execute(function(){
FormBase.prototype.remove.call(self);
Galaxy.emit.debug('tool-form-base::remove()', 'Destroy view.');
this.$el.off().hide();
this.deferred.execute( function() {
FormBase.prototype.remove.call( self );
Galaxy.emit.debug( 'tool-form-base::_destroy()', 'Destroy view.' );
});
},
/** Build form */
_buildForm: function(options) {
_buildForm: function( options ) {
var self = this;
this.options = Utils.merge(options, this.options);
this.options = Utils.merge({
icon : options.icon,
title : '<b>' + options.name + '</b> ' + options.description + ' (Galaxy Version ' + options.version + ')',
operations : !this.options.hide_operations && this._operations(),
onchange : function() {
self.refresh();
this.model.set( options );
this.model.set({
title : options.title || '<b>' + options.name + '</b> ' + options.description + ' (Galaxy Version ' + options.version + ')',
operations : !this.model.get( 'hide_operations' ) && this._operations(),
onchange : function() {
self.deferred.reset();
self.deferred.execute( function ( process ) {
self.model.get( 'postchange' )( process, self );
});
}
}, this.options);
this.options.customize && this.options.customize( this.options );
});
this.model.get( 'customize' ) && this.model.get( 'customize' )( this );
this.render();
if ( !this.options.collapsible ) {
if ( !this.model.get( 'collapsible' ) ) {
this.$el.append( $( '<div/>' ).addClass( 'ui-margin-top-large' ).append( this._footer() ) );
}
},
/** Builds a new model through api call and recreates the entire form
*/
_buildModel: function(process, options, hide_message) {
/** Builds a new model through api call and recreates the entire form */
_buildModel: function( process, new_options, hide_message ) {
var self = this;
this.options.id = options.id;
this.options.version = options.version;
var options = this.model.attributes;
options.version = new_options.version;
options.id = new_options.id;
// build request url
var build_url = '';
var build_data = {};
if ( options.job_id ) {
build_url = Galaxy.root + 'api/jobs/' + options.job_id + '/build_for_rerun';
var job_id = '';
// When re-running a job the job_id is found in the new_options object.
// When re-running a job and requesting a new tool_version,
// the job_id is in the options object.
if ( new_options.job_id ) {
job_id = new_options.job_id;
} else if (options.job_id) {
job_id = options.job_id;
}
if ( job_id ) {
build_url = Galaxy.root + 'api/jobs/' + job_id + '/build_for_rerun';
} else {
build_url = Galaxy.root + 'api/tools/' + options.id + '/build';
if ( Galaxy.params && Galaxy.params.tool_id == options.id ) {
build_data = $.extend( {}, Galaxy.params );
options.version && ( build_data[ 'tool_version' ] = options.version );
}
build_data = $.extend( {}, Galaxy.params );
build_data[ 'tool_id' ] && ( delete build_data[ 'tool_id' ] );
}
options.version && ( build_data[ 'tool_version' ] = options.version );
// get initial model
Utils.get({
url : build_url,
data : build_data,
success : function(new_model) {
new_model = new_model.tool_model || new_model;
if( !new_model.display ) {
success : function( data ) {
if( !data.display ) {
window.location = Galaxy.root;
return;
}
self._buildForm(new_model);
self._buildForm( data );
!hide_message && self.message.update({
status : 'success',
message : 'Now you are using \'' + self.options.name + '\' version ' + self.options.version + ', id \'' + self.options.id + '\'.',
message : 'Now you are using \'' + options.name + '\' version ' + options.version + ', id \'' + options.id + '\'.',
persistent : false
});
Galaxy.emit.debug('tool-form-base::initialize()', 'Initial tool model ready.', new_model);
Galaxy.emit.debug('tool-form-base::_buildModel()', 'Initial tool model ready.', data);
process.resolve();
},
error : function(response, status) {
error : function( response, status ) {
var error_message = ( response && response.err_msg ) || 'Uncaught error.';
if ( status == 401 ) {
window.location = Galaxy.root + 'user/login?' + $.param({ redirect : Galaxy.root + '?tool_id=' + self.options.id });
} else if ( self.$el.is(':empty') ) {
self.$el.prepend((new Ui.Message({
window.location = Galaxy.root + 'user/login?' + $.param({ redirect : Galaxy.root + '?tool_id=' + options.id });
} else if ( self.$el.is( ':empty' ) ) {
self.$el.prepend( ( new Ui.Message({
message : error_message,
status : 'danger',
persistent : true,
large : true
})).$el);
}) ).$el );
} else {
Galaxy.modal && Galaxy.modal.show({
title : 'Tool request failed',
@@ -125,75 +124,39 @@ define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view',
}
});
}
Galaxy.emit.debug('tool-form::initialize()', 'Initial tool model request failed.', response);
Galaxy.emit.debug( 'tool-form-base::_buildModel()', 'Initial tool model request failed.', response );
process.reject();
}
});
},
/** Request a new model for an already created tool form and updates the form inputs
*/
_updateModel: function(process) {
// link this
var self = this;
var model_url = this.options.update_url || Galaxy.root + 'api/tools/' + this.options.id + '/build';
var current_state = {
tool_id : this.options.id,
tool_version : this.options.version,
inputs : $.extend(true, {}, self.data.create())
}
this.wait(true);
// log tool state
Galaxy.emit.debug('tool-form-base::_updateModel()', 'Sending current state.', current_state);
// post job
Utils.request({
type : 'POST',
url : model_url,
data : current_state,
success : function(new_model) {
self.update(new_model['tool_model'] || new_model);
self.options.update && self.options.update(new_model);
self.wait(false);
Galaxy.emit.debug('tool-form-base::_updateModel()', 'Received new model.', new_model);
process.resolve();
},
error : function(response) {
Galaxy.emit.debug('tool-form-base::_updateModel()', 'Refresh request failed.', response);
process.reject();
}
});
},
/** Create tool operation menu
*/
/** Create tool operation menu */
_operations: function() {
var self = this;
var options = this.options;
var options = this.model.attributes;
// button for version selection
var versions_button = new Ui.ButtonMenu({
icon : 'fa-cubes',
title : (!options.narrow && 'Versions') || null,
title : ( !options.narrow && 'Versions' ) || null,
tooltip : 'Select another tool version'
});
if (!options.sustain_version && options.versions && options.versions.length > 1) {
for (var i in options.versions) {
var version = options.versions[i];
if (version != options.version) {
if ( !options.sustain_version && options.versions && options.versions.length > 1 ) {
for ( var i in options.versions ) {
var version = options.versions[ i ];
if ( version != options.version ) {
versions_button.addMenu({
title : 'Switch to ' + version,
version : version,
icon : 'fa-cube',
onclick : function() {
// here we update the tool version (some tools encode the version also in the id)
var id = options.id.replace(options.version, this.version);
var id = options.id.replace( options.version, this.version );
var version = this.version;
// queue model request
self.deferred.reset();
self.deferred.execute(function(process) {
self._buildModel(process, {id: id, version: version})
self.deferred.execute( function( process ) {
self._buildModel( process, { id : id, version : version } )
});
}
});
@@ -206,84 +169,64 @@ define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view',
// button for options e.g. search, help
var menu_button = new Ui.ButtonMenu({
icon : 'fa-caret-down',
title : (!options.narrow && 'Options') || null,
title : ( !options.narrow && 'Options' ) || null,
tooltip : 'View available options'
});
if(options.biostar_url) {
if ( options.biostar_url ) {
menu_button.addMenu({
icon : 'fa-question-circle',
title : 'Question?',
tooltip : 'Ask a question about this tool (Biostar)',
onclick : function() {
window.open(options.biostar_url + '/p/new/post/');
window.open( options.biostar_url + '/p/new/post/' );
}
});
menu_button.addMenu({
icon : 'fa-search',
title : 'Search',
tooltip : 'Search help for this tool (Biostar)',
onclick : function() {
window.open(options.biostar_url + '/local/search/page/?q=' + options.name);
window.open( options.biostar_url + '/local/search/page/?q=' + options.name );
}
});
};
menu_button.addMenu({
icon : 'fa-share',
title : 'Share',
tooltip : 'Share this tool',
onclick : function() {
prompt('Copy to clipboard: Ctrl+C, Enter', window.location.origin + Galaxy.root + 'root?tool_id=' + options.id);
prompt( 'Copy to clipboard: Ctrl+C, Enter', window.location.origin + Galaxy.root + 'root?tool_id=' + options.id );
}
});
// add admin operations
if (Galaxy.user && Galaxy.user.get('is_admin')) {
if ( Galaxy.user && Galaxy.user.get( 'is_admin' ) ) {
menu_button.addMenu({
icon : 'fa-download',
title : 'Download',
tooltip : 'Download this tool',
onclick : function() {
window.location.href = Galaxy.root + 'api/tools/' + options.id + '/download';
}
});
}
// add admin operations for tool XML reloading
if (Galaxy.user && Galaxy.user.get('is_admin')) {
menu_button.addMenu({
icon : 'fa-refresh',
title : 'Reload Tool XML',
tooltip : 'Reload tool XML file',
title : 'Reload XML',
onclick : function() {
var modalMessage = new Ui.Modal.View();
$.ajax({
url: '/api/tools/' + options.id + '/reload',
type: "GET",
}).done(function(data){
modalMessage.show({
title : data.done ? 'Tool XML Reload' : 'Tool XML Reload Error',
body : data.done ? data.done : data.error,
buttons : { 'Close' : function() { modalMessage.hide() } }
});
window.setTimeout(function(){modalMessage.hide();}, 2000);
}).fail(function(error){
modalMessage.show({
title: "Tool XML Reload AJAX Error",
body: options.id + " " + error,
buttons : { 'Close' : function() { modalMessage.hide() } }
});
Utils.get({
url : Galaxy.root + 'api/tools/' + options.id + '/reload',
success : function( response ) {
self.message.update( { persistent : false, message : 'Tool XML has been reloaded.', status : 'success' } );
},
error : function( response ) {
self.message.update( { persistent : false, message : response.err_msg, status : 'danger' } );
}
});
}
});
}
// button for version selection
if (options.requirements && options.requirements.length > 0) {
if ( options.requirements && options.requirements.length > 0 ) {
menu_button.addMenu({
icon : 'fa-info-circle',
title : 'Requirements',
tooltip : 'Display tool requirements',
onclick : function() {
if ( !this.requirements_visible || self.portlet.collapsed ) {
this.requirements_visible = true;
@@ -298,27 +241,41 @@ define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view',
}
// add toolshed url
if (options.sharable_url) {
if ( options.sharable_url ) {
menu_button.addMenu({
icon : 'fa-external-link',
title : 'See in Tool Shed',
tooltip : 'Access the repository',
onclick : function() {
window.open(options.sharable_url);
window.open( options.sharable_url );
}
});
}
// add tool menu webhooks
$.getJSON('/api/webhooks/tool-menu/all', function(webhooks) {
_.each(webhooks, function(webhook) {
if (webhook.activate && webhook.config.function) {
menu_button.addMenu({
icon : webhook.config.icon,
title : webhook.config.title,
onclick : function() {
var func = new Function('options', webhook.config.function);
func(options);
}
});
}
});
});
return {
menu : menu_button,
versions : versions_button
}
},
/** Create footer
*/
/** Create footer */
_footer: function() {
var options = this.options;
var options = this.model.attributes;
var $el = $( '<div/>' ).append( this._templateHelp( options ) );
if ( options.citations ) {
var $citations = $( '<div/>' );
@@ -332,8 +289,7 @@ define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view',
return $el;
},
/** Templates
*/
/** Templates */
_templateHelp: function( options ) {
var $tmpl = $( '<div/>' ).addClass( 'ui-form-help' ).append( options.help );
$tmpl.find( 'a' ).attr( 'target', '_blank' );
@@ -347,7 +303,7 @@ define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view',
_.each( options.requirements, function( req, i ) {
requirements_message += req.name + ( req.version ? ' (Version ' + req.version + ')' : '' ) + ( i < nreq - 2 ? ', ' : ( i == nreq - 2 ? ' and ' : '' ) );
});
var requirements_link = $( '<a/>' ).attr( 'target', '_blank' ).attr( 'href', 'https://wiki.galaxyproject.org/Tools/Requirements' ).text( 'here' );
var requirements_link = $( '<a/>' ).attr( 'target', '_blank' ).attr( 'href', 'https://galaxyproject.org/tools/requirements/' ).text( 'here' );
return $( '<span/>' ).append( requirements_message + '. Click ' ).append( requirements_link ).append( ' for more information.' );
}
return 'No requirements found.';
@@ -1,6 +1,7 @@
/** This is the run workflow tool form view. */
define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view', 'mvc/form/form-data', 'mvc/tool/tool-form-base', 'mvc/ui/ui-modal', 'mvc/webhooks' ],
function( Utils, Deferred, Ui, Form, FormData, ToolFormBase, Modal, Webhooks ) {
define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view', 'mvc/form/form-data', 'mvc/tool/tool-form-base', 'mvc/ui/ui-modal', 'mvc/webhooks', 'mvc/workflow/workflow-icons' ],
function( Utils, Deferred, Ui, Form, FormData, ToolFormBase, Modal, Webhooks, WorkflowIcons ) {
var View = Backbone.View.extend({
initialize: function( options ) {
var self = this;
@@ -14,9 +15,6 @@ define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view'
$( 'body' ).append( this.$el );
this._configure();
this.render();
this._refresh();
this.$el.on( 'click', function() { self._refresh() } );
this.$steps.scroll( function() { self._refresh() } );
$( window ).resize( function() { self._refresh() } );
},
@@ -24,7 +22,7 @@ define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view'
_refresh: function( step_index ) {
var margin = _.reduce( this.$el.children(), function( memo, child ) {
return memo + $( child ).outerHeight();
}, 0 ) - this.$steps.height() + 25;
}, 0 ) - this.$steps.height() + 90;
this.$steps.css( 'height', $( window ).height() - margin );
},
@@ -37,12 +35,19 @@ define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view'
this.parms = [];
_.each( this.model.get( 'steps' ), function( step, i ) {
Galaxy.emit.debug( 'tool-form-composite::initialize()', i + ' : Preparing workflow step.' );
var icon = WorkflowIcons[ step.step_type ];
var title = parseInt( i + 1 ) + ': ' + ( step.step_label || step.step_name );
if ( step.annotation ) {
title += ' - ' + step.annotation;
}
if ( step.step_version ) {
title += ' (Galaxy Version ' + step.step_version + ')';
}
step = Utils.merge( {
index : i,
name : 'Step ' + ( parseInt( i ) + 1 ) + ': ' + step.name,
icon : '',
title : _.escape( title ),
icon : icon || '',
help : null,
description : step.annotation && ' - ' + step.annotation || step.description,
citations : null,
collapsible : true,
collapsed : i > 0 && !self._isDataStep( step ),
@@ -143,15 +148,16 @@ define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view'
if ( step.step_type == 'tool' ) {
var data_resolved = true;
FormData.visitInputs( step.inputs, function ( input, name, context ) {
var is_runtime_value = input.value && input.value.__class__ == 'RuntimeValue';
var is_data_input = ([ 'data', 'data_collection' ]).indexOf( input.type ) != -1;
var data_ref = context[ input.data_ref ];
input.step_linked && !self._isDataStep( input.step_linked ) && ( data_resolved = false );
input.options && ( ( input.options.length == 0 && !data_resolved ) || input.wp_linked ) && ( input.is_workflow = true );
data_ref && ( input.is_workflow = ( data_ref.step_linked && !self._isDataStep( data_ref.step_linked ) ) || input.wp_linked );
( is_data_input || ( input.value && input.value.__class__ == 'RuntimeValue' && !input.step_linked ) ) && ( step.collapsed = false );
input.value && input.value.__class__ == 'RuntimeValue' && ( input.value = null );
is_runtime_value && ( input.value = input.default_value );
input.flavor = 'workflow';
if ( !is_data_input && input.type !== 'hidden' && !input.wp_linked ) {
if ( !is_runtime_value && !is_data_input && input.type !== 'hidden' && !input.wp_linked ) {
if ( input.optional || ( !Utils.isEmpty( input.value ) && input.value !== '' ) ) {
input.collapsible_value = input.value;
input.collapsible_preview = true;
@@ -259,6 +265,31 @@ define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view'
this.deferred.execute( function( promise ) {
self.$steps.addClass( 'ui-steps' );
if ( step.step_type == 'tool' ) {
step.postchange = function( process, form ) {
var self = this;
var current_state = {
tool_id : step.id,
tool_version : step.version,
inputs : $.extend( true, {}, form.data.create() )
}
form.wait( true );
Galaxy.emit.debug( 'tool-form-composite::postchange()', 'Sending current state.', current_state );
Utils.request({
type : 'POST',
url : Galaxy.root + 'api/tools/' + step.id + '/build',
data : current_state,
success : function( data ) {
form.update( data );
form.wait( false );
Galaxy.emit.debug( 'tool-form-composite::postchange()', 'Received new model.', data );
process.resolve();
},
error : function( response ) {
Galaxy.emit.debug( 'tool-form-composite::postchange()', 'Refresh request failed.', response );
process.reject();
}
});
};
form = new ToolFormBase( step );
if ( step.post_job_actions && step.post_job_actions.length ) {
form.portlet.append( $( '<div/>' ).addClass( 'ui-form-element-disabled' )
@@ -270,15 +301,17 @@ define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view'
);
}
} else {
_.each( step.inputs, function( input ) { input.flavor = 'module' } );
var is_simple_input = ([ 'data_input', 'data_collection_input' ]).indexOf( step.step_type ) != -1;
_.each( step.inputs, function( input ) { input.flavor = 'module'; input.hide_label = is_simple_input; } );
form = new Form( Utils.merge({
title : '<b>' + step.name + '</b>',
title : step.title,
onchange : function() { _.each( self.links[ step.index ], function( link ) { self._refreshStep( link ) } ) },
inputs : step.inputs && step.inputs.length > 0 ? step.inputs : [ { type: 'hidden', name: 'No options available.', ignore: null } ]
}, step ) );
}
self.forms[ step.index ] = form;
self._append( self.$steps, form.$el );
self._refresh();
step.needs_refresh && self._refreshStep( step );
form.portlet[ !self.show_progress ? 'enable' : 'disable' ]();
self.show_progress && self.execute_btn.model.set( { wait : true,
@@ -317,7 +350,7 @@ define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view'
var wp_field = self.wp_form.field_list[ self.wp_form.data.match( match[ 1 ] ) ];
var wp_value = wp_field && wp_field.value();
if ( wp_value ) {
new_value = new_value.replace( new RegExp( '\\' + match[ 0 ], 'g' ), wp_value );
new_value = new_value.split( match[ 0 ] ).join( wp_value );
}
}
}
@@ -417,12 +450,14 @@ define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view'
Galaxy.emit.debug( 'tool-form-composite::submit', 'Submission successful.', response );
self.$el.children().hide();
self.$el.append( self._templateSuccess( response ) );
// Show Webhook if job is running
if ($.isArray( response ) && response.length > 0) {
self.$el.append( $( '<div/>', { id: 'webhook-view' } ) );
var WebhookApp = new Webhooks.WebhookView({
urlRoot: Galaxy.root + 'api/webhooks/workflow'
urlRoot: Galaxy.root + 'api/webhooks/workflow',
toolId: job_def.tool_id,
toolVersion: job_def.tool_version,
});
}
@@ -434,7 +469,7 @@ define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view'
if ( response && response.err_data ) {
for ( var i in self.forms ) {
var form = self.forms[ i ];
var step_related_errors = response.err_data[ form.options.step_index ];
var step_related_errors = response.err_data[ form.model.get( 'step_index' ) ];
if ( step_related_errors ) {
var error_messages = form.data.matchResponse( step_related_errors );
for ( var input_id in error_messages ) {
+34 -7
View File
@@ -8,7 +8,8 @@ define([ 'utils/utils', 'mvc/ui/ui-misc', 'mvc/ui/ui-modal', 'mvc/tool/tool-form
this.form = new ToolFormBase( Utils.merge({
listen_to_history : true,
always_refresh : false,
customize : function( options ) {
customize : function( form ) {
var options = form.model.attributes;
// build execute button
options.buttons = {
execute: execute_btn = new Ui.Button({
@@ -19,17 +20,17 @@ define([ 'utils/utils', 'mvc/ui/ui-misc', 'mvc/ui/ui-modal', 'mvc/tool/tool-form
floating : 'clear',
onclick : function() {
execute_btn.wait();
self.form.portlet.disable();
form.portlet.disable();
self.submit( options, function() {
execute_btn.unwait();
self.form.portlet.enable();
form.portlet.enable();
} );
}
})
};
}
// remap feature
if ( options.job_id && options.job_remap ) {
options.inputs[ 'rerun_remap_job_id' ] = {
options.inputs.push({
label : 'Resume dependencies from this job',
name : 'rerun_remap_job_id',
type : 'select',
@@ -38,8 +39,33 @@ define([ 'utils/utils', 'mvc/ui/ui-misc', 'mvc/ui/ui-modal', 'mvc/tool/tool-form
value : '__ignore__',
options : [ [ 'Yes', options.job_id ], [ 'No', '__ignore__' ] ],
help : 'The previous run of this tool failed and other tools were waiting for it to finish successfully. Use this option to resume those tools using the new output(s) of this tool run.'
}
});
}
},
postchange : function( process, form ) {
var self = this;
var current_state = {
tool_id : form.model.get( 'id' ),
tool_version : form.model.get( 'version' ),
inputs : $.extend(true, {}, form.data.create())
}
form.wait( true );
Galaxy.emit.debug( 'tool-form::postchange()', 'Sending current state.', current_state );
Utils.request({
type : 'POST',
url : Galaxy.root + 'api/tools/' + form.model.get( 'id' ) + '/build',
data : current_state,
success : function( data ) {
form.update( data );
form.wait( false );
Galaxy.emit.debug( 'tool-form::postchange()', 'Received new model.', data );
process.resolve();
},
error : function( response ) {
Galaxy.emit.debug( 'tool-form::postchange()', 'Refresh request failed.', response );
process.reject();
}
});
}
}, options ) );
this.deferred = this.form.deferred;
@@ -84,7 +110,8 @@ define([ 'utils/utils', 'mvc/ui/ui-misc', 'mvc/ui/ui-modal', 'mvc/tool/tool-form
if ( response.jobs && response.jobs.length > 0 ) {
self.$el.append( $( '<div/>', { id: 'webhook-view' } ) );
var WebhookApp = new Webhooks.WebhookView({
urlRoot: Galaxy.root + 'api/webhooks/tool'
urlRoot: Galaxy.root + 'api/webhooks/tool',
toolId: job_def.tool_id
});
}
parent.Galaxy && parent.Galaxy.currHistoryPanel && parent.Galaxy.currHistoryPanel.refreshContents();
+2 -4
View File
@@ -532,10 +532,7 @@ var ToolLinkView = BaseView.extend({
var self = this;
$link.find('a').on('click', function(e) {
e.preventDefault();
var form = new ToolForm.View( { id : self.model.id, version : self.model.get('version') } );
form.deferred.execute(function() {
Galaxy.app.display( form );
});
Galaxy.router.push( '/', { tool_id : self.model.id, version : self.model.get('version') } );
});
}
@@ -624,6 +621,7 @@ var ToolSearchView = Backbone.View.extend({
events: {
'click': 'focus_and_select',
'keyup :input': 'query_changed',
'change :input': 'query_changed',
'click #search-clear-btn': 'clear'
},
@@ -0,0 +1,107 @@
define(['mvc/toolshed/toolshed-model', 'mvc/toolshed/util'], function(toolshed_model, toolshed_util) {
var ToolShedCategories = Backbone.View.extend({
el: '#center',
defaults: {
tool_shed: "https://toolshed.g2.bx.psu.edu/"
},
initialize: function(options) {
var self = this;
var shed = options.tool_shed.replace(/\//g, '%2f');
this.options = _.defaults(this.options || options, this.defaults);
this.model = new toolshed_model.Categories();
this.listenTo(this.model, 'sync', this.render);
this.model.url = this.model.url + '?tool_shed_url=' + this.options.tool_shed;
this.model.tool_shed = shed;
this.model.fetch();
},
render: function(options) {
this.options = _.extend(this.options, options);
this.options.categories = this.model.models;
this.options.queue = toolshed_util.queueLength();
var category_list_template = this.templateCategoryList;
this.$el.html(category_list_template(this.options));
$("#center").css('overflow', 'auto');
this.bindEvents();
},
bindEvents: function() {
var that = this;
require(['libs/jquery/jquery-ui'], function() {
$("#search_box").autocomplete({
source: function(request, response) {
var shed_url = that.model.tool_shed.replace(/%2f/g, '/');
var base_url = Galaxy.root + 'api/tool_shed/search';
var params = {term: request.term, tool_shed_url: shed_url};
$.post(base_url, params, function(data) {
console.log(data);
result_list = toolshed_util.shedParser(data);
response(result_list);
});
},
minLength: 3,
select: function(event, ui) {
var tsr_id = ui.item.value;
var api_url = Galaxy.root + 'api/tool_shed/repository';
var params = {"tool_shed_url": that.model.tool_shed, "tsr_id": tsr_id};
var new_route = 'repository/s/' + that.model.tool_shed + '/r/' + tsr_id;
Backbone.history.navigate(new_route, {trigger: true, replace:true});
},
});
});
},
reDraw: function(options) {
this.$el.empty();
this.model.url = this.model.url + '?tool_shed_url=' + this.options.tool_shed;
this.initialize(options);
},
templateCategoryList: _.template([
'<style type="text/css">',
'.ui-autocomplete { background-color: #fff; }',
'li.ui-menu-item { list-style-type: none; }',
'</style>',
'<div class="unified-panel-header" id="panel_header" unselectable="on">',
'<div class="unified-panel-header-inner" style="layout: inline;">Categories in <%= tool_shed.replace(/%2f/g, "/") %></div>',
'<div class="unified-panel-header-inner" style="position: absolute; right: 5px; top: 0px;"><a href="#/queue">Repository Queue (<%= queue %>)</a></div>',
'</div>',
'<div class="unified-panel-body" id="list_categories">',
'<div id="standard-search" style="height: 2em; margin: 1em;">',
'<span class="ui-widget" >',
'<input class="search-box-input" id="search_box" data-shedurl="<%= tool_shed.replace(/%2f/g, "/") %>" name="search" placeholder="Search repositories by name or id" size="60" type="text" />',
'</span>',
'</div>',
'<div style="clear: both; margin-top: 1em;">',
'<table class="grid">',
'<thead id="grid-table-header">',
'<tr>',
'<th>Name</th>',
'<th>Description</th>',
'<th>Repositories</th>',
'</tr>',
'</thead>',
'<% _.each(categories, function(category) { %>',
'<tr>',
'<td>',
'<a href="#/category/s/<%= tool_shed %>/c/<%= category.get("id") %>"><%= category.get("name") %></a>',
'</td>',
'<td><%= category.get("description") %></td>',
'<td><%= category.get("repositories") %></td>',
'</tr>',
'<% }); %>',
'</table>',
'</div>',
'</div>',
].join(''))
});
return {
CategoryView: ToolShedCategories,
};
});
@@ -0,0 +1,104 @@
define(['toolshed/repository-queue-model'], function() {
var ToolShedRepositoryQueue = Backbone.View.extend({
el: '#category_list',
render: function(options) {
this.options = _.extend(this.options, options);
var repository_queue_template = this.templateRepoQueue();
this.$el.html(repository_queue_template({repository: this.model}));
},
templateRepoQueueTab: _.template([
'<li class="nav-tab" role="presentation" id="repository_installation_queue">',
'<a href="#repository_queue" data-toggle="tab">Repository Installation Queue</a>',
'</li>',
].join('')),
templateRepoQueue: _.template([
'<div class="tab-pane" id="repository_queue">',
'<table id="queued_repositories" class="grid" border="0" cellpadding="2" cellspacing="2" width="100%">',
'<thead id="grid-table-header">',
'<tr>',
'<th class="datasetRow"><input class="btn btn-primary" type="submit" id="install_all" name="install_all" value="Install all" /></th>',
'<th class="datasetRow"><input class="btn btn-primary" type="submit" id="clear_queue" name="clear_queue" value="Clear queue" /></th>',
'<th class="datasetRow">ToolShed</th>',
'<th class="datasetRow">Name</th>',
'<th class="datasetRow">Owner</th>',
'<th class="datasetRow">Revision</th>',
'</tr>',
'</thead>',
'<tbody>',
'<\% _.each(repositories, function(repository) { \%>',
'<tr id="queued_repository_<\%= repository.id \%>">',
'<td class="datasetRow">',
'<input class="btn btn-primary install_one" data-repokey="<\%= repository.queue_key \%>" type="submit" id="install_repository_<\%= repository.id \%>" name="install_repository" value="Install now" />',
'</td>',
'<td class="datasetRow">',
'<input class="btn btn-primary remove_one" data-repokey="<\%= repository.queue_key \%>" type="submit" id="unqueue_repository_<\%= repository.id \%>" name="unqueue_repository" value="Remove from queue" />',
'</td>',
'<td class="datasetRow"><\%= repository.tool_shed_url \%></td>',
'<td class="datasetRow"><\%= repository.name \%></td>',
'<td class="datasetRow"><\%= repository.owner \%></td>',
'<td class="datasetRow"><\%= repository.changeset \%></td>',
'</tr>',
'<\% }); \%>',
'</tbody>',
'</table>',
'<input type="button" class="btn btn-primary" id="from_workflow" value="Add from workflow" />',
'</div>',
].join('')),
workflows_missing_tools: _.template([
'<table id="workflows_missing_tools" class="grid" border="0" cellpadding="2" cellspacing="2" width="100%">',
'<thead id="grid-table-header">',
'<tr>',
'<th class="datasetRow">Workflows</th>',
'<th class="datasetRow">Tool IDs</th>',
'<th class="datasetRow">Shed</th>',
'<th class="datasetRow">Name</th>',
'<th class="datasetRow">Owner</th>',
'<th class="datasetRow">Actions</th>',
'</tr>',
'</thead>',
'<tbody>',
'<\% _.each(Object.keys(workflows), function(workflow_key) { \%>',
'<\% var workflow_details = workflow_key.split("/"); \%>',
'<\% var workflow = workflows[workflow_key]; \%>',
'<tr>',
'<td class="datasetRow">',
'<ul class="workflow_names">',
'<\% _.each(workflow.workflows.sort(), function(wf) { \%>',
'<li class="workflow_names"><\%= wf \%></li>',
'<\% }); \%>',
'</ul>',
'</td>',
'<td class="datasetRow">',
'<ul class="workflow_tools">',
'<\% _.each(workflow.tools.sort(), function(tool) { \%>',
'<li class="workflow_tools"><\%= tool \%></li>',
'<\% }); \%>',
'</ul>',
'</td>',
'<td class="datasetRow"><\%= workflow_details[0] \%></td>',
'<td class="datasetRow"><\%= workflow_details[2] \%></td>',
'<td class="datasetRow"><\%= workflow_details[1] \%></td>',
'<td class="datasetRow">',
'<ul class="workflow_tools">',
'<li class="workflow_tools"><input type="button" class="show_wf_repo btn btn-primary" data-shed="<\%= workflow_details[0] \%>" data-owner="<\%= workflow_details[1] \%>" data-repo="<\%= workflow_details[2] \%>" data-toolids="<\%= workflow.tools.join(",") \%>" value="Preview" /></li>',
'<li><input type="button" class="queue_wf_repo btn btn-primary" data-shed="<\%= workflow_details[0] \%>" data-owner="<\%= workflow_details[1] \%>" data-repo="<\%= workflow_details[2] \%>" data-toolids="<\%= workflow.tools.join(",") \%>" value="Add to queue" /></li>',
'</ul>',
'</td>',
'</tr>',
'<\% }); \%>',
'</ul>',
'</div>',
].join(''))
});
return {
RepoQueue: ToolShedRepositoryQueue,
};
});
@@ -0,0 +1,134 @@
define(['mvc/toolshed/toolshed-model', 'mvc/toolshed/util'], function(toolshed_model, toolshed_util) {
var ToolShedRepoStatusView = Backbone.View.extend({
el: '#center',
initialize: function(options) {
var self = this;
this.options = _.defaults(this.options || [{}], options, this.defaults);
this.model = new toolshed_model.RepoStatus();
this.listenTo(this.model, 'sync', this.render);
this.model.url += '?repositories=' + this.options.repositories.join('|');
this.model.fetch();
this.timer = setInterval(function(self) {
var terminal_states = ['installed', 'error'];
var all_done = true;
_.some(self.model.models, function(repository) {
repo_id = repository.get('id');
repo_status = repository.get('status').toLowerCase();
if (terminal_states.indexOf(repo_status) === -1) {
all_done = false;
return true;
}
});
if (all_done) {
clearInterval(self.timer);
}
else {
self.model.fetch();
}
}, 2000, this);
},
close: function() {
clearInterval(this.timer);
},
render: function(options) {
this.options = _.extend(this.options, options);
var repo_status_template = this.templateRepoStatus;
this.$el.html(repo_status_template({title: 'Repository Status', repositories: this.model.models, queue: toolshed_util.queueLength()}));
$("#center").css('overflow', 'auto');
this.bindEvents();
},
bindEvents: function() {
var that = this;
},
reDraw: function(options){
this.$el.empty();
this.initialize(options);
},
templateRepoStatus: _.template([
'<div class="unified-panel-header" id="panel_header" unselectable="on">',
'<div class="unified-panel-header-inner"><%= title %></div>',
'<div class="unified-panel-header-inner" style="position: absolute; right: 5px; top: 0px;"><a href="#/queue">Repository Queue (<%= queue %>)</a></div>',
'</div>',
'<style type="text/css">',
'.state-color-new,',
'.state-color-deactivated,',
'.state-color-uninstalled { border-color:#bfbfbf; background:#eee }',
'.state-color-cloning,',
'.state-color-setting-tool-versions,',
'.state-color-installing-repository-dependencies,',
'.state-color-installing-tool-dependencies,',
'.state-color-loading-proprietary-datatypes { border-color:#AAAA66; background:#FFFFCC }',
'.state-color-installed { border-color:#20b520; background:#b0f1b0 }',
'.state-color-error { border-color:#dd1b15; background:#f9c7c5 }',
'</style>',
'<table id="grid-table" class="grid">',
'<thead id="grid-table-header">',
'<tr>',
'<th id="null-header">Name<span class="sort-arrow"></span></th>',
'<th id="null-header">Description<span class="sort-arrow"></span></th>',
'<th id="null-header">Owner<span class="sort-arrow"></span></th>',
'<th id="null-header">Revision<span class="sort-arrow"></span></th>',
'<th id="null-header">Installation Status<span class="sort-arrow"></span></th>',
'</tr>',
'</thead>',
'<tbody id="grid-table-body">',
'<% _.each(repositories, function(repository) { %>',
'<tr>',
'<td>',
'<div id="" class="">',
'<label id="repo-name-<%= repository.get("id") %>" for="<%= repository.get("id") %>">',
'<%= repository.get("name") %>',
'</label>',
'</div>',
'</td>',
'<td>',
'<div id="" class="">',
'<label id="repo-desc-<%= repository.get("id") %>" for="<%= repository.get("id") %>">',
'<%= repository.get("description") %>',
'</label>',
'</div>',
'</td>',
'<td>',
'<div id="" class="">',
'<label id="repo-user-<%= repository.get("id") %>" for="<%= repository.get("id") %>">',
'<%= repository.get("owner") %>',
'</label>',
'</div>',
'</td>',
'<td>',
'<div id="" class="">',
'<label id="repo-changeset-<%= repository.get("id") %>" for="<%= repository.get("id") %>">',
'<%= repository.get("changeset_revision") %>',
'</label>',
'</div>',
'</td>',
'<td>',
'<div id="" class="">',
'<label id="RepositoryStatus-<%= repository.get("id") %>" for="<%= repository.get("id") %>">',
'<div class="repo-status count-box state-color-<%= repository.get("status").toLowerCase().replace(/ /g, "-") %>" id="RepositoryStatus-<%= repository.get("id") %>">',
'<%= repository.get("status") %>',
'</div>',
'</label>',
'</div>',
'</td>',
'</tr>',
'<% }); %>',
'</tbody>',
'<tfoot id="grid-table-footer"></tfoot>',
'</table>'
].join(''))
});
return {
RepoStatus: ToolShedRepoStatusView,
};
});
@@ -0,0 +1,101 @@
define(['mvc/toolshed/toolshed-model', 'mvc/toolshed/util'], function(toolshed_model, toolshed_util) {
var ToolShedCategoryContentsView = Backbone.View.extend({
el: '#center',
initialize: function(params) {
var self = this;
this.model = new toolshed_model.CategoryCollection();
this.listenTo(this.model, 'sync', this.render);
var shed = params.tool_shed.replace(/\//g, '%2f');
this.model.url += '?tool_shed_url=' + shed + '&category_id=' + params.category_id;
this.model.tool_shed = shed;
this.model.category = params.category_id;
this.model.fetch();
},
render: function(options) {
this.options = _.extend(this.options, options);
var category_contents_template = this.templateCategoryContents;
this.$el.html(category_contents_template({category: this.model.models[0], tool_shed: this.model.tool_shed, queue: toolshed_util.queueLength()}));
$("#center").css('overflow', 'auto');
this.bindEvents();
},
bindEvents: function() {
var that = this;
require(['libs/jquery/jquery-ui'], function() {
$("#search_box").autocomplete({
source: function(request, response) {
var shed_url = that.model.tool_shed.replace(/%2f/g, '/');
var base_url = Galaxy.root + 'api/tool_shed/search';
var params = {term: request.term, tool_shed_url: shed_url};
$.post(base_url, params, function(data) {
result_list = toolshed_util.shedParser(data);
response(result_list);
});
},
minLength: 3,
select: function(event, ui) {
var tsr_id = ui.item.value;
var new_route = 'repository/s/' + that.model.tool_shed + '/r/' + tsr_id;
Backbone.history.navigate(new_route, {trigger: true, replace:true});
},
});
});
},
reDraw: function(options){
this.$el.empty();
this.initialize(options);
},
templateCategoryContents: _.template([
'<style type="text/css">',
'.ui-autocomplete { background-color: #fff; }',
'li.ui-menu-item { list-style-type: none; }',
'</style>',
'<div class="unified-panel-header" id="panel_header" unselectable="on">',
'<div class="unified-panel-header-inner">Repositories in <%= category.get("name") %></div>',
'<div class="unified-panel-header-inner" style="position: absolute; right: 5px; top: 0px;"><a href="#/queue">Repository Queue (<%= queue %>)</a></div>',
'</div>',
'<div class="unified-panel-body" id="list_repositories">',
'<div id="standard-search" style="height: 2em; margin: 1em;">',
'<span class="ui-widget" >',
'<input class="search-box-input" id="search_box" name="search" data-shedurl="<%= tool_shed.replace(/%2f/g, "/") %>" placeholder="Search repositories by name or id" size="60" type="text" />',
'</span>',
'</div>',
'<div style="clear: both; margin-top: 1em;">',
'<table class="grid">',
'<thead id="grid-table-header">',
'<tr>',
'<th style="width: 10%;">Owner</th>',
'<th style="width: 15%;">Name</th>',
'<th>Synopsis</th>',
'<th style="width: 10%;">Type</th>',
'</tr>',
'</thead>',
'<% _.each(category.get("repositories"), function(repository) { %>',
'<tr>',
'<td><%= repository.owner %></td>',
'<td>',
'<div style="float: left; margin-left: 1px;" class="menubutton split">',
'<a href="#/repository/s/<%= tool_shed %>/r/<%= repository.id %>"><%= repository.name %></a>',
'</div>',
'</td>',
'<td><%= repository.description %></td>',
'<td><%= repository.type %></td>',
'</tr>',
'<% }); %>',
'</table>',
'</div>',
'</div>'
].join(''))
});
return {
Category: ToolShedCategoryContentsView,
};
});
@@ -0,0 +1,142 @@
define(['mvc/toolshed/toolshed-model', 'mvc/toolshed/util'], function(toolshed_model, toolshed_util) {
var View = Backbone.View.extend({
el: '#center',
defaults: [{}],
initialize: function(options) {
var that = this;
this.model = new toolshed_model.RepoQueue();
this.listenTo(this.model, 'sync', this.render);
this.model.fetch();
that.render();
},
render: function(options) {
var that = this;
var repo_queue_template = that.templateRepoQueue;
var repositories = that.model.models;
that.$el.html(repo_queue_template({title: 'Repository Installation Queue', repositories: repositories, queue: toolshed_util.queueLength()}));
$("#center").css('overflow', 'auto');
that.bindEvents();
},
bindEvents: function() {
var that = this;
$('.install_one').on('click', function() {
var repository_metadata = that.loadFromQueue($(this).attr('data-repokey'));
that.installFromQueue(repository_metadata, $(this).attr('data-repokey'));
});
$('.remove_one').on('click', function() {
var queue_key = $(this).attr('data-repokey');
var repo_queue = JSON.parse(localStorage.repositories);
if (repo_queue.hasOwnProperty(queue_key)) {
var repository_id = repo_queue[queue_key].repository.id;
delete repo_queue[queue_key];
$('#queued_repository_' + repository_id).remove();
}
localStorage.repositories = JSON.stringify(repo_queue);
});
$('#clear_queue').on('click', function() {
localStorage.repositories = "{}";
});
$('#from_workflow').on('click', function() { Backbone.history.navigate('workflows', {trigger: true, replace:true}); });
},
installFromQueue: function(repository_metadata, queue_key) {
var that = this;
var params = Object();
params.install_tool_dependencies = repository_metadata.install_tool_dependencies;
params.install_repository_dependencies = repository_metadata.install_repository_dependencies;
params.install_resolver_dependencies = repository_metadata.install_resolver_dependencies;
params.tool_panel_section = repository_metadata.tool_panel_section;
params.shed_tool_conf = repository_metadata.shed_tool_conf;
params.repositories = JSON.stringify([[repository_metadata.repository.id, repository_metadata.changeset_revision]]);
params.tool_shed_repository_ids = JSON.stringify([repository_metadata.repository.id]);
params.tool_shed_url = queue_key.split('|')[0];
params.changeset = repository_metadata.changeset_revision;
var url = Galaxy.root + 'api/tool_shed_repositories/install?async=True';
$('#queued_repository_' + repository_metadata.repository.id).remove();
if (localStorage.repositories) {
if (queue_key === undefined) {
queue_key = toolshed_util.queueKey(repository_metadata);
}
repository_queue = JSON.parse(localStorage.repositories);
if (repository_queue.hasOwnProperty(queue_key)) {
delete repository_queue[queue_key];
localStorage.repositories = JSON.stringify(repository_queue);
}
}
$.post(url, params, function(data) {
var iri_params = JSON.parse(data);
var repositories = iri_params.repositories;
var new_route = 'status/r/' + repositories.join('|');
$.post(Galaxy.root + 'admin_toolshed/manage_repositories', iri_params, function(data) {
console.log( "Initializing repository installation succeeded" );
});
Backbone.history.navigate(new_route, {trigger: true, replace:true});
});
},
loadFromQueue: function(queue_key) {
repository_queue = JSON.parse(localStorage.repositories);
if (repository_queue.hasOwnProperty(queue_key)) {
return repository_queue[queue_key];
}
return undefined;
},
reDraw: function(options) {
this.$el.empty();
this.initialize(options);
this.model.fetch();
this.render(options);
},
templateRepoQueue: _.template([
'<div class="unified-panel-header" id="panel_header" unselectable="on">',
'<div class="unified-panel-header-inner"><%= title %></div>',
'<div class="unified-panel-header-inner" style="position: absolute; right: 5px; top: 0px;"><a href="#/queue">Repository Queue (<%= queue %>)</a></div>',
'</div>',
'<div class="tab-pane" id="panel_header" id="repository_queue">',
'<table id="queued_repositories" class="grid" border="0" cellpadding="2" cellspacing="2" width="100%">',
'<thead id="grid-table-header">',
'<tr>',
'<th class="datasetRow">Name</th>',
'<th class="datasetRow">Owner</th>',
'<th class="datasetRow">Revision</th>',
'<th class="datasetRow">ToolShed</th>',
'<th class="datasetRow">Install</th>',
'<th class="datasetRow"><input class="btn btn-primary" type="submit" id="clear_queue" name="clear_queue" value="Clear queue" /></th>',
'</tr>',
'</thead>',
'<tbody>',
'<% _.each(repositories, function(repository) { %>',
'<tr id="queued_repository_<%= repository.get("id") %>">',
'<td class="datasetRow"><%= repository.get("repository").name %></td>',
'<td class="datasetRow"><%= repository.get("repository").owner %></td>',
'<td class="datasetRow"><%= repository.get("changeset_revision") %></td>',
'<td class="datasetRow"><%= repository.get("tool_shed_url") %></td>',
'<td class="datasetRow">',
'<input class="btn btn-primary install_one" data-repokey="<%= repository.get("queue_key") %>" type="submit" id="install_repository_<%= repository.get("id") %>" name="install_repository" value="Install now" />',
'</td>',
'<td class="datasetRow">',
'<input class="btn btn-primary remove_one" data-repokey="<%= repository.get("queue_key") %>" type="submit" id="unqueue_repository_<%= repository.get("id") %>" name="unqueue_repository" value="Remove from queue" />',
'</td>',
'</tr>',
'<% }); %>',
'</tbody>',
'</table>',
'<input type="button" class="btn btn-primary" id="from_workflow" value="Add from workflow" />',
'</div>',
].join(''))
});
return {
RepoQueueView: View,
};
});
@@ -0,0 +1,487 @@
define(['mvc/toolshed/toolshed-model',
'libs/jquery/jstree',
'utils/utils',
'mvc/ui/ui-modal',
'mvc/form/form-view',
'mvc/toolshed/util'], function(toolshed_model,
jstree,
Utils,
Modal,
FormView,
toolshed_util) {
var ToolShedRepositoryView = Backbone.View.extend({
el: '#center',
initialize: function(params) {
var self = this;
this.options = _.defaults(this.options || {}, this.defaults);
this.model = new toolshed_model.RepositoryCollection();
this.listenTo(this.model, 'sync', this.render);
shed = params.tool_shed.replace(/\//g, '%2f');
this.model.url += '?tool_shed_url=' + shed + '&repository_id=' + params.repository_id;
this.model.tool_shed_url = params.tool_shed.replace(/%2f/g, '/');
this.model.tool_shed = shed;
this.model.category = params.repository_id;
this.model.fetch();
},
render: function(options) {
var repo_details_template = this.templateRepoDetails;
var models = this.model.models[0];
this.options = {
repository: models.get('repository'),
tool_shed: this.model.tool_shed,
queue: toolshed_util.queueLength(),
}
var changesets = Object.keys(this.options.repository.metadata);
this.options.current_changeset = (this.options.current_changeset || changesets[changesets.length - 1]);
this.options.current_metadata = this.options.repository.metadata[this.options.current_changeset];
this.options.current_metadata.tool_shed_url = this.model.tool_shed_url;
this.options.tools = this.options.current_metadata.tools;
this.options.repository_dependencies_template = this.templateRepoDependencies;
this.options.repository_dependency_template = this.templateRepoDependency;
this.options.tps_template_global_select = this.templateGlobalSectionSelect;
this.options.tps_template_tool_select = this.templateToolSectionSelect;
this.options.tps_select_options = this.templatePanelSelectOptions;
this.options.tool_dependencies = models.get('tool_dependencies');
this.options.shed_tool_conf = this.templateShedToolConf({'stc_html': models.get('shed_conf')});
this.options.panel_section_dict = models.get('panel_section_dict');
this.options.api_url = Galaxy.root + 'api/tool_shed_repositories/install?async=True';
this.options = _.extend(this.options, options);
this.$el.html(repo_details_template(this.options));
this.checkInstalled(this.options.current_metadata);
this.bindEvents();
$("#center").css('overflow', 'auto');
},
bindEvents: function(){
var that = this;
$('#changeset').on('change', function () {
that.options.current_changeset = $('#changeset').find("option:selected").text();
that.options.current_metadata = that.options.repository.metadata[that.options.current_changeset];
that.checkInstalled(that.options.current_metadata);
that.reDraw();
});
$('#tool_panel_section_select').on('change', function() {
that.tpsSelection();
});
$('#install_repository').on('click', function(ev) {
var form = $('#repository_installation');
ev.preventDefault();
var params = {};
params.repositories = JSON.stringify([[$('#install_repository').attr('data-tsrid'), $('#changeset').find("option:selected").val()]]);
params.tool_shed_repository_ids = JSON.stringify([$('#install_repository').attr('data-tsrid')]);
params.tool_shed_url = that.model.tool_shed_url;
params.install_tool_dependencies = $("#install_tool_dependencies").val();
params.install_repository_dependencies = $("#install_repository_dependencies").val();
params.install_resolver_dependencies = $("#install_resolver_dependencies").val();
var tps = that.panelSelect(params);
params.tool_panel_section = JSON.stringify(that.panelSelect(params));
params.shed_tool_conf = $("select[name='shed_tool_conf']").find('option:selected').val()
params.changeset = $('#changeset').find("option:selected").val();
url = $('#repository_installation').attr('action');
that.prepareInstall(params, url);
});
$('#queue_install').on('click', function(ev) {
that.options.current_changeset = $('#changeset').find("option:selected").text();
that.options.current_metadata = that.options.repository.metadata[that.options.current_changeset];
var changeset = that.options.current_changeset;
var repository_metadata = {};
_.each(Object.keys(that.options.current_metadata), function(key) {
if (!repository_metadata[key]) {
repository_metadata[key] = that.options.current_metadata[key];
}
});
repository_metadata.install_tool_dependencies = $("#install_tool_dependencies").val();
repository_metadata.install_repository_dependencies = $("#install_repository_dependencies").val();
repository_metadata.install_resolver_dependencies = $("#install_resolver_dependencies").val();
repository_metadata.tool_panel_section = JSON.stringify(that.panelSelect({}));
repository_metadata.shed_tool_conf = $("select[name='shed_tool_conf']").find('option:selected').val()
repository_metadata.tool_shed_url = that.model.tool_shed_url;
if (repository_metadata.tool_shed_url.substr(-1) == '/') {
repository_metadata.tool_shed_url = repository_metadata.tool_shed_url.substr(0, repository_metadata.tool_shed_url.length - 1);
}
toolshed_util.addToQueue(repository_metadata);
that.checkInstalled(repository_metadata);
});
$('.tool_panel_section_picker').on('change', function() {
new_value = $(this).find('option:selected').val();
default_tps = $('#tool_panel_section_select').find('option:selected').val();
if (new_value == default_tps) {
$(this).attr('default', 'active');
}
else {
$(this).removeAttr('default');
}
});
$(function() { $('#repository_dependencies').jstree(); });
$('.tool_form').on('click', function() {
var guid = $(this).attr('data-guid');
var clean = $(this).attr('data-clean');
var name = $(this).attr('data-name');
var desc = $(this).attr('data-desc');
var tool_shed_url = that.model.tool_shed_url;
var tsr_id = $('#repository_details').attr('data-tsrid');
var changeset = $('#changeset').find("option:selected").val();
var api_url = Galaxy.root + 'api/tool_shed/tool_json';
var params = {'guid': guid, 'tool_shed_url': tool_shed_url, 'tsr_id': tsr_id, 'changeset': changeset};
$.get(api_url, params, function(data) {
data.cls = 'ui-portlet-plain';
var toolform = new FormView(data);
Utils.deepeach(data.inputs, function( input ) {
if (input.type) {
if (['data', 'data_collection'].indexOf(input.type) != -1) {
input.type = 'hidden';
input.info = 'Data input \'' + input.name + '\' (' + Utils.textify(input.extensions) + ')';
}
}
});
var modal = new Modal.View();
var modal_title = '<u>' + name + '</u> ' + desc;
modal.show({'closing_events': true,
'title': modal_title,
'body': toolform.$el,
'buttons': { 'Close': function() { modal.hide(); }}});
});
});
},
checkInstalled: function(metadata) {
var that = this;
var params = {name: metadata.name, owner: metadata.owner};
var already_installed = false;
var queued = that.repoQueued(metadata);
$.get(Galaxy.root + 'api/tool_shed_repositories', params, function(data) {
for (var index = 0; index < data.length; index++) {
var repository = data[index];
var installed = !repository.deleted && !repository.uninstalled;
var changeset_match = repository.changeset_revision == metadata.changeset_revision ||
repository.installed_changeset_revision == metadata.changeset_revision;
if (repository.name == metadata.repository.name && repository.owner == metadata.repository.owner && installed && changeset_match) {
already_installed = true;
}
if (already_installed) {
$('#install_repository').prop('disabled', true);
$('#install_repository').val('This revision is already installed');
}
else {
$('#install_repository').prop('disabled', false);
$('#install_repository').val('Install this revision');
}
}
if (that.repoQueued(metadata) || already_installed) {
$('#queue_install').hide();
$('#queue_install').val('This revision is already in the queue');
}
else {
$('#queue_install').show();
$('#queue_install').val('Install this revision later');
}
});
},
panelSelect: function(params) {
var tool_panel_section = {};
if ($('#tool_panel_section_select').length) {
params.tool_panel_section_id = $('#tool_panel_section_select').find("option:selected").val();
}
else {
params.new_tool_panel_section = $("#new_tool_panel_section").val();
}
$('.tool_panel_section_picker').each(function() {
element_name = $(this).attr('name');
tool_guid = $(this).attr('data-toolguid');
if (element_name === 'tool_panel_section_id') {
tool_panel_section[tool_guid] = { tool_panel_section: $(this).find("option:selected").val(), action: 'append' }
}
else {
tool_panel_section[tool_guid] = { tool_panel_section: $(this).val(), action: 'create' }
}
});
return tool_panel_section;
},
reDraw: function(options) {
this.$el.empty();
this.initialize(options);
},
repoQueued: function(metadata) {
var that = this;
if (!localStorage.repositories) {
return;
}
var repository_queue = JSON.parse(localStorage.repositories);
var changeset = metadata.changeset_revision;
var queue_key = that.queueKey(metadata);
if (localStorage.repositories) {
queued_repos = JSON.parse(localStorage.repositories);
}
if (queued_repos.hasOwnProperty(queue_key)) {
return true;
}
return false;
},
queueKey: function (metadata) {
var shed_url = this.model.tool_shed_url;
// Make sure there is never a trailing slash on the shed URL.
if (shed_url.substr(-1) == '/') {
shed_url = shed_url.substr(0, shed_url.length - 1);
}
return shed_url + '|' + metadata.repository_id + '|' + metadata.changeset_revision;
},
tpsSelection: function() {
new_tps = $('#tool_panel_section_select').find('option:selected').val();
$('.tool_panel_section_picker[default="active"]').each(function() {
$(this).val(new_tps);
});
},
prepareInstall: function(params, api_url) {
var that = this;
$.post(api_url, params, function(data) {
iri_parameters = JSON.parse(data);
that.doInstall(iri_parameters);
});
},
doInstall: function(params) {
controller_url = Galaxy.root + 'admin_toolshed/manage_repositories';
var repositories = params.repositories;
var new_route = 'status/r/' + repositories.join('|');
$.post(controller_url, params, function(data) {
console.log( "Initializing repository installation succeeded" );
})
Backbone.history.navigate(new_route, {trigger: true, replace:true});
},
templateRepoDetails: _.template([
'<div class="unified-panel-header" id="panel_header" unselectable="on">',
'<div class="unified-panel-header-inner">Repository information for <strong><%= repository.name %></strong> from <strong><%= repository.owner %></strong></div>',
'<div class="unified-panel-header-inner" style="position: absolute; right: 5px; top: 0px;"><a href="#/queue">Repository Queue (<%= queue %>)</a></div>',
'</div>',
'<div class="unified-panel-body" id="repository_details" data-tsrid="<%= repository.id %>">',
'<form id="repository_installation" name="install_repository" method="post" action="<%= api_url %>">',
'<input type="hidden" id="repositories" name="<%= repository.id %>" value="ID" />',
'<input type="hidden" id="tool_shed_url" name="tool_shed_url" value="<%= tool_shed %>" />',
'<div class="toolForm">',
'<div class="toolFormTitle">Changeset</div>',
'<div class="toolFormBody changeset">',
'<select id="changeset" name="changeset" style="margin: 5px;">',
'<% _.each(Object.keys(repository.metadata), function(changeset) { %>',
'<% if (changeset == current_changeset) { var selected = "selected "; } else { var selected = ""; } %>',
'<option <%= selected %>value="<%= changeset.split(":")[1] %>"><%= changeset %></option>',
'<% }); %>',
'</select>',
'<input class="btn btn-primary preview-button" data-tsrid="<\%= current_metadata.repository.id \%>" type="submit" id="install_repository" name="install_repository" value="Install this revision now" />',
'<input class="btn btn-primary preview-button" type="button" id="queue_install" name="queue_install" value="Install this revision later" />',
'<div class="toolParamHelp" style="clear: both;">Please select a revision and review the settings below before installing.</div>',
'</div>',
'</div>',
'<%= shed_tool_conf %>',
'<% if (current_metadata.has_repository_dependencies) { %>',
'<div class="toolFormTitle">Repository dependencies for <strong id="current_changeset"><%= current_changeset %></strong></div>',
'<div class="toolFormBody">',
'<p id="install_repository_dependencies_checkbox">',
'<input type="checkbox" checked id="install_repository_dependencies" />',
'<label for="install_repository_dependencies">Install repository dependencies</label>',
'</p>',
'<% current_metadata.repository_dependency_template = repository_dependency_template; %>',
'<div class="tables container-table" id="repository_dependencies">',
'<div class="expandLink">',
'<a class="toggle_folder" data_target="repository_dependencies_table">',
'Repository dependencies &ndash; <em>installation of these additional repositories is required</em>',
'</a>',
'</div>',
'<%= repository_dependencies_template(current_metadata) %>',
'</div>',
'</div>',
'<% } %>',
'<% if (current_metadata.includes_tool_dependencies) { %>',
'<div class="toolFormTitle">Tool dependencies</div>',
'<div class="toolFormBody">',
'<p id="install_resolver_dependencies_checkbox">',
'<input type="checkbox" checked id="install_resolver_dependencies" />',
'<label for="install_resolver_dependencies">Install resolver dependencies</label>',
'</p>',
'<p id="install_tool_dependencies_checkbox">',
'<input type="checkbox" checked id="install_tool_dependencies" />',
'<label for="install_tool_dependencies">Install tool dependencies</label>',
'</p>',
'<div class="tables container-table" id="tool_dependencies">',
'<div class="expandLink">',
'<a class="toggle_folder" data_target="tool_dependencies_table">',
'Tool dependencies &ndash; <em>repository tools require handling of these dependencies</em>',
'</a>',
'</div>',
'<table class="tables container-table" id="tool_dependencies_table" border="0" cellpadding="2" cellspacing="2" width="100%">',
'<thead>',
'<tr style="display: table-row;" class="datasetRow" parent="0" id="libraryItem-rt-f9cad7b01a472135">',
'<th style="padding-left: 40px;">Name</th>',
'<th>Version</th>',
'<th>Type</th>',
'</tr>',
'</thead>',
'<tbody id="tool_deps">',
'<% _.each(tool_dependencies[current_changeset], function(dependency) { %>',
'<tr class="datasetRow tool_dependency_row" style="display: table-row;">',
'<td style="padding-left: 40px;">',
'<%= dependency.name %></td>',
'<td><%= dependency.version %></td>',
'<td><%= dependency.type %></td>',
'</tr>',
'<% }); %>',
'</tbody>',
'</table>',
'</div>',
'</div>',
'<% } %>',
'<% if (current_metadata.includes_tools_for_display_in_tool_panel) { %>',
'<div class="toolFormTitle">Tools &ndash; <em>click the name to preview the tool and use the pop-up menu to inspect all metadata</em></div>',
'<div class="toolFormBody">',
'<div class="tables container-table" id="tools_toggle">',
'<table class="tables container-table" id="valid_tools" border="0" cellpadding="2" cellspacing="2" width="100%">',
'<thead>',
'<tr style="display: table-row;" class="datasetRow" parent="0" id="libraryItem-rt-f9cad7b01a472135">',
'<th style="padding-left: 40px;">Name</th>',
'<th>Description</th>',
'<th>Version</th>',
'<th><%= tps_template_global_select({tps: panel_section_dict, tps_select_options: tps_select_options}) %></tr>',
'</thead>',
'<tbody id="tools_in_repo">',
'<% _.each(current_metadata.tools, function(tool) { %>',
'<tr id="libraryItem-<%= tool.clean %>" class="tool_row" style="display: table-row;" style="width: 15%">',
'<td style="padding-left: 40px;">',
'<div id="tool-<%= tool.clean %>" class="menubutton split popup" style="float: left;">',
'<a class="tool_form view-info" data-toggle="modal" data-target="toolform_<%= tool.clean %>" data-clean="<%= tool.clean %>" data-guid="<%= tool.guid %>" data-name="<%= tool.name %>" data-desc="<%= tool.description %>"><%= tool.name %></a>',
'</div>',
'</td>',
'<td><%= tool.description %></td>',
'<td style="width: 15%"><%= tool.version %></td>',
'<td style="width: 35%" id="tool_tps_<%= tool.clean %>">',
'<%= tps_template_tool_select({tool: tool, tps: panel_section_dict, tps_select_options: tps_select_options}) %>',
'</td>',
'</tr>',
'<% }); %>',
'</tbody>',
'</table>',
'</div>',
'</div>',
'<% } %>',
'</form>',
'</div>',
].join('')),
templateRepoDependencies: _.template([
'<div class="toolFormTitle">Repository Dependencies</div>',
'<div class="toolFormBody tables container-table" id="repository_dependencies">',
'<ul>',
'<li>Repository installation requires the following',
'<% if (has_repository_dependencies) { %>',
'<% _.each(repository_dependencies, function(dependency) { %>',
'<% dependency.repository_dependency_template = repository_dependency_template; %>',
'<%= repository_dependency_template(dependency) %>',
'<% }); %>',
'<% } %>',
'</li>',
'</ul>',
'</div>'].join('')),
templateRepoDependency: _.template([
'<li id="metadata_<%= id %>" class="datasetRow repository_dependency_row">',
'Repository <b><%= repository.name %></b> revision <b><%= changeset_revision %></b> owned by <b><%= repository.owner %></b>',
'<% if (has_repository_dependencies) { %>',
'<ul class="child_dependencies">',
'<% _.each(repository_dependencies, function(dependency) { %>',
'<% dependency.repository_dependency_template = repository_dependency_template; %>',
'<%= repository_dependency_template(dependency) %>',
'<% }); %>',
'</ul>',
'<% } %>',
'</li>',
].join('')),
templateShedToolConf: _.template([
'<div class="toolFormTitle">Shed tool configuration file:</div>',
'<div class="toolFormBody">',
'<div class="form-row">',
'<%= stc_html %>',
'<div class="toolParamHelp" style="clear: both;">Select the file whose <b>tool_path</b> setting you want used for installing repositories.</div>',
'</div>',
'</div>',
].join('')),
templateToolDependency: _.template([
'<% if (has_repository_dependencies) { %>',
'<% _.each(repository_dependencies, function(dependency) { %>',
'<% if (dependency.includes_tool_dependencies) { %>',
'<% dependency.tool_dependency_template = tool_dependency_template %>',
'<%= tool_dependency_template(dependency) %>',
'<% } %>',
'<% }); %>',
'<% } %>',
].join('')),
templateGlobalSectionCreate: _.template([
'<div id="tool_panel_section">',
'<div class="form-row" id="new_tps">',
'<input id="new_tool_panel_section" name="new_tool_panel_section" type="textfield" value="" size="40"/>',
'<input class="btn btn-primary" type="button" id="select_existing" value="Select existing" />',
'<div class="toolParamHelp" style="clear: both;">',
'Add a new tool panel section to contain the installed tools (optional).',
'</div>',
'</div>',
'</div>',
].join('')),
templateGlobalSectionSelect: _.template([
'<div id="tool_panel_section">',
'<div class="toolFormTitle">Tool Panel Section</div>',
'<div class="toolFormBody">',
'<div class="tab-pane" id="select_tps">',
'<select name="<%= name %>" id="<%= tps.id %>">',
'<%= tps_select_options({sections: tps.sections}) %>',
'</select>',
'<input class="btn btn-primary" type="button" id="create_new" value="Create new" />',
'<div class="toolParamHelp" style="clear: both;">',
'Select an existing tool panel section to contain the installed tools (optional).',
'</div>',
'</div>',
'</div>',
'</div>',
].join('')),
templateToolSectionCreate: _.template([
'<div id="new_tps_<%= tool.clean %>" data-clean="<%= tool.clean %>" class="form-row">',
'<input data-toolguid="<%= tool.guid %>" class="tool_panel_section_picker" size="40" name="new_tool_panel_section" id="new_tool_panel_section_<%= tool.clean %>" type="text">',
'<input id="per_tool_select_<%= tool.clean %>" class="btn btn-primary" data-toolguid="<%= tool.guid %>" value="Select existing" id="select_existing_<%= tool.clean %>" type="button">',
'</div>',
].join('')),
templateToolSectionSelect: _.template([
'<div id="select_tps_<%= tool.clean %>" data-clean="<%= tool.clean %>" class="tps_creator">',
'<select default="active" style="width: 30em;" data-toolguid="<%= tool.guid %>" class="tool_panel_section_picker" name="tool_panel_section_id" id="tool_panel_section_select_<%= tool.clean %>">',
'<%= tps_select_options({sections: tps.sections}) %>',
'</select>',
'<input id="per_tool_create_<%= tool.clean %>" data-clean="<%= tool.clean %>" class="btn btn-primary create-tps-button" data-toolguid="<%= tool.guid %>" value="Create new" id="create_new_<%= tool.clean %>" type="button">',
'<div style="clear: both;" class="toolParamHelp"></div>',
'</div>',
].join('')),
templatePanelSelectOptions: _.template([
'<% _.each(sections, function(section) { %>',
'<option value="<%= section.id %>"><%= section.name %></option>',
'<% }); %>',
].join(''))
});
return {
RepoDetails: ToolShedRepositoryView,
};
});
@@ -0,0 +1,59 @@
define(['mvc/toolshed/toolshed-model', 'mvc/toolshed/util'], function(toolshed_model, toolshed_util) {
var View = Backbone.View.extend({
defaults: {
tool_sheds: [{"url": "https://toolshed.g2.bx.psu.edu/", "name": "Galaxy Main Tool Shed"}]
},
initialize: function(options) {
var self = this;
this.options = _.defaults(this.options || {}, this.defaults);
this.model = new toolshed_model.ShedsCollection();
this.listenTo(this.model, 'sync', this.render);
this.model.fetch();
},
el: '#center',
render: function(options) {
this.options = _.defaults(this.options || {}, options, this.defaults);
var toolshed_list_template = this.templateToolshedList;
this.$el.html(toolshed_list_template({title: 'Configured Galaxy Tool Sheds', tool_sheds: this.model.models, queue: toolshed_util.queueLength()}));
},
reDraw: function(options){
this.$el.empty();
this.render(options);
},
templateToolshedList: _.template([
'<div class="unified-panel-header" id="panel_header" unselectable="on">',
'<div class="unified-panel-header-inner"><%= title %></div>',
'<div class="unified-panel-header-inner" style="position: absolute; right: 5px; top: 0px;"><a href="#/queue">Repository Queue (<%= queue %>)</a></div>',
'</div>',
'<div class="unified-panel-body" id="list_toolsheds">',
'<div class="form-row">',
'<table class="grid">',
'<% _.each(tool_sheds, function(shed) { %>',
'<tr class="libraryTitle">',
'<td>',
'<div style="float: left; margin-left: 1px;" class="menubutton split">',
'<a class="view-info shed-selector" href="#/categories/s/<%= shed.get("url") %>"><%= shed.get("name") %></a>',
'</div>',
'</td>',
'</tr>',
'<% }); %>',
'</table>',
'</div>',
'<div style="clear: both"></div>',
'</div>',
].join(''))
});
return {
ShedListView: View,
};
});
@@ -0,0 +1,99 @@
define([], function() {
var ToolShedModel = Backbone.Model.extend({
defaults: {"url": "https://toolshed.g2.bx.psu.edu/", "name": "Galaxy Main Tool Shed"},
urlRoot: Galaxy.root + 'api/tool_shed',
});
var ToolShedsCollection = Backbone.Collection.extend({
url: Galaxy.root + 'api/tool_shed',
model: ToolShedModel
});
var ToolShedCategoriesModel = Backbone.Model.extend({
defaults: [{}],
urlRoot: Galaxy.root + 'api/tool_shed/contents',
});
var ToolShedCategoriesCollection = Backbone.Collection.extend({
url: Galaxy.root + 'api/tool_shed/contents',
model: ToolShedCategoriesModel
});
var ToolShedCategoryModel = Backbone.Model.extend({
defaults: [{}],
urlRoot: Galaxy.root + 'api/tool_shed/category',
});
var ToolShedCategoryCollection = Backbone.Collection.extend({
url: Galaxy.root + 'api/tool_shed/category',
model: ToolShedCategoryModel
});
var ToolShedRepositoryModel = Backbone.Model.extend({
defaults: [{}],
urlRoot: Galaxy.root + 'api/tool_shed/repository',
});
var ToolShedRepositoryCollection = Backbone.Collection.extend({
url: Galaxy.root + 'api/tool_shed/repository',
model: ToolShedRepositoryModel
});
var RepoQueueModel = Backbone.Model.extend({
url: '#',
});
var RepoQueueCollection = Backbone.Collection.extend({
url: '#',
model: RepoQueueModel,
fetch: function() {
var collection = this;
var repositories = Array();
var repositories_enc = JSON.parse(localStorage.repositories);
var queue_keys = Object.keys(repositories_enc);
_.each(queue_keys, function(key) {
var repo = repositories_enc[key];
repo.queue_key = key;
repositories.push(repo);
});
collection.reset(repositories);
return Backbone.Collection.prototype.fetch.call(this);
},
});
var RepoStatusModel = Backbone.Model.extend({
defaults: [{}],
urlRoot: Galaxy.root + 'api/tool_shed/status',
});
var RepoStatusCollection = Backbone.Collection.extend({
url: Galaxy.root + 'api/tool_shed/status',
model: RepoStatusModel
});
var WorkflowToolsModel = Backbone.Model.extend({
defaults: [{}],
urlRoot: Galaxy.root + 'api/workflows?missing_tools=True',
});
var WorkflowToolsCollection = Backbone.Collection.extend({
url: Galaxy.root + 'api/workflows?missing_tools=True',
model: WorkflowToolsModel
});
return {
ShedModel: ToolShedModel,
ShedsCollection: ToolShedsCollection,
Category: ToolShedCategoriesModel,
Categories: ToolShedCategoriesCollection,
CategoryModel: ToolShedCategoryModel,
CategoryCollection: ToolShedCategoryCollection,
RepositoryModel: ToolShedRepositoryModel,
RepositoryCollection: ToolShedRepositoryCollection,
RepoQueue: RepoQueueCollection,
RepoStatus: RepoStatusCollection,
WorkflowTools: WorkflowToolsCollection
};
});
@@ -0,0 +1,50 @@
define([], function() {
var searchShed = function(request, response) {
var that = this;
var shed_url = this.shed_url;
var base_url = Galaxy.root + 'api/tool_shed/search';
$.get(base_url, {term: request.term, tool_shed_url: shed_url}, function(data) {
result_list = that.shedParser(data);
response(result_list);
});
};
var shedParser = function(jsondata) {
var results = [];
var hits = jsondata.hits;
$.each(hits, function(hit) {
var record = hits[hit];
var label = record.repository.name + ' by ' + record.repository.repo_owner_username + ': ' + record.repository.description;
result = {value: record.repository.id, label: label};
results.push(result);
});
return results;
};
var addToQueue = function(metadata) {
if (metadata.tool_shed_url.substr(-1) == '/') {
metadata.tool_shed_url = metadata.tool_shed_url.substr(0, metadata.tool_shed_url.length - 1);
}
var key = metadata.tool_shed_url + '|' + metadata.repository_id + '|' + metadata.changeset_revision;
var queued_repos = new Object();
if (localStorage.repositories) {
queued_repos = JSON.parse(localStorage.repositories);
}
queued_repos[key] = metadata;
localStorage.repositories = JSON.stringify(queued_repos);
}
var queueLength = function() {
if (localStorage.hasOwnProperty('repositories')) {
repo_queue = JSON.parse(localStorage.repositories);
queue_length = Object.keys(repo_queue).length;
return queue_length;
}
else {
return 0;
}
}
return {searchShed: searchShed, shedParser: shedParser, addToQueue: addToQueue, queueLength: queueLength};
});
@@ -0,0 +1,122 @@
define(['mvc/toolshed/toolshed-model', 'mvc/toolshed/util'], function(toolshed_model, toolshed_util) {
var View = Backbone.View.extend({
el: '#center',
defaults: [{}],
initialize: function(options) {
var that = this;
this.model = new toolshed_model.WorkflowTools();
this.listenTo(this.model, 'sync', this.render);
this.model.fetch();
that.render();
},
render: function(options) {
var that = this;
var workflows_missing_tools = that.templateWorkflows;
var workflows = that.model.models;
that.$el.html(workflows_missing_tools({title: 'Workflows Missing Tools', workflows: workflows, queue: toolshed_util.queueLength()}));
$("#center").css('overflow', 'auto');
that.bindEvents();
},
bindEvents: function() {
var that = this;
$('.show_wf_repo').on('click', function() {
var tool_ids = $(this).attr('data-toolids');
var toolshed = $(this).attr('data-shed');
var api_url = Galaxy.root + 'api/tool_shed/repository'
var params = {'tool_ids': tool_ids};
$.get(api_url, params, function(data) {
repository_id = data.repository.id;
var new_route = 'repository/s/' + toolshed.replace(/:/g, '%3a').replace(/\//g, '%2f') + '/r/' + data.repository.id;
Backbone.history.navigate(new_route, {trigger: true, replace:true});
});
});
$('.queue_wf_repo').on('click', function() {
var elem = $(this);
var tool_ids = elem.attr('data-toolids');
var toolshed = elem.attr('data-shed');
var api_url = Galaxy.root + 'api/tool_shed/repository'
var params = {'tool_ids': tool_ids};
$.get(api_url, params, function(data) {
repository_id = data.repository.id;
params = {tool_shed_url: toolshed, repository_id: repository_id};
$.get(api_url, params, function(data) {
var changesets = Object.keys(data.repository.metadata);
var current_changeset = changesets[0];
var current_metadata = data.repository.metadata[current_changeset];
current_metadata.tool_shed_url = toolshed;
toolshed_util.addToQueue(current_metadata);
elem.remove();
});
});
});
$('#from_workflow').on('click', that.loadWorkflows);
},
reDraw: function(options) {
this.$el.empty();
this.initialize(options);
},
templateWorkflows: _.template([
'<div class="unified-panel-header" id="panel_header" unselectable="on">',
'<div class="unified-panel-header-inner"><%= title %></div>',
'<div class="unified-panel-header-inner" style="position: absolute; right: 5px; top: 0px;"><a href="#/queue">Repository Queue (<%= queue %>)</a></div>',
'</div>',
'<style type="text/css">',
'.workflow_names, .workflow_tools { list-style-type: none; } ul.workflow_tools, ul.workflow_names { padding-left: 0px; }',
'</style>',
'<table id="workflows_missing_tools" class="grid" border="0" cellpadding="2" cellspacing="2" width="100%">',
'<thead id="grid-table-header">',
'<tr>',
'<th class="datasetRow">Workflows</th>',
'<th class="datasetRow">Tool IDs</th>',
'<th class="datasetRow">Shed</th>',
'<th class="datasetRow">Name</th>',
'<th class="datasetRow">Owner</th>',
'<th class="datasetRow">Actions</th>',
'</tr>',
'</thead>',
'<tbody>',
'<% _.each(workflows, function(workflow) { %>',
'<tr>',
'<td class="datasetRow">',
'<ul class="workflow_names">',
'<% _.each(workflow.get("workflows"), function(name) { %>',
'<li class="workflow_names"><%= name %></li>',
'<% }); %>',
'</ul>',
'</td>',
'<td class="datasetRow">',
'<ul class="workflow_tools">',
'<% _.each(workflow.get("tools"), function(tool) { %>',
'<li class="workflow_tools"><%= tool %></li>',
'<% }); %>',
'</ul>',
'</td>',
'<td class="datasetRow"><%= workflow.get("shed") %></td>',
'<td class="datasetRow"><%= workflow.get("repository") %></td>',
'<td class="datasetRow"><%= workflow.get("owner") %></td>',
'<td class="datasetRow">',
'<ul class="workflow_tools">',
'<li class="workflow_tools">',
'<input type="button" class="show_wf_repo btn btn-primary" data-shed="<%= workflow.get("shed") %>" data-owner="<%= workflow.get("owner") %>" data-repo="<%= workflow.get("repository") %>" data-toolids="<%= workflow.get("tools").join(",") %>" value="Show Repository" /></li>',
'</ul>',
'</td>',
'</tr>',
'<% }); %>',
'</ul>',
'</div>',
].join(''))
});
return {
Workflows: View,
};
});
+1 -1
View File
@@ -31,7 +31,7 @@ define(['libs/bootstrap-tour'],function(BootstrapTour) {
};
}
if (step.postclick){
step.onHide = function(){
step.onNext = function(){
_.each(step.postclick, function(postclick){
// TODO: click delay between clicks
$(postclick).click();
@@ -112,6 +112,7 @@ define( [ 'utils/utils' ], function( Utils ) {
title : options.title,
target : options.target || '_top',
disabled : options.disabled } )
.tooltip( { placement: 'bottom' } )
.off( 'click' ).on( 'click' , function() {
options.onclick && !options.disabled && options.onclick();
});
@@ -64,67 +64,43 @@ define(['utils/utils'], function( Utils ) {
/** Build color panel */
_build: function() {
var $content = this._content({
label : 'Theme Colors',
colors : this.colors.base,
padding : 10
});
this._content( { colors : this.colors.base } );
for ( var i in this.colors.theme ) {
var line_def = {};
if ( i == 0 ) {
line_def[ 'bottom' ] = true;
} else if ( i != this.colors.theme.length - 1 ) {
line_def[ 'top' ] = true;
line_def[ 'bottom' ] = true;
} else {
if ( i != this.colors.theme.length - 1 ) {
line_def[ 'top' ] = true;
line_def[ 'bottom' ] = true;
} else {
line_def[ 'top' ] = true;
line_def[ 'padding' ] = 5;
}
line_def[ 'top' ] = true;
}
line_def[ 'colors' ] = this.colors.theme[ i ];
this._content( line_def );
}
this._content({
label : 'Standard Colors',
colors : this.colors.standard,
padding : 5
});
this._content( { colors : this.colors.standard } );
},
/** Create content */
_content: function( options ) {
var label = options.label;
var colors = options.colors;
var padding = options.padding;
var top = options.top;
var bottom = options.bottom;
var colors = options.colors;
var $content = $( this._templateContent() );
var $label = $content.find( '.label' );
if ( options.label ) {
$label.html( options.label );
} else {
$label.hide();
}
var $line = $content.find( '.line' );
this.$panel.append( $content );
for ( var i in colors ) {
var $box = $( this._templateBox( colors[ i ] ) );
if ( top ) {
if ( options.top ) {
$box.css( 'border-top', 'none' );
$box.css( 'border-top-left-radius', '0px' );
$box.css( 'border-top-right-radius', '0px' );
}
if ( bottom ) {
if ( options.bottom ) {
$box.css( 'border-bottom', 'none' );
$box.css( 'border-bottom-left-radius', '0px' );
$box.css( 'border-bottom-right-radius', '0px' );
}
$line.append( $box );
}
if (padding) {
$line.css( 'padding-bottom', padding );
}
return $content;
},
@@ -136,7 +112,6 @@ define(['utils/utils'], function( Utils ) {
/** Content template */
_templateContent: function() {
return '<div class="ui-color-picker-content">' +
'<div class="label"/>' +
'<div class="line"/>' +
'</div>';
},
+73 -19
View File
@@ -62,7 +62,7 @@ define(['utils/utils',
this.$el.addClass( 'alert' ).addClass( 'alert-' + status );
}
if ( this.model.get( 'message' ) ) {
this.$el.html( this.model.get( 'message' ) );
this.$el.html( this.messageForDisplay() );
this.$el[ this.model.get( 'fade' ) ? 'fadeIn' : 'show' ]();
this.timeout && window.clearTimeout( this.timeout );
if ( !this.model.get( 'persistent' ) ) {
@@ -75,6 +75,15 @@ define(['utils/utils',
this.$el.fadeOut();
}
return this;
},
messageForDisplay: function() {
return _.escape( this.model.get( 'message' ) );
}
});
var UnescapedMessage = Message.extend({
messageForDisplay: function() {
return this.model.get( 'message' );
}
});
@@ -115,6 +124,11 @@ define(['utils/utils',
.attr( 'placeholder', this.model.get( 'placeholder' ) )
.css( 'color', this.model.get( 'color' ) || '' )
.css( 'border-color', this.model.get( 'color' ) || '' );
var datalist = this.model.get( 'datalist' );
if ( $.isArray( datalist ) && datalist.length > 0 ) {
this.$el.autocomplete( { source : function( request, response ) { response( self.model.get( 'datalist' ) ) },
change : function() { self._onchange() } } );
}
if ( this.model.get( 'value' ) !== this.$el.val() ) {
this.$el.val( this.model.get( 'value' ) );
}
@@ -146,27 +160,67 @@ define(['utils/utils',
render: function() {
this.$el.attr( 'id', this.model.id );
this.$hidden.val( this.model.get( 'value' ) );
this.model.get( 'info' ) ? this.$info.show().html( this.model.get( 'info' ) ) : this.$info.hide();
this.model.get( 'info' ) ? this.$info.show().text( this.model.get( 'info' ) ) : this.$info.hide();
return this;
}
});
/** Creates a upload element input field */
var Upload = Backbone.View.extend({
initialize: function( options ) {
var self = this;
this.model = options && options.model || new Backbone.Model( options );
this.setElement( $ ( '<div/>' ).append( this.$info = $( '<div/>' ) )
.append( this.$file = $( '<input/>' ).attr( 'type', 'file' ).addClass( 'ui-margin-bottom' ) )
.append( this.$text = $( '<textarea/>' ).addClass( 'ui-textarea' ).attr( 'disabled', true ) )
.append( this.$wait = $( '<i/>' ).addClass( 'fa fa-spinner fa-spin' ) ) );
this.listenTo( this.model, 'change', this.render, this );
this.$file.on( 'change', function( e ) { self._readFile( e ) } );
this.render();
},
value: function( new_val ) {
new_val !== undefined && this.model.set( 'value', new_val );
return this.model.get( 'value' );
},
render: function() {
this.$el.attr( 'id', this.model.id );
this.model.get( 'info' ) ? this.$info.show().text( this.model.get( 'info' ) ) : this.$info.hide();
this.model.get( 'value' ) ? this.$text.text( this.model.get( 'value' ) ).show() : this.$text.hide();
this.model.get( 'wait' ) ? this.$wait.show() : this.$wait.hide();
return this;
},
_readFile: function( e ) {
var self = this;
var file = e.target.files && e.target.files[ 0 ];
if ( file ) {
var reader = new FileReader();
reader.onload = function() {
self.model.set( { wait: false, value: this.result } );
}
this.model.set( { wait: true, value: null } );
reader.readAsText( file );
}
}
});
return {
Button : Buttons.ButtonDefault,
ButtonIcon : Buttons.ButtonIcon,
ButtonCheck : Buttons.ButtonCheck,
ButtonMenu : Buttons.ButtonMenu,
ButtonLink : Buttons.ButtonLink,
Input : Input,
Label : Label,
Message : Message,
Modal : Modal,
RadioButton : Options.RadioButton,
Checkbox : Options.Checkbox,
Radio : Options.Radio,
Select : Select,
Hidden : Hidden,
Slider : Slider,
Drilldown : Drilldown
Button : Buttons.ButtonDefault,
ButtonIcon : Buttons.ButtonIcon,
ButtonCheck : Buttons.ButtonCheck,
ButtonMenu : Buttons.ButtonMenu,
ButtonLink : Buttons.ButtonLink,
Input : Input,
Label : Label,
Message : Message,
UnescapedMessage : UnescapedMessage,
Upload : Upload,
Modal : Modal,
RadioButton : Options.RadioButton,
Checkbox : Options.Checkbox,
Radio : Options.Radio,
Select : Select,
Hidden : Hidden,
Slider : Slider,
Drilldown : Drilldown
}
});
});
@@ -49,6 +49,11 @@ var Base = Backbone.View.extend({
this._changeVisible();
},
/** Update available options */
update: function( options ) {
this.model.set( 'data', options );
},
_changeData: function() {
var self = this;
this.$options.empty();
+10 -1
View File
@@ -15,7 +15,8 @@ var View = Backbone.View.extend({
operations : null,
collapsible : false,
collapsible_button : false,
collapsed : false
collapsed : false,
onchange_title : null
} ).set( options );
this.setElement( this._template() );
@@ -66,6 +67,14 @@ var View = Backbone.View.extend({
options.collapsed ? this.collapse() : this.expand();
}
// allow title editing
this.$title_text.prop( 'disabled', !options.onchange_title );
options.onchange_title && this.$title_text.make_text_editable({
on_finish: function( new_title ) {
options.onchange_title( new_title );
}
});
// render buttons
if ( options.buttons ) {
this.$buttons.empty().show();
@@ -11,7 +11,7 @@ var Configurations = {
{ src: 'hdca', icon: 'fa-folder-o', tooltip: 'Dataset collection', multiple: false, batch: Batch.LINKED } ],
data_multiple: [
{ src: 'hda', icon: 'fa-files-o', tooltip: 'Multiple datasets', multiple: true, batch: Batch.DISABLED },
{ src: 'hdca', icon: 'fa-folder-o', tooltip: 'Dataset collection', multiple: false, batch: Batch.DISABLED } ],
{ src: 'hdca', icon: 'fa-folder-o', tooltip: 'Dataset collections', multiple: true, batch: Batch.DISABLED } ],
data_collection: [
{ src: 'hdca', icon: 'fa-folder-o', tooltip: 'Dataset collection', multiple: false, batch: Batch.DISABLED } ],
workflow_data: [
@@ -34,7 +34,8 @@ var View = Backbone.View.extend({
var self = this;
this.model = options && options.model || new Backbone.Model({
src_labels : { 'hda' : 'dataset', 'hdca': 'dataset collection' },
pagelimit : 100
pagelimit : 100,
statustimer : 1000
}).set( options );
this.setElement( $( '<div/>' ).addClass( 'ui-select-content' ) );
this.button_product = new Ui.RadioButton.View( {
@@ -54,6 +55,12 @@ var View = Backbone.View.extend({
.append( $( '<div/>' ).css( 'clear', 'both' ) )
};
// add drag-drop event handlers
this.$el.on( 'dragenter', function( e ) { this.lastenter = e.target; self.$el.addClass( 'ui-dragover' ); } )
.on( 'dragover', function( e ) { e.preventDefault(); } )
.on( 'dragleave', function( e ) { this.lastenter === e.target && self.$el.removeClass( 'ui-dragover' ); } )
.on( 'drop', function( e ) { self._handleDrop( e ); } );
// track current history elements
this.history = {};
@@ -221,7 +228,8 @@ var View = Backbone.View.extend({
hid : item.hid,
keep : item.keep,
label: item.hid + ': ' + item.name,
value: item.id
value: item.id,
tags : item.tags
});
self.history[ item.id + '_' + src ] = item;
});
@@ -259,6 +267,49 @@ var View = Backbone.View.extend({
}
},
/** Handles drop events e.g. from history panel */
_handleDrop: function( ev ) {
try {
var data = this.model.get( 'data' );
var current = this.model.get( 'current' );
var config = this.config[ current ];
var field = this.fields[ current ];
var drop_data = JSON.parse( ev.originalEvent.dataTransfer.getData( 'text' ) )[ 0 ];
var new_id = drop_data.id;
var new_src = drop_data.history_content_type == 'dataset' ? 'hda' : 'hdca';
var new_value = { id: new_id, src: new_src };
if ( data && _.findWhere( data[ new_src ], new_value ) ) {
if ( config.src == new_src ) {
var current_value = field.value();
if ( current_value && config.multiple ) {
if ( current_value.indexOf( new_id ) == -1 ) {
current_value.push( new_id );
}
} else {
current_value = new_id;
}
field.value( current_value );
} else {
this.model.set( 'value', { values: [ new_value ] } );
this.model.trigger( 'change:value' );
}
this.trigger( 'change' );
this._handleDropStatus( 'success' );
} else {
this._handleDropStatus( 'danger' );
}
} catch( e ) {
this._handleDropStatus( 'danger' );
}
},
/** Highlight drag result */
_handleDropStatus: function( status ) {
var self = this;
this.$el.removeClass( 'ui-dragover' ).addClass( 'ui-dragover-' + status );
setTimeout( function() { self.$el.removeClass( 'ui-dragover-' + status ) }, this.model.get( 'statustimer' ) );
},
/** Assists in identifying the batch mode */
_batch: function( result ) {
result[ 'batch' ] = false;
@@ -109,6 +109,11 @@ var View = Backbone.View.extend({
}
},
/** Matches a search term with a given text */
_match: function( term, text ) {
return !term || term == '' || String( text ).toUpperCase().indexOf( term.toUpperCase() ) >= 0
},
/** Updates the selection options */
_changeData: function() {
var self = this;
@@ -125,22 +130,44 @@ var View = Backbone.View.extend({
if ( this.model.get( 'searchable' ) ) {
this.data2 = [];
_.each( this.data, function( option, index ) {
self.data2.push( { order: index, id: option.value, text: option.label } );
self.data2.push( { order: index, id: option.value, text: option.label, tags: option.tags } );
});
this.$select.data( 'select2' ) && this.$select.select2( 'destroy' );
this.matched_tags = {};
this.$select.select2({
data : self.data2,
closeOnSelect : !this.model.get( 'multiple' ),
multiple : this.model.get( 'multiple' ),
query : function( q ) {
self.matched_tags = {};
var pagesize = self.model.get( 'pagesize' );
var results = _.filter( self.data2, function ( e ) {
return !q.term || q.term == '' || e.text.toUpperCase().indexOf( q.term.toUpperCase() ) >= 0;
var found = false;
_.each( e.tags, function( tag ) {
if ( self._match( q.term, tag ) ) {
found = self.matched_tags[ tag ] = true;
}
});
return found || self._match( q.term, e.text );
});
q.callback({
results: results.slice( ( q.page - 1 ) * pagesize, q.page * pagesize ),
more : results.length >= q.page * pagesize
});
},
formatResult : function( result ) {
return _.escape( result.text ) +
'<div class="ui-tags">' +
_.reduce( result.tags, function( memo, tag ) {
if ( self.matched_tags[ tag ] ) {
return memo + '&nbsp;' +
'<div class="label label-info">' +
_.escape( tag ) +
'</div>'
}
return memo;
}, '' ) +
'</div>';
}
});
this.$( '.select2-container .select2-search input' ).off( 'blur' );
+97 -95
View File
@@ -1,105 +1,107 @@
define([ 'utils/utils' ], function( Utils ) {
var View = Backbone.View.extend({
initialize : function( options ) {
var self = this;
this.options = Utils.merge( options, {
id : Utils.uid(),
min : null,
max : null,
step : null,
precise : false,
split : 10000
} );
var View = Backbone.View.extend({
initialize : function( options ) {
var self = this;
this.model = options && options.model || new Backbone.Model({
id : Utils.uid(),
min : null,
max : null,
step : null,
precise : false,
split : 10000,
value : null,
onchange : function(){}
}).set( options );
// create new element
this.setElement( this._template( this.options ) );
// create new element
this.setElement( this._template() );
this.$el.attr( 'id', this.model.id );
this.$text = this.$( '.ui-form-slider-text' );
this.$slider = this.$( '.ui-form-slider-element' );
// determine wether to use the slider
this.useslider = this.options.max !== null && this.options.min !== null && this.options.max > this.options.min;
// set default step size
if ( this.options.step === null ) {
this.options.step = 1.0;
if ( this.options.precise && this.useslider ) {
this.options.step = ( this.options.max - this.options.min ) / this.options.split;
}
}
// create slider if min and max are defined properly
if ( this.useslider ) {
this.$slider = this.$( '#slider' );
this.$slider.slider( this.options );
this.$slider.on( 'slide', function ( event, ui ) {
self.value( ui.value );
// add text field event
var pressed = [];
this.$text.on( 'change', function () {
self.value( $( this ).val() );
}).on( 'keyup', function( e ) {
pressed[e.which] = false;
}).on( 'keydown', function ( e ) {
var v = e.which;
pressed[ v ] = true;
if ( self.model.get( 'is_workflow' ) && pressed[ 16 ] && v == 52 ) {
self.value( '$' );
event.preventDefault();
} else if (!( v == 8 || v == 9 || v == 13 || v == 37 || v == 39 || ( v >= 48 && v <= 57 && !pressed[ 16 ] ) || ( v >= 96 && v <= 105 )
|| ( ( v == 190 || v == 110 ) && $( this ).val().indexOf( '.' ) == -1 && self.model.get( 'precise' ) )
|| ( ( v == 189 || v == 109 ) && $( this ).val().indexOf( '-' ) == -1 )
|| self._isParameter( $( this ).val() )
|| pressed[ 91 ] || pressed[ 17 ] ) ) {
event.preventDefault();
}
});
} else {
this.$( '.ui-form-slider-text' ).css( 'width', '100%' );
}
// link text input field
this.$text = this.$( '#text' );
// set initial value
this.options.value !== undefined && ( this.value( this.options.value ) );
// add text field event
var pressed = [];
this.$text.on( 'change', function () {
self.value( $( this ).val() );
});
this.$text.on( 'keyup', function( e ) {
pressed[e.which] = false;
self.options.onchange && self.options.onchange( $( this ).val() );
});
this.$text.on( 'keydown', function ( e ) {
var v = e.which;
pressed[ v ] = true;
if ( self.options.is_workflow && pressed[ 16 ] && v == 52 ) {
self.value( '$' )
event.preventDefault();
} else if (!( v == 8 || v == 9 || v == 13 || v == 37 || v == 39 || ( v >= 48 && v <= 57 && !pressed[ 16 ] ) || ( v >= 96 && v <= 105 )
|| ( ( v == 190 || v == 110 ) && $( this ).val().indexOf( '.' ) == -1 && self.options.precise )
|| ( ( v == 189 || v == 109 ) && $( this ).val().indexOf( '-' ) == -1 )
|| self._isParameter( $( this ).val() )
|| pressed[ 91 ] || pressed[ 17 ] ) ) {
event.preventDefault();
// build slider, cannot be rebuild in render
var opts = this.model.attributes;
this.has_slider = opts.max !== null && opts.min !== null && opts.max > opts.min;
var step = opts.step;
if ( !step ) {
if ( opts.precise && this.has_slider ) {
step = ( opts.max - opts.min ) / opts.split;
} else {
step = 1.0;
}
}
});
},
/** Set and Return the current value
*/
value : function ( new_val ) {
if ( new_val !== undefined ) {
if ( new_val !== null && new_val !== '' && !this._isParameter( new_val ) ) {
isNaN( new_val ) && ( new_val = 0 );
this.options.max !== null && ( new_val = Math.min( new_val, this.options.max ) );
this.options.min !== null && ( new_val = Math.max( new_val, this.options.min ) );
if ( this.has_slider ) {
this.$text.addClass( 'ui-form-slider-left' );
this.$slider.slider( { min: opts.min, max: opts.max, step: step } )
.on( 'slide', function ( event, ui ) { self.value( ui.value ) } );
} else {
this.$slider.hide();
}
this.$slider && this.$slider.slider( 'value', new_val );
this.$text.val( new_val );
this.options.onchange && this.options.onchange( new_val );
// add listeners
this.listenTo( this.model, 'change', this.render, this );
this.render();
},
render: function() {
var value = this.model.get( 'value' );
this.has_slider && this.$slider.slider( 'value', value );
value !== this.$text.val() && this.$text.val( value );
},
/** Set and return the current value */
value : function ( new_val ) {
var options = this.model.attributes;
if ( new_val !== undefined ) {
if ( new_val !== null && new_val !== '' && !this._isParameter( new_val ) ) {
isNaN( new_val ) && ( new_val = 0 );
!options.precise && ( new_val = Math.round( new_val ) );
options.max !== null && ( new_val = Math.min( new_val, options.max ) );
options.min !== null && ( new_val = Math.max( new_val, options.min ) );
}
this.model.set( 'value', new_val );
this.model.trigger( 'change' );
options.onchange( new_val );
}
return this.model.get( 'value' );
},
/** Return true if the field contains a workflow parameter i.e. $('name') */
_isParameter: function( value ) {
return this.model.get( 'is_workflow' ) && String( value ).substring( 0, 1 ) === '$';
},
/** Slider template */
_template: function() {
return '<div class="ui-form-slider">' +
'<input class="ui-form-slider-text" type="text"/>' +
'<div class="ui-form-slider-element"/>' +
'</div>';
}
return this.$text.val();
},
});
/** Return true if the field contains a workflow parameter i.e. $('name')
*/
_isParameter: function( value ) {
return this.options.is_workflow && String( value ).substring( 0, 1 ) === '$';
},
/** Slider template
*/
_template: function( options ) {
return '<div id="' + options.id + '" class="ui-form-slider">' +
'<input id="text" type="text" class="ui-form-slider-text"/>' +
'<div id="slider" class="ui-form-slider-element"/>' +
'</div>';
}
});
return {
View : View
};
return {
View : View
};
});
+169 -218
View File
@@ -1,228 +1,179 @@
// dependencies
define(['utils/utils'], function(Utils) {
/** This class creates a ui table element. */
define( [ 'utils/utils' ], function( Utils ) {
var View = Backbone.View.extend({
initialize : function(options) {
this.options = Utils.merge(options, {
content : 'No content available.',
onchange : null,
ondblclick : null,
onconfirm : null,
cls : 'ui-table',
selectable : true,
cls_tr : ''
});
this.setElement( this._template() );
this.$thead = this.$('thead');
this.$tbody = this.$('tbody');
this.$tmessage = this.$('tmessage');
this.row = this._row();
this.row_count = 0;
},
/**
* This class creates a ui table element.
*/
var View = Backbone.View.extend({
// current row
row: null,
// count rows
row_count: 0,
// defaults options
optionsDefault: {
content : 'No content available.',
onchange : null,
ondblclick : null,
onconfirm : null,
cls : 'ui-table',
cls_tr : ''
},
// events
events : {
'click' : '_onclick',
'dblclick' : '_ondblclick'
},
// initialize
initialize : function(options) {
// configure options
this.options = Utils.merge(options, this.optionsDefault);
// create new element
var $el = $(this._template(this.options));
// link sub-elements
this.$thead = $el.find('thead');
this.$tbody = $el.find('tbody');
this.$tmessage = $el.find('tmessage');
// set element
this.setElement($el);
// initialize row
this.row = this._row();
},
// add header cell
addHeader: function($el) {
var wrapper = $('<th></th>');
wrapper.append($el);
this.row.append(wrapper);
},
// header
appendHeader: function() {
// append header row
this.$thead.append(this.row);
events : {
'click' : '_onclick',
'dblclick' : '_ondblclick'
},
// row
this.row = $('<tr></tr>');
},
// add row cell
add: function($el, width, align) {
var wrapper = $('<td></td>');
if (width) {
wrapper.css('width', width);
}
if (align) {
wrapper.css('text-align', align);
}
wrapper.append($el);
this.row.append(wrapper);
},
// append
append: function(id, fade) {
this._commit(id, fade, false);
},
// prepend
prepend: function(id, fade) {
this._commit(id, fade, true);
},
// get element
get: function(id) {
return this.$el.find('#' + id);
},
// delete
del: function(id) {
var item = this.$tbody.find('#' + id);
if (item.length > 0) {
item.remove();
this.row_count--;
/** Add cell to header row */
addHeader: function( $el ) {
this.row.append( $( '<th/>' ).append( $el ) );
},
/** Append header row to table */
appendHeader: function() {
this.$thead.append( this.row );
this.row = $( '<tr/>' );
},
/** Add cell to row */
add: function($el, width, align) {
var wrapper = $( '<td/>' );
if ( width ) {
wrapper.css( 'width', width );
}
if ( align ) {
wrapper.css( 'text-align', align );
}
this.row.append( wrapper.append( $el ) );
},
/** Append row to table */
append: function( id, fade ) {
this._commit( id, fade, false );
},
/** Prepend row to table */
prepend: function( id, fade ) {
this._commit( id, fade, true );
},
/** Helper to get row element */
get: function( id ) {
return this.$( '#' + id );
},
/** Delete row by id */
del: function( id ) {
var item = this.$tbody.find( '#' + id );
if ( item.length > 0 ) {
item.remove();
this.row_count--;
this._refresh();
}
},
/** Delete all rows */
delAll: function() {
this.$tbody.empty();
this.row_count = 0;
this._refresh();
}
},
},
// delete all
delAll: function() {
this.$tbody.empty();
this.row_count = 0;
this._refresh();
},
// value
value: function(new_value) {
// get current id/value
this.before = this.$tbody.find('.current').attr('id');
// check if new_value is defined
if (new_value !== undefined) {
this.$tbody.find('tr').removeClass('current');
if (new_value) {
this.$tbody.find('#' + new_value).addClass('current');
}
}
// get current id/value
var after = this.$tbody.find('.current').attr('id');
if(after === undefined) {
return null;
} else {
// fire onchange
if (after != this.before && this.options.onchange) {
this.options.onchange(new_value);
}
// return current value
return after;
}
},
// size
size: function() {
return this.$tbody.find('tr').length;
},
// commit
_commit: function(id, fade, prepend) {
// remove previous item with same id
this.del(id);
// add
this.row.attr('id', id);
// add row
if (prepend) {
this.$tbody.prepend(this.row);
} else {
this.$tbody.append(this.row);
}
// fade mode
if (fade) {
this.row.hide();
this.row.fadeIn();
}
// row
this.row = this._row();
// row count
this.row_count++;
this._refresh();
},
// create new row
_row: function() {
return $('<tr class="' + this.options.cls_tr + '"></tr>');
},
// onclick
_onclick: function(e) {
// get values
var old_value = this.value();
var new_value = $(e.target).closest('tr').attr('id');
if (new_value != ''){
// check equality
if (new_value && old_value != new_value) {
if (this.options.onconfirm) {
this.options.onconfirm(new_value);
/** Set a value i.e. selects/highlights a particular row by id */
value: function( new_value ) {
if ( this.options.selectable ) {
this.before = this.$tbody.find( '.current' ).attr( 'id' );
if ( new_value !== undefined ) {
this.$tbody.find( 'tr' ).removeClass( 'current' );
if ( new_value ) {
this.$tbody.find( '#' + new_value ).addClass( 'current' );
}
}
var after = this.$tbody.find( '.current' ).attr( 'id' );
if ( after === undefined ) {
return null;
} else {
this.value(new_value);
if ( after != this.before && this.options.onchange ) {
this.options.onchange( new_value );
}
return after;
}
}
}
},
},
// ondblclick
_ondblclick: function(e) {
var value = this.value();
if (value && this.options.ondblclick) {
this.options.ondblclick(value);
/** Return the number of rows */
size: function() {
return this.$tbody.find( 'tr' ).length;
},
/** Helper to append rows */
_commit: function( id, fade, prepend ) {
this.del( id );
this.row.attr( 'id', id );
if ( prepend ) {
this.$tbody.prepend( this.row );
} else {
this.$tbody.append( this.row );
}
if ( fade ) {
this.row.hide();
this.row.fadeIn();
}
this.row = this._row();
this.row_count++;
this._refresh();
},
/** Helper to create new row */
_row: function() {
return $( '<tr class="' + this.options.cls_tr + '"></tr>' );
},
/** Handles onclick events */
_onclick: function(e) {
var old_value = this.value();
var new_value = $( e.target ).closest( 'tr' ).attr( 'id' );
if ( new_value != '' ){
if ( new_value && old_value != new_value ) {
if ( this.options.onconfirm ) {
this.options.onconfirm( new_value );
} else {
this.value( new_value );
}
}
}
},
/** Handles ondblclick events */
_ondblclick: function( e ) {
var value = this.value();
if ( value && this.options.ondblclick ) {
this.options.ondblclick( value );
}
},
/** Refresh helper */
_refresh: function() {
if ( this.row_count == 0 ) {
this.$tmessage.show();
} else {
this.$tmessage.hide();
}
},
/** Template */
_template: function() {
return '<div>' +
'<table class="' + this.options.cls + '">' +
'<thead/>' +
'<tbody/>' +
'</table>' +
'<tmessage>' + this.options.content + '</tmessage>' +
'<div>';
}
},
// refresh
_refresh: function() {
if (this.row_count == 0) {
this.$tmessage.show();
} else {
this.$tmessage.hide();
}
},
// load html template
_template: function(options) {
return '<div>' +
'<table class="' + options.cls + '">' +
'<thead></thead>' +
'<tbody></tbody>' +
'</table>' +
'<tmessage>' + options.content + '</tmessage>' +
'<div>';
});
return {
View: View
}
});
return {
View: View
}
});
});
@@ -0,0 +1,163 @@
/** Renders the collection uploader rows */
define( [ 'utils/utils', 'mvc/upload/upload-model', 'mvc/upload/upload-settings', 'mvc/ui/ui-popover', 'mvc/ui/ui-select' ],
function( Utils, UploadModel, UploadSettings, Popover, Select ) {
return Backbone.View.extend({
/** Dictionary of upload states and associated icons */
status_classes : {
init : 'upload-icon-button fa fa-trash-o',
queued : 'upload-icon fa fa-spinner fa-spin',
running : 'upload-icon fa fa-spinner fa-spin',
success : 'upload-icon-button fa fa-check',
error : 'upload-icon-button fa fa-exclamation-triangle'
},
initialize: function( app, options ) {
var self = this;
this.app = app;
this.model = options.model;
this.setElement( this._template( options.model ) );
this.$mode = this.$( '.upload-mode' );
this.$title = this.$( '.upload-title-extended' );
this.$text = this.$( '.upload-text' );
this.$size = this.$( '.upload-size' );
this.$info_text = this.$( '.upload-info-text' );
this.$info_progress = this.$( '.upload-info-progress' );
this.$text_content = this.$( '.upload-text-content' );
this.$symbol = this.$( '.upload-symbol' );
this.$progress_bar = this.$( '.upload-progress-bar' );
this.$percentage = this.$( '.upload-percentage' );
// append popup to settings icon
this.settings = new Popover.View({
title : 'Upload configuration',
container : this.$( '.upload-settings' ),
placement : 'bottom'
});
// identify default genome and extension values
var default_genome = this.app.select_genome.value();
var default_extension = this.app.select_extension.value();
// handle click event
this.$symbol.on('click', function() { self._removeRow(); });
// handle text editing event
this.$text_content.on( 'change input', function( e ) {
self.model.set( { 'url_paste': $( e.target ).val(),
'file_size': $( e.target ).val().length } );
});
// model events
this.listenTo( this.model, 'change:percentage', function() { self._refreshPercentage() } );
this.listenTo( this.model, 'change:status', function() { self._refreshStatus() } );
this.listenTo( this.model, 'change:info', function() { self._refreshInfo() } );
this.listenTo( this.model, 'change:file_size', function() { self._refreshFileSize() } );
this.listenTo( this.model, 'remove', function() { self.remove() } );
this.app.collection.on('reset', function() { self.remove() } );
},
render: function() {
var options = this.model.attributes;
this.$title.html( _.escape( options.file_name ) );
this.$size.html( Utils.bytesToString ( options.file_size ) );
this.$mode.removeClass().addClass( 'upload-mode' ).addClass( 'text-primary' );
if ( options.file_mode == 'new' ) {
this.$text.css( { 'width' : this.$el.width() - 16 + 'px', 'top' : this.$el.height() - 8 + 'px' } ).show();
this.$el.height( this.$el.height() - 8 + this.$text.height() + 16 );
this.$mode.addClass( 'fa fa-edit' );
} else if ( options.file_mode == 'local' ) {
this.$mode.addClass( 'fa fa-laptop' );
} else if ( options.file_mode == 'ftp' ) {
this.$mode.addClass( 'fa fa-folder-open-o' );
}
},
/** Refresh info text */
_refreshInfo: function() {
var info = this.model.get( 'info' );
if ( info ) {
this.$info_text.html( '<strong>Failed: </strong>' + info ).show();
} else {
this.$info_text.hide();
}
},
/** Refresh percentage status */
_refreshPercentage : function() {
var percentage = parseInt( this.model.get( 'percentage' ) );
this.$progress_bar.css( { width : percentage + '%' } );
this.$percentage.html( percentage != 100 ? percentage + '%' : 'Adding to history...' );
},
/** Refresh status */
_refreshStatus : function() {
var status = this.model.get( 'status' );
this.$symbol.removeClass().addClass( 'upload-symbol' ).addClass( this.status_classes[ status ] );
this.model.set( 'enabled', status == 'init' );
var enabled = this.model.get( 'enabled' );
this.$text_content.attr( 'disabled', !enabled );
if ( status == 'success' ) {
this.$el.addClass( 'success' );
this.$percentage.html( '100%' );
}
if ( status == 'error' ) {
this.$el.addClass( 'danger' );
this.$info_progress.hide();
}
},
/** Refresh file size */
_refreshFileSize: function() {
this.$size.html( Utils.bytesToString ( this.model.get( 'file_size' ) ) );
},
/** Remove row */
_removeRow: function() {
if ( [ 'init', 'success', 'error' ].indexOf( this.model.get( 'status' ) ) !== -1 ) {
this.app.collection.remove( this.model );
}
},
/** Attach file info popup */
_showSettings : function() {
if ( !this.settings.visible ) {
this.settings.empty();
this.settings.append( ( new UploadSettings( this ) ).$el );
this.settings.show();
} else {
this.settings.hide();
}
},
/** View template */
_template: function( options ) {
return '<tr id="upload-row-' + options.id + '" class="upload-row">' +
'<td>' +
'<div class="upload-text-column">' +
'<div class="upload-mode"/>' +
'<div class="upload-title-extended"/>' +
'<div class="upload-text">' +
'<div class="upload-text-info">You can tell Galaxy to download data from web by entering URL in this box (one per line). You can also directly paste the contents of a file.</div>' +
'<textarea class="upload-text-content form-control"/>' +
'</div>' +
'</div>' +
'</td>' +
'<td>' +
'<div class="upload-size"/>' +
'</td>' +
'<td>' +
'<div class="upload-info">' +
'<div class="upload-info-text"/>' +
'<div class="upload-info-progress progress">' +
'<div class="upload-progress-bar progress-bar progress-bar-success"/>' +
'<div class="upload-percentage">0%</div>' +
'</div>' +
'</div>' +
'</td>' +
'<td>' +
'<div class="upload-symbol ' + this.status_classes.init + '"/>' +
'</td>' +
'</tr>';
}
});
});
@@ -0,0 +1,396 @@
/** Renders contents of the collection uploader */
define([ 'utils/utils', 'mvc/upload/upload-model', 'mvc/upload/collection/collection-row', 'mvc/upload/upload-ftp', 'mvc/ui/ui-popover', 'mvc/ui/ui-select', 'mvc/ui/ui-misc', 'mvc/collection/list-collection-creator', 'utils/uploadbox' ],
function( Utils, UploadModel, UploadRow, UploadFtp, Popover, Select, Ui, LIST_COLLECTION_CREATOR ) {
return Backbone.View.extend({
// current upload size in bytes
upload_size: 0,
// contains upload row models
collection : new UploadModel.Collection(),
// keeps track of the current uploader state
counter : {
announce : 0,
success : 0,
error : 0,
running : 0,
reset : function() { this.announce = this.success = this.error = this.running = 0 }
},
initialize : function( app ) {
var self = this;
this.app = app;
this.options = app.options;
this.list_extensions = app.list_extensions;
this.list_genomes = app.list_genomes;
this.ui_button = app.ui_button;
this.ftp_upload_site = app.currentFtp();
this.setElement( this._template() );
// append buttons to dom
this.btnLocal = new Ui.Button( { id: 'btn-local', title: 'Choose local files', onclick: function() { self.uploadbox.select() }, icon: 'fa fa-laptop' } );
this.btnFtp = new Ui.Button( { id: 'btn-ftp', title: 'Choose FTP files', onclick: function() { self._eventFtp() }, icon: 'fa fa-folder-open-o' } );
this.btnCreate = new Ui.Button( { id: 'btn-new', title: 'Paste/Fetch data', onclick: function() { self._eventCreate() }, icon: 'fa fa-edit' } );
this.btnStart = new Ui.Button( { id: 'btn-start', title: 'Start', onclick: function() { self._eventStart() } } );
this.btnBuild = new Ui.Button( { id: 'btn-build', title: 'Build', onclick: function() { self._eventBuild() } } );
this.btnStop = new Ui.Button( { id: 'btn-stop', title: 'Pause', onclick: function() { self._eventStop() } } );
this.btnReset = new Ui.Button( { id: 'btn-reset', title: 'Reset', onclick: function() { self._eventReset() } } );
this.btnClose = new Ui.Button( { id: 'btn-close', title: 'Close', onclick: function() { self.app.modal.hide() } } );
_.each( [ this.btnLocal, this.btnFtp, this.btnCreate, this.btnStop, this.btnReset, this.btnStart, this.btnBuild, this.btnClose ], function( button ) {
self.$( '.upload-buttons' ).prepend( button.$el );
});
// file upload
this.uploadbox = this.$( '.upload-box' ).uploadbox({
url : this.app.options.nginx_upload_path,
announce : function( index, file ) { self._eventAnnounce( index, file ) },
initialize : function( index ) { return self.app.toData( [ self.collection.get( index ) ], self.history_id ) },
progress : function( index, percentage ) { self._eventProgress( index, percentage ) },
success : function( index, message ) { self._eventSuccess( index, message ) },
error : function( index, message ) { self._eventError( index, message ) },
complete : function() { self._eventComplete() },
ondragover : function() { self.$( '.upload-box' ).addClass( 'highlight' ) },
ondragleave : function() { self.$( '.upload-box' ).removeClass( 'highlight' ) }
});
console.log(this.list_extensions);
// add ftp file viewer
this.ftp = new Popover.View( { title: 'FTP files', container: this.btnFtp.$el } );
// select extension
this.select_extension = new Select.View({
css : 'upload-footer-selection-compressed',
container : this.$( '.upload-footer-extension' ),
data : _.filter( this.list_extensions, function( ext ) { return !ext.composite_files } ),
value : this.options.default_extension,
onchange : function( extension ) { self.updateExtension( extension ) }
});
this.collectionType = "list";
this.select_collection = new Select.View({
css : 'upload-footer-selection-compressed',
container : this.$( '.upload-footer-collection-type' ),
data : [{"id": "list", "text": "List"}, {"id": "paired", "text": "Paired"}, {"id": "list:paired", "text": "List of Pairs"}],
value : "list",
onchange : function( collectionType ) { self.updateCollectionType( collectionType ) }
})
// handle extension info popover
this.$( '.upload-footer-extension-info' ).on( 'click', function( e ) {
self.showExtensionInfo({
$el : $( e.target ),
title : self.select_extension.text(),
extension : self.select_extension.value(),
placement : 'top'
});
}).on( 'mousedown', function( e ) { e.preventDefault() } );
// genome extension
this.select_genome = new Select.View({
css : 'upload-footer-selection',
container : this.$( '.upload-footer-genome' ),
data : this.list_genomes,
value : this.options.default_genome,
onchange : function( genome ) { self.updateGenome(genome) }
});
// events
this.collection.on( 'remove', function( model ) { self._eventRemove( model ) } );
this._updateScreen();
},
/** A new file has been dropped/selected through the uploadbox plugin */
_eventAnnounce: function( index, file ) {
this.counter.announce++;
var new_model = new UploadModel.Model({
id : index,
file_name : file.name,
file_size : file.size,
file_mode : file.mode || 'local',
file_path : file.path,
file_data : file,
extension : this.select_extension.value(),
genome : this.select_genome.value()
});
this.collection.add( new_model );
var upload_row = new UploadRow( this, { model: new_model } );
this.$( '.upload-table > tbody:first' ).append( upload_row.$el );
this._updateScreen();
upload_row.render();
},
/** Progress */
_eventProgress: function( index, percentage ) {
var it = this.collection.get( index );
it.set( 'percentage', percentage );
this.ui_button.model.set( 'percentage', this._uploadPercentage( percentage, it.get( 'file_size' ) ) );
},
/** Success */
_eventSuccess: function( index, message ) {
// var hdaId = message["outputs"][0]["id"];
var hids = _.pluck(message["outputs"], "hid");
var it = this.collection.get( index );
it.set( { 'percentage': 100, 'status': 'success', 'hids': hids } );
this.ui_button.model.set( 'percentage', this._uploadPercentage( 100, it.get( 'file_size' ) ) );
this.upload_completed += it.get( 'file_size' ) * 100;
this.counter.announce--;
this.counter.success++;
this._updateScreen();
Galaxy.currHistoryPanel.refreshContents();
},
/** Error */
_eventError: function( index, message ) {
var it = this.collection.get( index );
it.set( { 'percentage': 100, 'status': 'error', 'info': message } );
this.ui_button.model.set( { 'percentage': this._uploadPercentage( 100, it.get( 'file_size' ) ), 'status': 'danger' } );
this.upload_completed += it.get( 'file_size' ) * 100;
this.counter.announce--;
this.counter.error++;
this._updateScreen();
},
/** Queue is done */
_eventComplete: function() {
this.collection.each( function( model ) { model.get( 'status' ) == 'queued' && model.set( 'status', 'init' ) } );
this.counter.running = 0;
this._updateScreen();
},
_eventBuild: function() {
var allHids = [];
_.forEach( this.collection.models, function( upload ) { allHids.push.apply(allHids, upload.get( 'hids' )); } );
var models = _.map( allHids, function( hid ) { return Galaxy.currHistoryPanel.collection.getByHid( hid ) } );
var selection = new Galaxy.currHistoryPanel.collection.constructor( models );
// I'm building the selection wrong because I need to set this historyId directly.
selection.historyId = Galaxy.currHistoryPanel.collection.historyId;
Galaxy.currHistoryPanel.buildCollection( this.collectionType, selection, true );
this.counter.running = 0;
this._updateScreen();
this._eventReset();
this.app.modal.hide();
},
/** Remove model from upload list */
_eventRemove: function( model ) {
var status = model.get( 'status' );
if ( status == 'success' ) {
this.counter.success--;
} else if ( status == 'error' ) {
this.counter.error--;
} else {
this.counter.announce--;
}
this.uploadbox.remove( model.id );
this._updateScreen();
},
//
// events triggered by this view
//
/** [public] display extension info popup */
showExtensionInfo: function( options ) {
var self = this;
var $el = options.$el;
var extension = options.extension;
var title = options.title;
var description = _.findWhere( self.list_extensions, { 'id': extension } );
this.extension_popup && this.extension_popup.remove();
this.extension_popup = new Popover.View({ placement: options.placement || 'bottom', container: $el } );
this.extension_popup.title( title );
this.extension_popup.empty();
this.extension_popup.append( this._templateDescription( description ) );
this.extension_popup.show();
},
/** Show/hide ftp popup */
_eventFtp: function() {
if ( !this.ftp.visible ) {
this.ftp.empty();
var self = this;
this.ftp.append( ( new UploadFtp({
collection : this.collection,
ftp_upload_site : this.ftp_upload_site,
onadd : function( ftp_file ) {
self.uploadbox.add([{
mode: 'ftp',
name: ftp_file.path,
size: ftp_file.size,
path: ftp_file.path
}]);
},
onremove: function( model_index ) {
self.collection.remove( model_index );
}
} ) ).$el );
this.ftp.show();
} else {
this.ftp.hide();
}
},
/** Create a new file */
_eventCreate: function (){
this.uploadbox.add( [ { name: 'New File', size: 0, mode: 'new' } ] );
},
/** Start upload process */
_eventStart: function() {
if ( this.counter.announce == 0 || this.counter.running > 0 ) {
return;
}
var self = this;
this.upload_size = 0;
this.upload_completed = 0;
this.collection.each( function( model ) {
if( model.get( 'status' ) == 'init' ) {
model.set( 'status', 'queued' );
self.upload_size += model.get( 'file_size' );
}
});
this.ui_button.model.set( { 'percentage': 0, 'status': 'success' } );
this.counter.running = this.counter.announce;
this.history_id = this.app.currentHistory();
this.uploadbox.start();
this._updateScreen();
},
/** Pause upload process */
_eventStop: function() {
if ( this.counter.running > 0 ) {
this.ui_button.model.set( 'status', 'info' );
$( '.upload-top-info' ).html( 'Queue will pause after completing the current file...' );
this.uploadbox.stop();
}
},
/** Remove all */
_eventReset: function() {
if ( this.counter.running == 0 ){
this.collection.reset();
this.counter.reset();
this.uploadbox.reset();
this.select_extension.value( this.options.default_extension );
this.select_genome.value( this.options.default_genome );
this.ui_button.model.set( 'percentage', 0 );
this._updateScreen();
}
},
/** Update extension for all models */
updateExtension: function( extension, defaults_only ) {
var self = this;
this.collection.each( function( model ) {
if ( model.get( 'status' ) == 'init' && ( model.get( 'extension' ) == self.options.default_extension || !defaults_only ) ) {
model.set( 'extension', extension );
}
});
},
/** Update collection type */
updateCollectionType: function( collectionType ) {
var self = this;
this.collectionType = collectionType;
},
/** Update genome for all models */
updateGenome: function( genome, defaults_only ) {
var self = this;
this.collection.each( function( model ) {
if ( model.get( 'status' ) == 'init' && ( model.get( 'genome' ) == self.options.default_genome || !defaults_only ) ) {
model.set( 'genome', genome );
}
});
},
/** Set screen */
_updateScreen: function () {
var message = '';
if( this.counter.announce == 0 ) {
if (this.uploadbox.compatible()) {
message = '&nbsp;';
} else {
message = 'Browser does not support Drag & Drop. Try Firefox 4+, Chrome 7+, IE 10+, Opera 12+ or Safari 6+.';
}
} else {
if ( this.counter.running == 0 ) {
message = 'You added ' + this.counter.announce + ' file(s) to the queue. Add more files or click \'Start\' to proceed.';
} else {
message = 'Please wait...' + this.counter.announce + ' out of ' + this.counter.running + ' remaining.';
}
}
this.$( '.upload-top-info' ).html( message );
var enable_reset = this.counter.running == 0 && this.counter.announce + this.counter.success + this.counter.error > 0;
var enable_start = this.counter.running == 0 && this.counter.announce > 0;
var enable_build = this.counter.running == 0 && this.counter.announce == 0 && this.counter.success > 0 && this.counter.error == 0
var enable_sources = this.counter.running == 0;
var show_table = this.counter.announce + this.counter.success + this.counter.error > 0;
this.btnReset[ enable_reset ? 'enable' : 'disable' ]();
this.btnStart[ enable_start ? 'enable' : 'disable' ]();
this.btnStart.$el[ enable_start ? 'addClass' : 'removeClass' ]( 'btn-primary' );
this.btnBuild[ enable_build ? 'enable' : 'disable' ]();
this.btnBuild.$el[ enable_build ? 'addClass' : 'removeClass' ]( 'btn-primary' );
this.btnStop[ this.counter.running > 0 ? 'enable' : 'disable' ]();
this.btnLocal[ enable_sources ? 'enable' : 'disable' ]();
this.btnFtp[ enable_sources ? 'enable' : 'disable' ]();
this.btnCreate[ enable_sources ? 'enable' : 'disable' ]();
this.btnFtp.$el[ this.ftp_upload_site ? 'show' : 'hide' ]();
this.$( '.upload-table' )[ show_table ? 'show' : 'hide' ]();
this.$( '.upload-helper' )[ show_table ? 'hide' : 'show' ]();
},
/** Calculate percentage of all queued uploads */
_uploadPercentage: function( percentage, size ) {
return ( this.upload_completed + ( percentage * size ) ) / this.upload_size;
},
/** Template for extensions description */
_templateDescription: function( options ) {
if ( options.description ) {
var tmpl = options.description;
if ( options.description_url ) {
tmpl += '&nbsp;(<a href="' + options.description_url + '" target="_blank">read more</a>)';
}
return tmpl;
} else {
return 'There is no description available for this file extension.';
}
},
/** Template */
_template: function() {
return '<div class="upload-view-default">' +
'<div class="upload-top">' +
'<h6 class="upload-top-info"/>' +
'</div>' +
'<div class="upload-box">' +
'<div class="upload-helper"><i class="fa fa-files-o"/>Drop files here</div>' +
'<table class="upload-table ui-table-striped" style="display: none;">' +
'<thead>' +
'<tr>' +
'<th>Name</th>' +
'<th>Size</th>' +
'<th>Status</th>' +
'<th/>' +
'</tr>' +
'</thead>' +
'<tbody/>' +
'</table>' +
'</div>' +
'<div class="upload-footer">' +
'<span class="upload-footer-title-compressed">Collection Type:</span>' +
'<span class="upload-footer-collection-type"/>' +
'<span class="upload-footer-title-compressed">File Type:</span>' +
'<span class="upload-footer-extension"/>' +
'<span class="upload-footer-extension-info upload-icon-button fa fa-search"/> ' +
'<span class="upload-footer-title-compressed">Genome (set all):</span>' +
'<span class="upload-footer-genome"/>' +
'</div>' +
'<div class="upload-buttons"/>' +
'</div>';
}
});
});
@@ -106,7 +106,7 @@ define( [ 'utils/utils' ], function( Utils ) {
_templateRow: function( options ) {
return '<tr class="upload-ftp-row">' +
'<td class="_has_collection" style="display: none;"><div class="icon"/></td>' +
'<td class="ftp-name">' + options.path + '</td>' +
'<td class="ftp-name">' + _.escape(options.path) + '</td>' +
'<td class="ftp-size">' + Utils.bytesToString( options.size ) + '</td>' +
'<td class="ftp-time">' + options.ctime + '</td>' +
'</tr>';
@@ -1,6 +1,6 @@
/** Upload app contains the upload progress button and upload modal, compiles model data for API request **/
define([ 'utils/utils', 'mvc/ui/ui-modal', 'mvc/ui/ui-tabs', 'mvc/upload/upload-button', 'mvc/upload/default/default-view', 'mvc/upload/composite/composite-view'],
function( Utils, Modal, Tabs, UploadButton, UploadViewDefault, UploadViewComposite ) {
define([ 'utils/utils', 'mvc/ui/ui-modal', 'mvc/ui/ui-tabs', 'mvc/upload/upload-button', 'mvc/upload/default/default-view', 'mvc/upload/composite/composite-view', 'mvc/upload/collection/collection-view'],
function( Utils, Modal, Tabs, UploadButton, UploadViewDefault, UploadViewComposite, UploadViewCollection ) {
return Backbone.View.extend({
options : {
nginx_upload_path : '',
@@ -109,6 +109,12 @@ function( Utils, Modal, Tabs, UploadButton, UploadViewDefault, UploadViewComposi
title : 'Composite',
$el : this.composite_view.$el
});
this.collection_view = new UploadViewCollection( this );
this.tabs.add({
id : 'collection',
title : 'Collection',
$el : this.collection_view.$el
})
this.modal = new Modal.View({
title : 'Download from web or upload from disk',
body : this.tabs.$el,
@@ -0,0 +1,223 @@
/** This class renders the chart configuration form. */
define( [ 'utils/utils', 'mvc/ui/ui-misc', 'mvc/form/form-view', 'mvc/ui/ui-table' ], function( Utils, Ui, Form, Table ) {
var Collection = Backbone.Collection.extend({
comparator: function( a, b ) {
a = a.get( 'name' );
b = b.get( 'name' );
return a > b ? 1 : a < b ? -1 : 0;
}
});
var View = Backbone.View.extend({
initialize: function( options ) {
var self = this;
var history_id = Galaxy.currHistoryPanel && Galaxy.currHistoryPanel.model.id;
this.model = new Backbone.Model();
this.model.url = Galaxy.root + 'api/histories/' + history_id + '/custom_builds_metadata';
this.collection = new Collection();
this.collection.url = Galaxy.root + 'api/users/' + Galaxy.user.id + '/custom_builds';
this.message = new Ui.Message({});
this.installed_builds = new Ui.Select.View({
optional : true,
onchange : function() { self.installed_builds.value( null ) },
empty_text : 'List of available builds:',
error_text : 'No system installed builds available.'
});
this.table = new Table.View( { cls: 'grid', selectable: false } );
this.table.addHeader( 'Name' );
this.table.addHeader( 'Key' );
this.table.addHeader( 'Number of chroms/contigs' );
this.table.addHeader( '' );
this.table.appendHeader();
this.setElement( $( '<div/>' ).append( $( '<h4/>' ).text( 'Current Custom Builds' ) )
.append( this.table.$el )
.append( this.$installed = $( '<div/>' ).append( $( '<h4/>' ).text( 'System Installed Builds' ).addClass( 'ui-margin-top' ) )
.append( this.installed_builds.$el ) )
.append( $( '<h4/>' ).text( 'Add a Custom Build' ).addClass( 'ui-margin-top' ) )
.append( $( '<span/>').addClass( 'ui-column' )
.append( $( '<div/>' ).addClass( 'ui-column-left' )
.append( this.message.$el )
.append( this.$form = $( '<div/>' ).addClass( 'ui-margin-top' ) ) )
.append( this.$help = $( '<div/>' ).addClass( 'ui-column-right' ) ) ) );
this.listenTo( this.collection, 'add remove reset', function() { self._renderTable() } );
this.listenTo( this.model, 'change', function() { self._renderForm() } );
this.collection.fetch();
this.model.fetch();
},
render: function() {
this._renderTable();
this._renderForm();
},
_renderTable: function() {
var self = this;
this.table.delAll();
this.collection.sort();
this.collection.each( function( model ) {
self.table.add( model.id );
self.table.add( model.get( 'name' ) );
self.table.add( model.get( 'count' ) !== undefined ? model.get( 'count' ) : 'Processing...' );
self.table.add( ( new Ui.ButtonIcon({
icon : 'fa-trash-o',
cls : 'ui-button-icon-plain',
tooltip : 'Delete custom build.',
onclick : function() { model.destroy() }
} ) ).$el );
self.table.append( model.id );
});
},
_renderForm: function() {
var self = this;
var initial_type = 'fasta';
var form = new Form({
inputs : [{
type : 'text',
name : 'name',
label : 'Name',
help : 'Specify a build name e.g. Hamster.'
},{
type : 'text',
name : 'id',
label : 'Key',
help : 'Specify a build key e.g. hamster_v1.'
},{
name : 'len',
type : 'conditional',
test_param : {
name : 'type',
label : 'Definition',
help : 'Provide the data source.',
type : 'select',
value : initial_type,
data : [ { value : 'fasta', label : 'FASTA-file from history' },
{ value : 'file', label : 'Len-file from disk' },
{ value : 'text', label : 'Len-file by copy/paste' } ]
},
cases : [ {
value : 'fasta',
inputs : [ {
type : 'select',
name : 'value',
label : 'FASTA-file',
data : this.model.get( 'fasta_hdas' )
} ]
},{
value : 'file',
inputs : [ {
type : 'upload',
name : 'value',
label : 'Len-file',
data : this.model.get( 'len_hdas' )
} ]
},{
value : 'text',
inputs : [ {
type : 'text',
area : true,
name : 'value',
label : 'Edit/Paste'
} ]
} ]
}],
buttons : {
save : new Ui.Button({
icon : 'fa-save',
tooltip : 'Create new Build',
title : 'Save',
cls : 'ui-button btn btn-primary',
floating : 'clear',
onclick : function() {
var data = form.data.create();
if ( !data.id || !data.name ) {
self.message.update( { message: 'All inputs are required.', status: 'danger' } );
} else {
self.collection.create( data, {
wait : true,
success : function( response ) {
if ( response.get( 'message' ) ) {
self.message.update( { message: response.get( 'message' ), status: 'warning' } );
} else {
self.message.update( { message: 'Successfully added a new custom build.', status: 'success' } );
}
},
error : function( response, err ) {
var message = err && err.responseJSON && err.responseJSON.err_msg;
self.message.update( { message: message || 'Failed to create custom build.', status: 'danger' } );
}
});
}
}
})
},
onchange: function() {
var input_id = form.data.match( 'len|type' );
if ( input_id ) {
var input_field = form.field_list[ input_id ];
self._renderHelp( input_field.value() );
}
}
});
this.$form.empty().append( form.$el );
var installed_builds = this.model.get( 'installed_builds' );
if ( installed_builds && installed_builds.length ) {
this.$installed.show();
this.installed_builds.update( this.model.get( 'installed_builds' ) );
} else {
this.$installed.hide();
}
this._renderHelp( initial_type );
},
_renderHelp: function( len_type ) {
this.$help.empty()
.addClass( 'alert alert-info' )
.html( len_type == 'fasta' ? this._templateFasta() : this._templateLen() );
},
_templateLen: function() {
return '<h4>Length Format</h4>' +
'<p>' +
'The length format is two-column, separated by whitespace, of the form:' +
'<pre>chrom/contig length of chrom/contig</pre>' +
'</p>' +
'<p>' +
'For example, the first few entries of <em>mm9.len</em> are as follows:' +
'<pre>' +
'chr1 197195432\n' +
'chr2 181748087\n' +
'chr3 159599783\n' +
'chr4 155630120\n' +
'chr5 152537259' +
'</pre>' +
'</p>' +
'<p>Trackster uses this information to populate the select box for chrom/contig, and' +
'to set the maximum basepair of the track browser. You may either upload a .len file' +
'of this format (Len File option), or directly enter the information into the box ' +
'(Len Entry option).</p>';
},
_templateFasta: function() {
return '<h4>FASTA format</h4>' +
'<p>' +
'This is a multi-fasta file from your current history that provides the genome' +
'sequences for each chromosome/contig in your build.' +
'</p>' +
'<p>' +
'Here is a snippet from an example multi-fasta file:' +
'<pre>' +
'>chr1\n' +
'ATTATATATAAGACCACAGAGAGAATATTTTGCCCGG...\n\n' +
'>chr2\n' +
'GGCGGCCGCGGCGATATAGAACTACTCATTATATATA...\n\n' +
'...' +
'</pre>' +
'</p>';
}
});
return {
View: View
}
});
@@ -1,40 +1,43 @@
/** User Preferences view */
define( [ 'mvc/form/form-view', 'mvc/ui/ui-misc' ], function( Form, Ui ) {
var View = Backbone.View.extend({
initialize: function() {
this.defs = {
/** Contains descriptive dictionaries describing user forms */
var Model = Backbone.Model.extend({
initialize: function( options ) {
options = options || {};
options.user_id = options.user_id || Galaxy.user.id;
this.set({
'user_id' : options.user_id,
'information': {
title : 'Manage information',
description : 'Edit your email, addresses and custom parameters or change your username.',
url : 'api/users/' + Galaxy.user.id + '/information/inputs',
url : 'api/users/' + options.user_id + '/information/inputs',
icon : 'fa-user'
},
'password': {
title : 'Change password',
description : 'Allows you to change your login credentials.',
icon : 'fa-unlock-alt',
url : 'api/users/' + Galaxy.user.id + '/password/inputs',
url : 'api/users/' + options.user_id + '/password/inputs',
submit_title : 'Save password',
},
'communication': {
title : 'Change communication settings',
description : 'Enable or disable the communication feature to chat with other users.',
url : 'api/users/' + Galaxy.user.id + '/communication/inputs',
url : 'api/users/' + options.user_id + '/communication/inputs',
icon : 'fa-comments-o'
},
'permissions': {
title : 'Set dataset permissions for new histories',
description : 'Grant others default access to newly created histories. Changes made here will only affect histories created after these settings have been stored.',
url : 'api/users/' + Galaxy.user.id + '/permissions/inputs',
url : 'api/users/' + options.user_id + '/permissions/inputs',
icon : 'fa-users',
submit_title : 'Save permissions'
},
'api_key': {
title : 'Manage API key',
description : 'Access your current API key or create a new one.',
url : 'api/users/' + Galaxy.user.id + '/api_key/inputs',
url : 'api/users/' + options.user_id + '/api_key/inputs',
icon : 'fa-key',
submit_title : 'Create a new key',
submit_icon : 'fa-check'
@@ -42,7 +45,7 @@ define( [ 'mvc/form/form-view', 'mvc/ui/ui-misc' ], function( Form, Ui ) {
'toolbox_filters': {
title : 'Manage Toolbox filters',
description : 'Customize your Toolbox by displaying or omitting sets of Tools.',
url : 'api/users/' + Galaxy.user.id + '/toolbox_filters/inputs',
url : 'api/users/' + options.user_id + '/toolbox_filters/inputs',
icon : 'fa-filter',
submit_title : 'Save filters'
},
@@ -54,6 +57,14 @@ define( [ 'mvc/form/form-view', 'mvc/ui/ui-misc' ], function( Form, Ui ) {
window.location.href = Galaxy.root + 'user/openid_manage?cntrller=user&use_panels=True';
}
},
'custom_builds': {
title : 'Manage custom builds',
description : 'Add or remove custom builds using history datasets.',
icon : 'fa-cubes',
onclick : function() {
window.location.href = Galaxy.root + 'custom_builds';
}
},
'logout': {
title : 'Sign out',
description : 'Click here to sign out of all sessions.',
@@ -64,12 +75,20 @@ define( [ 'mvc/form/form-view', 'mvc/ui/ui-misc' ], function( Form, Ui ) {
body : 'Do you want to continue and sign out of all active sessions?',
buttons : {
'Cancel' : function() { Galaxy.modal.hide(); },
'Sign out' : function() { window.location.href = Galaxy.root + 'user/logout'; }
'Sign out' : function() { window.location.href = Galaxy.root + 'user/logout?session_csrf_token=' + Galaxy.session_csrf_token; }
}
});
}
}
}
});
}
});
/** View of the main user preference panel with links to individual user forms */
var View = Backbone.View.extend({
initialize: function() {
this.model = new Model();
this.message = new Ui.Message();
this.setElement( '<div/>' );
this.render();
@@ -85,93 +104,42 @@ define( [ 'mvc/form/form-view', 'mvc/ui/ui-misc' ], function( Form, Ui ) {
.append( $( '<p/>' ).append( 'You are logged in as <strong>' + _.escape( data.email ) + '</strong>.' ) )
.append( self.$table = $( '<table/>' ).addClass( 'ui-panel-table' ) );
if( !config.use_remote_user ) {
self._link( self.defs.information );
self._link( self.defs.password );
self._addLink( 'information' );
self._addLink( 'password' );
}
if( config.enable_communication_server ) {
self._link( self.defs.communication );
self._addLink( 'communication' );
}
self._link( self.defs.permissions );
self._link( self.defs.api_key );
self._addLink( 'custom_builds' );
self._addLink( 'permissions' );
self._addLink( 'api_key' );
if( config.has_user_tool_filters ) {
self._link( self.defs.toolbox_filters );
self._addLink( 'toolbox_filters' );
}
if( config.enable_openid && !config.use_remote_user ) {
self._link( self.defs.openids );
self._addLink( 'openids' );
}
if(Galaxy.session_csrf_token) {
self._addLink( 'logout' );
}
self._link( self.defs.logout );
self.$preferences.append( self._templateFooter( data ) );
self.$el.empty().append( self.$preferences );
});
},
_link: function( page ) {
var self = this;
var $page_item = $( this._templateRow( page ) );
this.$table.append( $page_item );
$page_item.find( 'a' ).on( 'click', function() {
if ( page.url ) {
$.ajax({
url : Galaxy.root + page.url,
type : 'GET'
}).done( function( response ) {
var options = $.extend( {}, page, response );
var form = new Form({
title : options.title,
icon : options.icon,
inputs : options.inputs,
operations: {
'submit': new Ui.ButtonIcon({
tooltip : options.submit_tooltip,
title : options.submit_title || 'Save settings',
icon : options.submit_icon || 'fa-save',
onclick : function() { self._submit( form, options ) }
}),
'back': new Ui.ButtonIcon({
icon : 'fa-caret-left',
tooltip : 'Return to user preferences',
title : 'Preferences',
onclick : function() { form.remove(); self.$preferences.show(); }
})
}
});
self.$preferences.hide();
self.$el.append( form.$el );
}).fail( function( response ) {
self.message.update( { message: 'Failed to load resource ' + page.url + '.', status: 'danger' } );
})
} else {
page.onclick();
}
});
_addLink: function( action ) {
var options = this.model.get( action );
var $row = $( this._templateLink( options ) );
var $a = $row.find( 'a' );
if ( options.onclick ) {
$a.on( 'click', function() { options.onclick() } );
} else {
$a.attr( 'href', Galaxy.root + 'user/' + action );
}
this.$table.append( $row );
},
_submit: function( form, options ) {
var self = this;
$.ajax( {
url : options.url,
data : JSON.stringify(form.data.create()),
type : 'PUT',
contentType : 'application/json'
}).done( function( response ) {
var updated_values = false;
form.data.matchModel( response, function ( input, input_id ) {
form.field_list[ input_id ].value( input.value );
updated_values = true;
});
if ( updated_values ) {
form.message.update( { message: response.message, status: 'success' } );
} else {
form.remove();
self.$preferences.show();
self.message.update( { message: response.message, status: 'success' } );
}
}).fail( function( response ) {
form.message.update( { message: response.responseJSON.err_msg, status: 'danger' } );
});
},
_templateRow: function( options ) {
_templateLink: function( options ) {
return '<tr>' +
'<td>' +
'<div class="ui-panel-icon fa ' + options.icon + '">' +
@@ -187,12 +155,71 @@ define( [ 'mvc/form/form-view', 'mvc/ui/ui-misc' ], function( Form, Ui ) {
return '<p class="ui-panel-footer">' +
'You are using <strong>' + options.nice_total_disk_usage + '</strong> of disk space in this Galaxy instance. ' +
( Galaxy.config.enable_quotas ? 'Your disk quota is: <strong>' + options.quota + '</strong>. ' : '' ) +
'Is your usage more than expected? See the <a href="https://wiki.galaxyproject.org/Learn/ManagingDatasets" target="_blank">documentation</a> for tips on how to find all of the data in your account.' +
'Is your usage more than expected? See the <a href="https://galaxyproject.org/learn/managing-datasets/" target="_blank">documentation</a> for tips on how to find all of the data in your account.' +
'</p>';
}
});
/** View of individual user forms */
var Forms = Backbone.View.extend({
initialize: function( options ) {
this.model = new Model( options );
this.page = this.model.get( options.form_id );
this.setElement( '<div/>' );
this.render();
},
render: function() {
var self = this;
$.ajax({
url : Galaxy.root + this.page.url,
type : 'GET'
}).done( function( response ) {
var options = $.extend( {}, self.page, response );
var form = new Form({
title : options.title,
icon : options.icon,
inputs : options.inputs,
operations: {
'submit': new Ui.ButtonIcon({
tooltip : options.submit_tooltip,
title : options.submit_title || 'Save settings',
icon : options.submit_icon || 'fa-save',
onclick : function() { self._submit( form, options ) }
})
}
});
self.$el.empty().append( form.$el );
}).fail( function( response ) {
self.$el.empty().append( new Ui.Message({
message : 'Failed to load resource ' + self.page.url + '.',
status : 'danger',
persistent : true
}).$el );
});
},
_submit: function( form, options ) {
var self = this;
$.ajax( {
url : Galaxy.root + options.url,
data : JSON.stringify( form.data.create() ),
type : 'PUT',
contentType : 'application/json'
}).done( function( response ) {
form.data.matchModel( response, function ( input, input_id ) {
form.field_list[ input_id ].value( input.value );
});
form.message.update( { message: response.message, status: 'success' } );
}).fail( function( response ) {
form.message.update( { message: response.responseJSON.err_msg, status: 'danger' } );
});
}
});
return {
View: View
View : View,
Forms : Forms
};
});
@@ -128,8 +128,8 @@ var UserQuotaMeter = Backbone.View.extend( baseMVC.LoggableMixin ).extend(
return [
'<div id="quota-meter" class="quota-meter progress">',
'<div class="progress-bar" style="width: ', data.quota_percent, '%"></div>',
'<div class="quota-meter-text" style="top: 6px"',
(( data.nice_total_disk_usage )?( ' title="Using ' + data.nice_total_disk_usage + '">' ):( '>' )),
'<div class="quota-meter-text" data-placement="left" style="top: 6px"',
(( data.nice_total_disk_usage )?( ' title="Using ' + data.nice_total_disk_usage + '. This value is recalculated when you log out.">' ):( '>' )),
_l( 'Using' ), ' ', data.quota_percent, '%',
'</div>',
'</div>'
@@ -139,7 +139,7 @@ var UserQuotaMeter = Backbone.View.extend( baseMVC.LoggableMixin ).extend(
_templateUsage : function( data ){
return [
'<div id="quota-meter" class="quota-meter" style="background-color: transparent">',
'<div class="quota-meter-text" style="top: 6px; color: white">',
'<div class="quota-meter-text" data-placement="left" data-original-title="This value is recalculated when you log out." style="top: 6px; color: white">',
(( data.nice_total_disk_usage )?( _l( 'Using ' ) + data.nice_total_disk_usage ):( '' )),
'</div>',
'</div>'
+7 -2
View File
@@ -18,6 +18,11 @@ define([], function() {
initialize: function(options) {
var me = this;
var toolId = options.toolId || '';
var toolVersion = options.toolVersion || '';
this.$el.attr('tool_id', toolId);
this.$el.attr('tool_version', toolVersion);
this.model = new WebhookModel();
this.model.urlRoot = options.urlRoot;
@@ -34,7 +39,7 @@ define([], function() {
this.$el.html('<div id="' + webhook.name + '"></div>');
if (webhook.styles) $('<style/>', {type: 'text/css'}).text(webhook.styles).appendTo('head');
if (webhook.script) $('<script/>', {type: 'text/javascript'}).text(webhook.script).appendTo('head');
return this;
}
});
@@ -44,7 +49,7 @@ define([], function() {
webhooks.url = Galaxy.root + options.url;
webhooks.fetch({
async: (typeof options.async != 'undefined') ? options.async : true,
async: options.async ? options.async : true,
success: options.callback
});
};

Some files were not shown because too many files have changed in this diff Show More