5 <& /util/import_javascript.mas, classes => [ 'jquery', 'jquery.dataTables', 'jquery.dataTables-buttons-min', 'jszip-min', 'buttons.bootstrap-min', 'buttons.html5-min' ], &>
7 <p>Filter the sequence metadata by position, sequence metadata type and/or protocol, and/or by protocol attribute value(s).</p>
10 <a id="sequence_metadata_anchor"></a>
12 <!-- START QUERY SECTION -->
13 <div id="sequence_metadata_section_query">
15 <form class="form-horizontal" id="sequence_metadata_filter_form" name="sequence_metadata_filter_form">
18 <&| /page/info_section.mas, title => 'Query Range', collapsible=>1, collapsed=>0 &>
21 <!-- Refence Genome -->
22 <div class="form-group">
23 <label class="col-sm-2 control-label">Reference Genome: </label>
24 <div class="col-sm-10">
25 <select class="form-control" id="sequence_metadata_filter_reference_genome" disabled>
26 <option value="">Loading...</option>
32 <div class="form-group">
33 <label class="col-sm-2 control-label">Feature: </label>
34 <div class="col-sm-10">
35 <select class="form-control" id="sequence_metadata_filter_feature" disabled>
36 <option value="">Loading...</option>
42 <div class="form-group">
43 <label class="col-sm-2 control-label">Start: </label>
44 <div class="col-sm-4">
45 <input class="form-control" id="sequence_metadata_filter_start" type="text" value="">
47 <label class="col-sm-2 control-label">End: </label>
48 <div class="col-sm-4">
49 <input class="form-control" id="sequence_metadata_filter_end" type="text" value="">
55 <!-- END QUERY RANGE -->
58 <!-- DATA TYPE / PROTOCOL -->
59 <&| /page/info_section.mas, title => 'Protocol', collapsible=>1, collapsed=>0 &>
62 <!-- User Selected Type / Protocol -->
63 <div id="sequence_metadata_filter_type_protocol_user_selection">
66 <div class="form-group">
67 <div class="col-sm-2" style="text-align: right">
68 <label class="control-label">Protocol: </label><br /><br /><br />
69 <a id="sequence_metadata_filter_type_info_btn" href="#"><span class="glyphicon glyphicon-question-sign"></span> Data Type Info</a>
71 <a id="sequence_metadata_filter_protocol_info_btn" href="#"><span class="glyphicon glyphicon-question-sign"></span> Protocol Info</a>
73 <div class="col-sm-10">
74 <select class="form-control" id="sequence_metadata_filter_protocol" size="10" multiple disabled>
75 <option value="">Loading...</option>
83 <!-- URL Selected Protocol -->
84 <div id="sequence_metadata_filter_type_protocol_url_selection">
85 <div class="form-group">
86 <label class="col-sm-2 control-label">Selected Protocols: </label>
87 <div class="col-sm-9">
88 <p id="sequence_metadata_filter_type_protocol_url_selection_names">Loading...</p>
90 <div class="col-sm-1" style="margin-top: 10px;">
91 <button id='sequence_metadata_filter_type_protocol_url_selection_remove' class='btn btn-danger btn-xs'><span class='glyphicon glyphicon-remove'></span></button>
98 <!-- END DATA TYPE / PROTOCOL -->
101 <!-- ADVANCED SEARCH -->
102 <&| /page/info_section.mas, title=>'Advanced Search', subtitle=>'Filter by attribute values', collapsible=>1, collapsed=>1 &>
105 <p>Return only sequence metadata features that have attribute values that match the added comparisons. If more than one attribute
106 filter is added, the sequence metadata feature must match all of the filters.</p>
110 <!-- Score Min / Max -->
111 <div class="form-group">
112 <label class="col-sm-2 control-label">Score: </label>
113 <div class="col-sm-3">
114 <label class="control-label">Protocol</label><br />
115 <select class="form-control" id="sequence_metadata_filter_score_protocol" disabled>
116 <option value="">Loading...</option>
119 <div class="col-sm-3">
120 <label class="control-label">Comparison</label><br />
121 <select class="form-control" id="sequence_metadata_filter_score_comparison">
122 <option value="eq">Equal</option>
123 <option value="lte">Less Than or Equal</option>
124 <option value="lt">Less Than</option>
125 <option value="gte">Greater Than or Equal</option>
126 <option value="gt">Greater Than</option>
129 <div class="col-sm-3">
130 <label class="control-label">Value</label><br />
131 <input class="form-control" id="sequence_metadata_filter_score_value" type="text" value="">
133 <div class="col-sm-1">
135 <button id="sequence_metadata_filter_score_add" class="btn btn-info">Add</button>
140 <!-- Attribute Value -->
141 <div class="form-group">
142 <label class="col-sm-2 control-label">Attribute: </label>
143 <div class="col-sm-3">
144 <label class="control-label">Protocol</label><br />
145 <select class="form-control" id="sequence_metadata_filter_attribute_protocol" disabled>
146 <option value="">Loading...</option>
149 <div class="col-sm-2">
150 <label class="control-label">Key</label><br />
151 <select class="form-control" id="sequence_metadata_filter_attribute_key" disabled>
152 <option value="">Loading...</option>
155 <div class="col-sm-2">
156 <label class="control-label">Comparison</label><br />
157 <select class="form-control" id="sequence_metadata_filter_attribute_comparison">
158 <option value="con">Contains</option>
159 <option value="eq">Equal</option>
160 <option value="lte">Less Than or Equal</option>
161 <option value="lt">Less Than</option>
162 <option value="gte">Greater Than or Equal</option>
163 <option value="gt">Greater Than</option>
166 <div class="col-sm-2">
167 <label class="control-label">Value</label><br />
168 <input class="form-control" id="sequence_metadata_filter_attribute_value" type="text" value="">
170 <div class="col-sm-1">
172 <button id="sequence_metadata_filter_attribute_add" class="btn btn-info">Add</button>
177 <!-- Attribute Table -->
179 <p><strong>Attribute Filters:</strong></p>
180 <table id="sequence_metadata_filter_attributes_table" class="table table-striped table-hover">
193 <!-- END ADVANCED SEARCH -->
199 <button id="sequence_metadata_filter_query" class="btn btn-primary btn-block" style="max-width: 400px; margin: auto" disabled>Search</button>
208 <!-- END QUERY SECTION -->
211 <!-- START AUTO SEARCH SECTION -->
212 <div id="sequence_metadata_section_auto_search" style="display: none">
213 <div class="well" style="max-width: 500px; margin: 50px auto">
214 <h1>Searching...</h1>
216 <p>Querying the database for the requested sequence metadata...</p>
220 <!-- END AUTO SEARCH SECTION -->
223 <!-- Error Message -->
225 <div id="sequence_metadata_filter_error" class="alert alert-danger" role="alert" style="display:none"></div>
229 <!-- START RESULTS SECTION -->
230 <div id="sequence_metadata_section_results" style="display: none">
232 <!-- Query Results -->
233 <table id="sequence_metadata_filter_results" class="display"></table>
239 <button id="sequence_metadata_filter_return" class="btn btn-primary btn-block" style="max-width: 400px; margin: auto">
240 <span class="glyphicon glyphicon-chevron-left"> </span>Back to Search
249 <!-- DATA TYPE INFO MODAL -->
250 <div id="sequence_metadata_filter_type_info" class="modal fade" tabindex="-1" role="dialog">
251 <div class="modal-dialog modal-lg" role="document">
252 <div class="modal-content">
253 <div class="modal-header">
254 <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
255 <h4 class="modal-title">Data Type Information</h4>
257 <div class="modal-body"></div>
262 <!-- PROTOCOL INFO MODAL -->
263 <div id="sequence_metadata_filter_protocol_info" class="modal fade" tabindex="-1" role="dialog">
264 <div class="modal-dialog modal-lg" role="document">
265 <div class="modal-content">
266 <div class="modal-header">
267 <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
268 <h4 class="modal-title">Protocol Information</h4>
270 <div class="modal-body"></div>
275 <!-- JBROWSE CONFIGURATION -->
276 <!-- OVERWRITE THIS FILE IN THE INSTANCE-SPECIFIC MASON REPO TO CONFIGURE -->
277 <& /tools/sequence_metadata/jbrowse_config_sequence_metadata.mas &>
280 <script type="text/javascript">
282 var selected_reference_genome = false; // Selected reference genome via URL param
283 var selected_feature = false; // Selected feature name via URL param
284 var selected_protocols = []; // List of protocol ids selected via URL params
285 var auto_search = false; // Flag for automatically performing the search via URL param
286 var reference_genomes = []; // List of reference genomes / species fetched from the database
287 var features = {}; // Object of features (key = species) fetched from the database
288 var protocols = {}; // Object of sequence metadata protocols (key = reference) fetched from the database
289 var attribute_filter_count = 0; // Current count of attribute filters added by user
290 var markers; // Object of queried markers from the database for specific ranges
292 jQuery(document).ready(function() {
294 // Parse URL Arguments
297 // Get and setup the initial query data types
301 // CLICK AND CHANGE LISTENERS
304 // Reference Genome Change Listener
305 jQuery('#sequence_metadata_filter_reference_genome').change(set_features).change(set_protocols).change(set_attribute_protocols);
308 jQuery('#sequence_metadata_filter_type_info_btn').click(function() {
309 jQuery('#sequence_metadata_filter_type_info').modal();
312 jQuery('#sequence_metadata_filter_protocol_info_btn').click(function() {
313 jQuery('#sequence_metadata_filter_protocol_info').modal();
317 // Protocol Change Listeners
318 jQuery('#sequence_metadata_filter_protocol').change(set_attribute_protocols);
320 // Attribute Protocol Listener
321 jQuery('#sequence_metadata_filter_attribute_protocol').change(set_attribute_keys);
323 // Add Score / Attribute Buttons
324 jQuery('#sequence_metadata_filter_score_add').click(function() { add_attribute_filter('score'); return false });
325 jQuery('#sequence_metadata_filter_attribute_add').click(function() { add_attribute_filter('attribute'); return false });
327 // Remove URL-selected Protocol Button
328 jQuery('#sequence_metadata_filter_type_protocol_url_selection_remove').click(reset_selected_protocols)
331 jQuery('#sequence_metadata_filter_query').click(query);
334 jQuery('#sequence_metadata_filter_return').click(toggle_sections);
336 // Toggle Sections on history/state change
337 if (window.history && window.history.pushState) {
338 jQuery(window).on('popstate', toggle_sections);
346 // Set DataTables Buttons
349 extend: 'excelHtml5',
350 title: 'sequence_metadata_results',
357 title: 'sequence_metadata_results',
365 download(getQueryURL('JSON'), "sequence_metadata.json");
371 download(getQueryURL('GA4GH'), "sequence_metadata_ga4gh.json");
376 action: function ( e, dt, button, config ) {
377 download(getQueryURL("gff"), "sequence_metadata.gff");
381 if ( JBROWSE_CONFIG_SEQUENCE_METADATA && JBROWSE_CONFIG_SEQUENCE_METADATA.enabled ) {
383 text: "<span class='glyphicon glyphicon-new-window'></span> JBrowse",
385 window.open(getJBrowseURL(JBROWSE_CONFIG_SEQUENCE_METADATA), '_blank');
391 jQuery('#sequence_metadata_filter_results').DataTable({
396 { title: "Protocol", data: "nd_protocol_name" },
397 { title: "Feature", data: "feature_name" },
398 { title: "Start", data: "start" },
399 { title: "End", data: "end" },
400 { title: "Score", data: "score" },
401 { title: "Attributes", data: "attributes", render: renderAttributesColumn },
402 { title: "External Links", data: "links", render: renderLinksColumn },
403 { title: "Markers", data: renderMarkersColumn }
405 order: [[ 2, "asc" ]],
409 // Update the Marker Search display when the table is redrawn (on page change, etc...)
410 jQuery('#sequence_metadata_filter_results').DataTable().on('draw.dt', function() {
422 * Parse the URL query parameters for pre-defined filter attributes
424 function parse_args() {
425 const urlParams = new URLSearchParams(window.location.search);
426 if ( urlParams.has('nd_protocol_id') ) {
427 selected_protocols = urlParams.getAll('nd_protocol_id');
428 jQuery('#sequence_metadata_filter_type_protocol_user_selection').css('display', 'none');
429 jQuery('#sequence_metadata_filter_type_protocol_url_selection').css('display', 'block');
431 if ( urlParams.has('reference_genome') ) {
432 selected_reference_genome = urlParams.get('reference_genome');
434 if ( urlParams.has('feature') ) {
435 selected_feature = urlParams.get('feature');
437 if ( urlParams.has('start') ) {
438 jQuery('#sequence_metadata_filter_start').val(urlParams.get('start'));
440 if ( urlParams.has('end') ) {
441 jQuery('#sequence_metadata_filter_end').val(urlParams.get('end'));
443 if ( urlParams.has('results') && selected_feature ) {
450 * Setup the initial query data types
451 * - Call get_reference_genomes()
452 * - Then, call get_features(), get_types(), and get_protocols()
453 * - Enable the search once all are complete
454 * - If auto_search is enabled, start the query and display feedback
459 // Show the auto search section, if enabled
461 display_auto_search_loading();
464 // Setup the reference genomes...
465 get_reference_genomes(function() {
466 _complete("reference_genomes")
469 function _complete(type) {
470 completed.push(type);
472 // Setup the remaining data types...
473 if ( type === "reference_genomes" ) {
474 get_features(function() {
475 _complete("features")
477 get_types(function() {
480 get_protocols(function() {
481 _complete("protocols")
486 if ( completed.length === 4 ) {
487 jQuery('#sequence_metadata_filter_query').attr('disabled', false);
497 * Remove the pre-defined selected protocols and display the
498 * user-selectable type and protocol options
500 function reset_selected_protocols() {
501 selected_protocols = [];
503 jQuery('#sequence_metadata_filter_type_protocol_user_selection').css('display', 'block');
504 jQuery('#sequence_metadata_filter_type_protocol_url_selection').css('display', 'none');
511 // DATABASE QUERIES AND RESPONSE HANDLERS
515 * Get the reference genomes / species associated with the stored genotype protocols
516 * - populate the options for the reference genome select box
517 * @param {Function} [callback] Callback function() (only called if successful)
519 function get_reference_genomes(callback) {
523 url: '/ajax/markers/genotyped/reference_genomes',
524 success: function(data) {
525 if ( data && data.reference_genomes ) {
526 reference_genomes = data.reference_genomes;
528 for ( let i = 0; i < reference_genomes.length; i++ ) {
529 let reference = reference_genomes[i].reference_genome_name;
530 let species = reference_genomes[i].species_name;
531 let label = reference + " (" + species + ")";
532 let selected = selected_reference_genome && reference_genomes[i].reference_genome_name === selected_reference_genome ? 'selected' : '';
533 options += "<option value='" + reference + "' data-species='" + species + "' " + selected + ">" + label + "</option>";
535 jQuery('#sequence_metadata_filter_reference_genome').html(options);
536 jQuery('#sequence_metadata_filter_reference_genome').prop('disabled', false);
537 if ( callback ) return callback();
540 alert("ERROR: Could not load reference genomes!");
544 alert("ERROR: Could not load reference genomes!");
550 * Get the features associated with stored sequence metadata
551 * - call set_features() to populate the options for the feature select box
552 * @param {Function} [callback] Callback function() (only called if successful)
554 function get_features(callback) {
558 url: '/ajax/sequence_metadata/features',
559 success: function(data) {
560 if ( data && data.features ) {
562 // Save features by species
564 for ( let i = 0; i < data.features.length; i++ ) {
565 let s = data.features[i].organism_name;
566 if ( !features.hasOwnProperty(s) ) {
569 features[s].push(data.features[i]);
572 // Set feature options
575 // Return to callback, if provided
576 if ( callback ) return callback();
580 alert("ERROR: Could not load features!");
584 alert("ERROR: Could not load features");
590 * Set the options for the features select box based on the currently selected reference genome
592 function set_features() {
593 let s = jQuery("#sequence_metadata_filter_reference_genome option:selected").data("species");
597 for ( let i = 0; i < f.length; i++ ) {
598 let f_id = f[i].feature_id;
599 let f_name = f[i].feature_name;
600 let selected = selected_feature && selected_feature === f_name ? 'selected' : '';
601 options += "<option value='" + f_id + "' " + selected + ">" + f_name + "</option>";
604 jQuery('#sequence_metadata_filter_feature').html(options);
605 jQuery('#sequence_metadata_filter_feature').prop('disabled', false);
609 * Get the types associated with stored sequence metadata
610 * - build the data type info table
611 * @param {Function} [callback] Callback function() (only called if successful)
613 function get_types(callback) {
617 url: '/ajax/sequence_metadata/types',
618 success: function(data) {
619 if ( data && data.types ) {
622 let info = "<table class='table table-striped'><thead><tr><th>Data Type</th><th>Definition</th></tr></thead>";
624 for ( let i = 0; i < data.types.length; i++ ) {
625 info += "<tr><td>" + data.types[i].type_name + "</td><td>" + data.types[i].type_definition + "</td></tr>";
629 jQuery('#sequence_metadata_filter_type_info .modal-body').html(info);
631 // Return to callback, if provided
632 if ( callback ) return callback();
636 alert("ERROR: Could not load types!");
640 alert("ERROR: Could not load types");
646 * Get the protocols associated with stored sequence metadata
647 * - call set_protocols() to populate the options for the protocol select box'
648 * - call set_attribute_protocols() to set the options for the protocol lists for the attributes
649 * - build the protocol info table
651 function get_protocols(callback) {
655 url: '/ajax/sequence_metadata/protocols',
656 success: function(data) {
657 if ( data && data.protocols ) {
659 // Save protocols by reference genome
661 for ( let i = 0; i < data.protocols.length; i++ ) {
662 let g = data.protocols[i].nd_protocol_properties.reference_genome;
663 if ( !protocols.hasOwnProperty(g) ) {
666 protocols[g].push(data.protocols[i]);
669 // Set Select Options
671 set_attribute_protocols();
674 let info = "<table class='table table-striped'><thead><tr><th>Protocol</th><th>Description</th><th>Properties</th></tr></thead>";
676 for ( let i = 0; i < data.protocols.length; i++ ) {
677 let props = "<strong>Data Type:</strong> " + data.protocols[i].nd_protocol_properties.sequence_metadata_type + "<br />";
678 props += "<strong>Reference Genome:</strong> " + data.protocols[i].nd_protocol_properties.reference_genome + "<br />";
679 props += "<strong>Score:</strong> " + data.protocols[i].nd_protocol_properties.score_description + "<br />";
680 props += "<strong>Attributes:</strong><br />";
682 var keys = Object.keys(data.protocols[i].nd_protocol_properties.attribute_descriptions);
684 let t = "<table class='table' style='background: transparent !important'>";
685 t += "<thead><tr><th>Key</th><th>Definition</th></tr></thead>";
687 for ( let j = 0; j < keys.length; j++ ) {
689 let description = data.protocols[i].nd_protocol_properties.attribute_descriptions[key];
690 t += "<tr><td class='col-md-4'><strong>" + key + "</strong></td><td class='col-md-8'>" + description + "</td></tr>";
696 var links = data.protocols[i].nd_protocol_properties.links;
698 props += "<strong>Links:</strong><br />";
699 var titles = Object.keys(links);
701 let t = "<table class='table' style='background: transparent !important'>";
702 t += "<thead><tr><th>Title</th><th>URL Template</th></tr></thead>";
704 for ( let j = 0; j < titles.length; j++ ) {
705 let title = titles[j];
706 let url = data.protocols[i].nd_protocol_properties.links[title];
707 t += "<tr><td class='col-md-4'><strong>" + title + "</strong></td><td class='col-md-8'>" + url + "</td></tr>";
715 info += "<td class='col-md-2'>" + data.protocols[i].nd_protocol_name + "</td>";
716 info += "<td class='col-md-5'>" + data.protocols[i].nd_protocol_description + "</td>";
717 info += "<td class='col-md-5'>" + props + "</td>";
722 jQuery('#sequence_metadata_filter_protocol_info .modal-body').html(info);
724 if ( callback ) return callback();
728 alert("ERROR: Could not load protocols!");
732 alert("ERROR: Could not load protocols");
738 * Set the options for the Protocol select box
740 function set_protocols() {
741 let sel_reference_genome = jQuery('#sequence_metadata_filter_reference_genome').val();
742 let sel_protocols = jQuery('#sequence_metadata_filter_protocol').val();
744 // Add URL selected protocols
745 if ( !sel_protocols ) sel_protocols = [];
746 sel_protocols = sel_protocols.concat(selected_protocols);
747 let sel_protocol_names = [];
749 // Get protocols for the selected reference genome
750 let p = protocols[sel_reference_genome];
752 // Group protocols by type
755 for ( let i = 0; i < p.length; i++ ) {
756 let p_id = p[i].nd_protocol_id;
757 let p_name = p[i].nd_protocol_name;
758 let p_type = p[i].nd_protocol_properties.sequence_metadata_type;
760 // Reselect previosuly selected items
761 let selected = false;
762 if ( sel_protocols ) {
763 for ( let j = 0; j < sel_protocols.length; j++ ) {
764 if ( parseInt(sel_protocols[j]) === parseInt(p_id) ) {
766 sel_protocol_names.push(p_name);
771 // Add protocol info to group of type
772 if ( !grouped[p_type] ) grouped[p_type] = [];
773 grouped[p_type].push({
776 selected: selected ? 'selected' : ''
781 // Build options, grouped by type
783 let sorted_types = Object.keys(grouped).sort(function(x, y) {
784 if (x.toUpperCase() < y.toUpperCase()) return -1;
785 if (x.toUpperCase() > y.toUpperCase()) return 1;
788 for ( let i = 0; i < sorted_types.length; i++ ) {
789 options += "<optgroup label='" + sorted_types[i] + "'>";
790 for ( let j = 0; j < grouped[sorted_types[i]].length; j++ ) {
791 let prot = grouped[sorted_types[i]][j];
792 options += "<option value='" + prot.id + "' " + prot.selected + ">" + prot.name + "</option>";
794 options += "</optgroup>";
797 // Set protocol selection options
798 jQuery('#sequence_metadata_filter_protocol').html(options);
799 jQuery('#sequence_metadata_filter_protocol').prop('disabled', false);
801 // Set selected protocol names
802 jQuery('#sequence_metadata_filter_type_protocol_url_selection_names').html(sel_protocol_names.join(', '));
806 * Set the protocol lists for the score and attribute select menus
808 function set_attribute_protocols() {
809 let sel_reference_genome = jQuery('#sequence_metadata_filter_reference_genome').val();
810 let sel_protocols = jQuery('#sequence_metadata_filter_protocol').val();
813 let p = protocols[sel_reference_genome];
815 for ( let i = 0; i < p.length; i++ ) {
816 let p_id = p[i].nd_protocol_id;
817 let p_name = p[i].nd_protocol_name;
818 let p_type_id = p[i].nd_protocol_properties.sequence_metadata_type_id;
820 // Only enable selected protocols or selected data types
822 if ( sel_protocols ) {
824 for ( let j = 0; j < sel_protocols.length; j++ ) {
825 if ( parseInt(sel_protocols[j]) === parseInt(p_id) ) {
831 let d = !enabled ? 'disabled' : '';
832 options += "<option value='" + p_id + "' " + d + ">" + p_name + "</option>";
836 // Set protocol selection options
837 jQuery('#sequence_metadata_filter_score_protocol').html(options);
838 jQuery('#sequence_metadata_filter_score_protocol').prop('disabled', false);
839 jQuery('#sequence_metadata_filter_attribute_protocol').html(options);
840 jQuery('#sequence_metadata_filter_attribute_protocol').prop('disabled', false);
842 // Update the attribute keys
843 set_attribute_keys();
848 * Set the attribute keys for the currently selected attribute protocol
850 function set_attribute_keys() {
851 let sel_reference_genome = jQuery('#sequence_metadata_filter_reference_genome').val();
852 let sel_attribute_protocol = jQuery('#sequence_metadata_filter_attribute_protocol').val();
855 let p = protocols[sel_reference_genome];
857 for ( let i = 0; i < p.length; i++ ) {
858 if ( parseInt(sel_attribute_protocol) === parseInt(p[i].nd_protocol_id) ) {
859 let attrs = Object.keys(p[i].nd_protocol_properties.attribute_descriptions);
860 for ( let j = 0; j < attrs.length; j++ ) {
861 options += "<option val='" + attrs[j] + "'>" + attrs[j] + "</option>";
867 jQuery('#sequence_metadata_filter_attribute_key').html(options);
868 jQuery('#sequence_metadata_filter_attribute_key').prop('disabled', false);
873 * Add the specified attribute to the list of included attribute filters
874 * @param {String} type Attribute type ('score' or 'attribute')
876 function add_attribute_filter(type) {
877 let protocol = jQuery('#sequence_metadata_filter_' + type + '_protocol').val();
878 let protocol_name = jQuery('#sequence_metadata_filter_' + type + '_protocol option:selected').text();
879 let attribute = type === 'score' ? 'score' : jQuery('#sequence_metadata_filter_attribute_key').val();
880 let comp = jQuery('#sequence_metadata_filter_' + type + '_comparison').val();
881 let comp_name = jQuery('#sequence_metadata_filter_' + type + '_comparison option:selected').text();
882 let value = jQuery('#sequence_metadata_filter_' + type + '_value').val();
883 let attribute_param = [attribute, protocol, comp, value].join('|');
885 if ( value !== '' ) {
886 attribute_filter_count++;
887 let html = "<tr id='sequence_metadata_filter_attributes_table_row_" + attribute_filter_count + "'>";
888 html += "<td>" + protocol_name + "</td>";
889 html += "<td>" + attribute + "</td>";
890 html += "<td>" + comp_name + "</td>";
891 html += "<td>" + value + "</td>";
892 html += "<td style='text-align: right'>";
893 html += "<button id='sequence_metadata_filter_attributes_table_remove_" + attribute_filter_count + "' data-row='" + attribute_filter_count + "' class='btn btn-danger btn-xs'><span class='glyphicon glyphicon-remove'></span></button>";
894 html += "<input type='hidden' name='sequence_metadata_filter_attribute_" + attribute_filter_count + "' id='sequence_metadata_filter_attribute_" + attribute_filter_count + "' value='" + attribute_param + "'>";
898 jQuery('#sequence_metadata_filter_' + type + '_value').val("");
899 jQuery('#sequence_metadata_filter_attributes_table').append(html);
900 jQuery('#sequence_metadata_filter_attributes_table_remove_' + attribute_filter_count).click(function() {
901 let row = jQuery(this).attr('data-row');
902 jQuery('#sequence_metadata_filter_attributes_table_row_' + row).remove();
915 * Build the Query URL using the current filter properties
916 * @param {string} [format] Output format (default: JSON)
917 * @param {int[]} [nd_protocol_id] Protocol ID to use (instead of selected protocols)
918 * @returns {string} relative URL to query endpoint
920 function getQueryURL(format, nd_protocol_id) {
921 let reference_genome = jQuery('#sequence_metadata_filter_reference_genome').val();
922 let feature_id = jQuery('#sequence_metadata_filter_feature option:selected').val();
923 let start = jQuery('#sequence_metadata_filter_start').val();
924 let end = jQuery('#sequence_metadata_filter_end').val();
925 let sel_nd_protocol_ids = jQuery('#sequence_metadata_filter_protocol').val();
928 feature_id: feature_id,
929 format: format ? format : 'JSON'
931 if ( start && start !== '' ) params.start = start;
932 if ( end && end !== '' ) params.end = end;
933 if ( reference_genome && reference_genome !== '' ) params.reference_genome = reference_genome;
934 if ( nd_protocol_id ) {
935 params.nd_protocol_id = nd_protocol_id;
938 if ( sel_nd_protocol_ids && sel_nd_protocol_ids.length > 0 ) params.nd_protocol_id = sel_nd_protocol_ids;
941 for ( let i = 1; i <= attribute_filter_count; i++ ) {
942 let attr = jQuery('#sequence_metadata_filter_attribute_' + i).val();
943 if ( attr && attr !== '' ) {
944 attributes.push(attr);
947 if ( attributes.length > 0 ) params.attribute = attributes.join(',');
949 let q = new URLSearchParams(params).toString();
950 let url = '/ajax/sequence_metadata/query?' + q;
957 * Perform a sequence metadata query
958 * - Required filter params: feature_id, start, end
959 * - Optional filter params: type_id, nd_protocol_id
960 * - Get the query results and send to handle_query_results to parse
964 jQuery('#sequence_metadata_filter_query').html('Searching...');
965 jQuery('#sequence_metadata_filter_query').attr('disabled', true);
969 url: getQueryURL('JSON'),
971 success: handle_query_results,
973 alert("ERROR: Could query database!");
974 jQuery('#sequence_metadata_filter_query').html('Search');
975 jQuery('#sequence_metadata_filter_query').attr('disabled', false);
984 * Parse the query results (in JSON format)
985 * - Update the rows in the DataTable
986 * @param {Object} response JSON response from the query endpoint
987 * response.error message of error encountered by server
988 * response.results array of sequence metadata objects
990 function handle_query_results(response) {
993 let dt = jQuery('#sequence_metadata_filter_results').DataTable();
995 if ( response && response.error ) {
996 display_error(response.error);
998 else if ( response && response.results && response.results.length > 0) {
999 dt.rows.add(response.results);
1000 if ( auto_search ) {
1001 display_auto_search_results();
1006 if (window.history && window.history.pushState) {
1007 window.history.pushState('results', null, null);
1011 display_error("No results found - try modifying your filter criteria");
1016 markers = undefined;
1018 // Reset Query Button
1019 jQuery('#sequence_metadata_filter_query').html('Search');
1020 jQuery('#sequence_metadata_filter_query').attr('disabled', false);
1026 * Build a URL to view the seleceted data in jBrowse
1027 * @params {Object} config JBrowse configuration properties
1028 * @returns {string} url to jbrowse with remote tracks added
1030 function getJBrowseURL(config) {
1031 // Get Selected Types
1032 let sel_reference_genome = jQuery('#sequence_metadata_filter_reference_genome').val();
1033 let p = protocols[sel_reference_genome];
1035 // Get Selected Protocols (or all enabled protocols, if none selected)
1036 let sel_protocols = jQuery('#sequence_metadata_filter_protocol').val();
1037 if ( !sel_protocols ) {
1040 for ( let i = 0; i < p.length; i++ ) {
1041 sel_protocols.push(p[i].nd_protocol_id);
1046 // Get Selected Protocol names (for track labels)
1047 let sel_protocol_names = {};
1049 for ( let i = 0; i < sel_protocols.length; i++ ) {
1050 for ( let j = 0; j < p.length; j++ ) {
1051 if ( parseInt(sel_protocols[i]) === parseInt(p[j].nd_protocol_id) ) {
1052 sel_protocol_names[sel_protocols[i].toString()] = p[j].nd_protocol_name.replaceAll(" ", " ");
1058 // Get Selected Feature (for jBrowse chromosome name)
1059 let sel_species = jQuery("#sequence_metadata_filter_reference_genome option:selected").data("species");
1060 let sel_feature = jQuery('#sequence_metadata_filter_feature').val();
1061 let sel_feature_name = '';
1062 let f = features[sel_species];
1063 for ( let i = 0; i < f.length; i++ ) {
1064 if ( parseInt(f[i].feature_id) === parseInt(sel_feature) ) {
1065 sel_feature_name = f[i].feature_name;
1069 // Get Selected start and end for range
1070 let start = jQuery('#sequence_metadata_filter_start').val();
1071 let end = jQuery('#sequence_metadata_filter_end').val();
1073 if ( start && start !== '' && end && end !== '' ) {
1074 range = ":" + start + ".." + end
1077 // Build JBrowse Stores & Tracks
1080 for ( let i = 0; i < sel_protocols.length; i++ ) {
1081 let query_url = window.location.protocol + "//" + window.location.host + getQueryURL("gff", sel_protocols[i]);
1082 if ( config.query_params ) {
1083 let q = new URLSearchParams(config.query_params).toString();
1084 query_url += "&" + q;
1086 stores["url" + i] = {
1087 type: "JBrowse/Store/SeqFeature/GFF3",
1088 urlTemplate: query_url
1091 label: sel_protocol_names[sel_protocols[i].toString()],
1092 type: "JBrowse/View/Track/CanvasFeatures",
1098 let base_url = config.base_url;
1100 data: config.data_dir,
1101 loc: config.location_name(sel_feature_name) + range,
1102 tracks: config.tracks.concat(Object.values(sel_protocol_names)).join(','),
1103 addStores: JSON.stringify(stores),
1104 addTracks: JSON.stringify(tracks)
1106 let q = new URLSearchParams(params).toString();
1107 let url = base_url + '?' + q;
1115 // MARKER SEARCH FUNCTIONS
1120 * Update the contents of the marker column in the results table
1121 * for the currently displayed rows of results
1123 function updateMarkers() {
1124 let containers = jQuery('.sequence_metadata_marker_search_markers');
1125 for ( let i = 0; i < containers.length; i++ ) {
1126 let container = jQuery(containers[i]);
1127 let species = container.data("species");
1128 let reference_genome = container.data("reference-genome");
1129 let feature_name = container.data("feature-name");
1130 let start = container.data("start");
1131 let end = container.data("end");
1132 let key = [species, reference_genome, feature_name, start, end].join("-").replaceAll(' ', '');
1133 if ( markers && markers[key] ) {
1134 displayMarkers(key, species, reference_genome, feature_name, start, end);
1137 getMarkers(key, species, reference_genome, feature_name, start, end);
1144 * Get markers matching the specified sequence metadata
1145 * - Query the DB for matching markers
1146 * - Update the display of the markers
1147 * @param {string} key Sequence Metadata results key (species-reference_genome-feature_name-start-end)
1148 * @param {string} species Name of the species
1149 * @param {string} reference_genome Name of the reference genome
1150 * @param {string} feature_name Name of the sequence metadata's associated feature
1151 * @param {int} start Start position of the sequence metadata
1152 * @param {int} end End position of the sequence metadata
1154 function getMarkers(key, species, reference_genome, feature_name, start, end) {
1156 // Display searching
1157 if ( !markers ) markers = {};
1158 markers[key] = "Searching...";
1159 displayMarkers(key);
1161 // Query the Database for Markers
1164 url: '/ajax/markers/genotyped/query',
1167 reference_genome: reference_genome,
1168 chrom: feature_name,
1174 success: function(data) {
1176 // Save the markers and update the DataTables
1177 if ( data && data.results ) {
1178 if ( !markers ) markers = {};
1179 markers[key] = data.results;
1180 displayMarkers(key, species, reference_genome, feature_name, start, end);
1185 alert("ERROR: Could not perform marker search");
1192 * Display the marker info for the specified sequence metadata row
1193 * @param {string} key Sequence Metadata results key (species-reference_genome-feature_name-start-end)
1194 * @param {string} species Species name
1195 * @param {string} reference_genome Reference genome name
1196 * @param {string} feature_name Feature / chromosome name
1197 * @param {int} start Start position
1198 * @param {int} end End position
1200 function displayMarkers(key, species, reference_genome, feature_name, start, end) {
1201 let marker_info = markers[key];
1202 let container = jQuery('.sequence_metadata_marker_search_markers_' + key);
1203 if ( typeof marker_info === 'string' ) {
1204 container.html(marker_info);
1206 else if ( typeof marker_info === 'object' ) {
1208 let marker_count = marker_info.counts.markers;
1209 let variants = marker_info.variants;
1210 let markers_displayed = 0;
1211 if ( marker_count > 0 ) {
1212 let m = marker_count === 1 ? "marker" : "markers";
1213 html += "<strong>" + marker_count + " " + m + " found:</strong>";
1214 html += "<div class='sequence_metadata_marker_search_markers_list'>";
1215 let sorted_variants = Object.keys(variants).sort();
1216 for ( let i = 0; i < sorted_variants.length; i++ ) {
1217 let variant_name = sorted_variants[i];
1218 if ( variants.hasOwnProperty(variant_name) ) {
1219 let m = variants[variant_name][0];
1220 let url = "/variant/" + variant_name + "/details";
1221 let label = m.chrom + " @ " + m.pos + " (" + m.ref + "/" + m.alt + ")";
1222 html += "<a href='" + url + "' target='_blank'>" + label + "</a>";
1223 html += "<ul style='padding-left: 25px'>";
1224 for ( let i = 0; i < variants[variant_name].length; i++ ) {
1225 let m = variants[variant_name][i];
1226 markers_displayed++;
1227 html += "<li>" + m.marker_name + " (" + m.nd_protocol_name + ")</li>";
1232 if ( markers_displayed < marker_count ) {
1233 let diff = marker_count - markers_displayed;
1234 html += "<em>" + diff + " more not displayed...</em>";
1235 if ( species && reference_genome && feature_name ) {
1236 let url = "/search/variants/results?type=genotyped&refmap=" + reference_genome + "&species=" + species + "&chrom=" + feature_name + "&start=" + start + "&end=" + end;
1237 html += "<br /><a href='" + url + "' target='_blank'>View All Markers</a>";
1243 html += "<em>No markers found</em>";
1245 container.html(html);
1251 // DATA TABLE RENDER FUNCTIONS
1255 * Render the Markers column
1256 * @param {Object} row The current row's data
1257 * @param {String} type The display type
1258 * @returns {String} The text/html to display in the table
1260 function renderMarkersColumn(row, type) {
1262 // Get reference genome by protocol
1263 let reference_genome = "";
1264 for ( let ref in protocols ) {
1265 if ( protocols.hasOwnProperty(ref) ) {
1266 let ps = protocols[ref];
1267 for ( let i = 0; i < ps.length; i++ ) {
1268 if ( ps[i].nd_protocol_id === row.nd_protocol_id ) {
1269 reference_genome = ref;
1275 // Get species by feature
1277 for ( let sp in features ) {
1278 if ( features.hasOwnProperty(sp) ) {
1279 let fs = features[sp];
1280 for ( let i = 0; i < fs.length; i++ ) {
1281 if ( fs[i].feature_id === row.feature_id ) {
1288 // Set data properties for marker search
1289 let data = "data-reference-genome='" + reference_genome + "' data-species='" + species + "' data-feature-name='" + row.feature_name + "' data-start='" + row.start + "' data-end='" + row.end + "'";
1290 let key = [species, reference_genome, row.feature_name, row.start, row.end].join("-").replaceAll(' ', '');
1292 let html = "<div class='sequence_metadata_marker_search'>";
1293 html += "<div class='sequence_metadata_marker_search_markers sequence_metadata_marker_search_markers_" + key + "' " + data + "></div>";
1300 * Render the Attributes column
1301 * @param data The column's data for the current row
1302 * @param {String} type The display type
1303 * @param {Object} row The current row's data
1304 * @returns {String} The text/html to display in the table
1306 function renderAttributesColumn(data, type, row) {
1308 let sep = type === 'export' ? ';' : '<br />';
1310 var keys = Object.keys(data);
1312 for ( var i=0; i<keys.length; ++i ) {
1314 let value = data[key];
1315 if ( type === 'export' ) {
1316 rtn.push(key + '=' + value);
1319 rtn.push("<strong>" + key + ":</strong> " + value);
1323 return rtn.join(sep);
1327 * Render the Links column
1328 * @param data The column's data for the current row
1329 * @param {String} type The display type
1330 * @param {Object} row The current row's data
1331 * @returns {String} The text/html to display in the table
1333 function renderLinksColumn(data, type, row) {
1335 let sep = type === 'export' ? ';' : '<br /><br />';
1337 var titles = Object.keys(data);
1339 for ( var i=0; i<titles.length; i++ ) {
1340 let title = titles[i];
1341 let url = data[title];
1342 if ( type === 'export' ) {
1343 rtn.push(title + '=' + url);
1346 rtn.push("<a href='" + url + "' target='_blank'>" + title + "</a>");
1350 return rtn.join(sep);
1360 * Toggle the display of the query and results sections
1362 function toggle_sections() {
1363 jQuery("#sequence_metadata_section_query").toggle();
1364 jQuery("#sequence_metadata_section_results").toggle();
1365 let anchor = jQuery('#sequence_metadata_anchor');
1366 jQuery('html,body').animate({scrollTop: anchor.offset().top-150}, 'fast');
1370 * Display the auto search loading section
1372 function display_auto_search_loading() {
1373 jQuery("#sequence_metadata_section_query").hide();
1374 jQuery("#sequence_metadata_section_results").hide();
1375 jQuery("#sequence_metadata_section_auto_search").show();
1379 * Display the auto search results
1381 function display_auto_search_results() {
1382 jQuery("#sequence_metadata_section_query").hide();
1383 jQuery("#sequence_metadata_section_auto_search").hide();
1384 jQuery("#sequence_metadata_section_results").show();
1388 * Display an error message
1389 * @param {string} message The message to display (undefined to clear the message)
1391 function display_error(message) {
1392 jQuery('#sequence_metadata_filter_error').html(message ? message : "");
1393 jQuery('#sequence_metadata_filter_error').css('display', message ? 'block' : 'none');
1397 * Clear and hide the error message alert box
1399 function hide_error() {
1404 * Prompt a download of the specified url with the given file name
1405 * @param {String} url URL to download
1406 * @param {String} name Name to give the downloaded file
1408 function download(url, name) {
1409 var a = document.createElement('a');
1412 document.body.appendChild(a);
1414 document.body.removeChild(a);
1421 select option:disabled {
1426 #sequence_metadata_filter_type_protocol_url_selection {
1429 #sequence_metadata_filter_type_protocol_url_selection_names {
1433 .sequence_metadata_marker_search_markers_list {
1437 #sequence_metadata_filter_results {
1438 border: 1px solid #ddd;