630eb30bc3695afcf73a19be6e3bc9a2829b365f chmalee Wed May 13 13:04:37 2026 -0700 Make myVariants items bed12+ rather than bed9+, refs #33808 diff --git src/hg/hgc/myVariantsClick.c src/hg/hgc/myVariantsClick.c index 8ed8f6e5749..3df5f1c2dca 100644 --- src/hg/hgc/myVariantsClick.c +++ src/hg/hgc/myVariantsClick.c @@ -139,39 +139,91 @@ printf("<B>Description:</B> "); printInfoIcon("Longer notes or comments about this annotation. Displayed on this details page."); printf("<BR>\n"); cgiMakeTextArea(varName, item->description, 8, 80); printf("<BR>\n"); /* Non-editable chromosome. */ htmlPrintf("<B>Chromosome:</B> %s<BR>\n", item->chrom); /* Editable start and end. */ int chromSize = hChromSize(database, item->chrom); char chromSizeString[16]; safef(chromSizeString, sizeof(chromSizeString), "%d", chromSize); printf("<B>Start:</B> "); safef(varName, sizeof(varName), "%s_%s", trackName, "chromStart"); - cgiMakeIntVarInRange(varName, item->chromStart+1, NULL, 80, "1", chromSizeString); - printInfoIcon("1-based start position on the chromosome."); + cgiMakeIntVarInRange(varName, item->chromStart, NULL, 80, "0", chromSizeString); + printInfoIcon("0-based start position on the chromosome."); printf("<BR>\n"); printf("<B>End:</B> "); safef(varName, sizeof(varName), "%s_%s", trackName, "chromEnd"); cgiMakeIntVarInRange(varName, item->chromEnd, NULL, 80, "1", chromSizeString); - printInfoIcon("1-based end position on the chromosome (inclusive)."); + printInfoIcon("0-based half-open end position on the chromosome."); printf("<BR>\n"); + /* Blocks (BED12). Hidden inputs are kept in sync by the widget; + * updateBlocksFields in hgTracks reads them on submit. */ + char vBC[128], vBS[128], vBT[128], vCS[128], vCE[128]; + safef(vBC, sizeof(vBC), "%s_blockCount", trackName); + safef(vBS, sizeof(vBS), "%s_blockSizes", trackName); + safef(vBT, sizeof(vBT), "%s_chromStarts", trackName); + safef(vCS, sizeof(vCS), "%s_chromStart", trackName); + safef(vCE, sizeof(vCE), "%s_chromEnd", trackName); + struct dyString *sizesCsv = dyStringNew(64); + struct dyString *startsCsv = dyStringNew(64); + int bi; + for (bi = 0; bi < item->blockCount; bi++) + { + if (bi > 0) + { + dyStringAppendC(sizesCsv, ','); + dyStringAppendC(startsCsv, ','); + } + dyStringPrintf(sizesCsv, "%d", item->blockSizes[bi]); + dyStringPrintf(startsCsv, "%d", item->chromStarts[bi]); + } + char countStr[32]; + safef(countStr, sizeof(countStr), "%u", item->blockCount); + printf("<B>Blocks:</B> "); + printInfoIcon("BED12-style blocks. Leave empty to store a single full-span block."); + printf("<BR>\n"); + cgiMakeHiddenVarWithIdExtra(vBC, vBC, countStr, NULL); + cgiMakeHiddenVarWithIdExtra(vBS, vBS, dyStringContents(sizesCsv), NULL); + cgiMakeHiddenVarWithIdExtra(vBT, vBT, dyStringContents(startsCsv), NULL); + printf("<div id=\"myVariantsBlocksUi\" style=\"margin:4px 0 12px 0;\"></div>\n"); + jsIncludeFile("myVariantsBlocks.js", NULL); + jsInlineF( + "$(function(){\n" + " if (typeof myVariantsBlocks === 'undefined') { return; }\n" + " var sizesEl = document.getElementById('%s');\n" + " var startsEl = document.getElementById('%s');\n" + " var sizes = sizesEl.value ? sizesEl.value.split(',').map(Number) : [];\n" + " var starts = startsEl.value ? startsEl.value.split(',').map(Number) : [];\n" + " myVariantsBlocks.mount('myVariantsBlocksUi', {\n" + " initialSizes: sizes,\n" + " initialStarts: starts,\n" + " getStart: function(){ return parseInt(document.getElementById('%s').value, 10); },\n" + " getEnd: function(){ return parseInt(document.getElementById('%s').value, 10); },\n" + " hiddenCountInput: document.getElementById('%s'),\n" + " hiddenSizesInput: sizesEl,\n" + " hiddenStartsInput: startsEl\n" + " });\n" + "});\n", + vBS, vBT, vCS, vCE, vBC); + dyStringFree(&sizesCsv); + dyStringFree(&startsCsv); + /* Edit the color */ safef(varName, sizeof(varName), "%s_%s", trackName, "itemRgb"); char colorHex[8]; safef(colorHex, sizeof(colorHex), "#%06X", item->itemRgb); hPrintf("<label for=\"%s\"><b>Color:</b></label> ", varName); hPrintf("<input type=\"text\" name=\"%s\" id=\"%s\" value=\"%s\">\n", varName, varName, colorHex); jsInlineF( "$(function() {" "$(document.getElementById('%s')).spectrum({" "preferredFormat: 'hex'," "showInput: true," "showPalette: true," "hideAfterPaletteSelect: true" "});" @@ -285,32 +337,46 @@ cgiMakeButton(varName, "Delete"); printf(" "); } safef(varName, sizeof(varName), "%s_%s", trackName, "cancel"); cgiMakeButton(varName, "Cancel"); printf("</FORM>\n"); } else { /* Read-only display for shared items without write permission. * htmlPrintf escapes %s by default; that prevents stored XSS via * owner-controlled fields. */ htmlPrintf("<B>Label:</B> %s<BR>\n", item->name); htmlPrintf("<B>Description:</B><BR>\n%s<BR>\n", item->description); htmlPrintf("<B>Chromosome:</B> %s<BR>\n", item->chrom); - printf("<B>Start:</B> %d<BR>\n", item->chromStart + 1); + printf("<B>Start:</B> %d<BR>\n", item->chromStart); printf("<B>End:</B> %d<BR>\n", item->chromEnd); + if (item->blockCount > 1) + { + printf("<B>Blocks:</B> "); + int roBi; + for (roBi = 0; roBi < item->blockCount; roBi++) + { + if (roBi > 0) + printf(", "); + int relStart = item->chromStarts[roBi]; + int relEnd = relStart + item->blockSizes[roBi]; + printf("%d-%d (%dbp)", relStart, relEnd, item->blockSizes[roBi]); + } + printf("<BR>\n"); + } char colorHex[8]; safef(colorHex, sizeof(colorHex), "#%06X", item->itemRgb); printf("<B>Color:</B> <span style='background:%s; padding:2px 12px'> </span> %s<BR>\n", colorHex, colorHex); if (isNotEmpty(item->ref)) htmlPrintf("<B>Ref:</B> %s<BR>\n", item->ref); if (isNotEmpty(item->alt)) htmlPrintf("<B>Alt:</B> %s<BR>\n", item->alt); if (isNotEmpty(item->project)) htmlPrintf("<B>Project:</B> %s<BR>\n", item->project); if (isNotEmpty(item->mouseover)) htmlPrintf("<B>Mouseover:</B> %s<BR>\n", item->mouseover); /* Custom fields read-only */ {