");
+}
+
+static void pruneRedundantCartVis(struct track *trackList)
+/* When the config page or track form has been submitted, there usually
+ * are many track visibility cart variables that have not been changed
+ * from the default. To keep down cart bloat, prune those out before we
+ * save the cart. changeTrackVis does this too, but this is for the
+ * more common case where track visibilities are tweaked. */
+{
+struct track *track;
+if (measureTiming)
+ uglyTime("Done with trackForm");
+for (track = trackList; track != NULL; track = track->next)
+ {
+ char *cartVis = cartOptionalString(cart, track->track);
+ if (cartVis != NULL && hTvFromString(cartVis) == track->tdb->visibility)
+ cartRemove(cart, track->track);
+ }
+if (measureTiming)
+ {
+ uglyTime("Pruned redundant vis from cart");
+ }
+}
+
+static int getMaxWindowToDraw(struct trackDb *tdb)
+/* If trackDb setting maxWindowToDraw exists and is a sensible size, return it, else 0. */
+{
+if (tdb == NULL)
+ return 0;
+char *maxWinToDraw = trackDbSettingClosestToHome(tdb, "maxWindowToDraw");
+if (isNotEmpty(maxWinToDraw))
+ {
+ unsigned maxWTD = sqlUnsigned(maxWinToDraw);
+ if (maxWTD > 1)
+ return maxWTD;
+ }
+return 0;
+}
+
+static void drawMaxWindowWarning(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg,
+ int xOff, int yOff, int width, MgFont *font, Color color,
+ enum trackVisibility vis)
+/* This is a stub drawItems handler to be swapped in for the usual drawItems when the window
+ * size is larger than the threshold specified by trackDb setting maxWindowToDraw. */
+{
+int maxWinToDraw = getMaxWindowToDraw(tg->tdb);
+char commafied[256];
+sprintLongWithCommas(commafied, maxWinToDraw);
+char message[512];
+safef(message, sizeof(message), "zoom in to <= %s bases to view items", commafied);
+Color yellow = hvGfxFindRgb(hvg, &undefinedYellowColor);
+hvGfxBox(hvg, xOff, yOff, width, tg->heightPer, yellow);
+hvGfxTextCentered(hvg, xOff, yOff, width, tg->heightPer, MG_BLACK, font, message);
+}
+
+static void dontLoadItems(struct track *tg)
+/* No-op loadItems when we aren't going to try. */
+{
+}
+
+static void checkMaxWindowToDraw(struct track *tg)
+/* If (winEnd - winStart) > trackDb setting maxWindowToDraw, force track to a dense line
+ * that will ask the user to zoom in closer to see track items and return TRUE so caller
+ * can skip loading items. */
+{
+int maxWinToDraw = getMaxWindowToDraw(tg->tdb);
+if (tdbIsComposite(tg->tdb))
+ {
+ struct track *subtrack;
+ for (subtrack = tg->subtracks; subtrack != NULL; subtrack = subtrack->next)
+ {
+ if (!isSubtrackVisible(subtrack))
+ continue;
+ maxWinToDraw = getMaxWindowToDraw(subtrack->tdb);
+ if (maxWinToDraw > 1 && (winEnd - winStart) > maxWinToDraw)
+ {
+ subtrack->loadItems = dontLoadItems;
+ subtrack->drawItems = drawMaxWindowWarning;
+ subtrack->limitedVis = tvDense;
+ subtrack->limitedVisSet = TRUE;
+ }
+ }
+ }
+else if (maxWinToDraw > 1 && (winEnd - winStart) > maxWinToDraw)
+ {
+ tg->loadItems = dontLoadItems;
+ tg->drawItems = drawMaxWindowWarning;
+ tg->limitedVis = tvDense;
+ tg->limitedVisSet = TRUE;
+ }
+}
+
+#if defined(CONTEXT_MENU) || defined(TRACK_SEARCH)
+static void trackJson(struct dyString *trackDbJson, struct track *track, int count)
+{
+// add entry for given track to the trackDbJson string
+if(count)
+ dyStringAppend(trackDbJson, "\n,");
+dyStringPrintf(trackDbJson, "\t%s: {", track->track);
+if(tdbIsSuperTrackChild(track->tdb) || tdbIsCompositeChild(track->tdb))
+ dyStringPrintf(trackDbJson, "\n\t\tparentTrack: '%s',", track->tdb->parent->track);
+dyStringPrintf(trackDbJson, "\n\t\ttype: '%s',", track->tdb->type);
+if(sameWord(track->tdb->type, "remote") && trackDbSetting(track->tdb, "url") != NULL)
+ dyStringPrintf(trackDbJson, "\n\t\turl: '%s',", trackDbSetting(track->tdb, "url"));
+dyStringPrintf(trackDbJson, "\n\t\tshortLabel: '%s',\n\t\tlongLabel: '%s',\n\t\tcanPack: %d,\n\t\tvisibility: %d\n\t}",
+ javaScriptLiteralEncode(track->shortLabel), javaScriptLiteralEncode(track->longLabel), track->canPack, track->limitedVis);
+}
+#endif/// defined(CONTEXT_MENU) || defined(TRACK_SEARCH)
+
+void printTrackInitJavascript(struct track *trackList)
+{
+hPrintf("\n", hgtJsCommand, hgtJsCommand);
+hPrintf("\n");
+}
+
+void jsCommandDispatch(char *command, struct track *trackList)
+/* Dispatch a command sent to us from some javaScript event.
+ * This gets executed after the track list is built, but before
+ * the track->loadItems methods are called. */
+{
+if (startsWithWord("makeItems", command))
+ makeItemsJsCommand(command, trackList, trackHash);
+else
+ warn("Unrecognized jsCommand %s", command);
+}
+
+void subtrackVisCartCleanup(struct track *trackList,struct cart *newCart,struct hash *oldVars)
+/* When composite/view vis changes, remove subtrack specific vis */
+{
+struct track *track = trackList;
+for (;track != NULL; track = track->next)
+ {
+ if(!tdbIsComposite(track->tdb))
+ continue;
+ boolean compositeWide = cartValueHasChanged(newCart,oldVars,track->track,TRUE);
+
+ boolean hasViews = FALSE;
+ struct trackDb *tdbView = track->tdb->subtracks;
+ for (;tdbView != NULL; tdbView = tdbView->next)
+ {
+ char * view = NULL;
+ if (!tdbIsView(tdbView,&view))
+ break;
+
+ hasViews = TRUE;
+ boolean viewLevel = FALSE;
+ if(!compositeWide)
+ {
+ char settingName[512]; // wgEncodeOpenChromChip.Peaks.vis
+ safef(settingName,sizeof(settingName),"%s.%s.vis",track->track,view);
+ viewLevel = cartValueHasChanged(newCart,oldVars,settingName,TRUE);
+ }
+ if(compositeWide || viewLevel)
+ cartRemoveFromTdbTree(newCart,tdbView,NULL,TRUE); // clean up children, skipping view
+ }
+ if (compositeWide && !hasViews)
+ cartRemoveFromTdbTree(newCart,track->tdb,NULL,TRUE); // clean up children, skipping composite
+ }
+}
+
+void doTrackForm(char *psOutput, struct tempName *ideoTn)
+/* Make the tracks display form with the zoom/scroll buttons and the active
+ * image. If the ideoTn parameter is not NULL, it is filled in if the
+ * ideogram is created. */
+{
+struct group *group;
+struct track *track;
+char *freezeName = NULL;
+boolean hideAll = cgiVarExists("hgt.hideAll");
+boolean defaultTracks = cgiVarExists("hgt.reset");
+boolean showedRuler = FALSE;
+boolean showTrackControls = cartUsualBoolean(cart, "trackControlsOnMain", TRUE);
+long thisTime = 0, lastTime = 0;
+char *clearButtonJavascript;
+#if defined(CONTEXT_MENU) || defined(TRACK_SEARCH)
+struct dyString *trackDbJson = newDyString(1000);
+int trackDbJsonCount = 1;
+dyStringPrintf(trackDbJson, "\n");
+if(!trackImgOnly)
+ hPrintf(dyStringContents(trackDbJson));
+#endif/// defined(CONTEXT_MENU) || defined(TRACK_SEARCH)
+
+printTrackInitJavascript(trackList);
+
+/* Generate two lists of hidden variables for track group visibility. Kludgy,
+ but required b/c we have two different navigation forms on this page, but
+ we want open/close changes in the bottom form to be submitted even if the user
+ submits via the top form. */
+struct dyString *trackGroupsHidden1 = newDyString(1000);
+struct dyString *trackGroupsHidden2 = newDyString(1000);
+for (group = groupList; group != NULL; group = group->next)
+ {
+ if (group->trackList != NULL)
+ {
+ int looper;
+ for(looper=1;looper<=2;looper++)
+ {
+ boolean isOpen = !isCollapsedGroup(group);
+ char buf[1000];
+ safef(buf, sizeof(buf), "\n", collapseGroupVar(group->name), collapseGroupVar(group->name), looper, isOpen ? "0" : "1");
+ dyStringAppend(looper == 1 ? trackGroupsHidden1 : trackGroupsHidden2, buf);
+ }
+ }
+ }
+
+#ifdef IMAGEv2_DRAG_SCROLL
+if(theImgBox)
+ {
+ // If a portal was established, then set the global dimensions back to the portal size
+ if(imgBoxPortalDimensions(theImgBox,NULL,NULL,NULL,NULL,&winStart,&winEnd,&(tl.picWidth),NULL))
+ {
+ winBaseCount = winEnd - winStart;
+ insideWidth = tl.picWidth-gfxBorder-insideX;
+ }
+ }
+#endif//def IMAGEv2_DRAG_SCROLL
+/* Center everything from now on. */
+hPrintf("
\n");
+
+
+if (!hideControls)
+ {
+ /* set white-space to nowrap to prevent buttons from wrapping when screen is
+ * narrow */
+ hPrintf("
\n");
+ hotLinks();
+
+ /* Show title . */
+ freezeName = hFreezeFromDb(database);
+ if(freezeName == NULL)
+ freezeName = "Unknown";
+ hPrintf("");
+ if (startsWith("zoo",database) )
+ {
+ hPrintf("%s %s on %s June 2002 Assembly %s target1",
+ organization, browserName, organism, freezeName);
+ }
+ else
+ {
+ if (sameString(organism, "Archaea"))
+ {
+ hPrintf("%s %s on Archaeon %s Assembly",
+ organization, browserName, freezeName);
+ }
+ else
+ {
+ if (stringIn(database, freezeName))
+ hPrintf("%s %s on %s %s Assembly",
+ organization, browserName, organism, freezeName);
+ else
+ hPrintf("%s %s on %s %s Assembly (%s)",
+ organization, browserName, organism, freezeName, database);
+ }
+ }
+ hPrintf(" \n");
+
+ /* This is a clear submit button that browsers will use by default when enter is pressed in position box. */
+ hPrintf("");
+ /* Put up scroll and zoom controls. */
+#ifndef USE_NAVIGATION_LINKS
+ hWrites("move ");
+ hButtonWithMsg("hgt.left3", "<<<", "move 95% to the left");
+ hButtonWithMsg("hgt.left2", " <<", "move 47.5% to the left");
+ hButtonWithMsg("hgt.left1", " < ", "move 10% to the left");
+ hButtonWithMsg("hgt.right1", " > ", "move 10% to the right");
+ hButtonWithMsg("hgt.right2", ">> ", "move 47.5% to the right");
+ hButtonWithMsg("hgt.right3", ">>>", "move 95% to the right");
+ hWrites(" zoom in ");
+ /* use button maker that determines padding, so we can share constants */
+ topButton("hgt.in1", ZOOM_1PT5X);
+ topButton("hgt.in2", ZOOM_3X);
+ topButton("hgt.in3", ZOOM_10X);
+ topButton("hgt.inBase", ZOOM_BASE);
+ hWrites(" zoom out ");
+ topButton("hgt.out1", ZOOM_1PT5X);
+ topButton("hgt.out2", ZOOM_3X);
+ topButton("hgt.out3", ZOOM_10X);
+ hWrites(" \n");
+#endif//ndef USE_NAVIGATION_LINKS
+
+ if (showTrackControls)
+ {
+ /* Break into a second form so that zooming and scrolling
+ * can be done with a 'GET' so that user can back up from details
+ * page without Internet Explorer popping up an annoying dialog.
+ * Do rest of page as a 'POST' so that the ultra-long URL from
+ * all the track controls doesn't break things. IE URL limit
+ * is 2000 bytes, but some firewalls impose a ~1000 byte limit.
+ * As a side effect of breaking up the page into two forms
+ * we need to repeat the position in a hidden variable here
+ * so that zoom/scrolling always has current position to work
+ * from. */
+ hPrintf("", chromName, winStart+1, winEnd);
+ hPrintf("\n%s", trackGroupsHidden1->string);
+ hPrintf("\n");
+ hPrintf("