minor message
[sgn.git] / js / CXGN / BreedersToolbox / Accessions.js
blob3249e0554fb6cddf9723afacc5ab0dabddc52579
1 /*jslint browser: true, devel: true */
2 /**
4 =head1 Accessions.js
6 Dialogs for managing accessions
9 =head1 AUTHOR
11 Jeremy D. Edwards <jde22@cornell.edu>
13 =cut
17 var $j = jQuery.noConflict();
18 var list = new CXGN.List();
19 var accessionList;
20 var accession_list_id;
21 var validSpecies;
22 var fuzzyResponse;
23 var fullParsedData;
24 var infoToAdd;
25 var accessionListFound;
26 var speciesNames;
28 function disable_ui() {
29     jQuery('#working_modal').modal("show");
32 function enable_ui() {
33     jQuery('#working_modal').modal("hide");
36 jQuery(document).ready(function ($) {
38     jQuery('#manage_accessions_populations_new').click(function(){
39         jQuery("#create_population_list_div").html(list.listSelect("create_population_list_div", ["accessions"] ));
40         jQuery('#manage_populations_add_population_dialog').modal('show');
41     });
43     jQuery("#create_population_submit").click(function(){
44         jQuery.ajax({
45             type: 'POST',
46             url: '/ajax/population/new',
47             dataType: "json",
48             data: {
49                 'population_name': jQuery('#create_population_name').val(),
50                 'accession_list_id': jQuery('#create_population_list_div_list_select').val(),
51             },
52             beforeSend: function(){
53                 disable_ui();
54             },
55             success: function (response) {
56                 enable_ui();
57                 if (response.error){
58                     alert(response.error);
59                 }
60                 if (response.success){
61                     alert(response.success);
62                 }
63             },
64             error: function () {
65                 alert('An error occurred in adding population. sorry');
66             }
67         });
68     });
70     jQuery('#manage_accessions_populations_onswitch').click( function() {
71       var already_loaded_tables = jQuery('#accordion').find("table");
72       if (already_loaded_tables.length > 0) { return; }
74       jQuery.ajax ( {
75         url : '/ajax/manage_accessions/populations',
76         beforeSend: function() {
77           disable_ui();
78         },
79         success: function(response){
80           var populations = response.populations;
81           for (var i in populations) {
82             var name = populations[i].name;
83             var population_id = populations[i].stock_id;
84             var accessions = populations[i].members;
85             var table_id = name+i+"_pop_table";
87             var section_html = '<div class="row"><div class="panel panel-default"><div class="panel-heading" >';
88             section_html += '<div class="panel-title" name="populations_members_table_toggle" data-table_id="#'+table_id+'" data-population_id="'+population_id+'" data-population_name="'+name+'"><div class="row"><div class="col-sm-6" data-toggle="collapse" data-parent="#accordion" data-target="#collapse'+i+'"><a href="#'+table_id+'" class="accordion-toggle">'+name+'</a></div><div class="col-sm-3"><a href="/stock/'+population_id+'/view"><small>[Go To Population Page]</small></a></div><div class="col-sm-3"><a name="manage_populations_add_accessions" data-population_id="'+population_id+'" data-population_name="'+name+'"><small>[Add Accessions To Population]</small></a><br/><a name="manage_populations_delete_population" data-population_id="'+population_id+'" data-population_name="'+name+'"><small>[Delete Population]</small></a></div></div></div></div>';
89             section_html += '<div id="collapse'+i+'" class="panel-collapse collapse">';
90             section_html += '<div class="panel-body" style="overflow:hidden"><div class="table-responsive" style="margin-top: 10px;"><table id="'+table_id+'" class="table table-hover table-striped table-bordered" width="100%"></table><div id="populations_members_add_to_list_data_'+population_id+'" style="display:none"></div><br/><div id="populations_members_add_to_list_menu_'+population_id+'"></div></div>';
91             section_html += '</div><br/></div></div></div><br/>';
93             jQuery('#accordion').append(section_html);
94           }
95           enable_ui();
96         },
97         error: function(response) {
98           enable_ui();
99           alert('An error occured retrieving population data.');
100         }
101       });
102     });
104     jQuery(document).on("click", "div[name='populations_members_table_toggle']", function(){
105         var table_id = jQuery(this).data('table_id');
106         var population_id = jQuery(this).data('population_id');
107         var population_name = jQuery(this).data('population_name');
109         var table = jQuery(table_id).DataTable({
110             ajax: '/ajax/manage_accessions/population_members/'+population_id,
111             destroy: true,
112             columns: [
113                 { title: "Accession Name", "data": null, "render": function ( data, type, row ) { return "<a href='/stock/"+row.stock_id+"/view'>"+row.name+"</a>"; } },
114                 { title: "Description", "data": "description" },
115                 { title: "Synonyms", "data": "synonyms[, ]" },
116                 { title: "Remove From Population", "data": null, "render": function ( data, type, row ) { return "<a name='populations_member_remove' data-stock_relationship_id='"+row.stock_relationship_id+"'>X</a>"; } },
117             ],
118             "fnInitComplete": function(oSettings, json) {
119                 //console.log(json);
120                 var html = "";
121                 for(var i=0; i<json.data.length; i++){
122                     html += json.data[i].name+"\n";
123                 }
124                 jQuery("#populations_members_add_to_list_data_"+population_id).html(html);
125                 addToListMenu("populations_members_add_to_list_menu_"+population_id, "populations_members_add_to_list_data_"+population_id, {
126                     selectText: true,
127                     listType: 'accessions',
128                     listName: population_name
129                 });
130             }
131         });
133     });
135     var population_id;
136     var population_name;
138     jQuery(document).on("click", "a[name='manage_populations_add_accessions']", function(){
139         population_id = jQuery(this).data('population_id');
140         population_name = jQuery(this).data('population_name');
141         jQuery("#add_accession_to_population_list_div").html(list.listSelect("add_accession_to_population_list_div", ["accessions"] ));
142         jQuery('#add_accession_population_name').html(population_name);
143         jQuery('#manage_populations_add_accessions_dialog').modal('show');
144     });
146     jQuery(document).on("click", "a[name='manage_populations_delete_population']", function(){
147         population_id = jQuery(this).data('population_id');
148         population_name = jQuery(this).data('population_name');
149         jQuery('#delete_population_name').html(population_name);
150         jQuery('#manage_populations_delete_dialog').modal('show');
151     });
153     jQuery("#add_accessions_to_population_submit").click(function(){
154         jQuery.ajax({
155             type: 'POST',
156             url: '/ajax/population/add_accessions',
157             dataType: "json",
158             data: {
159                 'population_name': population_name,
160                 'accession_list_id': jQuery('#add_accession_to_population_list_div_list_select').val(),
161             },
162             beforeSend: function(){
163                 disable_ui();
164             },
165             success: function (response) {
166                 enable_ui();
167                 if (response.error){
168                     alert(response.error);
169                 }
170                 if (response.success){
171                     alert(response.success);
172                 }
173             },
174             error: function () {
175                 alert('An error occurred in adding accessions to population. sorry');
176             }
177         });
178     });
180     jQuery("#delete_population_submit").click(function(){
181         jQuery.ajax({
182             type: 'POST',
183             url: '/ajax/population/delete',
184             dataType: "json",
185             data: {
186                 'population_id': population_id,
187                 'population_name': population_name,
188             },
189             beforeSend: function(){
190                 disable_ui();
191             },
192             success: function (response) {
193                 enable_ui();
194                 if (response.error){
195                     alert(response.error);
196                 }
197                 if (response.success){
198                     alert(response.success);
199                 }
200             },
201             error: function () {
202                 alert('An error occurred in deleting population. sorry');
203             }
204         });
205     });
207     jQuery(document).on("click", "a[name='populations_member_remove']", function(){
208         var stock_relationship_id= jQuery(this).data("stock_relationship_id");
209         if (confirm("Are you sure?")){
210             jQuery.ajax({
211                 url: '/ajax/population/remove_member?stock_relationship_id='+stock_relationship_id,
212                 dataType: "json",
213                 beforeSend: function(){
214                     disable_ui();
215                 },
216                 success: function (response) {
217                     enable_ui();
218                     if (response.error){
219                         alert(response.error);
220                     }
221                     if (response.success){
222                         alert(response.success);
223                     }
224                 },
225                 error: function () {
226                     alert('An error occurred in removing accession from population. sorry');
227                 }
228             });
229         }
230     });
232     function add_accessions(full_info, species_names) {
233         //console.log(full_info);
234         $.ajax({
235             type: 'POST',
236             url: '/ajax/accession_list/add',
237             dataType: "json",
238             timeout: 36000000,
239             data: {
240                 'full_info': JSON.stringify(full_info),
241                 'allowed_organisms': JSON.stringify(species_names),
242             },
243             beforeSend: function(){
244                 disable_ui();
245             },
246             success: function (response) {
247                 enable_ui();
248                 if (response.error) {
249                     alert(response.error);
250                 } else {
251                     var html = 'The following stocks were added!<br/>';
252                     for (var i=0; i<response.added.length; i++){
253                         html = html + '<a href="/stock/'+response.added[i][0]+'/view">'+response.added[i][1]+'</a><br/>';
254                     }
255                     jQuery('#add_accessions_saved_message').html(html);
256                     jQuery('#add_accessions_saved_message_modal').modal('show');
257                 }
258             },
259             error: function () {
260                 alert('An error occurred in processing. sorry');
261             }
262         });
263     }
265     function verify_species_name() {
266         var speciesName = $("#species_name_input").val();
267         validSpecies = 0;
268         $.ajax({
269             type: 'GET',
270             url: '/organism/verify_name',
271             dataType: "json",
272             data: {
273                 'species_name': speciesName,
274             },
275             success: function (response) {
276                 if (response.error) {
277                     alert(response.error);
278                     validSpecies = 0;
279                 } else {
280                     validSpecies = 1;
281                 }
282             },
283             error: function () {
284                 alert('An error occurred verifying species name. sorry');
285                 validSpecies = 0;
286             }
287         });
288     }
290     $('#species_name_input').focusout(function () {
291         verify_species_name();
292     });
294     $('#review_absent_accessions_submit').click(function () {
295         if (fullParsedData == undefined){
296             var speciesName = $("#species_name_input").val();
297             var populationName = $("#population_name_input").val();
298             var organizationName = $("#organization_name_input").val();
299             var accessionsToAdd = accessionList;
300             if (!speciesName) {
301                 alert("Species name required");
302                 return;
303             }
304             if (!populationName) {
305                 populationName = '';
306             }
307             if (!accessionsToAdd || accessionsToAdd.length == 0) {
308                 alert("No accessions to add");
309                 return;
310             }
311             for(var i=0; i<accessionsToAdd.length; i++){
312                 infoToAdd.push({
313                     'species':speciesName,
314                     'defaultDisplayName':accessionsToAdd[i],
315                     'germplasmName':accessionsToAdd[i],
316                     'organizationName':organizationName,
317                     'populationName':populationName,
318                 });
319                 speciesNames.push(speciesName);
320             }
321         }
322         add_accessions(infoToAdd, speciesNames);
323         $('#review_absent_dialog').modal("hide");
324         //window.location.href='/breeders/accessions';
325     });
327     $('#new_accessions_submit').click(function () {
328         var selected_tab = jQuery('#add_new_accessions_tab_select .active').text()
329         if (selected_tab == 'Using Lists'){
330             accession_list_id = $('#list_div_list_select').val();
331             fullParsedData = undefined;
332             verify_accession_list(accession_list_id);
333         } else if (selected_tab == 'Uploading a File'){
334             var uploadFile = jQuery("#new_accessions_upload_file").val();
335             jQuery('#upload_new_accessions_form').attr("action", "/ajax/accessions/verify_accessions_file");
336             if (uploadFile === '') {
337                 alert("Please select a file");
338                 return;
339             }
340             jQuery("#upload_new_accessions_form").submit();
341         }
342         $('#add_accessions_dialog').modal("hide");
343     });
345     jQuery('#upload_new_accessions_form').iframePostForm({
346         json: true,
347         post: function () {
348             var uploadedSeedlotFile = jQuery("#new_accessions_upload_file").val();
349             jQuery('#working_modal').modal("show");
350             if (uploadedSeedlotFile === '') {
351                 jQuery('#working_modal').modal("hide");
352                 alert("No file selected");
353             }
354         },
355         complete: function (response) {
356             console.log(response);
357             jQuery('#working_modal').modal("hide");
359             if (response.error_string) {
360                 fullParsedData = undefined;
361                 alert(response.error_string);
362                 return;
363             }
364             if (response.success) {
365                 fullParsedData = response.full_data;
366                 review_verification_results(response, response.list_id);
367             }
368         }
369     });
371     $('[name="add_accessions_link"]').click(function () {
372         var list = new CXGN.List();
373         accessionList;
374         accession_list_id;
375         validSpecies;
376         fuzzyResponse;
377         fullParsedData;
378         infoToAdd;
379         accessionListFound;
380         speciesNames;
381         $('#add_accessions_dialog').modal("show");
382         $('#review_found_matches_dialog').modal("hide");
383         $('#review_fuzzy_matches_dialog').modal("hide");
384         $('#review_absent_dialog').modal("hide");
385         $("#list_div").html(list.listSelect("list_div", ["accessions"] ));
386     });
388     jQuery('#accessions_upload_spreadsheet_format_info').click(function(){
389         jQuery('#accessions_upload_spreadsheet_format_modal').modal("show");
390     });
392     $('body').on('hidden.bs.modal', '.modal', function () {
393         $(this).removeData('bs.modal');
394     });
396         $(document).on('change', 'select[name="fuzzy_option"]', function() {
397                 var value = $(this).val();
398                 if ($('#add_accession_fuzzy_option_all').is(":checked")){
399                         $('select[name="fuzzy_option"] option[value='+value+']').attr('selected','selected');
400                 }
401         });
403     $('#review_fuzzy_matches_download').click(function(){
404         //console.log(fuzzyResponse);
405         openWindowWithPost(JSON.stringify(fuzzyResponse));
406         //window.open('/ajax/accession_list/fuzzy_download?fuzzy_response='+JSON.stringify(fuzzyResponse));
407     });
409     jQuery('#review_absent_dialog').on('shown.bs.modal', function (e) {
410         jQuery('#infoToAdd_updated_table').DataTable({});
411         jQuery('#infoToAdd_new_table').DataTable({});
412     });
416 function openWindowWithPost(fuzzyResponse) {
417     var f = document.getElementById('add_accession_fuzzy_match_download');
418     f.fuzzy_response.value = fuzzyResponse;
419     window.open('', 'TheWindow');
420     f.submit();
423 function verify_accession_list(accession_list_id) {
424     accession_list = JSON.stringify(list.getList(accession_list_id));
425     doFuzzySearch = jQuery('#fuzzy_check').attr('checked'); //fuzzy search is always checked in a hidden input
426     //alert("should be disabled");
427     //alert(accession_list);
429     jQuery.ajax({
430         type: 'POST',
431         url: '/ajax/accession_list/verify',
432         timeout: 36000000,
433         //async: false,
434         dataType: "json",
435         data: {
436             'accession_list': accession_list,
437             'do_fuzzy_search': doFuzzySearch,
438         },
439         beforeSend: function(){
440             disable_ui();
441         },
442         success: function (response) {
443             enable_ui();
444             if (response.error) {
445                 alert(response.error);
446             } else {
447                 review_verification_results(response, accession_list_id);
448             }
449         },
450         error: function () {
451             enable_ui();
452             alert('An error occurred in processing. sorry');
453         }
454     });
457 function review_verification_results(verifyResponse, accession_list_id){
458     var i;
459     var j;
460     accessionListFound = {};
461     accessionList = [];
462     infoToAdd = [];
463     speciesNames = [];
464     //console.log(verifyResponse);
465     //console.log(accession_list_id);
467     if (verifyResponse.found) {
468         jQuery('#count_of_found_accessions').html("Total number already in the database("+verifyResponse.found.length+")");
469         var found_html = '<table class="table table-bordered" id="found_accessions_table"><thead><tr><th>Search Name</th><th>Found in Database</th></tr></thead><tbody>';
470         for( i=0; i < verifyResponse.found.length; i++){
471             found_html = found_html
472                 +'<tr><td>'+verifyResponse.found[i].matched_string
473                 +'</td><td>'
474                 +verifyResponse.found[i].unique_name
475                 +'</td></tr>';
476             accessionListFound[verifyResponse.found[i].unique_name] = 1;
477         }
478         found_html = found_html +'</tbody></table>';
480         jQuery('#view_found_matches').html(found_html);
482         jQuery('#review_found_matches_dialog').modal('show');
484         jQuery('#found_accessions_table').DataTable({});
486         accessionList = verifyResponse.absent;
488     }
490     if (verifyResponse.fuzzy.length > 0) {
491         fuzzyResponse = verifyResponse.fuzzy;
492         var fuzzy_html = '<table id="add_accession_fuzzy_table" class="table"><thead><tr><th class="col-xs-4">Name in Your List</th><th class="col-xs-4">Existing Name(s) in Database</th><th class="col-xs-4">Options&nbsp;&nbsp;&nbsp&nbsp;<input type="checkbox" id="add_accession_fuzzy_option_all"/> Use Same Option for All</th></tr></thead><tbody>';
493         for( i=0; i < verifyResponse.fuzzy.length; i++) {
494             fuzzy_html = fuzzy_html + '<tr id="add_accession_fuzzy_option_form'+i+'"><td>'+ verifyResponse.fuzzy[i].name + '<input type="hidden" name="fuzzy_name" value="'+ verifyResponse.fuzzy[i].name + '" /></td>';
495             fuzzy_html = fuzzy_html + '<td><select class="form-control" name ="fuzzy_select">';
496             for(j=0; j < verifyResponse.fuzzy[i].matches.length; j++){
497                 if (verifyResponse.fuzzy[i].matches[j].is_synonym){
498                     fuzzy_html = fuzzy_html + '<option value="' + verifyResponse.fuzzy[i].matches[j].synonym_of + '">' + verifyResponse.fuzzy[i].matches[j].name + ' (SYNONYM OF: '+verifyResponse.fuzzy[i].matches[j].synonym_of+')</option>';
499                 } else {
500                     fuzzy_html = fuzzy_html + '<option value="' + verifyResponse.fuzzy[i].matches[j].name + '">' + verifyResponse.fuzzy[i].matches[j].name + '</option>';
501                 }
502             }
503             fuzzy_html = fuzzy_html + '</select></td><td><select class="form-control" name="fuzzy_option"><option value="keep">Continue saving name in your list</option><option value="replace">Replace name in your list with selected existing name</option><option value="remove">Remove name in your list and ignore</option><option value="synonymize">Add name in your list as a synonym to selected existing name</option></select></td></tr>';
504         }
505         fuzzy_html = fuzzy_html + '</tbody></table>';
506         jQuery('#view_fuzzy_matches').html(fuzzy_html);
508         //Add to absent
509         for( i=0; i < verifyResponse.fuzzy.length; i++) {
510             verifyResponse.absent.push(verifyResponse.fuzzy[i].name);
511         }
512         accessionList = verifyResponse.absent;
513     }
515     if (verifyResponse.full_data){
516         for(var key in verifyResponse.full_data){
517             infoToAdd.push(verifyResponse.full_data[key]);
518             speciesNames.push(verifyResponse.full_data[key]['species'])
519         }
520     }
522     jQuery('#review_found_matches_hide').click(function(){
523         if (verifyResponse.fuzzy.length > 0){
524             jQuery('#review_fuzzy_matches_dialog').modal('show');
525         } else {
526             jQuery('#review_fuzzy_matches_dialog').modal('hide');
527             if (verifyResponse.absent.length > 0 || infoToAdd.length>0){
528                 populate_review_absent_dialog(verifyResponse.absent, infoToAdd);
529             } else {
530                 alert('All accessions in your list are now saved in the database. 3');
531             }
532         }
533     });
535     jQuery(document).on('click', '#review_fuzzy_matches_continue', function(){
536         process_fuzzy_options(accession_list_id);
537     });
541 function populate_review_absent_dialog(absent, infoToAdd){
542     console.log(infoToAdd);
543     console.log(absent);
545     jQuery('#count_of_absent_accessions').html("Total number to be added("+absent.length+")");
546     var absent_html = '';
547     jQuery("#species_name_input").autocomplete({
548         source: '/organism/autocomplete'
549     });
551     for( i=0; i < absent.length; i++){
552         absent_html = absent_html
553         +'<div class="left">'+absent[i]
554         +'</div>';
555     }
556     jQuery('#view_absent').html(absent_html);
557     jQuery('#view_infoToAdd').html('');
559     if (infoToAdd.length>0){
560         var infoToAdd_html = '<div class="well"><b>The following new accessions will be added:</b><br/><br/><table id="infoToAdd_new_table" class="table table-bordered table-hover"><thead><tr><th>uniquename</th><th>properties</th></tr></thead><tbody>';
561         for( i=0; i < infoToAdd.length; i++){
562             if (!('stock_id' in infoToAdd[i])){
563                 infoToAdd_html = infoToAdd_html + '<tr><td>'+infoToAdd[i]['germplasmName']+'</td>';
564                 var infoToAdd_properties_html = '';
565                 for (key in infoToAdd[i]){
566                     if (key != 'uniquename'){
567                         infoToAdd_properties_html = infoToAdd_properties_html + key+':'+infoToAdd[i][key]+'   ';
568                     }
569                 }
570                 infoToAdd_html = infoToAdd_html + '<td>'+infoToAdd_properties_html+'</td></tr>';
571             }
572         }
573         infoToAdd_html = infoToAdd_html + "</tbody></table></div>";
574         infoToAdd_html = infoToAdd_html + '<div class="well"><b>The following accessions will be updated:</b><br/><br/><table id="infoToAdd_updated_table" class="table table-bordered table-hover"><thead><tr><th>uniquename</th><th>properties</th></tr></thead><tbody>';
575         for( i=0; i < infoToAdd.length; i++){
576             if ('stock_id' in infoToAdd[i]){
577                 infoToAdd_html = infoToAdd_html + '<tr><td>'+infoToAdd[i]['germplasmName']+'</td>';
578                 var infoToAdd_properties_html = '';
579                 for (key in infoToAdd[i]){
580                     if (key != 'uniquename'){
581                         infoToAdd_properties_html = infoToAdd_properties_html + key+':'+infoToAdd[i][key]+'   ';
582                     }
583                 }
584                 infoToAdd_html = infoToAdd_html + '<td>'+infoToAdd_properties_html+'</td></tr>';
585             }
586         }
587         infoToAdd_html = infoToAdd_html + "</tbody></table></div>";
588         jQuery('#view_infoToAdd').html(infoToAdd_html);
589         jQuery('#add_accessions_using_list_inputs').hide();
590     } else {
591         jQuery('#add_accessions_using_list_inputs').show();
592     }
594     jQuery('#review_absent_dialog').modal('show');
597 function process_fuzzy_options(accession_list_id) {
598     var data={};
599     jQuery('#add_accession_fuzzy_table').find('tr').each(function(){
600         var id=jQuery(this).attr('id');
601         if (id !== undefined){
602             var row={};
603             jQuery(this).find('input,select').each(function(){
604                 var type = jQuery(this).attr('type');
605                 if (type == 'radio'){
606                     if (jQuery(this).is(':checked')){
607                         row[jQuery(this).attr('name')]=jQuery(this).val();
608                     }
609                 } else {
610                     row[jQuery(this).attr('name')]=jQuery(this).val();
611                 }
612             });
613             data[id]=row;
614         }
615     });
616     //console.log(data);
618     jQuery.ajax({
619         type: 'POST',
620         url: '/ajax/accession_list/fuzzy_options',
621         dataType: "json",
622         data: {
623             'accession_list_id': accession_list_id,
624             'fuzzy_option_data': JSON.stringify(data),
625             'names_to_add': JSON.stringify(accessionList)
626         },
627         success: function (response) {
628             //console.log(response);
629             infoToAdd = [];
630             speciesNames = [];
631             accessionList = response.names_to_add;
632             if (accessionList.length > 0){
634                 if (fullParsedData != null){
635                     for (var i=0; i<accessionList.length; i++){
636                         var accession_name = accessionList[i];
637                         infoToAdd.push(fullParsedData[accession_name]);
638                         speciesNames.push(fullParsedData[accession_name]['species']);
639                     }
640                     for (var i=0; i<accessionListFound.length; i++){
641                         var accession_name = accessionListFound[i];
642                         infoToAdd.push(fullParsedData[accession_name]);
643                         speciesNames.push(fullParsedData[accession_name]['species']);
644                     }
645                 }
647                 populate_review_absent_dialog(accessionList, infoToAdd);
648                 jQuery('#review_absent_dialog').modal('show');
649             } else {
650                 alert('All accessions in your list are now saved in the database. 2');
651             }
652         },
653         error: function () {
654             alert('An error occurred checking your fuzzy options! Do not try to add a synonym to a synonym! Also do not use any special characters!');
655         }
656     });