5 /search/variants/index.mas - Unified Marker / Variants search page
9 This page allows the user to search for mapped and genotyped markers using various
10 filter criteria, such as the marker name and/or position.
14 David Waring <djw64@cornell.edu>
20 <& /page/page_title.mas, title=>"Marker Search" &>
25 <!-- FILTER BY NAME -->
26 <&| /page/info_section.mas, title=>'Filter By Name', collapsible=>1, collapsed=>0 &>
30 <div class="form-group">
31 <label class="col-sm-3 control-label">Marker Name: </label>
32 <div class="col-sm-4">
33 <select id="marker-name-match" class="form-control">
34 <option value="exactly">Exactly</option>
35 <option value="contains">Contains</option>
36 <option value="starts_with">Starts With</option>
37 <option value="ends_with">Ends With</option>
40 <div class="col-sm-5">
41 <input id="marker-name" class="form-control" type="text" placeholder="Any Name" />
52 <!-- FILTER BY POSITION -->
53 <&| /page/info_section.mas, title=>'Filter By Position', collapsible=>1, collapsed=>0 &>
56 <!-- Reference Genome / Map -->
57 <div class="form-group">
58 <label class="col-sm-3 control-label">Reference Genome / Map: </label>
59 <div class="col-sm-9">
60 <select id="marker-refmap" class="form-control" disabled>
61 <option>Loading...</option>
69 <div class="form-group">
70 <label class="col-sm-3 control-label">Chromosome: </label>
71 <div class="col-sm-9">
72 <select id="marker-chromosome" class="form-control" disabled>
73 <option>Loading...</option>
80 <!-- Start / End Position -->
81 <div class="form-group">
82 <label class="col-sm-3 control-label">Start: </label>
83 <div class="col-sm-4">
84 <div class="input-group">
85 <input id="marker-position-start" class="form-control" type="text" disabled />
86 <span class="input-group-addon marker-position-units"></span>
89 <label class="col-sm-1 control-label">End: </label>
90 <div class="col-sm-4">
91 <div class="input-group">
92 <input id="marker-position-end" class="form-control" type="text" disabled />
93 <span class="input-group-addon marker-position-units"></span>
105 <!-- ADDITIONAL FILTERS -->
106 <&| /page/info_section.mas, title=>'Additional Filters', collapsible=>1, collapsed=>0 &>
110 <div class="form-group">
111 <label class="col-sm-3 control-label">Species: </label>
112 <div class="col-sm-9">
113 <select id="marker-species" class="form-control" disabled>
114 <option value=''>Loading...</option>
122 <div class="form-group">
123 <label class="col-sm-3 control-label">Protocol: </label>
124 <div class="col-sm-9">
125 <select id="marker-protocol" class="form-control" disabled>
126 <option value=''>Loading...</option>
137 <button class="btn btn-primary btn-block marker-search-button">Search</button>
141 <script type="text/javascript">
143 // Pre-selected values from query params
144 let SEL_MARKER_NAME, SEL_MARKER_NAME_MATCH;
145 let SEL_REFMAP, SEL_CHROM, SEL_START, SEL_END;
146 let SEL_SPECIES, SEL_PROTOCOL;
148 let REFMAPS = undefined; // References and maps retrieved from the database, grouped by marker type
149 let CHROMOSOMES = undefined; // Chromosomes retrieved from the database, grouped by marker type
150 let SPECIES = undefined; // List of supported species, based on the refmaps
151 let PROTOCOLS = undefined; // Protocols retrieved from the database, grouped by marker type
154 jQuery(document).ready(function() {
156 // Search Setup Functions
162 // Click / Change Listeners
163 jQuery('#marker-refmap').change(updateChromosomes).change(updatePositions).change(updateSpecies).change(updateProtocols);
164 jQuery('#marker-chromosome').change(updatePositions);
165 jQuery('#marker-species').change(updateRefMaps).change(updateProtocols);
166 jQuery('#marker-protocol').change(updateSpecies).change(updateRefMaps);
167 jQuery('.marker-search-button').click(search);
173 * Parse query arguments for initial search values
175 function parseArgs() {
176 const params = new URLSearchParams(window.location.search);
177 SEL_MARKER_NAME = _getValue("marker_name");
178 SEL_MARKER_NAME_MATCH = _getValue("marker_name_match");
179 SEL_REFMAP = _getValue("refmap");
180 SEL_SPECIES = _getValue("species");
181 SEL_CHROM = _getValue("chrom");
182 SEL_START = _getValue("start");
183 SEL_END = _getValue("end");
184 SEL_PROTOCOL = _getValue("protocol");
186 // Set the initial value of items that do need to be fetched from the DB
187 if ( SEL_MARKER_NAME ) jQuery("#marker-name").val(SEL_MARKER_NAME);
188 if ( SEL_MARKER_NAME_MATCH ) jQuery("#marker-name-match").val(SEL_MARKER_NAME_MATCH);
189 if ( SEL_REFMAP && SEL_CHROM && SEL_START ) jQuery("#marker-position-start").val(SEL_START);
190 if ( SEL_REFMAP && SEL_CHROM && SEL_END ) jQuery("#marker-position-end").val(SEL_END);
192 function _getValue(key, def) {
193 return params.has(key) ? params.get(key) : def;
199 // DATABASE QUERY FUNCTIONS
203 * Get the genotyped marker reference genomes and the mapped
204 * marker maps to display as options for the position filter
205 * - call getReferences()
207 * - call updateRefMaps()
209 function getRefMaps() {
210 let references = undefined;
211 let maps = undefined;
213 getReferences(function(_references) {
214 references = _references;
217 getMaps(function(_maps) {
223 if ( references && maps ) {
225 genotyped: references,
235 * Get the reference genomes and species of the genotyped markers
236 * @param {function} callback Callback function(references)
238 function getReferences(callback) {
241 url: '/ajax/markers/genotyped/reference_genomes',
243 success: function(data) {
244 return callback(data && data.reference_genomes ? data.reference_genomes : []);
247 alert("ERROR: Could not get reference genomes");
253 * Get the maps of the mapped markers
254 * @param {function} callback Callback function(maps)
256 function getMaps(callback) {
259 url: '/ajax/markers/mapped/maps',
261 success: function(data) {
262 return callback(data && data.maps ? data.maps : []);
265 alert("ERROR: Could not get maps");
272 * Get the chromosome names associated with genotyped and mapped markers
273 * - Call getGenotypedChromosomes()
274 * - Call getMappedChromosomes()
275 * - Set the CHROMOSOMES objects
276 * - Call updateChromosomes()
278 function getChromosomes() {
279 let genotyped = undefined;
280 let mapped = undefined;
282 getGenotypedChromosomes(function(_genotyped) {
283 genotyped = _genotyped;
286 getMappedChromosomes(function(_mapped) {
292 if ( genotyped && mapped ) {
294 genotyped: genotyped,
304 * Get the chromosome names associated with genotyped markers
305 * @param {function} callback Callback function(chromosomes)
307 function getGenotypedChromosomes(callback) {
310 url: '/ajax/markers/genotyped/chromosomes',
312 success: function(data) {
313 return callback(data && data.chromosomes ? data.chromosomes : []);
316 alert("ERROR: Could not get genotyped chromosomes");
323 * Get the chromosomes names associated with mapped markers
324 * @param {function} callback Callback function(chromosomes)
326 function getMappedChromosomes(callback) {
329 url: '/ajax/markers/mapped/chromosomes',
331 success: function(data) {
332 return callback(data && data.chromosomes ? data.chromosomes : []);
335 alert("ERROR: Could not get mapped chromosomes");
342 * Get the protocols associated with genotyped and mapped markers
343 * - Call getGenotypedProtocols()
344 * - Call getMappedProtocols()
345 * - Set the PROTOCOLS objects
346 * - Call updateProtocols()
348 function getProtocols() {
349 let genotyped = undefined;
350 let mapped = undefined;
352 getGenotypedProtocols(function(_genotyped) {
353 genotyped = _genotyped;
356 getMappedProtocols(function(_mapped) {
362 if ( genotyped && mapped ) {
364 genotyped: genotyped,
374 * Get the protocols associated with genotyped markers
375 * @param {function} callback Callback function(protocols)
377 function getGenotypedProtocols(callback) {
380 url: '/ajax/markers/genotyped/protocols',
382 success: function(data) {
383 return callback(data && data.protocols ? data.protocols : []);
386 alert("ERROR: Could not get genotyped protocols");
393 * Get the protocols associated with mapped markers
394 * @param {function} callback Callback function(protocols)
396 function getMappedProtocols(callback) {
399 url: '/ajax/markers/mapped/protocols',
401 success: function(data) {
402 return callback(data && data.protocols ? data.protocols : []);
405 alert("ERROR: Could not get mapped protocols");
417 * Display the reference genomes and maps in the position filter
418 * - Build the list of species used by the species select box
419 * - call updateChromosomes() and updateSpecies() when done
421 function updateRefMaps() {
422 let refmap = jQuery("#marker-refmap").val();
423 let protocol_refmap = jQuery("#marker-protocol option:selected").data("refmap");
424 let protocol_type = jQuery("#marker-protocol option:selected").data("type");
425 let species = jQuery("#marker-species").val();
427 if ( !REFMAPS || !REFMAPS.genotyped || !REFMAPS.mapped ) return;
428 let references = REFMAPS.genotyped;
429 let maps = REFMAPS.mapped;
432 let ad = protocol_refmap && !Array.isArray(protocol_refmap) || (Array.isArray(protocol_refmap) && protocol_refmap.length === 1) ? 'disabled' : '';
433 let html = "<option value='' " + ad + ">Any Reference Genome / Map</option>";
435 // Add Genotype Reference Genomes
436 html += "<optgroup label='Reference Genomes of Genotyped Markers'>";
437 let gd = (protocol_type && protocol_type !== 'genotyped') || protocol_refmap ? 'disabled' : '';
438 html += "<option value='' data-type='genotyped' " + gd + ">Any Reference Genome</option>";
439 for ( let i = 0; i < references.length; i++ ) {
440 let d = (protocol_refmap && protocol_refmap !== references[i].reference_genome_name)
441 || (protocol_type && protocol_type !== 'genotyped')
442 || (species && references[i].species_name !== species)
445 let s = d !== 'disabled' && refmap === references[i].reference_genome_name ? 'selected' : '';
446 html += "<option value='" + references[i].reference_genome_name + "' ";
447 html += "data-type='genotyped' data-units='bp' data-species='" + references[i].species_name + "' ";
448 html += d + " " + s + ">";
449 html += references[i].reference_genome_name + " (" + references[i].species_name + ")";
452 if ( !SPECIES.includes(references[i].species_name) ) SPECIES.push(references[i].species_name);
454 html += "</optgroup>";
457 html += "<optgroup label='Maps of Mapped Markers'>";
458 let md = (protocol_type && protocol_type !== 'mapped') || protocol_refmap ? 'disabled' : '';
459 html += "<option value='' data-type='mapped' " + md + ">Any Map</option>";
460 for ( let i = 0; i < maps.length; i++ ) {
461 let d = (protocol_type === 'mapped' && protocol_refmap && !protocol_refmap.includes(maps[i].map_id+''))
462 || (protocol_type && protocol_type !== 'mapped')
463 || (species && maps[i].species_name !== species)
465 let s = d !== 'disabled' && refmap === maps[i].map_id+'' ? 'selected' : '';
466 html += "<option value='" + maps[i].map_id + "' "
467 html += "data-type='mapped' data-units='" + maps[i].map_units + "' data-species='" + maps[i].species_name + "' data-map='" + maps[i].map_name + "' ";
468 html += d + " " + s + ">";
469 html += maps[i].map_name + " (" + maps[i].species_name + ")";
472 if ( !SPECIES.includes(maps[i].species_name) ) SPECIES.push(maps[i].species_name);
474 html += "</optgroup>";
476 jQuery("#marker-refmap").html(html);
477 jQuery("#marker-refmap").attr('disabled', false);
479 // Set intial value, if not already set
481 jQuery("#marker-refmap").val(SEL_REFMAP);
482 SEL_REFMAP = undefined;
491 * Update the list of chromosomes based on the selected ref / map
493 function updateChromosomes() {
494 let chrom = jQuery("#marker-chromosome").val();
495 let refmap = jQuery("#marker-refmap").val();
496 let type = jQuery("#marker-refmap option:selected").data("type");
497 let species = jQuery("#marker-refmap option:selected").data("species");
498 let map = jQuery("#marker-refmap option:selected").data("map");
504 if ( type === 'genotyped' ) {
505 chroms = CHROMOSOMES.genotyped[species][refmap];
507 else if ( type === 'mapped' ) {
508 chroms = CHROMOSOMES.mapped[map];
511 html += "<option value=''>Any Chromosome</option>";
512 for ( let i = 0; i < chroms.length; i++ ) {
513 let s = chrom && chroms[i] === chrom ? 'selected' : '';
514 html += "<option value='" + chroms[i] + "' " + s + ">" + chroms[i] + "</option>";
517 jQuery("#marker-chromosome").html(html);
518 jQuery("#marker-chromosome").attr("disabled", false);
520 // Set intial value, if not already set
522 jQuery("#marker-chromosome").val(SEL_CHROM);
523 SEL_CHROM = undefined;
531 * Update the start/end postions based on the selected ref/map and chrom
532 * - update position units
533 * - enable fields only when ref/map and chrom have been selected
535 function updatePositions() {
536 let refmap = jQuery("#marker-refmap").val();
537 let chrom = jQuery("#marker-chromosome").val();
538 let units = jQuery("#marker-refmap option:selected").data("units");
540 let pos_disabled = refmap === '' || chrom === null || ['', 'Loading...'].includes(chrom);
541 jQuery(".marker-position-units").html(units ? units : ' ');
542 jQuery("#marker-position-start").attr("disabled", pos_disabled);
543 jQuery("#marker-position-end").attr("disabled", pos_disabled);
544 if ( pos_disabled ) {
545 jQuery("#marker-position-start").val("");
546 jQuery("#marker-position-end").val("");
552 * Update the list of species
554 function updateSpecies() {
555 let refmap_species = jQuery("#marker-refmap option:selected").data("species");
556 let protocol_species = jQuery("#marker-protocol option:selected").data("species");
557 let species = jQuery("#marker-species").val();
560 let ad = refmap_species
561 || (protocol_species && !Array.isArray(protocol_species))
562 || (protocol_species && Array.isArray(protocol_species) && protocol_species.length === 1)
564 let html = "<option value='' " + ad + ">Any Species</option>";
565 for ( let i = 0; i < SPECIES.length; i++ ) {
566 let d = (refmap_species && refmap_species !== SPECIES[i])
567 || (protocol_species && !Array.isArray(protocol_species) && protocol_species !== SPECIES[i])
568 || (protocol_species && Array.isArray(protocol_species) && !protocol_species.includes(SPECIES[i]))
570 let s = d !== 'disabled' && SPECIES[i] === species ? 'selected' : '';
571 html += "<option value='" + SPECIES[i] + "' " + d + " " + s + ">";
576 jQuery("#marker-species").html(html);
577 jQuery("#marker-species").attr("disabled", false);
579 // Set intial value, if not already set
581 jQuery("#marker-species").val(SEL_SPECIES);
582 SEL_SPECIES = undefined;
589 * Update the list of protocols
591 function updateProtocols() {
592 let protocol = jQuery("#marker-protocol").val();
593 let refmap = jQuery("#marker-refmap").val();
594 let type = jQuery("#marker-refmap option:selected").data("type");
595 let species = jQuery("#marker-species").val();
599 html += "<option value=''>Any Protocol</option>";
601 html += "<optgroup label='Protocols of Genotyped Markers'>";
602 for ( let i = 0; i < PROTOCOLS.genotyped.length; i++ ) {
603 let pi = PROTOCOLS.genotyped[i].nd_protocol_id;
604 let pn = PROTOCOLS.genotyped[i].nd_protocol_name;
605 let sn = PROTOCOLS.genotyped[i].species_name;
606 let rn = PROTOCOLS.genotyped[i].reference_genome_name;
608 let d = (type && type !== 'genotyped') || (refmap && refmap !== rn) || (species && sn !== species) ? 'disabled' : ''
609 let s = d !== 'disabled' && protocol && pn === protocol ? 'selected' : '';
610 html += "<option value='" + pi + "' data-type='genotyped' data-species='" + sn + "' data-refmap='" + rn + "' " + d + " " + s + ">";
611 html += pn + " (" + sn + " / " + rn + ")";
615 html += "<optgroup label='Protocols of Mapped Markers'>";
616 for ( let i = 0; i < PROTOCOLS.mapped.length; i++ ) {
617 let pn = PROTOCOLS.mapped[i].protocol;
618 let ss = PROTOCOLS.mapped[i].species;
619 let ss_str = JSON.stringify(ss);
620 let ms = PROTOCOLS.mapped[i].maps;
623 for ( let j = 0; j < ms.length; j++ ) {
624 mns.push(ms[j].map_name);
625 mis.push(ms[j].map_id+'');
627 let mis_str = JSON.stringify(mis);
629 let d = (type && type !== 'mapped') || (refmap && !mis.includes(refmap)) || (species && !ss.includes(species)) ? 'disabled' : '';
630 let s = d !== 'disabled' && protocol && protocol === pn ? 'selected' : '';
631 html += "<option value='" + pn + "' data-type='mapped' data-species='" + ss_str + "' data-refmap='" + mis_str + "' " + d + " " + s + ">";
632 html += pn + " (" + ss.join('; ') + " / " + mns.join('; ') + ")";
636 jQuery("#marker-protocol").html(html);
637 jQuery("#marker-protocol").attr("disabled", false);
639 // Set intial value, if not already set
640 if ( SEL_PROTOCOL ) {
641 jQuery("#marker-protocol").val(SEL_PROTOCOL);
642 SEL_PROTOCOL = undefined;
655 * Perform the marker search with the user input
656 * - Build results URL
657 * - Go to the marker search results page
662 let marker_name = jQuery('#marker-name').val();
663 let marker_name_match = jQuery('#marker-name-match').val();
664 let refmap = jQuery("#marker-refmap").val();
665 let refmap_type = jQuery("#marker-refmap option:selected").data("type");
666 let refmap_species = jQuery("#marker-refmap option:selected").data("species");
667 let chrom = jQuery("#marker-chromosome").val();
668 let start = jQuery("#marker-position-start").val();
669 let end = jQuery("#marker-position-end").val();
670 let species = jQuery("#marker-species").val();
671 let protocol = jQuery("#marker-protocol").val();
672 let protocol_type = jQuery("#marker-protocol option:selected").data("type");
674 // Check for required params
675 if ( (start && !end) || (end && !start) ) {
676 alert("Both the start and end position are required when filtering by position");
682 if ( marker_name ) params.marker_name = marker_name;
683 if ( marker_name_match ) params.marker_name_match = marker_name_match;
684 if ( refmap_type || protocol_type ) params.type = refmap_type || protocol_type;
685 if ( refmap ) params.refmap = refmap;
686 if ( refmap_species || species ) params.species = refmap_species || species;
687 if ( chrom && chrom !== 'Loading...' ) params.chrom = chrom;
688 if ( start ) params.start = start;
689 if ( end ) params.end = end;
690 if ( protocol ) params.protocol = protocol;
692 // Go to results page to perform search
693 let q = new URLSearchParams(params);
694 let url = "/search/variants/results?" + q.toString();
695 window.location = url;
704 h2.marker-search-header {
707 border-bottom: 1px solid #ccc;
710 button.marker-search-button {