8d551f7d3103d3972b7314e27caa2b32d7aa8df8 max Thu Oct 23 09:17:05 2025 -0700 dont show color pickers when the field has too many values, small perf improvement, for dev-whole-brain-hqm&meta=Initial_Class_markers_level_2, brittney diff --git src/cbPyLib/cellbrowser/cbWeb/js/cellBrowser.js src/cbPyLib/cellbrowser/cbWeb/js/cellBrowser.js index 5706a1d..3d75a5c 100644 --- src/cbPyLib/cellbrowser/cbWeb/js/cellBrowser.js +++ src/cbPyLib/cellbrowser/cbWeb/js/cellBrowser.js @@ -76,52 +76,55 @@ // width of a single gene cell in the meta gene bar tables //var gGeneCellWidth = 66; // height of the trace viewer at the bottom of the screen var traceHeight = 100; // height of bottom gene bar var geneBarHeight = 100; var geneBarMargin = 5; // color for missing value when coloring by expression value //var cNullColor = "CCCCCC"; //const cNullColor = "DDDDDD"; //const cNullColor = "95DFFF"; //= light blue, also tried e1f6ff //const cNullColor = "e1f6ff"; //= light blue const cNullColor = "AFEFFF"; //= light blue - const nanColor = "DDDDDD"; // light grey + //const nanColor = "DDDDDD"; // light grey const cDefGradPalette = "magma"; // default legend gradient palette for gene expression // this is a special palette, tol-sq with the first entry being a light blue, so 0 stands out a bit more const cDefGradPaletteHeat = "magma"; // default legend gradient palette for the heatmap const cDefQualPalette = "rainbow"; // default legend palette for categorical values var datasetGradPalette = cDefGradPalette; var datasetQualPalette = cDefQualPalette; const exprBinCount = 10; //number of expression bins for genes // has to match cbData.js.exprBinCount - TODO - share the constant between these two files var HIDELABELSNAME = "Hide labels"; var SHOWLABELSNAME = "Show labels"; var METABOXTITLE = "By Annotation"; // maximum number of distinct values that one can color on - const MAXCOLORCOUNT = 1500; + const MAXCOLORCOUNT = 7000; const MAXLABELCOUNT = 500; + // after this number of rows in the legend, no more color pickers are shown + const MAXCOLPICK = 300; + // histograms show only the top X values and summarize the rest into "other" var HISTOCOUNT = 12; // the sparkline is a bit shorter var SPARKHISTOCOUNT = 12; // links to various external databases var dbLinks = { "HPO" : "https://hpo.jax.org/app/browse/gene/", // entrez ID "OMIM" : "https://omim.org/entry/", // OMIM ID "COSMIC" : "http://cancer.sanger.ac.uk/cosmic/gene/analysis?ln=", // gene symbol "SFARI" : "https://gene.sfari.org/database/human-gene/", // gene symbol "BrainSpLMD" : "http://www.brainspan.org/lcm/search?exact_match=true&search_type=gene&search_term=", // entrez "BrainSpMouseDev" : "http://developingmouse.brain-map.org/gene/show/", // internal Brainspan ID "Eurexp" : "http://www.eurexpress.org/ee/databases/assay.jsp?assayID=", // internal ID "LMD" : "http://www.brainspan.org/lcm/search?exact_match=true&search_type=gene&search_term=" // entrez @@ -620,30 +623,31 @@ $( "#tabLink2" ).hide(); $( "#pane3" ).hide(); $( "#tabLink3" ).hide(); } var tabIdx = 0; if (openTab==="images") tabIdx=3; $("#tpOpenDialogTabs").tabs("refresh").tabs("option", "active", tabIdx); if (openTab==="images") $("#tabLinkImg").click(); } let descLabels = { "paper_url":"Publication", "other_url" : "Website", + "hubUrl" : "UCSC Genome Browser", "geo_series" : "NCBI GEO Series", // = CIRM tagsV5 "sra" : "NCBI Short Read Archive", "pmid" : "PubMed Abstract", "pmcid" : "PubMed Fulltext", "sra_study" : "NCBI Short-Read Archive", "ega_study" : "European Genotype-Phenot. Archive Study", "ega_dataset" : "European Genotype-Phenot. Archive Dataset", "bioproject" : "NCBI Bioproject", "dbgap" : "NCBI DbGaP", "biorxiv_url" : "BioRxiv preprint", "doi" : "Publication Fulltext", "cbDoi" : "Data Citation DOI", "cap_project" : "Cell Annotation Platform", "arrayexpress" : "ArrayExpress", "ena_project" : "European Nucleotide Archive", @@ -688,30 +692,35 @@ // for 99% of the cases, it'll be a string though let urls = desc[key]; if (!(urls instanceof Array)) urls = [urls]; let frags = []; // html fragments, one per identifier for (let url of urls) { url = url.toString(); // in case it's an integer or float let urlLabel = url; let spcPos = url.indexOf(" "); if (spcPos!==-1) { urlLabel = url.slice(spcPos+1); url = url.slice(0,spcPos); } + if (key==="hubUrl") { + url = makeHubUrl(null); + urlLabel = "Connect Track Hub"; + } + if (!url.startsWith("http")) url = descUrls[key]+url; let parts = [] parts.push(""); parts.push(urlLabel); parts.push(""); let htmlLine = parts.join(""); frags.push(htmlLine); } htmls.push(frags.join(", ")); htmls.push("
"); } @@ -998,30 +1007,31 @@ if (desc.lab) { htmls.push("Lab: "+desc.lab); htmls.push("
"); } if (desc.institution) { htmls.push("Institution: "+desc.institution); htmls.push("
"); } htmlAddLink(htmls, desc, "cbDoi"); htmlAddLink(htmls, desc, "biorxiv_url"); htmlAddLink(htmls, desc, "paper_url"); htmlAddLink(htmls, desc, "other_url"); + htmlAddLink(htmls, db.conf, "hubUrl"); htmlAddLink(htmls, desc, "geo_series"); htmlAddLink(htmls, desc, "pmid"); htmlAddLink(htmls, desc, "dbgap"); htmlAddLink(htmls, desc, "sra_study"); htmlAddLink(htmls, desc, "bioproject"); htmlAddLink(htmls, desc, "sra"); htmlAddLink(htmls, desc, "doi"); htmlAddLink(htmls, desc, "cap_project"); htmlAddLink(htmls, desc, "arrayexpress"); htmlAddLink(htmls, desc, "cirm_dataset"); htmlAddLink(htmls, desc, "ega_study"); htmlAddLink(htmls, desc, "ega_dataset"); htmlAddLink(htmls, desc, "ena_project"); htmlAddLink(htmls, desc, "hca_dcp"); @@ -1077,30 +1087,31 @@ console.log(datasetInfo); if ( datasetInfo.atacSearch) { htmls.push("ATAC-seq search gene models: " + datasetInfo.atacSearch); htmls.push("
"); } htmls.push("Dataset classification: "); buildClassification(htmls, datasetInfo, "body_parts", "Organs", true); buildClassification(htmls, datasetInfo, "diseases", "Diseases", true); buildClassification(htmls, datasetInfo, "organisms", "Organism", true); buildClassification(htmls, datasetInfo, "life_stages", "Life Stage", true); buildClassification(htmls, datasetInfo, "domains", "Scientific Domain", true); buildClassification(htmls, datasetInfo, "sources", "Source Database", false); + buildClassification(htmls, datasetInfo, "assay", "Assay", false); htmls.push("

If you use the Cell Browser of this dataset, please cite the " + "original publication and " + "" + "Speir et al. 2021. Feedback? Email us at cells@ucsc.edu."+ "

"); htmls.push("

Cell Browser dataset ID: "+datasetInfo.name+ "

"); } } $( "#pane1" ).html(htmls.join("")); @@ -1147,40 +1158,42 @@ var clickClass = "tpDatasetButton"; if (dataset.isCollection) clickClass = "tpCollectionButton"; if (dataset.name===selName || (selName===undefined && i===0)) { clickClass += " active"; selIdx = i; } let bodyPartStr = getFacetString(dataset, "body_parts"); let disStr = getFacetString(dataset, "diseases"); let orgStr = getFacetString(dataset, "organisms"); let projStr = getFacetString(dataset, "projects"); let domStr = getFacetString(dataset, "domains"); let lifeStr = getFacetString(dataset, "life_stages"); + let assayStr = getFacetString(dataset, "assays"); let sourceStr = getFacetString(dataset, "sources"); var line = ""; // bootstrap seems to remove the id htmls.push(line); if (!dataset.isSummary) htmls.push(''); if (dataset.sampleCount!==undefined) { var countDesc = prettyNumber(dataset.sampleCount); htmls.push(""+countDesc+""); } if (dataset.datasetCount!==undefined) { htmls.push(""+dataset.datasetCount+" datasets"); } @@ -1378,30 +1391,32 @@ function onFilterChange(ev) { /* called when user changes a filter: updates list of datasets shown */ var filtNames = $(this).val(); var param = null; if (this.id==="tpBodyCombo") param = "bp"; else if (this.id=="tpDisCombo") param = "dis"; else if (this.id=="tpOrgCombo") param = "org"; else if (this.id=="tpProjCombo") param = "proj"; else if (this.id=="tpDomCombo") param = "dom"; + else if (this.id=="tpAssayCombo") + param = "assay"; else if (this.id=="tpStageCombo") param = "stage"; // change the URL var filtArg = filtNames.join("~"); var urlArgs = {} urlArgs[param] = filtArg; changeUrl(urlArgs); filterDatasetsDom(); } // -- end inline functions gOpenDataset = openDsInfo; var activeIdx = 0; @@ -1422,55 +1437,58 @@ noteLines.push( "

The collection '"+openDsInfo.shortLabel+"' contains "+dsCount+" datasets. " + "Double-click or click 'Open' below.
To move between datasets later in the cell browser, " + "use the 'Collection' dropdown.

"); changeUrl({"ds":openDsInfo.name.replace(/\//g, " ")}); // + is easier to type } let doFilters = false; let filtList = []; let bodyParts = null; let diseases = null; let organisms = null; let projects = null; let lifeStages = null; let domains = null; + let assays = null; let sources = null; if (openDsInfo.parents === undefined && openDsInfo.datasets !== undefined) { bodyParts = getDatasetAttrs(openDsInfo.datasets, "body_parts"); diseases = getDatasetAttrs(openDsInfo.datasets, "diseases"); organisms = getDatasetAttrs(openDsInfo.datasets, "organisms"); projects = getDatasetAttrs(openDsInfo.datasets, "projects"); lifeStages = getDatasetAttrs(openDsInfo.datasets, "life_stages"); + assays = getDatasetAttrs(openDsInfo.datasets, "assays"); domains = getDatasetAttrs(openDsInfo.datasets, "domains"); sources = getDatasetAttrs(openDsInfo.datasets, "sources"); // mirror websites are not using the filters at all. So switch off the entire filter UI if they're not used if (bodyParts.length!==0 || diseases.length!==0 || organisms.length!==0 || projects.length!==0 || domains.length!==0 || lifeStages.length!==0 || sources.length!==0) doFilters = true; if (doFilters) { noteLines.push("
Filters:
"); buildFilter(noteLines, bodyParts, "Organ", "body", "tpBodyCombo", "select organs..."); buildFilter(noteLines, diseases, "Disease", "dis", "tpDisCombo", "select diseases..."); buildFilter(noteLines, organisms, "Species", "org", "tpOrgCombo", "select species..."); buildFilter(noteLines, projects, "Project", "proj", "tpProjCombo", "select project..."); noteLines.push("
"); buildFilter(noteLines, lifeStages, "Life Stages", "stage", "tpStageCombo", "select stage..."); buildFilter(noteLines, domains, "Scient. Domain", "dom", "tpDomCombo", "select domain..."); + buildFilter(noteLines, assays, "Assay", "assay", "tpAssayCombo", "select assay..."); buildFilter(noteLines, sources, "Source DB", "source", "tpSourceCombo", "select db..."); } } // create links to the parents of the dataset if (openDsInfo && openDsInfo.parents && !onlyInfo) { noteLines.push("Go back to: " ); // make the back links let backLinks = []; let allParents = []; let parents = openDsInfo.parents; for (let i=0; i Find by ID'."); return null; } if (metaInfo.diffValCount > MAXCOLORCOUNT && metaInfo.type==="enum") { warn("This field has "+metaInfo.diffValCount+" different values. Coloring on a field that has more than "+MAXCOLORCOUNT+" different values is not supported."); return null; } - if (fieldName===defaultMetaField) changeUrl({"meta":null, "gene":null}); else changeUrl({"meta":fieldName, "gene":null}); db.conf.activeColorField = fieldName; if (metaInfo.arr) // eg custom fields onMetaArrLoaded(metaInfo.arr, metaInfo); else db.loadMetaVec(metaInfo, onMetaArrLoaded, onProgress, {}, db.conf.binStrategy); changeUrl({"pal":null}); // clear the gene search box @@ -3501,42 +3519,54 @@ // update the "recent genes" div for (var i = 0; i < gRecentGenes.length; i++) { // remove previous gene entry with the same symbol var recIntId = gRecentGenes[i][0]; if (!db.isAtacMode()) recIntId = recIntId.split("|")[0]; // only keep geneId for comparisons if (recIntId===locusStr || gRecentGenes[i][1]===locusStr) { // match symbol or ID gRecentGenes.splice(i, 1); break; } } // make sure that recent genes table has symbol and Id var locusWithSym = locusStr; if (db.isAtacMode()) { + for (let qg of db.conf.quickGenes) { + if (qg[0]===locusStr) { + geneDesc = qg[1]; + //selectizeSetValue(geneDesc); + let geneId = db.mustFindOneGeneExact(geneDesc); + updatePeakListWithGene(geneId); + updatePeakListCheckboxes(locusStr); + } + } //locusWithSym = shortenRange(locusStr); } else { if (locusStr.indexOf("+")===-1) { let geneInfo = db.getGeneInfo(locusStr); if ((geneInfo.sym!==geneInfo.geneId)) locusWithSym = geneInfo.id+"|"+geneInfo.sym; } else { let geneCount = locusStr.split("+").length; locusWithSym = locusStr+"|Sum of "+geneCount+" genes"; } } + if (db.isAtacMode()) { + selectizeSetValue(geneDesc) + } gRecentGenes.unshift([locusWithSym, geneDesc]); // insert at position 0 gRecentGenes = gRecentGenes.slice(0, 9); // keep only nine last buildGeneTable(null, "tpRecentGenes", null, null, gRecentGenes); $('#tpRecentGenes .tpGeneBarCell').click( onGeneClick ); resizeGeneTableDivs("tpRecentGenes"); } // clear the meta combo $('#tpMetaCombo').val(0).trigger('chosen:updated'); console.log("Loading gene expression vector for "+locusStr); db.loadExprAndDiscretize(locusStr, gotGeneVec, onProgress, db.conf.binStrategy); } @@ -3982,40 +4012,41 @@ } function onTransClick(ev) { /* user has clicked transparency menu entry */ var transText = ev.target.innerText; var transStr = transText.slice(0, -1); // remove last char var transFloat = 1.0 - (parseFloat(transStr)/100.0); transparency = transFloat; plotDots(); $("#tpTransMenu").children().removeClass("active"); $("#tpTrans"+transStr).addClass("active"); renderer.render(stage); } function legendSort(sortBy) { - /* sort the legend by "name" or "count" */ + /* sort the legend by "name" or "count" or "orderKey"*/ var rows = gLegend.rows; if (sortBy==="name") { // index 2 is the label rows.sort(function(a, b) { return naturalSort(a.label, b.label); }); - } - else { + } else if (sortBy==="count") { // sort this list by count = index 3 rows.sort(function(a, b) { return b.count - a.count; }); // reverse-sort by count + } else { + rows.sort(function(a, b) { return a.orderKey - b.orderKey; }); // sort by order key } buildLegendBar(); } //function filterCoordsAndUpdate(cellIds, mode) { /* hide/show currently selected cell IDs or "show all". Rebuild the legend and the coloring. */ //if (mode=="hide") //shownCoords = removeCellIds(shownCoords, cellIds); //else if (mode=="showOnly") //shownCoords = showOnlyCellIds(shownCoords, cellIds); //else //shownCoords = allCoords.slice(); //pixelCoords = scaleData(shownCoords); @@ -4277,80 +4308,75 @@ } } function legendSetColors(legend, colors, keyName) { /* set the colors for all legend rows, keyName can be "color" or "defColor", depending on * whether the current row color or the row default color should be changed. * colors can also be null to reset all values to null. */ if (!keyName) keyName = "color"; var rows = legend.rows; var palIdx = 0; var hasNan = false; for (let i = 0; i < rows.length; i++) { var colorVal = null; var legendRow = rows[i]; + // any meta legend with a "special" value, so 0 or empty gets grey if ((i==0 && legendRow.label == "0" && legend.type=="expr" && !hasNan) || (likeEmptyString(legendRow.label) && legend.type=="meta")) { colorVal = cNullColor; - } else if ((legendRow.label == "-12345.00" && legend.type=="expr")) { - legendRow.label = "NaN"; + // gene expression diagrams: grey if NA + } else if ((legendRow.label == "NaN" && legend.type=="expr")) { + //legendRow.label = "NaN"; colorVal = cNullColor; hasNan = true; } else if (colors) { colorVal = colors[palIdx]; palIdx++; } legendRow[keyName] = colorVal; } } function legendSetPalette(legend, origPalName) { /* update the defColor [1] attribute of all legend rows. pal is an array of hex colors. * Will use the predefined colors that are * in the legend.metaInfo.colors configuration, if present. * */ var palName = origPalName; if (origPalName==="default") { if (legend.rowType==="category") palName = datasetQualPalette; else palName = datasetGradPalette; } var rows = legend.rows; // the number of colors needed is not the number of legend rows, because some values // do not get a color from the palette, e.g. "Unknown" and "0" rows //var n = rows.length; - var n = legend.rows.length; - var hasNan = false; - var hasZero = false; + var n = rows.length; + var hasSpecialBin = false; for (var row of rows) { - if (row.strKey==="noExpr") - hasZero = true; - if (row.strKey=="nan") - hasNan=true; + if (row.strKey==="noExpr" || row.strKey=="NaN") + hasSpecialBin = true; } - if (hasZero) - n--; - if (hasNan) + if (hasSpecialBin) n--; - if (hasZero && hasNan) - n++; var pal = null; var usePredefined = false; // if this is a field for which colors were defined manually during the cbBuild, use them if (legend.metaInfo!==undefined && legend.metaInfo.colors!==undefined && origPalName==="default") { // the order of the color values in the metaInfo object is the same as the order of the order of the values in the // JSON file. But the legend has been sorted now, so we cannot just copy over the array as it is pal = makeColorPalette(palName, rows.length); var rows = legend.rows; var predefColors = legend.metaInfo.colors; for (var i=0; i < rows.length; i++) { var origIndex = rows[i].intKey; var col = predefColors[origIndex]; if (col !== null) @@ -4387,30 +4413,32 @@ var maxDig = 2; //if (binMin % 1 === 0) // maxDig = 0 if (isAllInt) { minDig = 0; maxDig = 0 } var legLabel = ""; if (binMax===0 && binMax===0) legLabel = "0"; else if (binMin==="Unknown") legLabel = "Unknown"; + else if (binMin==="NaN") + legLabel = "NaN"; else if (binMin!==binMax) { if (Math.abs(binMin) > 1000000) binMin = binMin.toPrecision(4); if (Math.abs(binMax) > 1000000) binMax = binMax.toPrecision(4); if (typeof(binMin)=== 'number') binMin = binMin.toFixed(minDig); if (typeof(binMax)=== 'number') binMax = binMax.toFixed(minDig); legLabel = binMin+' – '+binMax; } else legLabel = binMin.toFixed(minDig); return legLabel; @@ -4740,32 +4768,37 @@ $("#tpGeneProgress").progressbar( { value: false, max : gLoad_geneList.length }); } function shortenRange(s) { /* reformat atac range chr1|start|end to chr1:10Mbp */ var parts = s.split("|"); return parts[0]+":"+prettyNumber(parts[1]); } function humanizeRange(s) { /* reformat atac range chr1|start|end to chr1:xxx-yyy */ - var parts = s.split("|"); - return parts[0]+":"+parts[1]+"-"+parts[2]; + let ranges = s.split("+"); + let humanRanges = []; + for (let oneRange of ranges) { + var parts = oneRange.split("|"); + humanRanges.push( parts[0]+":"+parts[1]+"-"+parts[2] ); + } + return humanRanges.join(" and "); } function htmlAddInfoIcon(htmls, helpText, placement) { /* add an info icon with some text to htmls */ var iconHtml = ''; var addAttrs = ""; if (placement!==undefined) addAttrs = " data-placement='"+placement+"'" htmls.push(" "+iconHtml+""); return htmls; } function buildGeneTable(htmls, divId, title, subtitle, geneInfos, noteStr, helpText) { /* create gene expression info table. if htmls is null, update DIV with divId in-place. * geneInfos is array of [gene, mouseover]. gene can be geneId+"|"+symbol. @@ -4809,33 +4842,35 @@ } var i = 0; while (i < geneInfos.length) { var geneInfo = geneInfos[i]; var geneIdOrSym = geneInfo[0]; var mouseOver = geneInfo[1]; // geneIdOrSym can be just the symbol (if we all we have is symbols) or geneId|symbol var internalId; var label; if (geneIdOrSym.indexOf("|")!==-1) { if (db.isAtacMode()) { label = shortenRange(geneIdOrSym); internalId = geneIdOrSym; - if (!mouseOver) + if (mouseOver) { + // inverse label <> mouseover for ATAC, much easier to read + label = mouseOver; + } mouseOver = humanizeRange(geneIdOrSym); - } else { var parts = geneIdOrSym.split("|"); internalId = parts[0]; label = parts[1]; } } else { internalId = geneIdOrSym; label = internalId; } if (mouseOver===undefined) mouseOver = internalId; htmls.push(''+label+''); i++; @@ -5040,30 +5075,31 @@ // default any color that looks like "NA" or "undefined" to grey if (likeEmptyString(label)) color = cNullColor; // override any color with the color specified in the current URL var savKey = COL_PREFIX+uniqueKey; color = getFromUrl(savKey, color); rows.push( { "color": color, "defColor": color, "label": label, "count": count, "intKey":valIdx, "strKey":uniqueKey, "longLabel" : desc, + "orderKey" : 0, } ); } legend.rows = rows; legend.isSortedByName = sortResult.isSortedByName; legend.rowType = "category"; legend.selectionDirection = "all"; legendSetPalette(legend, "default"); return legend; } function legendSetTitle(label) { $('#tpLegendTitle').text(label); } @@ -5411,30 +5447,33 @@ // allow config to override the default palettes datasetGradPalette = cDefGradPalette; datasetQualPalette = cDefQualPalette; if (db.conf.defQuantPal) datasetGradPalette = db.conf.defQuantPal; if (db.conf.defCatPal) datasetQualPalette = db.conf.defCatPal; if (db.conf.metaBarWidth) metaBarWidth = db.conf.metaBarWidth; else metaBarWidth = 250; renderer.setPos(null, metaBarWidth+metaBarMargin); + var hubUrl = makeHubUrl(null); + $('#tpOpenGenome').attr("href", hubUrl); + if (!db.conf.metaFields) { // pablo often has single-dataset installations, there is no need to open the // dataset selection box then. if (db.conf.datasets && db.conf.datasets.length===1 && datasetName==="") // "" is the root dataset loadDataset(db.conf.datasets[0].name, false); else showCollectionDialog(datasetName); return; } let binData = localStorage.getItem(db.name+"|custom"); if (binData) { let jsonStr = LZString.decompress(binData); let customMeta = JSON.parse(jsonStr); db.conf.metaFields.unshift(customMeta); @@ -5774,31 +5813,31 @@ if (foundRanges.length === 0) { htmls.push("No peaks around this gene"); } else for (let rangeInfo of foundRanges) { let foundStart = rangeInfo[0]; let foundEnd = rangeInfo[1]; //let label = chrom+":"+foundStart+"-"+foundEnd; let dist = foundStart-searchStart; let label = prettySeqDist(dist, true); let regLen = foundEnd-foundStart; if (regLen!==0) label += ", "+(foundEnd-foundStart)+" bp long"; let checkBoxId = "range:"+chrom+":"+foundStart+":"+foundEnd+":"+dist; htmls.push("
"); - htmls.push(""); + htmls.push(""); htmls.push(""); htmls.push("
"); i++; } var divEl = document.getElementById("tpPeakList"); divEl.innerHTML = htmls.join(""); // set the DIV classAddListener("tpPeak", "input", onPeakChange); } function onPeakAll(ev) { /* select all peaks */ let peaks = peakListGetPeaksWith("off"); let peakNames = []; if (peaks.length>100) { alert("More than 100 peaks to select. This will take too long. Please contact us if you need this feature."); @@ -5888,30 +5927,49 @@ return callback(); this.clearOptions(); var genes = db.findGenes(query); selectizeSendGenes(genes, callback); } function updatePeakListWithGene(geneId) { /* update the peak list box with all peaks close to a gene's TSS */ var peaksInView = db.findRangesByGene(geneId); var gene = db.getGeneInfoAtac(geneId); peakListShowTitle(gene.sym, gene.chrom, gene.chromStart); peakListShowRanges(gene.chrom, peaksInView.ranges, gene.chromStart); changeUrl({"locusGene":geneId}); } + function updatePeakListCheckboxes(rangeStr) { + /* check all the boxes of peaks for rangeStr, which has format chr7|115585018|115585471+chr7|115596153|115596958 */ + + let checkRanges = rangeStr.split("+"); + for (let el of document.getElementsByClassName("tpPeakCheckbox")) { + // id is something like "range:chr7:115193275:115194105:-404621" + let idStr = el.id; + let parts = idStr.split(":"); + let chrom = parts[1]; + let start = parts[2]; + let end = parts[3]; + let range = chrom+"|"+start+"|"+end; + console.log(range, checkRanges); + if (checkRanges.includes(range)) { + el.checked=true; + } + } + } + function comboLoadAtac(query, callback) { /* The load() function for selectize for ATAC datasets. * called when the user types something into the gene box, calls callback with matching gene symbols or peaks * or shows the peaks in the peakList box */ if (!query.length) return callback(); this.clearOptions(); this.renderCache = {}; var range = cbUtil.parseRange(query); if (range===null) { if (!db.geneToTss || db.geneToTss===undefined) db.indexGenesAtac(); @@ -7129,30 +7187,31 @@ var nameParts = dataset.name.split("/"); var parentName = null; if (nameParts.length > 1) { //buildCollectionCombo(htmls, "tpCollectionCombo", 330, nextLeft, 0); buildCollectionCombo(htmls, "tpCollectionCombo", 330, null, 0); nameParts.pop(); parentName = nameParts.join("/"); } htmls.push(""); $(document.body).append(htmls.join("")); $('#tpOpenXena').click(onXenaButtonClick); + $('#tpOpenGenome').click(onGenomeButtonClick); activateTooltip('.tpIconButton'); activateTooltip('#tpOpenUcsc'); activateTooltip('#tpOpenDatasetButton'); activateTooltip('#tpOpenExprButton'); activateTooltip('#tpOpenImgButton'); $('#tpButtonInfo').click( function() { openDatasetDialog(db.conf, db.name) } ); $('#tpOpenImgButton').click( function() { openDatasetDialog(db.conf, db.name, "images") } ); activateCombobox("tpLayoutCombo", layoutComboWidth); if (parentName!==null) { @@ -7454,35 +7513,37 @@ htmls.push(""); // tpAnnotTab htmls.push("
"); buildGeneCombo(htmls, "tpGeneCombo", 0, metaBarWidth-10); htmls.push('
'); if (db.conf.atacSearch) buildPeakList(htmls); var geneLabel = getGeneLabel(); var recentHelp = "Shown below are the 10 most recently searched genes. Click any gene to color the plot on the right-hand side by the gene."; buildGeneTable(htmls, "tpRecentGenes", "Recent "+geneLabel+"s", - "Hover or select cells to update colors here
Click to color by gene", gRecentGenes, null, recentHelp); + "Hover or select cells to update colors here
Click to color by "+gFeatDesc, gRecentGenes, null, recentHelp); var noteStr = "No genes or peaks defined: Use quickGenesFile in cellbrowser.conf."; var geneHelp = "The dataset genes were defined by the dataset submitter, publication author or data wrangler at UCSC. " + "Click any of them to color the plot on the right hand side by the gene."; + if (db.conf.atacSearch) + geneHelp = "Predefined dataset ranges were defined by the dataset submitter. Click any to color by a list of loci, so a sum of the peaks contained in the range. The exact peaks are listed on mouse over."; buildGeneTable(htmls, "tpQuickGenes", "Dataset "+geneLabel+"s", null, db.conf.quickGenes, noteStr, geneHelp); htmls.push("
"); // tpGeneTab htmls.push("
"); buildLayoutCombo(db.conf.coordLabel, htmls, db.conf.coords, "tpLayoutCombo", 0, 2); htmls.push("
"); // tpLayoutTab htmls.push(""); // tpLeftSidebar $(document.body).append(htmls.join("")); resizeGeneTableDivs("tpRecentGenes"); resizeGeneTableDivs("tpQuickGenes"); @@ -8178,52 +8239,55 @@ if (status==="none") renderer.selectClear(); renderer.drawDots(); } function legendColorOnlyChecked(ev) { /* re-assign colors from palette, for only checked rows. Or reset all colors. */ if (gLegend.isColorOnlyChecked===undefined || gLegend.isColorOnlyChecked===false) { // make a new palette and assign to checked legend rows, otherwise grey let rows = gLegend.rows; let checkedCount = 0; for (let rowIdx=0; rowIdx"); htmls.push(""); htmls.push(""); @@ -8360,31 +8426,31 @@ var rows = gLegend.rows; var legTitle = gLegend.title; var subTitle = gLegend.subTitle; htmls.push(''+legTitle+""); if (subTitle) htmls.push('
'+subTitle+"
"); htmls.push('
Click buttons to select '+gSampleDesc+'s
'); htmls.push(""); htmls.push(""); htmls.push(""); htmls.push(""); - let buttonText = "Recolor checked fields"; + let buttonText = "Recolor checked values"; if (gLegend.isColorOnlyChecked===true) { buttonText = "Reset colors"; } htmls.push(""); htmls.push(""); // title htmls.push('
'); htmls.push('
'); // get the sum of all, to calculate frequency var sum = 0; for (var i = 0; i < rows.length; i++) { let count = rows[i].count; sum += count; @@ -8427,31 +8493,35 @@ else { if (label.length > 20) mouseOver = label; } var classStr = "tpLegend"; var line = "
"; htmls.push(line); let checkedStr = ""; if (row.isChecked) checkedStr = " checked"; htmls.push(""); + + if (gLegend.rows.length < MAXCOLPICK) htmls.push(""); + else + htmls.push("
 
"); htmls.push(""); htmls.push(label); htmls.push(""); var prec = 1; if (freq<1) //prec = minPrec; prec = 1+countLeadingZerosAfterDecimal(freq) // one more digit than the smallest frequency htmls.push(""+freq.toFixed(prec)+"%"); htmls.push(""); htmls.push("
"); } htmls.push('
'); // tpLegendRows @@ -8513,30 +8583,31 @@ // activate the color pickers for (let i = 0; i < colors.length; i++) { var colInfo = colors[i]; var rowIdx = colInfo[0]; var hexCode = colInfo[1]; var opt = { hideAfterPaletteSelect : true, color : hexCode, showPalette: true, allowEmpty : true, showInput: true, preferredFormat: "hex", change: onColorPickerChange } + if (gLegend.rows.length < MAXCOLPICK) $("#tpLegendColorPicker_"+rowIdx).spectrum(opt); } buildViolinPlot(); } function onColorPickerChange(color, ev) { /* called when user manually selects a color in the legend with the color picker */ console.log(ev); /* jshint validthis: true */ var valueIdx = parseInt(this.id.split("_")[1]); var rows = gLegend.rows; var clickedRow = rows[valueIdx]; var oldColorHex = clickedRow.color; var defColorHex = clickedRow.defColor; @@ -9763,26 +9834,28 @@ renderer.onNoLabelHover = onNoClusterNameHover; renderer.onCellClick = onCellClickOrHover; renderer.onCellHover = onCellClickOrHover; renderer.onNoCellHover = clearMetaAndGene; renderer.onLineHover = onLineHover; renderer.onZoom100Click = onZoom100Click; renderer.onSelChange = onSelChange; renderer.onRadiusAlphaChange = onRadiusAlphaChange; renderer.canvas.addEventListener("mouseleave", hideTooltip); loadDataset(datasetName, false, rootMd5); } // only export these functions return { - "main":main + "main":main, + "selectizeClear" : selectizeClear, + "selectizeSetValue" : selectizeSetValue } }(); function _tpReset() { /* for debugging: reset the intro setting */ localStorage.removeItem("introShown"); }