5 <& /util/import_javascript.mas, classes => [ 'jquery', 'jqueryui' ] &>
7 <div class="modal fade" id="upload_sequence_metadata_workflow_modal" name="upload_high_dim_phenotypes_spreadsheet_dialog" tabindex="-1" role="dialog" aria-labelledby="addHighDimDataDialog">
8 <div class="modal-dialog modal-xl" role="document">
9 <div class="modal-content">
10 <div class="modal-header">
11 <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
12 <h4 class="modal-title" id="addHighDimDataDialog">Upload Sequence Metadata</h4>
14 <div class="modal-body">
15 <div class="container-fluid">
17 <&| /util/workflow.mas, id=> "upload_sequence_metadata_workflow" &>
19 <!-- Step 1: Intro -->
20 <&| /util/workflow.mas:step, title=> "Intro" &>
21 <& /page/page_title.mas, title=>"Upload Sequence Metadata" &>
23 <p>This workflow will guide you through the process of uploading sequence metadata to the database using a
24 <a href="https://m.ensembl.org/info/website/upload/gff3.html">gff3-formatted</a> file.</p>
25 <p>The following gff3 columns are used by the sequence metadata uploader:</p>
28 <strong>#1 / seqid:</strong> The name of the database feature (ie chromosome) the metadata is associated with<br />
29 <em>The names in this column must match existing features in the database.</em>
31 <li><strong>#4 / start:</strong> The metadata's start position</li>
32 <li><strong>#5 / end:</strong> The metadata's end position</li>
33 <li><strong>#6 / score:</strong> The primary score attribute of the metadata</li>
35 <strong>#9 / attributes:</strong> Secondary key/value attributes to be saved with the score<br />
36 <em>The attribute key cannot be either <strong>score</strong>, <strong>start</strong>, or <strong>end</strong>.</em>
42 <button class="btn btn-primary" id="sequence_metadata_intro_step">Go to Next Step</button>
48 <!-- Step 2: Data Type -->
49 <&| /util/workflow.mas:step, title=> "Data Type" &>
50 <& /page/page_title.mas, title=>"Select the type of sequence metadata" &>
52 <form class="form-horizontal">
53 <div class="form-group">
54 <label class="col-sm-3 control-label">Data Type: </label>
55 <div class="col-sm-9">
56 <select class="form-control" id="sequence_metadata_upload_type_select" disabled>
57 <option value="">Loading...</option>
61 <p><span class="glyphicon glyphicon-info-sign"></span> <span id="sequence_metadata_upload_type_definition"></span></p>
66 <p><span class="glyphicon glyphicon-question-sign"></span> If your data is not one of the above data types, please <a href="/contact/form">contact us</a> about adding a new data type.</p>
71 <button class="btn btn-primary" id="sequence_metadata_upload_type_step">Go to Next Step</button>
74 <!-- End Data Type -->
77 <!-- Step 3: Protocol Info -->
78 <&| /util/workflow.mas:step, title=> "Protocol Info" &>
79 <& /page/page_title.mas, title=>"Select a protocol for how your data was generated or create a new protocol" &>
81 <ul class="nav nav-tabs">
82 <li class="active"><a data-toggle="tab" href="#sequence_metadata_upload_protocol_select">Select Existing Protocol</a></li>
83 <li><a data-toggle="tab" href="#sequence_metadata_upload_protocol_div">Create New Protocol</a></li>
86 <div class="tab-content">
90 <!-- Will be filled with existing protocol table -->
91 <div id="sequence_metadata_upload_protocol_select" class="tab-pane fade in active"></div>
93 <!-- Form for creating a new protocol -->
94 <div id="sequence_metadata_upload_protocol_div" class="tab-pane fade">
95 <form class="form-horizontal" id="sequence_metadata_upload_protocol_form" name="sequence_metadata_upload_file_form">
97 <h3>Protocol Information</h3>
99 <!-- Protocol Name -->
100 <div class="form-group">
101 <label class="col-sm-3 control-label">Protocol Name: </label>
102 <div class="col-sm-9">
103 <input class="form-control" id="sequence_metadata_upload_protocol_name" type="text">
107 <!-- Protocol Description -->
108 <div class="form-group">
109 <label class="col-sm-3 control-label">Protocol Description: </label>
110 <div class="col-sm-9">
111 <textarea class="form-control" id="sequence_metadata_upload_protocol_description" rows="5" placeholder="Include a description of the methods used to generate the data"></textarea>
115 <!-- Sequence Metadata Type -->
116 <div class="form-group">
117 <label class="col-sm-3 control-label">Sequence Metadata Type: </label>
118 <div class="col-sm-9">
119 <input class="form-control" id="sequence_metadata_upload_protocol_sequence_metadata_type" type="text" disabled>
123 <!-- Reference Genome -->
124 <div class="form-group">
125 <label class="col-sm-3 control-label">Reference Genome: </label>
126 <div class="col-sm-9">
127 <select class="form-control" id="sequence_metadata_upload_protocol_reference_genome" disabled>
128 <option value="">Loading...</option>
135 <!-- Attribute Descriptions -->
136 <h3>Attribute Descriptions</h3>
138 <div class="form-group">
139 <label class="col-sm-3 control-label">Score Description: </label>
140 <div class="col-sm-9">
141 <input class="form-control" id="sequence_metadata_upload_score_description" type="text" placeholder="Include a description of the values in the 'score' column">
146 <p><strong>Add a description for each of the key/value pairs included in the attributes column.</strong></p>
147 <p><em>Specifying the attribute here in the protocol will allow the attribute to be used as a filter in a query. Make sure the attribute key exactly matches the key used in the gff file.</em></p>
149 <div class="form-group">
150 <label class="col-sm-3 control-label">Attribute Key: </label>
151 <div class="col-sm-9">
152 <input class="form-control" id="sequence_metadata_upload_new_attribute_key" type="text" placeholder="Enter attribute key">
156 <div class="form-group">
157 <label class="col-sm-3 control-label">Attribute Description: </label>
158 <div class="col-sm-9">
159 <input class="form-control" id="sequence_metadata_upload_new_attribute_description" type="text" placeholder="Enter a description of this attribute">
163 <div class="form-group center">
164 <button class="btn btn-info" id="sequence_metadata_upload_new_attribute_submit">Save Attribute</button>
168 <p><strong>Saved Attributes:</strong></p>
169 <table id="sequence_metadata_upload_new_attributes_table" class="table table-striped table-hover">
180 <!-- External Link Templates -->
181 <h3>External Links</h3>
183 <p><strong>Add templates for generating external links displayed with the sequence metadata results.</strong></p>
184 <p>External links are defined using templates that can include the feature/chrom name, start and/or end position, and/or any attribute value
185 as a variable in the URL. The variable will be replaced with the value for each sequence metadata result and displayed in the query results table. The variable in the URL takes the form of: <code>{{variable}}</code> where <code>variable</code> can be: <code>feature</code>, <code>start</code>,
186 <code>end</code> or the key of any attribute defined by the sequence metadata protocol.</p>
187 <p>Example: <code>https://wheat.pw.usda.gov/jb/?data=/ggds/whe-iwgsc2018&loc={{Locus}}&highlight={{feature}}:{{start}}..{{end}}</code></p>
189 <div class="form-group">
190 <label class="col-sm-3 control-label">Link Title: </label>
191 <div class="col-sm-9">
192 <input class="form-control" id="sequence_metadata_upload_new_link_title" type="text" placeholder="Enter external link title">
196 <div class="form-group">
197 <label class="col-sm-3 control-label">Link URL Template: </label>
198 <div class="col-sm-9">
199 <input class="form-control" id="sequence_metadata_upload_new_link_url" type="text" placeholder="Enter external link URL template">
203 <div class="form-group center">
204 <button class="btn btn-info" id="sequence_metadata_upload_new_link_submit">Save Link</button>
208 <p><strong>Saved Links:</strong></p>
209 <table id="sequence_metadata_upload_new_links_table" class="table table-striped table-hover">
220 <!-- End new protocol form -->
227 <button class="btn btn-primary" id="sequence_metadata_upload_protocol_step">Go to Next Step</button>
233 <!-- End Protocol Info -->
236 <!-- Step 4: Upload -->
237 <&| /util/workflow.mas:step, title=> "Upload" &>
238 <& /page/page_title.mas, title=>"Upload your gff3 file" &>
241 <p>Select your gff3 file to upload. Then, click the <strong>Verify</strong> button to upload your file and check its format and contents. If
242 successfully verified, click the <strong>Store</strong> button to store the data in the database.</p>
246 <form id="sequence_metadata_upload_file_form" class="form-horizontal" enctype="multipart/form-data">
247 <div class="form-group">
248 <label class="col-sm-3 control-label">Select GFF3 File: </label>
249 <div class="col-sm-9" >
250 <input type="file" id="sequence_metadata_upload_file_input" name="sequence_metadata_upload_file_input" encoding="multipart/form-data" />
258 <button id="sequence_metadata_upload_verify" class="btn btn-info">Verify</button>
259 <button id="sequence_metadata_upload_store" class="btn btn-primary" disabled>Store</button>
264 <div id="sequence_metdata_upload_messages">
265 <ul id="sequence_metadata_upload_messages_list" class='list-group'></ul>
274 <div class="modal-footer">
275 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
289 var sequence_metadata_data_types = []; // List of sequence metadata type properties (pulled from the database)
290 var sequence_metadata_upload_type_id = undefined; // Selected sequence metadata type id
291 var new_attribute_count = 0; // Count of (max) number of attribute keys/descriptions added
292 var new_link_count = 0; // Count of (max) number of external links added
293 var use_existing_protocol = undefined; // Flag set to true if user has selected an existing protocol
294 var new_protocol_name = undefined; // Name of new protocol
295 var new_protocol_description = undefined; // Description of new protocol
296 var processed_filepath = undefined; // Server filepath to processed file generated and returned by verification step
298 jQuery(document).ready(function() {
300 // Start SMD Upload Workflow
301 jQuery('#upload_smd').click( function() {
302 jQuery('#upload_sequence_metadata_workflow_modal').modal("show");
305 // Complete the Intro Step
306 jQuery('#sequence_metadata_intro_step').click(complete_intro_step);
308 // Complete the Data Type Step
309 jQuery('#sequence_metadata_upload_type_step').click(complete_data_type_step);
311 // Complete Protocol Step
312 jQuery('#sequence_metadata_upload_protocol_step').click(complete_protocol_step);
314 // Save a new attribute description
315 jQuery('#sequence_metadata_upload_new_attribute_submit').click(save_attribute_description);
317 // Save a new external link definition
318 jQuery('#sequence_metadata_upload_new_link_submit').click(save_link_definition);
320 // Upload and Verify File
321 jQuery('#sequence_metadata_upload_verify').click(upload_and_verify);
324 jQuery('#sequence_metadata_upload_store').click(store);
330 // WORKFLOW STEP COMPLETION
334 * Complete the Intro Step
335 * - Get the available data types from the DB
336 * - Set the data types select box in the Data Type step
337 * - Complete Intro Step and move to Data Type step
339 function complete_intro_step() {
341 // Get and set the data types
345 url: '/ajax/sequence_metadata/types',
346 success: function(data) {
347 if ( data && data.types ) {
348 sequence_metadata_data_types = data.types;
350 for ( let i = 0; i < data.types.length; i++ ) {
351 html += "<option value='" + data.types[i].type_id + "'>" + data.types[i].type_name + "</option>";
353 jQuery('#sequence_metadata_upload_type_select').html(html);
354 jQuery('#sequence_metadata_upload_type_select').prop('disabled', false);
355 jQuery('#sequence_metadata_upload_type_select').on('change', function() {
356 setDataTypeDefinition(this.value);
358 setDataTypeDefinition(data.types[0].type_id);
361 alert("ERROR: Could not load data types!");
365 alert("ERROR: Could not load data types!");
369 // Proceeed to Next Step
370 Workflow.complete('#sequence_metadata_intro_step');
371 Workflow.focus('#upload_sequence_metadata_workflow', 1);
376 * Complete the Data Type Step
377 * - Set the selected sequence metadata type id
378 * - Get the existing protocols for matching data type from DB
379 * - Set existing protocol table in Protocol step
380 * - Get the available reference genomes from the DB
381 * - Set the reference genomes select box in the Protocol step
382 * - Complete Data Type step and move to Protocol step
384 function complete_data_type_step() {
385 sequence_metadata_upload_type_id = jQuery('#sequence_metadata_upload_type_select').val();
387 // Set the existing protocol list
388 get_select_box('sequence_metadata_protocols', 'sequence_metadata_upload_protocol_select', {'checkbox_name': 'sequence_metadata_upload_protocol_id', 'sequence_metadata_data_type_id': sequence_metadata_upload_type_id});
390 // Set sequence metadata type value
391 for ( let i = 0; i < sequence_metadata_data_types.length; i++ ) {
392 if ( parseInt(sequence_metadata_data_types[i].type_id) === parseInt(sequence_metadata_upload_type_id) ) {
393 jQuery('#sequence_metadata_upload_protocol_sequence_metadata_type:text').val(sequence_metadata_data_types[i].type_name);
397 // Set the reference genome list
401 url: '/ajax/markers/genotyped/reference_genomes',
402 success: function(data) {
403 if ( data && data.reference_genomes ) {
405 for ( let i = 0; i < data.reference_genomes.length; i++ ) {
406 let rg_name = data.reference_genomes[i].reference_genome_name;
407 let sp_name = data.reference_genomes[i].species_name;
408 let rg_label = rg_name + " (" + sp_name + ")";
409 options += "<option value='" + rg_name + "' data-species='" + sp_name + "'>" + rg_label + "</option>";
411 jQuery('#sequence_metadata_upload_protocol_reference_genome').html(options);
412 jQuery('#sequence_metadata_upload_protocol_reference_genome').prop('disabled', false);
415 alert("ERROR: Could not load reference genomes!");
419 alert("ERROR: Could not load reference genomes");
423 Workflow.complete("#sequence_metadata_upload_type_step");
424 Workflow.focus('#upload_sequence_metadata_workflow', 2);
428 * Complete the Protocol Step
429 * - Determine if user has selected an existing protocol / created a new protocol
430 * - Check for required protocol params
431 * - Complete the Protocol Step and move to Upload step
433 function complete_protocol_step() {
434 use_existing_protocol = jQuery('#sequence_metadata_upload_protocol_select').is(':visible');
435 if ( use_existing_protocol ) {
436 if ( jQuery('input[name="sequence_metadata_upload_protocol_id"]:checked').length !== 1 ) {
437 alert('Please select one existing protocol!');
442 new_protocol_name = jQuery('#sequence_metadata_upload_protocol_name').val();
443 new_protocol_description = jQuery('#sequence_metadata_upload_protocol_description').val();
444 if ( !new_protocol_name || new_protocol_name === "" ) {
445 alert('Please enter a name for the new protocol');
448 if ( !new_protocol_description || new_protocol_description === "" ) {
449 alert('Please enter a description for the new protocol');
454 jQuery('#sequence_metadata_upload_file_input').val(undefined);
455 jQuery('#sequence_metadata_upload_verify').attr('disabled', false);
456 jQuery('#sequence_metadata_upload_store').attr('disabled', true);
457 jQuery('#sequence_metadata_upload_messages_list').empty();
459 Workflow.complete("#sequence_metadata_upload_protocol_step");
460 Workflow.focus('#upload_sequence_metadata_workflow', 3);
468 * Upload and verify the gff file
469 * - Make POST request to verify endpoint (upload file, run verification script)
470 * - Handle response and display verification messages
471 * - Enable store button if verification passes
473 function upload_and_verify() {
474 let file = jQuery('#sequence_metadata_upload_file_input').val();
475 if ( !file || file === '' ) {
476 alert("Please select your gff file");
480 // Setup working modal
481 jQuery('#working_msg').html("Uploading GFF File");
482 jQuery('#working_modal_progress').css('width', 0).attr('aria-valuenow', 0);
483 jQuery('#working_modal_progress_div').css('display', 'block');
484 jQuery('#working_modal').modal("show");
485 jQuery('#sequence_metadata_upload_messages_list').empty();
488 // Build the FormData to pass as POST arguments
489 let formData = new FormData();
490 formData.append('file', jQuery('#sequence_metadata_upload_file_input').prop('files')[0]);
491 formData.append('use_existing_protocol', use_existing_protocol);
492 if ( use_existing_protocol ) {
493 formData.append('existing_protocol_id', jQuery('input[name="sequence_metadata_upload_protocol_id"]:checked').val());
496 formData.append('species', jQuery('#sequence_metadata_upload_protocol_reference_genome option:selected').attr('data-species'));
497 formData.append('new_protocol_attribute_count', new_attribute_count);
498 for ( let i = 0; i <= new_attribute_count; i++ ) {
499 formData.append('new_protocol_attribute_key_' + i, jQuery('#sequence_metadata_upload_attribute_key_' + i).val());
503 // Make the POST request
505 url: '/ajax/sequence_metadata/file_upload_verify',
512 var myXhr = jQuery.ajaxSettings.xhr();
514 myXhr.upload.addEventListener('progress', function (e) {
515 if (e.lengthComputable) {
516 let prog = (e.loaded / e.total)*100;
517 jQuery('#working_modal_progress').css('width', prog + "%").attr('aria-valuenow', prog);
519 jQuery('#working_msg').html("Verifying GFF File");
526 success: function(response) {
528 // Clear working modal
529 jQuery('#working_msg').html("");
530 jQuery('#working_modal_progress').css('width', 0).attr('aria-valuenow', 0);
531 jQuery('#working_modal_progress_div').css('display', 'none');
532 jQuery('#working_modal').modal("hide");
534 console.log("VERIFICATION RESPONSE");
535 console.log(response);
537 // Add Response Messages
538 if ( response && response.error ) {
539 add_message("error", response.error);
541 else if ( response && response.results ) {
542 if ( response.results.processed === 1 ) {
543 add_message("success", "File successfully uploaded and processed");
546 add_message("error", "ERROR: File not successfully uploaded and processed!");
549 if ( response.results.verified === 1 ) {
550 add_message("success", "File successfully verified");
553 add_message("error", "ERROR: File not successfully verfied!");
556 if ( response.results.missing_features && response.results.missing_features.length > 0 ) {
557 let msg = "ERROR: The following features do not exist in the database. Contact us to have them added or change the feature names to match existing features.";
559 for ( let i = 0; i < response.results.missing_features.length; i++ ) {
560 msg += "<li>" + response.results.missing_features[i] + "</li>";
563 add_message("error", msg);
566 if ( response.results.missing_attributes && response.results.missing_attributes.length > 0 ) {
567 let msg = "WARNING: The following attributes are defined in the protocol but are <em>not present in the gff file</em>.";
569 for ( let i = 0; i < response.results.missing_attributes.length; i++ ) {
570 msg += "<li>" + response.results.missing_attributes[i] + "</li>";
573 msg += "You can either <a href='javascript:Workflow.focus(\"#upload_sequence_metadata_workflow\", 2);'>remove the attributes from the protocol info</a> or continue to store the file with the attributes defined in the protocol but missing from the file.";
574 add_message("warning", msg);
577 if ( response.results.undefined_attributes && response.results.undefined_attributes.length > 0 ) {
578 let msg = "WARNING: The following attributes are present in the gff file but are <em>not defined in the protocol</em>.";
580 for ( let i = 0; i < response.results.undefined_attributes.length; i++ ) {
581 msg += "<li>" + response.results.undefined_attributes[i] + "</li>";
584 msg += "You can either <a href='javascript:Workflow.focus(\"#upload_sequence_metadata_workflow\", 2);'>add the attributes to the protocol info</a> or continue to store the file with the attributes not defined in the protocol.";
585 add_message("warning", msg);
589 // Enable the Store button if processed and verified
590 if ( response && response.results && response.results.processed && response.results.processed === 1 && response.results.verified && response.results.verified === 1 && response.results.processed_filepath ) {
591 jQuery('#sequence_metadata_upload_verify').attr('disabled', true);
592 jQuery('#sequence_metadata_upload_store').attr('disabled', false);
593 add_message("info", "Verification Complete. Next, click the <strong>Store</strong> button to save the data to the database.");
594 processed_filepath = response.results.processed_filepath;
599 jQuery('#working_msg').html("");
600 jQuery('#working_modal_progress').css('width', 0).attr('aria-valuenow', 0);
601 jQuery('#working_modal_progress_div').css('display', 'none');
602 jQuery('#working_modal').modal("hide");
604 alert("ERROR: Could not upload and verify gff file due to server error!");
614 // Setup working modal
615 jQuery('#working_msg').html("Storing Sequence Metadata");
616 jQuery('#working_modal').modal("show");
617 jQuery('#sequence_metadata_upload_messages_list').empty();
619 // Setup FormData to pass as POST arguments
620 let formData = new FormData();
621 formData.append('processed_filepath', processed_filepath);
622 formData.append('use_existing_protocol', use_existing_protocol);
623 if ( use_existing_protocol ) {
624 formData.append('existing_protocol_id', jQuery('input[name="sequence_metadata_upload_protocol_id"]:checked').val());
625 formData.append('existing_protocol_sequence_metadata_type', jQuery('#sequence_metadata_upload_type_select option:selected').val());
628 formData.append('new_protocol_name', new_protocol_name);
629 formData.append('new_protocol_description', new_protocol_description);
630 formData.append('new_protocol_sequence_metadata_type', jQuery('#sequence_metadata_upload_type_select option:selected').val());
631 formData.append('new_protocol_reference_genome', jQuery('#sequence_metadata_upload_protocol_reference_genome option:selected').val());
632 formData.append('new_protocol_species', jQuery('#sequence_metadata_upload_protocol_reference_genome option:selected').attr('data-species'));
633 formData.append('new_protocol_score_description', jQuery('#sequence_metadata_upload_score_description').val());
634 formData.append('new_protocol_attribute_count', new_attribute_count);
635 for ( let i = 1; i <= new_attribute_count; i++ ) {
636 formData.append('new_protocol_attribute_key_' + i, jQuery('#sequence_metadata_upload_attribute_key_' + i).val());
637 formData.append('new_protocol_attribute_description_' + i, jQuery('#sequence_metadata_upload_attribute_description_' + i).val());
639 formData.append('new_protocol_link_count', new_link_count);
640 for ( let i = 1; i <= new_link_count; i++ ) {
641 formData.append('new_protocol_link_title_' + i, jQuery('#sequence_metadata_upload_link_title_' + i).val());
642 formData.append('new_protocol_link_url_' + i, jQuery('#sequence_metadata_upload_link_url_' + i).val());
646 // Make the POST request
648 url: '/ajax/sequence_metadata/store',
654 success: function(response) {
656 // Clear working modal
657 jQuery('#working_msg').html("");
658 jQuery('#working_modal').modal("hide");
660 console.log("STORE RESPONSE");
661 console.log(response);
663 // Add Response Messages
664 if ( response && response.error ) {
665 add_message("error", response.error);
667 else if ( response && response.results ) {
668 if ( response.results.stored === 1 ) {
669 add_message("success", "File successfully stored");
670 if ( response.results.chunks ) {
671 add_message("info", response.results.chunks + " chunks written to the database.");
675 add_message("error", "ERROR: File not stored!");
679 // Disable the verify and store buttons
680 if ( response && response.results && response.results.stored && response.results.stored === 1 ) {
681 jQuery('#sequence_metadata_upload_verify').attr('disabled', true);
682 jQuery('#sequence_metadata_upload_store').attr('disabled', true);
687 jQuery('#working_msg').html("");
688 jQuery('#working_modal').modal("hide");
689 alert("ERROR: Could not store sequence metadata due to server error!");
701 * Callback listener for data type selection change
702 * - Update the data type definition
704 function setDataTypeDefinition(id) {
705 for ( let i = 0; i < sequence_metadata_data_types.length; i++ ) {
706 if ( parseInt(sequence_metadata_data_types[i].type_id) === parseInt(id) ) {
707 jQuery('#sequence_metadata_upload_type_definition').html(sequence_metadata_data_types[i].type_definition);
713 * Save attribute key and description
715 * - Add key and description to table (along with hidden inputs used by the form)
717 function save_attribute_description() {
718 new_attribute_count++;
720 let key = jQuery('#sequence_metadata_upload_new_attribute_key').val();
721 let description = jQuery('#sequence_metadata_upload_new_attribute_description').val();
722 if ( !key || key === "" ) {
723 alert("An attribute key is required!");
727 let html = "<tr id='sequence_metadata_upload_new_attributes_table_row_" + new_attribute_count + "'>";
728 html += "<td>" + key + "<input type='hidden' name='sequence_metadata_upload_attribute_key_" + new_attribute_count + "' id='sequence_metadata_upload_attribute_key_" + new_attribute_count + "' value='" + key + "'></td>";
729 html += "<td>" + description + "<input type='hidden' name='sequence_metadata_upload_attribute_description_" + new_attribute_count + "' id='sequence_metadata_upload_attribute_description_" + new_attribute_count + "' value='" + description + "'</td>";
730 html += "<td style='text-align: right'><button id='sequence_metadata_upload_new_attributes_table_remove_" + new_attribute_count + "' data-row='" + new_attribute_count + "' class='btn btn-danger btn-xs'><span class='glyphicon glyphicon-remove'></span></button></td>";
733 jQuery('#sequence_metadata_upload_new_attribute_key').val("");
734 jQuery('#sequence_metadata_upload_new_attribute_description').val("");
735 jQuery('#sequence_metadata_upload_new_attributes_table').append(html);
736 jQuery('#sequence_metadata_upload_new_attributes_table_remove_' + new_attribute_count).click(function() {
737 let row = jQuery(this).attr('data-row');
738 jQuery('#sequence_metadata_upload_new_attributes_table_row_' + row).remove();
745 * Save external link title and url definition
747 * - Add title and url to table (along with hidden inputs used by the form)
749 function save_link_definition() {
752 let title = jQuery('#sequence_metadata_upload_new_link_title').val();
753 let url = jQuery('#sequence_metadata_upload_new_link_url').val();
754 if ( !title || title === "" ) {
755 alert("A link title is required!");
758 if ( !url || url === "" ) {
759 alert("A link url is required!");
763 let html = "<tr id='sequence_metadata_upload_new_links_table_row_" + new_link_count + "'>";
764 html += "<td>" + title + "<input type='hidden' name='sequence_metadata_upload_link_title" + new_link_count + "' id='sequence_metadata_upload_link_title_" + new_link_count + "' value='" + title + "'></td>";
765 html += "<td>" + url + "<input type='hidden' name='sequence_metadata_upload_link_url_" + new_link_count + "' id='sequence_metadata_upload_link_url_" + new_link_count + "' value='" + url + "'</td>";
766 html += "<td style='text-align: right'><button id='sequence_metadata_upload_new_links_table_remove_" + new_link_count + "' data-row='" + new_link_count + "' class='btn btn-danger btn-xs'><span class='glyphicon glyphicon-remove'></span></button></td>";
769 jQuery('#sequence_metadata_upload_new_link_title').val("");
770 jQuery('#sequence_metadata_upload_new_link_url').val("");
771 jQuery('#sequence_metadata_upload_new_links_table').append(html);
772 jQuery('#sequence_metadata_upload_new_links_table_remove_' + new_link_count).click(function() {
773 let row = jQuery(this).attr('data-row');
774 jQuery('#sequence_metadata_upload_new_links_table_row_' + row).remove();
781 * Add Message to Upload step
782 * @param {string} type Message Type (success, error)
783 * @param {string} message Message Text
785 function add_message(type, message) {
787 let class_name = "danger";
788 let icon_name = "remove";
790 if ( type === "success" ) {
791 class_name = "success";
794 else if ( type === "warning" ) {
795 class_name = "warning";
796 icon_name = "asterisk";
798 else if ( type === "info" ) {
800 icon_name = "info-sign";
803 html += "<li class='list-group-item list-group-item-" + class_name + "'>";
804 html += "<span class='badge'><span class='glyphicon glyphicon-" + icon_name + "'></span></span>";
808 jQuery('#sequence_metadata_upload_messages_list').append(html);