06d7be056190c14b85e71bc12523f18ea6815b5e
markd
  Mon Dec 7 00:50:29 2020 -0800
BLAT mmap index support merge with master

diff --git src/hg/hgc/hgc.c src/hg/hgc/hgc.c
index 5da2e8a..4fea33b 100644
--- src/hg/hgc/hgc.c
+++ src/hg/hgc/hgc.c
@@ -814,30 +814,42 @@
 if (bedSize >= 6)
    {
    strand = bed->strand;
    }
 printPos(bed->chrom, bed->chromStart, bed->chromEnd, strand, TRUE, bed->name);
 
 }
 
 void genericHeader(struct trackDb *tdb, char *item)
 /* Put up generic track info. */
 {
 if (item != NULL && item[0] != 0)
     cartWebStart(cart, database, "%s (%s)", tdb->longLabel, item);
 else
     cartWebStart(cart, database, "%s", tdb->longLabel);
+
+// QA noticed that clicking the +- buttons to collapse item detail tables was
+// generating messages in the Apache log if you went directly to an item page
+// without first visiting hgTracks. Clicking those buttons causes a cartDump
+// in order to save the state of visibility of the table, which in
+// turn needs an hgsid in order to save the state correctly. However, because
+// we aren't in a form, we have never saved the hgsid to a hidden
+// input element, and so the javascript that creates the cartDump link attaches
+// an empty 'hgsid=' parameter, which cartDump doesn't like. Since we aren't in
+// a form, use the 'common' object to store the parameter so the links to cartDump
+// are correct:
+jsInlineF("var common = {hgsid:\"%s\"};\n", cartSessionId(cart));
 }
 
 void printItemDetailsHtml(struct trackDb *tdb, char *itemName)
 /* if track has an itemDetailsHtml, retrieve and print the HTML */
 {
 char *tableName = trackDbSetting(tdb, "itemDetailsHtmlTable");
 if (tableName != NULL)
     {
     struct sqlConnection *conn = hAllocConn(database);
     struct itemDetailsHtml *html, *htmls;
     // if the details table has chrom/start/end columns, then use these to lookup html
     if (sqlColumnExists(conn, tableName, "chrom"))
         {
         char *chrom = cgiString("c");
         int start   = cgiInt("o");
@@ -1538,85 +1550,157 @@
 for (count = 0; col != NULL && count < fieldCount; col = col->next)
     {
     struct slPair *field;
     AllocVar(field);
     char *fieldName = col->name;
     char *fieldVal = row[count];
     field->name = fieldName;
     field->val = fieldVal;
     slAddHead(&fields, field);
     count++;
     }
 slReverse(fields);
 return fields;
 }
 
+void printExtraDetailsTable(char *trackName, char *tableName, char *fileName, struct dyString *tableText)
+// convert a tab-sep table to HTML
+{
+struct lineFile *lf = lineFileOnString(fileName, TRUE, tableText->string);
+char *description = tableName != NULL ? tableName : "Additional Details";
+printf("<table>");
+jsBeginCollapsibleSection(cart, trackName, "extraTbl", description, FALSE);
+printf("<table class=\"bedExtraTbl\">\n");
+char *line;
+while (lineFileNext(lf, &line, NULL))
+    {
+    printf("<tr><td>");
+    char *toPrint = replaceChars(line, "\t", "</td><td>");
+    printf("%s", toPrint);
+    printf("</td></tr>\n");
+    }
+printf("</table>\n"); // closes bedExtraTbl
+jsEndCollapsibleSection();
+printf("</table>\n"); // close wrapper table
+}
+
+static struct slName *findFieldsInExtraFile(char *detailsTableUrl, struct asColumn *col, struct dyString *ds)
+// return a list of the ${}-enclosed fields from an extra file
+{
+struct slName *foundFields = NULL;
+char *table = netReadTextFileIfExists(hReplaceGbdb(detailsTableUrl));
+if (table)
+    {
+    for (; col != NULL; col = col->next)
+        {
+        char field[256];
+        safef(field, sizeof(field), "${%s}", col->name);
+        if (stringIn(field, table))
+            {
+            struct slName *replaceField = slNameNew(col->name);
+            slAddHead(&foundFields, replaceField);
+            }
+        }
+    dyStringPrintf(ds, "%s", table);
+    slReverse(foundFields);
+    }
+return foundFields;
+}
+
 int extraFieldsPrintAs(struct trackDb *tdb,struct sqlResult *sr,char **fields,int fieldCount, struct asObject *as)
 // Any extra bed or bigBed fields (defined in as and occurring after N in bed N + types.
 // sr may be null for bigBeds.
 // Returns number of extra fields actually printed.
 {
 // We are trying to print extra fields so we need to figure out how many fields to skip
 int start = extraFieldsStart(tdb, fieldCount, as);
 
 struct asColumn *col = as->columnList;
 char *urlsStr = trackDbSettingClosestToHomeOrDefault(tdb, "urls", NULL);
 struct hash* fieldToUrl = hashFromString(urlsStr);
 boolean skipEmptyFields = trackDbSettingOn(tdb, "skipEmptyFields");
 
 // make list of fields to skip
 char *skipFieldsStr = trackDbSetting(tdb, "skipFields");
 struct slName *skipIds = NULL;
 if (skipFieldsStr)
     skipIds = slNameListFromComma(skipFieldsStr);
 
 // make list of fields that are separated from other fields
 char *sepFieldsStr = trackDbSetting(tdb, "sepFields");
 struct slName *sepFields = NULL;
 if (sepFieldsStr)
     sepFields = slNameListFromComma(sepFieldsStr);
 
+// make list of fields that we want to substitute
+// this setting has format description|URLorFilePath, with the stuff before the pipe optional
+char *extraDetailsTableName = NULL, *extraDetails = cloneString(trackDbSetting(tdb, "extraDetailsTable"));
+if (extraDetails && strchr(extraDetails,'|'))
+    {
+    extraDetailsTableName = extraDetails;
+    extraDetails = strchr(extraDetails,'|');
+    *extraDetails++ = 0;
+    }
+struct dyString *extraTblStr = dyStringNew(0);
+struct slName *detailsTableFields = NULL;
+if (extraDetails)
+    detailsTableFields = findFieldsInExtraFile(extraDetails, col, extraTblStr);
+
 // iterate over fields, print as table rows
 int count = 0;
 for (;col != NULL && count < fieldCount;col=col->next)
     {
     if (start > 0)  // skip past already known fields
         {
         start--;
         continue;
         }
     int ix = count;
     if (sr != NULL)
         {
         ix = sqlFieldColumn(sr, col->name); // If sr provided, name must match sql columnn name!
         if (ix == -1 || ix > fieldCount)      // so extraField really just provides a label
             continue;
         }
 
     char *fieldName = col->name;
 
     if (count == 0)
         printf("<br><table class='bedExtraTbl'>");
     
     count++;
 
     // do not print a row if the fieldName from the .as file is in the "skipFields" list
     // or if a field name starts with _. This maked bigBed extra fields consistent with
     // external extra fields in that _ field names have some meaning and are not shown
     if (startsWith("_", fieldName) || (skipIds && slNameInList(skipIds, fieldName)))
         continue;
 
+    // don't print this field if we are gonna print it later in a custom table
+    if (detailsTableFields && slNameInList(detailsTableFields, fieldName))
+        {
+        int fieldLen = strlen(fieldName);
+        char *replaceField = needMem(fieldLen+4);
+        replaceField[0] = '$';
+        replaceField[1] = '{';
+        strcpy(replaceField+2, fieldName);
+        replaceField[fieldLen+2] = '}';
+        replaceField[fieldLen+3] = 0;
+        extraTblStr = dyStringSub(extraTblStr->string, replaceField, fields[ix]);
+        continue;
+        }
+
     // skip this row if it's empty and "skipEmptyFields" option is set
     if (skipEmptyFields && isEmpty(fields[ix]))
         continue;
 
     // split this table to separate current row from the previous one, if the trackDb option is set
     if (sepFields && slNameInList(sepFields, fieldName))
         printf("</tr></table>\n<p>\n<table class='bedExtraTbl'>");
 
     // field description
     char *entry;
     if (sameString(fieldName, "cdsStartStat") && sameString("enum('none','unk','incmpl','cmpl')", col->comment))
         entry = "Status of CDS start annotation (none, unknown, incomplete, or complete)";
     else if (sameString(fieldName, "cdsEndStat") && sameString("enum('none','unk','incmpl','cmpl')", col->comment))
         entry = "Status of CDS end annotation (none, unknown, incomplete, or complete)";
     else
@@ -1632,30 +1716,36 @@
             printf("<td>%g</td></tr>\n", valDouble);
         else
             printf("<td>%s</td></tr>\n", fields[ix]); // decided not to print error
         }
     else
         printf("<td>%s</td></tr>\n", fields[ix]);
     }
 if (skipIds)
     slFreeList(skipIds);
 if (sepFields)
     slFreeList(sepFields);
 
 if (count > 0)
     printf("</table>\n");
 
+if (detailsTableFields)
+    {
+    printf("<br>\n");
+    printExtraDetailsTable(tdb->track, extraDetailsTableName, extraDetails, extraTblStr);
+    }
+
 return count;
 }
 
 int extraFieldsPrint(struct trackDb *tdb,struct sqlResult *sr,char **fields,int fieldCount)
 // Any extra bed or bigBed fields (defined in as and occurring after N in bed N + types.
 // sr may be null for bigBeds.
 // Returns number of extra fields actually printed.
 {
 struct asObject *as = asForDb(tdb, database);
 if (as == NULL)
     return 0;
 
 int ret =  extraFieldsPrintAs(tdb, sr, fields,fieldCount, as);
 //asObjectFree(&as);
 
@@ -4143,31 +4233,31 @@
         headerItem = NULL;
     else if ((  sameString(type, "narrowPeak")
              || sameString(type, "broadPeak")
              || sameString(type, "gappedPeak") )
          &&  headerItem
          &&  sameString(headerItem, ".") )
         headerItem = NULL;
     }
 /* Print header. */
 genericHeader(tdb, headerItem);
 
 if (differentString(type, "bigInteract") && differentString(type, "interact"))
     {
     // skip generic URL code as these may have multiple items returned for a click
     itemForUrl = getIdInUrl(tdb, item);
-    if (itemForUrl != NULL && trackDbSetting(tdb, "url"))
+    if (itemForUrl != NULL && trackDbSetting(tdb, "url") && differentString(type, "bigBed"))
         {
         printCustomUrl(tdb, itemForUrl, item == itemForUrl);
         printIframe(tdb, itemForUrl);
         }
     }
 if (plus != NULL)
     {
     fputs(plus, stdout);
     }
 if (container != NULL)
     {
     genericContainerClick(conn, container, tdb, item, itemForUrl);
     }
 else if (wordCount > 0)
     {
@@ -11577,31 +11667,33 @@
         }
     }
 return NULL;
 }
 
 char *getRefSeqSummary(struct sqlConnection *conn, char *acc)
 /* RefSeq summary or NULL if not available; free result */
 {
 char * summary = NULL;
 if (sqlTableExists(conn, refSeqSummaryTable))
     {
     char query[256];
     sqlSafef(query, sizeof(query),
           "select summary from %s where mrnaAcc = '%s'", refSeqSummaryTable, acc);
     summary = sqlQuickString(conn, query);
+    summary = abbreviateRefSeqSummary(summary);
     }
+
 return summary;
 }
 
 char *geneExtraImage(char *geneFileBase)
 /* check if there is a geneExtra image for the specified gene, if so return
  * the relative URL in a static buffer, or NULL if it doesn't exist */
 {
 static char *imgExt[] = {"png", "gif", "jpg", NULL};
 static char path[256];
 int i;
 
 for (i = 0; imgExt[i] != NULL; i++)
     {
     safef(path, sizeof(path), "../htdocs/geneExtra/%s.%s", geneFileBase, imgExt[i]);
     if (access(path, R_OK) == 0)
@@ -21379,31 +21471,31 @@
     doLongTabix(ct->tdb, item);
 else if (sameWord(type, "encodePeak"))
     doEncodePeak(ct->tdb, ct, fileName);
 else if (sameWord(type, "bigNarrowPeak"))
     doBigEncodePeak(ct->tdb, NULL, item);
 else if (sameWord(type, "bigWig"))
     bigWigCustomClick(ct->tdb);
 else if (sameWord(type, "bigChain"))
     genericChainClick(NULL, ct->tdb, item, start, "seq");
 else if (sameWord(type, "bigPsl"))
     genericBigPslClick(NULL, ct->tdb, item, start, end);
 else if (sameWord(type, "bigMaf"))
     genericMafClick(NULL, ct->tdb, item, start);
 else if (sameWord(type, "bigDbSnp"))
     doBigDbSnp(ct->tdb, item);
-else if (sameWord(type, "bigBed") || sameWord(type, "bigGenePred"))
+else if (sameWord(type, "bigBed") || sameWord(type, "bigGenePred") || sameWord(type, "bigLolly"))
     bigBedCustomClick(ct->tdb);
 else if (sameWord(type, "bigBarChart") || sameWord(type, "barChart"))
     doBarChartDetails(ct->tdb, item);
 else if (sameWord(type, "bigInteract") || sameWord(type, "interact"))
     doInteractDetails(ct->tdb, item);
 else if (sameWord(type, "bam"))
     doBamDetails(ct->tdb, itemName);
 else if (sameWord(type, "vcfTabix") || sameWord(type, "vcfPhasedTrio"))
     doVcfTabixDetails(ct->tdb, itemName);
 else if (sameWord(type, "vcf"))
     doVcfDetails(ct->tdb, itemName);
 else if (sameWord(type, "makeItems"))
     doMakeItemsDetails(ct, fileName);	// fileName is first word, which is, go figure, id
 else if (ct->wiggle)
     {