4 $sp_person_autocomplete_uri => '/ajax/people/autocomplete'
5 $trait_autocomplete_uri => '/ajax/stock/trait_autocomplete'
6 $onto_autocomplete_uri => '/ajax/cvterm/autocomplete'
11 $breeding_programs => undef
12 $editable_stock_props => undef
15 <& /util/import_javascript.mas, classes => [ 'jquery', 'jquery.dataTables', 'CXGN.Effects', 'CXGN.List' ] &>
17 <div class="container-fluid">
19 <& /page/page_title.mas, title=>'Search Accessions and Plots' &>
24 use CXGN::Page::FormattingHelpers qw / conditional_like_input_html simple_selectbox_html/;
26 my $accession_cvterm_id;
28 for (my $i=0; $i<= scalar(@$stock_types); $i++) {
29 if ( $stock_types->[$i][1] eq "accession" ) {
30 $accession_cvterm_id = $stock_types->[$i][0];
35 my $stock_type_select = simple_selectbox_html(
36 choices => $stock_types,
37 id => "stock_type_select",
38 selected => $accession_cvterm_id
41 my $organism_select = simple_selectbox_html(
42 choices => $organisms ,
43 id => "organism_select",
46 my $breeding_programs_select = simple_selectbox_html(
47 choices => $breeding_programs,
48 id => "breeding_program" ,
53 <&| /page/info_section.mas, title => 'Search', collapsible=>1, collapsed=>0 &>
55 <div id="stock_search_form" class="well well-sm">
57 <&| /page/info_section.mas, title => 'Name Search', collapsible=>1, collapsed=>0 &>
60 <div class="col-sm-11">
61 <div class="form-group form-group-sm">
62 <label class="col-sm-3 control-label">Stock Name or Description: </label>
63 <div class="col-sm-9" >
64 <div class="form-group form-group-sm">
65 <div class="input-group">
66 <span class="input-group-btn" width="20%">
67 <select class="form-control" id="any_name_matchtype" name="any_name_matchtype">
68 <option title="starts_with" value="starts_with" selected="selected">starts with</option>
69 <option title="contains" value="contains">contains</option>
70 <option title="ends with" value="ends_with">ends with</option>
71 <option title="exactly" value="exactly">exactly</option>
74 <span class="input-group-btn">
75 <input class="form-control" name="any_name" id="any_name" value="" size="30" type="text" placeholder="Type search here..."/>
79 <div class="form-group form-group-sm">
80 <label class="col-sm-3 control-label">Stock Type: </label>
81 <div class="col-sm-9" >
82 <% $stock_type_select %>
91 <&| /page/info_section.mas, title => 'Advanced Search', collapsible=>1, collapsed=>1, id=>'advanced_search_panel' &>
93 <&| /page/info_section.mas, title => 'Properties', collapsible=>1, collapsed=>1 &>
96 <div class="col-sm-6">
97 <div class="form-group form-group-sm">
98 <label class="col-sm-3 control-label">Organism: </label>
99 <div class="col-sm-9" >
100 <% $organism_select %>
106 <div class="col-sm-6">
107 <div class="form-group form-group-sm">
108 <label class="col-sm-4 control-label">Organization: </label>
109 <div class="col-sm-8" >
110 <input type="text" class="form-control" id="organization" placeholder="Type to Autocomplete"/>
118 <div class="col-sm-11">
119 <div class="form-group form-group-sm">
120 <label class="col-sm-3 control-label">Search By Another Property: </label>
121 <div class="col-sm-9" >
122 <div class="form-group">
123 <div class="input-group">
124 <span class="input-group-btn">
125 <select class="form-control" id="editable_stockprop_search_term" name="editable_stockprop_search_term">
126 % foreach my $stockprop (@$editable_stock_props){
127 <option title="<% $stockprop %>" value="<% $stockprop %>" ><% $stockprop %></option>
131 <span class="input-group-btn">
132 <button class="btn btn-primary btn-sm" id="editable_stockprop_search_add">Add</button>
141 <div id="editable_stockprop_search_div">
145 <&| /page/info_section.mas, title => 'Usage', collapsible=>1, collapsed=>1 &>
148 <div class="col-sm-6">
149 <div class="form-group form-group-sm">
150 <label class="col-sm-3 control-label">Project Name: </label>
151 <div class="col-sm-9" >
152 <input type="text" class="form-control" id="project" placeholder="Type to Autocomplete"/>
156 <div class="col-sm-6">
157 <div class="form-group form-group-sm">
158 <label class="col-sm-3 control-label">Project Location: </label>
159 <div class="col-sm-9" >
160 <input type="text" class="form-control" id="location" placeholder="Type to Autocomplete"/>
166 <div class="col-sm-6">
167 <div class="form-group form-group-sm">
168 <label class="col-sm-3 control-label">Project Year: </label>
169 <div class="col-sm-9" >
170 <input type="text" class="form-control" id="year" placeholder="Type to Autocomplete"/>
174 <div class="col-sm-6">
175 <div class="form-group form-group-sm">
176 <label class="col-sm-3 control-label">Breeding Program: </label>
177 <div class="col-sm-9" >
178 <% $breeding_programs_select %>
185 <&| /page/info_section.mas, title => 'Phenotypes', collapsible=>1, collapsed=>1 &>
188 <div class="col-sm-6">
189 <div class="form-group form-group-sm">
190 <label class="col-sm-3 control-label">Trait: </label>
191 <div class="col-sm-9" >
192 <input type="text" class="form-control" id="trait" placeholder="Type to Autocomplete"/>
196 <div class="col-sm-6">
197 <div class="form-group form-group-sm">
198 <label class="col-sm-3 control-label">Minimum Trait Value: </label>
199 <div class="col-sm-9" >
200 <input type="text" class="form-control" id="minimum_trait_value" />
206 <div class="col-sm-6">
207 <div class="form-group form-group-sm">
208 <label class="col-sm-3 control-label">Maximum Trait Value: </label>
209 <div class="col-sm-9" >
210 <input type="text" class="form-control" id="maximum_trait_value" />
217 <&| /page/info_section.mas, title => 'Search Accessions Using Genotypes', collapsible=>1, collapsed=>1 &>
219 <a href="/search/stocks_using_genotypes"><p>Go to Search Accessions Using Genotypes Page.</p></a>
223 </&> <!-- closes advanced search -->
226 <button class="btn btn-primary" id="submit_stock_search" />Search</button>
234 <&| /page/info_section.mas, title => 'Search Results', collapsible=>1, collapsed=>0 &>
236 <div class="well well-sm">
239 <div class="col-sm-11">
240 <div class="form-group form-group-sm">
241 <label class="col-sm-4 control-label">View Another Property: </label>
242 <div class="col-sm-8" >
243 <div class="form-group">
244 <div class="input-group">
245 <span class="input-group-btn">
246 <select class="form-control" id="editable_stockprop_view_column_term" name="editable_stockprop_view_column_term">
247 % foreach my $stockprop (sort { "\L$a" cmp "\L$b" } @$editable_stock_props){
248 <option title="<% $stockprop %>" value="<% $stockprop %>" ><% $stockprop %></option>
252 <span class="input-group-btn">
253 <button class="btn btn-primary btn-sm" id="editable_stockprop_view_column_add">Add</button>
262 <div class="panel panel-default">
263 <div class="panel-body">
264 <div id="stock_search_results_div" style="overflow:scroll" >
265 <table id="stock_search_results" width="100%" class="table table-hover table-striped">
272 <th>Organization</th>
281 <div class="panel panel-default">
282 <div class="panel-body">
283 <&| /page/info_section.mas, title => 'Copy Results to a List', collapsible=>1, collapsed=>0, subtitle=>'<i>Copy the stock names currently showing in the search results table to a new or exisiting list</i>'&>
285 <div style="text-align:right" id="results_to_list_menu"></div>
286 <div id="search_result_names" style="display: none;"></div>
295 <& /page/detail_page_2_col_section.mas, info_section_title => "<h4 style='display:inline'>Graphical Filtering on Phenotype Performance</h4>", info_section_subtitle => 'Graphically filter an accessions average performance for combinations of traits', icon_class => "glyphicon glyphicon-equalizer", info_section_id => "stocks_search_graphical_filtering" &>
304 jQuery(document).ready(function () {
306 window.addEventListener('load', () => {
307 // Reset the advanced search form parameters on load.
308 // Very similar to later functionality but that one is for advanced search and on load trigger only.
309 var input_list = document.getElementById('advanced_search_panel_content').getElementsByTagName('input');
310 for (let input_elem of input_list) {
311 input_elem.value = '';
314 var select_list = document.getElementById('advanced_search_panel_content').getElementsByTagName('select');
315 for (let select_elem of select_list) {
316 select_elem.selectedIndex = 0;
319 var type_select = document.getElementById('stock_type_select');
320 type_select.selectedIndex = [...type_select.options].findIndex(elem => elem.text.trim().toLowerCase() == 'accession') ?? 1;
324 const textFields = document.querySelectorAll('input[type="text"]');
325 // Loop through the text fields and set their values to an empty string
326 textFields.forEach(textField => {
327 textField.value = '';
330 // Get all the dropdown select elements in the form
331 // Reset all but 'stock_type_select' dropdown to zero value
332 // Leave 'stock_type_select' with the default "selected" value from the mason file.
333 const selectFields = document.querySelectorAll('select');
334 selectFields.forEach(selectField => {
335 if (selectField.id != 'stock_type_select') {
336 selectField.selectedIndex = 0;
340 jQuery("#person").autocomplete({
341 source: '<% $sp_person_autocomplete_uri %>'
343 jQuery("#trait").autocomplete({
344 source: '<% $trait_autocomplete_uri %>'
346 jQuery("#onto").autocomplete({
347 source: '<% $onto_autocomplete_uri %>' + "?db_name=" + '<% $trait_db_name %>'
349 jQuery("#project").autocomplete({
350 source: '/ajax/stock/project_autocomplete',
352 jQuery("#any_name").autocomplete({
353 source: '/ajax/stock/stock_autocomplete?stock_type_id='+jQuery('#stock_type_select option:selected').val(),
355 jQuery('#stock_type_select').change(function(){
356 jQuery("#any_name").autocomplete({
357 source: '/ajax/stock/stock_autocomplete?stock_type_id='+jQuery('#stock_type_select option:selected').val(),
360 jQuery("#location").autocomplete({
361 source: '/ajax/stock/geolocation_autocomplete',
363 jQuery("#year").autocomplete({
364 source: '/ajax/stock/project_year_autocomplete',
366 jQuery("#organization").autocomplete({
367 source: '/ajax/stock/stockproperty_autocomplete?property=organization',
370 // Update changed organization
371 jQuery(document).on("change", "#organization", function() {
372 editable_stockprops_search["organization"] = {
373 "matchtype": "contains",
374 "value": jQuery(this).val()
378 var editable_stockprops_search = {};
381 jQuery('#editable_stockprop_search_add').click( function(){
382 // console.log("editable stockprops search is "+JSON.stringify(editable_stockprops_search));
383 // console.log("Value is "+jQuery('#editable_stockprop_search_term').val());
384 editable_stockprops_search[jQuery('#editable_stockprop_search_term').val()] = {"matchtype": "contains"};
385 console.log("editable stockprops search is "+JSON.stringify(editable_stockprops_search));
386 _draw_editable_stockprops_search_fields();
389 jQuery(document).on('click', 'button[name="editable_stockprop_search_remove"]', function(){
390 delete editable_stockprops_search[jQuery(this).data('property')];
391 _draw_editable_stockprops_search_fields();
394 jQuery(document).on("keyup", 'input[name="editable_stockprop_search_inputs"]', function(){
395 //Store typed value in object, in case of redraw inputs (input deletion or addition) the value will be still there. Also for search submission
396 editable_stockprops_search[jQuery(this).data('property')]["value"] = jQuery(this).val();
397 console.log("editable stockprops search is "+JSON.stringify(editable_stockprops_search));
400 jQuery(document).on("change", 'select[name="editable_stockprop_matchtype"]', function(){
402 // jQuery(document).on("change", 'input[name="editable_stockprop_matchtype"]', function(){
403 //Store typed value in object, in case of redraw inputs (input deletion or addition) the value will be still there. Also for search submission
404 console.log("prop is "+jQuery(this).data('property'));
405 editable_stockprops_search[jQuery(this).data('property')]["matchtype"] = jQuery(this).val();
406 console.log("editable stockprops search is "+JSON.stringify(editable_stockprops_search));
409 function _draw_editable_stockprops_search_fields(){
410 jQuery('#editable_stockprop_search_div').html('');
411 for (var property in editable_stockprops_search) {
412 if (editable_stockprops_search.hasOwnProperty(property)) {
413 jQuery('#editable_stockprop_search_div').append('<div class="form-group form-group-sm"><label class="col-sm-3 control-label">'+property+': </label><div class="col-sm-9" ><div class="form-group"><div class="input-group"><span class="input-group-btn" width="20%"><select class="form-control" id="editable_stockprop_matchtype" name="editable_stockprop_matchtype" data-property="'+property+'"><option title="contains" value="contains" selected="selected">contains</option><option title="starts with" value="starts_with">starts with</option><option title="ends with" value="ends_with">ends with</option><option title="exactly" value="exactly">exactly</option><option title="one of" value="one of">one of (separate multiple possibilities with commas)</option></select></span><span class="input-group-btn"><input type="text" class="form-control" name="editable_stockprop_search_inputs" id="'+property+'_input_id" data-property="'+property+'" placeholder="Type to Autocomplete"/></span><span class="input-group-btn"><button name="editable_stockprop_search_remove" data-property="'+property+'" class="btn"><span class="glyphicon glyphicon-remove"></span></button></span></div></div></div></div>');
415 jQuery('#editable_stockprop_search_div').find("#"+property+"_input_id").autocomplete({
416 source: '/ajax/stock/stockproperty_autocomplete?property='+property,
422 //For adding stockprop columns to search result
423 var stockprop_extra_columns_view = { 'organization':1 };
424 var stockprop_extra_columns_view_array = [ 'organization' ];
425 jQuery('#editable_stockprop_view_column_add').click(function(){
426 var selected_property = jQuery('#editable_stockprop_view_column_term').val();
427 if (!(selected_property in stockprop_extra_columns_view)){
428 stockprop_extra_columns_view[selected_property] = 1;
429 stockprop_extra_columns_view_array.push(selected_property);
431 var table_header_html = '<table id="stock_search_results" class="table table-hover table-striped"><thead><tr><th>Stock Name</th><th>Stock Type</th><th>Organism</th><th>Synonyms</th>';
432 for (var i=0; i<stockprop_extra_columns_view_array.length; i++){
433 table_header_html = table_header_html + '<th>' + stockprop_extra_columns_view_array[i] + '</th>';
435 table_header_html = table_header_html + '</tr></thead></table>';
436 jQuery('#stock_search_results_div').html(table_header_html);
438 stock_table.destroy();
439 _load_stock_search_results(jQuery('#stock_type_select option:selected').text(), editable_stockprops_search, stockprop_extra_columns_view, stockprop_extra_columns_view_array);
442 parseArgs(editable_stockprops_search);
444 jQuery('#stock_search_results').on( 'draw.dt', function () {
445 var name_links = stock_table.column(0).data();
448 for (var i = 0; i < name_links.length; i++) { //extract text from anchor tags
449 names.push(name_links[i].match(/<a [^>]+>([^<]+)<\/a>/)[1]+'\n');
452 jQuery('#search_result_names').html(names);
453 addToListMenu('results_to_list_menu', 'search_result_names', {
454 listType: jQuery('#stock_type_select option:selected').text()+'s' || 'null'
459 jQuery('#submit_stock_search').click( function() {
460 //stock_table.search("stock_search_results").draw();
461 _load_stock_search_results(jQuery('#stock_type_select option:selected').text(), editable_stockprops_search, stockprop_extra_columns_view, stockprop_extra_columns_view_array);
464 jQuery('#stock_search_form').keypress( function( e ) {
465 var code = e.keyCode || e.which;
467 jQuery('#submit_stock_search').click();
472 // For graphical filtering, using same search params
474 jQuery('#stock_search_graphical_filter_onswitch').on("click", function(){
475 var stock_type = jQuery('#stock_type_select option:selected').text();
476 if (stock_type == 'accession'){
478 url : '/ajax/search/stocks',
481 any_name : jQuery('#any_name').val(),
482 any_name_matchtype : jQuery('#any_name_matchtype').val(),
483 stock_type : jQuery('#stock_type_select option:selected').text(),
484 organism : jQuery('#organism_select').val(),
485 person : jQuery('#person').val(),
486 trait : jQuery('#trait').val(),
487 minimum_trait_value : jQuery('#minimum_trait_value').val(),
488 maximum_trait_value : jQuery('#maximum_trait_value').val(),
489 project : jQuery('#project').val(),
490 location : jQuery('#location').val(),
491 year : jQuery('#year').val(),
492 breeding_program : jQuery('#breeding_program').val(),
493 editable_stockprop_values : JSON.stringify(editable_stockprops_search)
495 beforeSend: function() {
496 jQuery("#working_modal").modal("show");
497 jQuery('#stock_search_graphical_filter_div').html("[LOADING...]");
499 success: function(response){
500 //console.log(response);
501 jQuery("#working_modal").modal("hide");
503 for (var i=0; i<response.data.length; i++){
504 stock_ids.push(response.data[i][0]);
508 jQuery("#stock_search_graphical_filter_results_div").DataTable().destroy();
509 jQuery("#stock_search_graphical_filter_results_div").html("");
512 params = {"germplasmDbIds" : stock_ids, "observationLevel" : 'plot'};
513 loadBrAPIData("<% $c->config->{main_production_site_url} %>",params,function(response){
514 useBrAPIData(response,true);
518 error: function(response){
519 jQuery("#working_modal").modal("hide");
520 alert("Error retrieving accessions in graphical filtering.");
524 jQuery('#stock_search_graphical_filter_div').html("Graphical Filtering only configured for accessions. Currently searching across "+stock_type+". Change your search to accesions to use graphical filtering.");
531 function parseArgs(editable_stockprops_search) {
532 const params = new URLSearchParams(window.location.search);
533 if ( params.has('any_name') ) {
534 jQuery('#any_name').val(params.get('any_name'));
535 jQuery('#stock_type_select').val(0);
537 if ( params.has('stock_type') ) {
538 var type = params.get('stock_type');
539 var value = jQuery("#stock_type_select option[title="+ type +"]").val();
540 jQuery('#stock_type_select').val(value);
543 if ( params.has('prop') & params.has('value') ) {
544 editable_stockprops_search[params.get('prop')] = {"matchtype": "exactly"};
545 editable_stockprops_search[params.get('prop')]["value"] = params.get('value');
549 function _load_stock_search_results(stock_type, editable_stockprops_search, stockprop_extra_columns_view, stockprop_extra_columns_view_array){
550 console.log(stock_type);
551 console.log("Making AJAX request with editable stockprop "+JSON.stringify(editable_stockprops_search));
552 stock_table = jQuery('#stock_search_results').DataTable({
558 'lengthMenu': [10,20,50,100,1000,5000],
559 'ajax': { 'url': '/ajax/search/stocks',
560 'data': function(d) {
561 d.any_name = jQuery('#any_name').val();
562 d.any_name_matchtype = jQuery('#any_name_matchtype').val();
563 d.stock_type = stock_type;
564 d.organism = jQuery('#organism_select').val();
565 d.person = jQuery('#person').val();
566 d.trait = jQuery('#trait').val();
567 d.minimum_trait_value = jQuery('#minimum_trait_value').val();
568 d.maximum_trait_value = jQuery('#maximum_trait_value').val();
569 d.project = jQuery('#project').val();
570 d.location = jQuery('#location').val();
571 d.year = jQuery('#year').val();
572 d.breeding_program = jQuery('#breeding_program').val();
573 d.editable_stockprop_values = JSON.stringify(editable_stockprops_search);
574 d.extra_stockprop_columns_view = JSON.stringify(stockprop_extra_columns_view);
575 d.stockprop_extra_columns_view_array = JSON.stringify(stockprop_extra_columns_view_array);