5e8e0ed53c05f2ef9b51ce897440bae308367f47
chmalee
  Wed Feb 9 14:45:54 2022 -0800
Add csv output option to table browser to support opening files in
Excel, refs #27623. Also change the output filename message to hint
that Excel can auto-recognize .csv extensions and behave appropriately.
Lastly, fix output dropdown selection to properly warn on update
when using GTF output option or a snp table.

diff --git src/hg/hgTables/mainPage.c src/hg/hgTables/mainPage.c
index 0e9bbcd..8ea3e09 100644
--- src/hg/hgTables/mainPage.c
+++ src/hg/hgTables/mainPage.c
@@ -384,50 +384,61 @@
     if (sameString(ot->name, outBed) || sameString(ot->name, outWigBed))
         hPrintf(" id=\"outBed\"");
     hPrintf(">%s\n", ot->label);
     }
 hPrintf("</SELECT>\n");
 hPrintf(" ");
 
 hPrintf("<DIV style='display:none; opacity:0.9; border: 1px solid #EEE; margin: 2px; padding: 4px' id='gffNote'>"
         "<b>Note:</b> Table Browser GTF files contain transcripts, but no gene identifiers or symbols.<br> "
         "If you are looking for fully formatted "
         "gene model files for use in genome analysis pipelines,<br>check the "
         "<a href='https://hgdownload.soe.ucsc.edu/goldenPath/%s/bigZips/genes'>bigZips/genes</a> "
         "directory on our download server.</DIV>", database);
 hPrintf(" ");
 
-jsInline("function checkGtfNote() {"
-    "if (document.getElementById('outputTypeDropdown').value==='gff') "
-    "    document.getElementById('gffNote').style.display=''; "
-    "else "
-    "    document.getElementById('gffNote').style.display='none'; "
-    "}"
+jsInline("function checkGtfNote(event) {\n"
+    "if (document.getElementById('outputTypeDropdown').value==='gff')\n"
+    "    document.getElementById('gffNote').style.display='';\n"
+    "else\n"
+    "    document.getElementById('gffNote').style.display='none';\n"
+    "}\n"
     "$(document).ready(checkGtfNote);\n"
 );
-jsOnEventById("change", "outputTypeDropdown", "checkGtfNote()");
-
-jsInline("function checkSnpTablesNote() {"  
-    "var trackName = document.getElementById('hgta_track').value;"
-    "if (trackName.startsWith('dbSnp') || trackName.startsWith('snp')) "
-    "    document.getElementById('snpTablesNote').style.display=''; "
-    "else "
-    "    document.getElementById('snpTablesNote').style.display='none'; "
-    "}"
+jsAddEventForId("change", "outputTypeDropdown", "checkGtfNote");
+
+jsInline("function checkSnpTablesNote(event) {\n"  
+    "var trackName = document.getElementById('hgta_track').value;\n"
+    "if (trackName.startsWith('dbSnp') || trackName.startsWith('snp'))\n"
+    "    document.getElementById('snpTablesNote').style.display='';\n"
+    "else\n"
+    "    document.getElementById('snpTablesNote').style.display='none';\n"
+    "}\n"
     "$(document).ready(checkSnpTablesNote);\n"
 );
-jsOnEventById("change", "outputTypeDropdown", "checkSnpTablesNote()");
+jsAddEventForId("change", "outputTypeDropdown", "checkSnpTablesNote");
+
+jsInlineF("function checkForCsv(event) {\n"
+    "var outputType = document.getElementById('outputTypeDropdown').value;\n"
+    "if (outputType === 'primaryTable' || outputType === 'selectedFields')\n"
+    "   document.getElementById('%s').parentElement.style.display='';\n"
+    "else\n"
+    "   document.getElementById('%s').parentElement.style.display='none';\n"
+    "}\n"
+    "$(document).ready(checkForCsv);\n"
+    , hgtaOutSep, hgtaOutSep);
+jsAddEventForId("change", "outputTypeDropdown", "checkForCsv");
 
 if (!cfgOptionBooleanDefault("hgta.disableSendOutput", FALSE))
     {
     hPrintf(" Send output to ");
     struct dyString *dy = dyStringNew(256);
     dyStringAppend(dy, 
 	"document.getElementById('checkboxGreat').checked=false;");
     if (isGenomeSpaceEnabled())
 	dyStringAppend(dy, 
 	      	  "document.getElementById('checkboxGenomeSpace').checked=false;");
     dyStringAppend(dy, 
 	      	  "return true;");
     cgiMakeCheckBoxWithId("sendToGalaxy", doGalaxy(), "checkboxGalaxy");
     jsOnEventById("click", "checkboxGalaxy", dy->string);
     hPrintf("<A HREF=\""GALAXY_URL_BASE"\" target=_BLANK>Galaxy</A>\n");
@@ -904,42 +915,53 @@
 
         }
     else
         cgiMakeButton(hgtaDoCorrelatePage, "create");
 
     hPrintf("</TD></TR>\n");
     }
 
 /* Print output type line. */
 
 printStep(stepNumber++);
 showOutputTypeRow(isWig, isBedGr, isPositional, isMaf, isChromGraphCt, isPal, isArray, isHalSnake);
 
 /* Print output destination line. */
     {
-    char *compressType =
-	cartUsualString(cart, hgtaCompressType, textOutCompressNone);
+    char *compressType = cartUsualString(cart, hgtaCompressType, textOutCompressNone);
+    char *fieldSep = cartUsualString(cart, hgtaOutSep, outTab);
     char *fileName = cartUsualString(cart, hgtaOutFileName, "");
     hPrintf("<TR><TD>\n");
     hPrintf("<B>output filename:</B>&nbsp;");
     cgiMakeTextVar(hgtaOutFileName, fileName, 29);
-    hPrintf("&nbsp;(leave blank to keep output in browser)</TD></TR>\n");
+    hPrintf("&nbsp;(add .csv extension if opening in Excel, leave blank to keep output in browser)</TD></TR>\n");
+    hPrintf("<TR><TD>\n");
+    hPrintf("<B>output field separator:&nbsp;</B>");
+
+    // tab or csv output
+    cgiMakeRadioButton(hgtaOutSep, outTab, sameWord(outTab, fieldSep));
+    hPrintf("&nbsp;tsv (tab-separated)&nbsp&nbsp;");
+
+    cgiMakeRadioButton(hgtaOutSep, outCsv, sameWord(outCsv, fieldSep));
+    hPrintf("&nbsp;csv (for excel)&nbsp;");
+
+    hPrintf("</TD></TR>\n");
     hPrintf("<TR><TD>\n");
     hPrintf("<B>file type returned:&nbsp;</B>");
     cgiMakeRadioButton(hgtaCompressType, textOutCompressNone,
         sameWord(textOutCompressNone, compressType));
-    hPrintf("&nbsp;plain text&nbsp&nbsp");
+    hPrintf("&nbsp;plain text&nbsp;");
     cgiMakeRadioButton(hgtaCompressType, textOutCompressGzip,
         sameWord(textOutCompressGzip, compressType));
     hPrintf("&nbsp;gzip compressed");
     hPrintf("</TD></TR>\n");
     }
 
 hPrintf("</TABLE>\n");
 
 
 /* Submit buttons. */
     {
     hPrintf("<BR>\n");
     if (isWig || isBam || isVcf || isLongTabix || isHic)
 	{
 	char *name;