9a24eab7aa53e82ca200d92f532411f37d8487f3
braney
  Sat Feb 24 18:09:09 2018 -0800
add lazy loading to hgCollection, modify CSP policy to allow blob:

diff --git src/hg/hgCollection/hgCollection.c src/hg/hgCollection/hgCollection.c
index 69f9167..a39a3bd 100644
--- src/hg/hgCollection/hgCollection.c
+++ src/hg/hgCollection/hgCollection.c
@@ -66,97 +66,45 @@
         {
         hashStore(nameHash, buffer);
         return cloneString(buffer);
         }
     }
 
 return NULL;
 }
 
 static boolean trackCanBeAdded(struct trackDb *tdb)
 // are we allowing this track into a custom composite
 {
 return  (tdb->subtracks == NULL) && !startsWith("wigMaf",tdb->type) &&  (startsWith("wig",tdb->type) || startsWith("bigWig",tdb->type) || startsWith("bedGraph",tdb->type)) ;
 }
 
-static void printGroup(char *parent, struct trackDb *tdb, boolean folder, boolean user)
+static void printTrack(char *parent, struct trackDb *tdb,  boolean user)
 // output list elements for a group
 {
 char *userString = "";
-char *prefix = "";
-char *viewFunc = NULL;
-char *missingMethod = NULL;
 
-if (user)
-    {
-    if (tdb->parent && tdb->subtracks) 
-        {
-        viewFunc = trackDbSetting(tdb, "viewFunc");
-        missingMethod = trackDbSetting(tdb, "missingMethod");
-        userString = "data-jstree='{\\\"icon\\\":\\\"../images/folderC.png\\\"}' viewType='view' class='folder'";
-        }
-    else if (tdb->subtracks)
-        {
-        viewFunc = trackDbSetting(tdb, "viewFunc");
-        missingMethod = trackDbSetting(tdb, "missingMethod");
-        userString = "data-jstree='{\\\"icon\\\":\\\"../images/folderC.png\\\"}' viewType='collection' class='folder'";
-        }
-    else
-        userString = "data-jstree='{\\\"icon\\\":\\\"fa fa-minus-square\\\"}' class='track' viewType='track'";
-    }
-else
-    {
-    if (tdb->parent && tdb->subtracks) 
-        userString = "data-jstree='{\\\"icon\\\":\\\"../images/folderC.png\\\"}' class='nodrop' viewType='view'";
-    else if (tdb->subtracks)
-        userString = "data-jstree='{\\\"icon\\\":\\\"../images/folderC.png\\\"}' class='collection' viewType='track'";
+if (tdb->subtracks)
+    userString = "icon:'../images/folderC.png',children:true,";
+else if (user)
+    userString = "icon:'fa fa-minus-square',";
 else
-        userString = "data-jstree='{\\\"icon\\\":\\\"fa fa-plus\\\"}' class='nodrop' viewType='track'";
-    }
-    
+    userString = "icon:'fa fa-plus',";
     
 #define IMAKECOLOR_32(r,g,b) ( ((unsigned int)b<<0) | ((unsigned int)g << 8) | ((unsigned int)r << 16))
 
-char buffer[1024];
-char *viewFuncString = "";
-if (viewFunc != NULL)
-    {
-    safef(buffer, sizeof buffer, "viewFunc='%s' ", viewFunc);
-    viewFuncString = cloneString(buffer);
-    }
-
-char *missingString = "";
-if (missingMethod != NULL)
-    {
-    safef(buffer, sizeof buffer, "missingMethod='%s' ", missingMethod);
-    missingString = cloneString(buffer);
-    }
-
-jsInlineF("<li shortLabel='%s' longLabel='%s' color='#%06x' %s %s visibility='%s'  name='%s%s' %s>%s",  tdb->shortLabel, tdb->longLabel,IMAKECOLOR_32(tdb->colorR,tdb->colorG,tdb->colorB), viewFuncString, missingString, hStringFromTv(tdb->visibility), prefix,  trackHubSkipHubName(tdb->track),   userString,  tdb->shortLabel );
-jsInlineF(" (%s)", tdb->longLabel);
-
-
-if (tdb->subtracks)
-    {
-    struct trackDb *subTdb;
-
-    jsInlineF("<ul>");
-    for(subTdb = tdb->subtracks; subTdb; subTdb = subTdb->next)
-        printGroup(trackHubSkipHubName(tdb->track), subTdb, user && (subTdb->subtracks != NULL), user);
-    jsInlineF("</ul>");
-    }
-jsInlineF("</li>");
+jsInlineF("{%s id:'%s',li_attr:{shortlabel:'%s', longlabel:'%s',color:'#%06x',name:'%s'},text:'%s (%s)',parent:'%s'}",userString, trackHubSkipHubName(tdb->track), tdb->shortLabel, tdb->longLabel, IMAKECOLOR_32(tdb->colorR,tdb->colorG,tdb->colorB),trackHubSkipHubName(tdb->track),tdb->shortLabel,tdb->longLabel,parent);
 }
 
 static void outHubHeader(FILE *f, char *db)
 // output a track hub header
 {
 fprintf(f,"hub hub1\n\
 shortLabel Track Collections\n\
 longLabel Track Collections\n\
 useOneFile on\n\
 email genome-www@soe.ucsc.edu\n\n");
 fprintf(f,"genome %s\n\n", db);  
 }
 
 
 static char *getHubName(struct cart *cart, char *db)
@@ -258,121 +206,182 @@
             isVisible =  tdb->visibility != tvHide;
         else
             isVisible =  differentString(cartVis, "hide");
         }
     else if (isParentVisible(cart, tdb) &&  isSubtrackVisible(cart, tdb))
         isVisible = TRUE;
 
     if (isVisible)
         {
         struct trackDbRef *tdbRef;
         AllocVar(tdbRef);
         tdbRef->tdb = tdb;
         slAddHead(list, tdbRef);
         safef(buffer, sizeof buffer, "%s_imgOrd", tdb->track);
 
-        tdbRef->order = cartUsualInt(cart, buffer, 0);
+        tdbRef->order = cartUsualInt(cart, buffer, tdb->priority);
         }
     }
 }
 
 static int tdbRefCompare (const void *va, const void *vb)
 // Compare to sort on imgTrack->order.
 {
 const struct trackDbRef *a = *((struct trackDbRef **)va);
 const struct trackDbRef *b = *((struct trackDbRef **)vb);
 return (a->order - b->order);
 }       
 
-static void addVisibleTracks(struct cart *cart, struct trackDb *trackList)
+static void addVisibleTracks(struct dyString *rootChildren, struct cart *cart, struct trackDb *trackList)
 // add the visible tracks table rows.
 {
 struct trackDb *tdb;
 struct trackDbRef *tdbRefList = NULL, *tdbRef;
-//checkForVisible(fullTrackList);
 for(tdb = trackList; tdb; tdb = tdb->next)
     {
     checkForVisible(cart, &tdbRefList, tdb);
     }
 
 slSort(&tdbRefList, tdbRefCompare);
-
-jsInlineF("<ul>");
-jsInlineF("<li data-jstree='{\\\"icon\\\":\\\"../images/folderC.png\\\"}' class='nodrop folder' name='%s'>%s", "visibile", "Visible Tracks");
-jsInlineF("<ul>");
+if (!isEmpty(rootChildren->string))
+    dyStringPrintf(rootChildren, ",");
+dyStringPrintf(rootChildren, "{icon:'../images/folderC.png',id:'visible', text:'Visible Tracks', parent:'#'");
+if (tdbRefList != NULL)
+    dyStringPrintf(rootChildren, ",children:true");
+dyStringPrintf(rootChildren, "}");
+
+jsInlineF("trackData['visible'] = [");
 for(tdbRef = tdbRefList; tdbRef; tdbRef = tdbRef->next)
-    printGroup("visible", tdbRef->tdb, FALSE, FALSE);
+    {
+    printTrack("visible", tdbRef->tdb,  FALSE);
+    if (tdbRef->next != NULL)
+        jsInlineF(",");
+    }
+jsInlineF("];");
+}
+
+void printSubtracks(char *arrayName, struct trackDb *parentTdb, boolean user)
+{
+if (parentTdb->subtracks == NULL)
+    return;
+jsInlineF("%s['%s'] = [", arrayName, trackHubSkipHubName(parentTdb->track));
+boolean first = TRUE;
+struct trackDb *tdb;
+for(tdb = parentTdb->subtracks; tdb;  tdb = tdb->next)
+    {
+    if (!first)
+        jsInlineF(",");
+    printTrack(trackHubSkipHubName(parentTdb->track), tdb, user);
+    first = FALSE;
+    }
+jsInlineF("];");
+for(tdb = parentTdb->subtracks; tdb;  tdb = tdb->next)
+    printSubtracks(arrayName,tdb, user);
+}
+
+void addSubtrackNames(struct dyString *dy, struct trackDb *parentTdb)
+{
+if (parentTdb->subtracks == NULL)
+    return;
 
-jsInlineF("</ul>");
-jsInlineF("</li>");
-jsInlineF("</ul>");
+struct trackDb *tdb;
+for(tdb = parentTdb->subtracks; tdb;  tdb = tdb->next)
+    {
+    dyStringPrintf(dy, ",'%s'", trackHubSkipHubName(tdb->track));
+    addSubtrackNames(dy, tdb);
+    }
 }
 
 static void doTable(struct cart *cart, char *db, struct grp *groupList, struct trackDb *trackList)
 // output the tree table
 {
 char *hubName = hubNameFromUrl(getHubName(cart, db));
 struct grp *curGroup;
 for(curGroup = groupList; curGroup;  curGroup = curGroup->next)
     {
     if ((hubName != NULL) && sameString(curGroup->name, hubName))
         break;
     }
 
-jsInlineF("$('#currentCollection').append(\"");
-jsInlineF("<div id='root'>");
+jsInlineF("var collectionData = []; ");
+struct dyString *dy = newDyString(100);
 if (curGroup != NULL)
     {
     // print out all the tracks in all the collections
     struct trackDb *tdb;
+    jsInlineF("collectionData['#'] = [");
+    boolean first = TRUE;
     for(tdb = trackList; tdb;  tdb = tdb->next)
         {
         if (sameString(tdb->grp, hubName))
             {
-            jsInlineF("<ul>");
-            printGroup("collections", tdb, TRUE, TRUE);
-            jsInlineF("</ul>");
+            if (!first)
+                {
+                jsInlineF(",");
+                dyStringPrintf(dy, ",");
                 }
+            printTrack("#", tdb,  TRUE);
+            dyStringPrintf(dy, "'%s'", trackHubSkipHubName(tdb->track));
+            first = FALSE;
             }
         }
-jsInlineF("</div>");
-jsInlineF("\");\n");
+    jsInlineF("];");
+    for(tdb = trackList; tdb;  tdb = tdb->next)
+        {
+        if ( sameString(tdb->grp, curGroup->name))
+            {
+            printSubtracks("collectionData", tdb, TRUE);
+            addSubtrackNames(dy, tdb);
+            }
+        }
+    }
+else
+    jsInlineF("collectionData['#'] = [];");
+
+jsInlineF("var collectionNames = new Set([%s]);", dy->string);
 
-jsInlineF("$('#tracks').append(\"");
-addVisibleTracks(cart, trackList);
+jsInlineF("var trackData = []; ");
+struct dyString *rootChildren = newDyString(512);
+addVisibleTracks(rootChildren, cart, trackList);
 for(curGroup = groupList; curGroup;  curGroup = curGroup->next)
     {
     if ((hubName != NULL) && sameString(curGroup->name, hubName))
         continue;
-    jsInlineF("<ul>");
-    jsInlineF("<li data-jstree='{\\\"icon\\\":\\\"../images/folderC.png\\\"}' class='nodrop folder' name='%s'>%s", curGroup->name, curGroup->label );
+    if (!isEmpty(rootChildren->string))
+        dyStringPrintf(rootChildren, ",");
+    dyStringPrintf(rootChildren, "{icon:'../images/folderC.png',id:'%s', text:'%s', parent:'#', children:true}", curGroup->name, curGroup->label);
     struct trackDb *tdb;
-    jsInlineF("<ul>");
+    jsInlineF("trackData['%s'] = [", curGroup->name);
+    boolean first = TRUE;
     for(tdb = trackList; tdb;  tdb = tdb->next)
         {
         if ( sameString(tdb->grp, curGroup->name))
             {
-            printGroup(curGroup->name, tdb, FALSE, FALSE);
+            if (!first)
+                jsInlineF(",");
+            printTrack(curGroup->name, tdb, FALSE);
+            first = FALSE;
             }
         }
-    jsInlineF("</ul>");
-    jsInlineF("</li>");
-    jsInlineF("</ul>");
-
+    jsInlineF("];");
+    for(tdb = trackList; tdb;  tdb = tdb->next)
+        {
+        if ( sameString(tdb->grp, curGroup->name))
+            printSubtracks("trackData", tdb, FALSE);
         }
-jsInlineF("\");\n");
-jsReloadOnBackButton(cart);
+    }
+jsInlineF("trackData['#'] = [%s];", rootChildren->string);
 jsInlineF("hgCollection.init();\n");
 }
 
 static void printHelp()
 // print out the help page
 {
 puts(
 "<br><a name='INFO_SECTION'></a>\n"
 "    <div class='row gbsPage'>\n"
 "        <div ><h1>Track Collection Builder Help</h1></div>\n"
 "        <div >\n"
 );
 puts(
 "       </div>\n"
 "    </div>\n"
@@ -658,52 +667,39 @@
         {
         struct hash *attrHash = jsonObjectVal(attEle, "name");
         struct jsonElement *strEle = (struct jsonElement *)hashFindVal(attrHash, "name");
         if (strEle == NULL)
             return;
         track->name = jsonStringEscape(strEle->val.jeString);
         hashAdd(trackHash, parentId, track);
 
         strEle = (struct jsonElement *)hashMustFindVal(attrHash, "shortlabel");
         track->shortLabel = jsonStringEscape(strEle->val.jeString);
         strEle = (struct jsonElement *)hashMustFindVal(attrHash, "longlabel");
         track->longLabel = jsonStringEscape(strEle->val.jeString);
         track->visibility = "pack";
         strEle = (struct jsonElement *)hashMustFindVal(attrHash, "color");
         track->color = hexStringToLong(jsonStringEscape(strEle->val.jeString));
-        /*
-        strEle = (struct jsonElement *)hashMustFindVal(attrHash, "visibility");
-        track->visibility = jsonStringEscape(strEle->val.jeString);
-        strEle = (struct jsonElement *)hashMustFindVal(attrHash, "color");
-        track->color = hexStringToLong(jsonStringEscape(strEle->val.jeString));
-        strEle = (struct jsonElement *)hashFindVal(attrHash, "viewfunc");
-        if (strEle)
-            track->viewFunc = jsonStringEscape(strEle->val.jeString);
-        strEle = (struct jsonElement *)hashFindVal(attrHash, "missingmethod");
-        if (strEle)
-            track->missingMethod = jsonStringEscape(strEle->val.jeString);
-            */
         }
 
     if (sameString(parentName, "#"))
         slAddHead(collectionList, track);
     else
         {
         struct track *parent = hashMustFindVal(trackHash, parentName);
         slAddTail(&parent->trackList, track);
         }
-
     }
 }
 
 static struct track *parseJsonElements( struct jsonElement *collectionElements)
 // parse the JSON returned from the ap
 {
 struct track *collectionList = NULL;
 struct hash *trackHash = hashNew(5);
 struct jsonParseData jpd = {&collectionList, trackHash};
 jsonElementRecurse(collectionElements, NULL, FALSE, jsonObjStart, NULL, &jpd);
 
 slReverse(&collectionList);
 return collectionList;
 }
 
@@ -789,31 +785,31 @@
 struct hash *superHash = newHash(5);
 
 for(tdb = trackList; tdb;  tdb = nextTdb)
     {
     nextTdb = tdb->next;
 
     if (tdb->parent)
         {
         // part of a super track
         if (hashLookup(superHash, tdb->parent->track) == NULL)
             {
             hashStore(superHash, tdb->parent->track);
 
             slAddHead(&newList, tdb->parent);
             }
-        slAddHead(&tdb->parent->subtracks, tdb);
+        slAddTail(&tdb->parent->subtracks, tdb);
         }
     else
         slAddHead(&newList, tdb);
     }
 
 slReverse(&newList);
 
 return newList;
 }
 
 static void outOneTdb(char *db, struct sqlConnection *conn, FILE *f, struct trackDb *tdb, int numTabs)
 /* Put out a single trackDb entry to our collections hub. */
 {
 char *tabs = "";
 if (numTabs == 1)