fcccdfc15c15096674cea4176724ad95a340b3d4
tdreszer
  Tue Aug 2 15:48:52 2011 -0700
More incremental work on subCfg
diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index 0e357c9..a987a11 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -2277,30 +2277,43 @@
 return dimensions;
 }
 
 static void dimensionsFree(dimensions_t **dimensions)
 /* frees any previously obtained dividers setting */
 {
 if(dimensions && *dimensions)
     {
     freeMem((*dimensions)->setting);
     freeMem((*dimensions)->names);
     freeMem((*dimensions)->subgroups);
     freez(dimensions);
     }
 }
 
+static char *firstCharNoDigit(char *name)
+// Turns out css classes cannot begin with a number.  So prepend 'A'
+// If this wee more widely used, could move to common.
+{
+if (!isdigit(*name))
+     return name;
+
+char *newName = needMem(strlen(name)+1);
+*newName = 'A';
+strcpy(newName+1,name);
+return newName;
+}
+
 #define SUBGROUP_MAX 9
 
 #define FILTER_COMPOSITE_ONLYONE
 #ifdef FILTER_COMPOSITE_ONLYONE
 // FIXME: do we even support anything but multi???  If not, this is a boolean
 enum filterCompositeType
 /* How to look at a track. */
     {
     fctNone=0,      // do not offer filter for this dimension
     fctOne=1,       // filter composite by one or all
     fctOneOnly=2,   // filter composite by only one
     fctMulti=3,     // filter composite by multiselect: all, one or many
     };
 #endif///def FILTER_COMPOSITE_ONLYONE
 
@@ -2385,31 +2398,31 @@
 if(count <= 1)
     {
     freeMem(members->setting);
     freeMem(members);
     return NULL;
     }
 members->groupTag   = words[0];
 members->groupTitle = strSwapChar(words[1],'_',' '); // Titles replace '_' with space
 members->tags       = needMem(count*sizeof(char*));
 members->titles     = needMem(count*sizeof(char*));
 for (ix = 2,members->count=0; ix < count; ix++)
     {
     char *name,*value;
     if (parseAssignment(words[ix], &name, &value))
         {
-        members->tags[members->count]  = name;
+        members->tags[members->count]  = firstCharNoDigit(name);
         members->titles[members->count] = strSwapChar(value,'_',' ');
         members->count++;
         }
     }
 return members;
 }
 
 // No longer used, but it could come back!
 //static members_t *subgroupMembersGetByDimension(struct trackDb *parentTdb, char dimension)
 ///* Finds the dimension requested and return its associated
 // * subgroupMembership, returning the count of members or 0 */
 //{
 //int ix;
 //dimensions_t *dimensions = dimensionSettingsGet(parentTdb);
 //if(dimensions!=NULL)
@@ -2815,36 +2828,36 @@
 if(cnt <= 0)
     {
     freeMem(membership->setting);
     freeMem(membership);
     return NULL;
     }
 membership->subgroups  = needMem(cnt*sizeof(char*));
 membership->membership = needMem(cnt*sizeof(char*));
 membership->titles     = needMem(cnt*sizeof(char*));
 for (ix = 0,membership->count=0; ix < cnt; ix++)
     {
     char *name,*value;
     if (parseAssignment(words[ix], &name, &value))
         {
         membership->subgroups[membership->count]  = name;
-        membership->membership[membership->count] = value;//strSwapChar(value,'_',' ');
+        membership->membership[membership->count] = firstCharNoDigit(value);//strSwapChar(value,'_',' ');
         members_t* members = subgroupMembersGet(childTdb->parent, name);
         membership->titles[membership->count] = NULL; // default
         if(members != NULL)
             {
-            int ix2 = stringArrayIx(value,members->tags,members->count);
+            int ix2 = stringArrayIx(membership->membership[membership->count],members->tags,members->count);
             if(ix2 != -1)
                 membership->titles[membership->count] = strSwapChar(cloneString(members->titles[ix2]),'_',' ');
             subgroupMembersFree(&members);
             }
         membership->count++;
         }
     }
 tdbExtrasMembershipSet(childTdb,membership);
 return membership;
 }
 
 
 static boolean membershipInAllCurrentABCs(membership_t *membership,membersForAll_t*membersForAll)
 /* looks for a match between a membership set and ABC dimensions currently checked */
 {
@@ -3556,53 +3569,60 @@
     printf("<script type='text/javascript'>onload=function(){ if( $.browser.msie ) { $(\"select[name^='%s.filterBy.']\").children('option[selected]').each( function(i) { $(this).attr('selected',true); }); }}</script>\n",tdb->track);
 #endif///ndef NEW_JQUERY
 puts("</TR></TABLE>");
 
 return;
 }
 
 #define COLOR_BG_DEFAULT_IX     0
 #define COLOR_BG_ALTDEFAULT_IX  1
 #define DIVIDING_LINE "<TR valign=\"CENTER\" line-height=\"1\" BGCOLOR=\"%s\"><TH colspan=\"5\" align=\"CENTER\"><hr noshade color=\"%s\" width=\"100%%\"></TD></TR>\n"
 #define DIVIDER_PRINT(color) printf(DIVIDING_LINE,COLOR_BG_DEFAULT,(color))
 
 static char *checkBoxIdMakeForTrack(struct trackDb *tdb,members_t** dims,int dimMax,membership_t *membership)
 /* Creates an 'id' string for subtrack checkbox in style that matrix understand: "cb_dimX_dimY_view_cb" */
 {
+#ifdef SUBTRACK_CFG
+int len = strlen(tdb->track) + 10;
+char *id = needMem(len);
+safef(id,len,"%s_sel",tdb->track);
+return id;
+#else///#ifndef SUBTRACK_CFG
 int ix;
 #define CHECKBOX_ID_SZ 128
 // What is wanted: id="cb_ES_K4_SIG_cb"
 struct dyString *id = newDyString(CHECKBOX_ID_SZ);
 dyStringPrintf(id,"cb_%s_", tdb->track);
 int dimIx=1; // Skips over view dim for now
 for(;dimIx<dimMax;dimIx++)
     {
     if(dims[dimIx] != NULL)
         {
         ix = stringArrayIx(dims[dimIx]->groupTag, membership->subgroups, membership->count);
         if(ix >= 0)
             dyStringPrintf(id,"%s_", membership->membership[ix]);
         }
     }
 if(dims[0] != NULL) // The view is saved for last
     {
     ix = stringArrayIx("view", membership->subgroups, membership->count);   // view is a known tagname
     if(ix >= 0)
         dyStringPrintf(id,"%s_", membership->membership[ix]);
     }
 dyStringAppend(id,"cb");
 return dyStringCannibalize(&id);
+#endif///ndef SUBTRACK_CFG
 }
 
 static void checkBoxIdFree(char**id)
 /* Frees 'id' string */
 {
 if(id && *id)
     freez(id);
 }
 
 static boolean divisionIfNeeded(char **lastDivide,dividers_t *dividers,membership_t *membership)
 /* Keeps track of location within subtracks in order to provide requested division lines */
 {
 boolean division = FALSE;
 if(dividers)
     {
@@ -3850,38 +3870,38 @@
         break;
         }
     }
 
 // Table wraps around entire list so that "Top" link can float to the correct place.
 cgiDown(0.7);
 printf("<table><tr><td class='windowSize'>");
 printf("<A NAME='DISPLAY_SUBTRACKS'></A>");
 if (sortOrder != NULL)
     {
     // First table row contains the display "selected/visible" or "all" radio buttons
     // NOTE: list subtrack radio buttons are inside tracklist table header if there are no sort columns
     //       The reason is to ensure spacing of lines column headers when the only column header is "Restricted Until"
     printf("<B>List subtracks:&nbsp;");
     char javascript[JBUFSIZE];
-    safef(javascript, sizeof(javascript), "onclick=\"showOrHideSelectedSubtracks(true);\"");
+    safef(javascript, sizeof(javascript), "class='allOrOnly' onclick='showOrHideSelectedSubtracks(true);'");
     if (subCount > LARGE_COMPOSITE_CUTOFF)
         safef(buffer,SMALLBUF,"%s.displaySubtracks",parentTdb->track);
     else
         safecpy(buffer,SMALLBUF,"displaySubtracks");
     cgiMakeOnClickRadioButton(buffer, "selected", !displayAll,javascript);
     puts("only selected/visible &nbsp;&nbsp;");
-    safef(javascript, sizeof(javascript), "onclick=\"showOrHideSelectedSubtracks(false);\"");
+    safef(javascript, sizeof(javascript), "class='allOrOnly' onclick='showOrHideSelectedSubtracks(false);'");
     cgiMakeOnClickRadioButton(buffer, "all", displayAll,javascript);
     printf("all</B>");
     if (slCount(subtrackRefList) > 5)
         printf("&nbsp;&nbsp;&nbsp;&nbsp;(<span class='subCBcount'></span>)");
     makeTopLink(parentTdb);
     printf("</td></tr></table>");
     }
 else
     makeTopLink(parentTdb);
 
 // Now we can start in on the table of subtracks  It may be sortable and/or dragAndDroppable
 printf("\n<TABLE CELLSPACING='2' CELLPADDING='0' border='0'");
 dyStringClear(dyHtml);
 if (sortOrder != NULL)
     dyStringPrintf(dyHtml, "sortable");
@@ -3907,38 +3927,38 @@
 if (sortOrder != NULL)
     colspan = sortOrder->count+2;
 if (doColorPatch)
     colspan += 1;
 int columnCount = 0;
 if (sortOrder != NULL)
     printf("<TR id=\"subtracksHeader\" class='sortable%s'>\n",useDragAndDrop?" nodrop nodrag":"");
 else
     {
     printf("<TR%s>",useDragAndDrop?" id='noDrag' class='nodrop nodrag'":"");
     // First table row contains the display "selected/visible" or "all" radio buttons
     // NOTE: list subtrack radio buttons are inside tracklist table header if there are no sort columns
     //       The reason is to ensure spacing of lines column headers when the only column header is "Restricted Until"
     printf("<TD colspan='%d'><B>List subtracks:&nbsp;", colspan);
     char javascript[JBUFSIZE];
-    safef(javascript, sizeof(javascript), "onclick=\"showOrHideSelectedSubtracks(true);\"");
+    safef(javascript, sizeof(javascript), "class='allOrOnly' onclick='showOrHideSelectedSubtracks(true);'");
     if (subCount > LARGE_COMPOSITE_CUTOFF)
         safef(buffer,SMALLBUF,"%s.displaySubtracks",parentTdb->track);
     else
         safecpy(buffer,SMALLBUF,"displaySubtracks");
     cgiMakeOnClickRadioButton(buffer, "selected", !displayAll,javascript);
     puts("only selected/visible &nbsp;&nbsp;");
-    safef(javascript, sizeof(javascript), "onclick=\"showOrHideSelectedSubtracks(false);\"");
+    safef(javascript, sizeof(javascript), "class='allOrOnly' onclick='showOrHideSelectedSubtracks(false);'");
     cgiMakeOnClickRadioButton(buffer, "all", displayAll,javascript);
     printf("all</B>");
     if (slCount(subtrackRefList) > 5)
         printf("&nbsp;&nbsp;&nbsp;&nbsp;(<span class='subCBcount'></span>)");
     puts("</TD>");
     columnCount = colspan;
     }
 
 // Add column headers which are sort button links
 if (sortOrder != NULL)
     {
     printf("<TH>&nbsp;<INPUT TYPE=HIDDEN NAME='%s' class='sortOrder' VALUE='%s'></TH>\n", sortOrder->htmlId, sortOrder->sortOrder); // keeing track of sortOrder
     columnCount++;
     // Columns in tdb order (unchanging), sort in cart order (changed by user action)
     int sIx=0;
@@ -4028,30 +4048,31 @@
     #else///ifndef SUBTRACK_CFG
         if (trackDbSettingClosestToHomeOn(subtrack, "configurable") == FALSE)
     #endif///ndef SUBTRACK_CFG
             cType = cfgNone;
         }
     membership_t *membership = subgroupMembershipGet(subtrack);
 
     if (sortOrder == NULL && !useDragAndDrop)
         {
         if ( divisionIfNeeded(lastDivide,dividers,membership) )
             colorIx = (colorIx == COLOR_BG_DEFAULT_IX ? COLOR_BG_ALTDEFAULT_IX : COLOR_BG_DEFAULT_IX);
         }
 
     // Start the TR which must have an id that is directly related to the checkBox id
     char *id = checkBoxIdMakeForTrack(subtrack,membersForAll->members,membersForAll->dimMax,membership); // view is known tag
+
     printf("<TR valign='top' class='%s%s'",colors[colorIx],(useDragAndDrop?" trDraggable":""));
     printf(" id=tr_%s p%s>\n",id,(!visibleCB && !displayAll?" style='display:none'":""));
 
     // Now the TD that holds the checkbox
     printf("<TD%s%s>",
            (enabledCB?"":" title='view is hidden'"),
            (useDragAndDrop?" class='dragHandle' title='Drag to reorder'":""));
 
     // The checkbox has identifying classes including subCB and the tag for each dimension (e.g. class='subCB GM12878 CTCF Peak')
     dyStringClear(dyHtml);
     dyStringAppend(dyHtml, "subCB"); // always first
     for(di=dimX;di<membersForAll->dimMax;di++)
         {
         if (membersForAll->members[di] && -1 != (ix = stringArrayIx(membersForAll->members[di]->groupTag, membership->subgroups, membership->count)))
             dyStringPrintf(dyHtml," %s",membership->membership[ix]);