8a0ff4ba63dfd3e13391b2c602a157daaf54d665 kate Thu Mar 1 21:11:27 2012 -0800 Migrate ChipMatrix to refactoring 1 (dataMatrix.js) diff --git src/hg/js/encodeChipMatrix.js src/hg/js/encodeChipMatrix.js index 0c180b1..bddb781 100644 --- src/hg/js/encodeChipMatrix.js +++ src/hg/js/encodeChipMatrix.js @@ -1,250 +1,269 @@ -// encodeChipMatrix.js - pull experiment table and metadata from server -// and display ChIP antibodies vs. cell types in a matrix -// Formatted: jsbeautify.py -j -// Syntax checked: jslint indent:4, plusplus: true, continue: true, unparam: true, sloppy: true, browser: true */ +/* encodeChipMatrix.js - ENCODE web app to display ChIP-seq data vs. cell types in a matrix + + Formatted: jsbeautify.py -j + Syntax checked: jslint indent:4, plusplus: true, continue: true, unparam: true, sloppy: true, browser: true +*/ /*global $, encodeProject */ $(function () { var dataType, server, requests = [ // requests to server API encodeProject.serverRequests.experiment, encodeProject.serverRequests.cellType, encodeProject.serverRequests.antibody, encodeProject.serverRequests.expId ]; - var cellTypeHash = {}, antibodyHash = {}, targetHash = {}; - var cellType, antibody, target; - var organism, assembly, server, header; - var karyotype; - var spinner; + var $matrixTable = $('#matrixTable'); - function tableOut(matrix, cellTiers, antibodyGroups) { - // Create table with rows for each cell type and columns for each antibody target - var table, tableHeader, row, td; + function handleServerData(responses) { + // Main actions, called when loading data from server is complete - // fill in column headers from antibody targets returned by server - tableHeader = $('#columnHeaders'); - $.each(antibodyGroups, function (i, group) { - tableHeader.append('<th class="groupType"><div class="verticalText">' + - group.label + '</div></th>'); - $.each(group.targets, function (i, target) { - // prune out targets with no experiments - if (targetHash[target] === undefined) { + var experiments = responses[0], + cellTypes = responses[1], + antibodies = responses[2], + expIds = responses[3]; + + var antibodyGroups, cellTiers, expIdHash; + var antibodyTarget, cellType; + var matrix, antibodyTargetExps = {}; + + // hide spinner and show table + encodeMatrix.show($matrixTable); + + // set up structures for antibody targets and groups + antibodyGroups = encodeProject.getAntibodyGroups(antibodies); + + // set up structures for cell types and their tiers + cellTiers = encodeProject.getCellTiers(cellTypes); + + // use to filter out experiments not in this assembly + expIdHash = encodeProject.getExpIdHash(expIds); + + // gather experiments into matrix + matrix = makeExperimentMatrix(experiments, expIdHash, antibodyTargetExps); + + // fill in table using matrix + tableOut($matrixTable, matrix, cellTiers, antibodyGroups, antibodyTargetExps); + } + + function makeExperimentMatrix(experiments, expIdHash, antibodyTargetExps) { + // Populate antibodyTarget vs. cellType array with counts of experiments + + var antibody, target, cellType; + var matrix = {}; + + $.each(experiments, function (i, exp) { + // exclude ref genome annotations + if (exp.cellType === 'None') { return true; } - if (targetHash[target].count !== undefined) { - tableHeader.append('<th class="elementType" title="' + - targetHash[target].description + - '"><div class="verticalText">' + - target + '</div></th>'); + if (expIdHash[exp.ix] === undefined) { + return true; } - }); - }); - // fill in matrix: - // add rows with cell type labels (column 1) and cells for experiments - table = $('#matrixTable'); - - // add sections for each Tier of cells - $.each(cellTiers, function (i, tier) { - //skip bogus 4th tier (not my property ?) - if (tier === undefined) { + // todo: filter out with arg to hgApi ? + if (exp.dataType !== 'ChipSeq') { return true; } - table.append($('<tr class="matrix"><th class="groupType">' + "Tier " + - tier.term + '</th></td></tr>')); - - $.each(tier.cellTypes, function (i, cellType) { - if (!cellType) { + // count experiments per target so we can prune those having none + // (the matrix[cellType] indicates this for cell types + // so don't need hash for those + antibody = encodeProject.antibodyFromExp(exp); + if (antibody === undefined) { + return true; + } + target = encodeProject.targetFromAntibody(antibody); + if (target === undefined) { return true; } + if (antibodyTargetExps[target] === undefined) { + antibodyTargetExps[target] = 0; + } + antibodyTargetExps[target]++; + + cellType = exp.cellType; if (!matrix[cellType]) { + matrix[cellType] = {}; + } + if (!matrix[cellType][target]) { + matrix[cellType][target] = 0; + } + matrix[cellType][target]++; + }); + return matrix; + } + + function tableHeaderOut($table, antibodyGroups, antibodyTargetExps) { + // Generate table header and add to document + + var $tableHeader, $thead; + var maxLen; // for resizing header cells to accomodate label lengths + var antibodyTarget; + + // fill in column headers from antibody targets returned by server + $tableHeader = $('#columnHeaders'); + $thead = $('thead'); + + // 1st column is row headers + // colgroups are needed to support cross-hair hover effect + $thead.before('<colgroup></colgroup>'); + + $.each(antibodyGroups, function (i, group) { + $tableHeader.append('<th class="groupType"><div class="verticalText">' + + group.label + '</div></th>'); + maxLen = Math.max(maxLen, group.label.length); + $thead.before('<colgroup></colgroup>'); + $.each(group.targets, function (i, target) { + // prune out targets with no experiments + if (antibodyTargetExps[target] === undefined) { return true; } - karyotype = cellTypeHash[cellType].karyotype; - if (karyotype !== 'cancer' && karyotype !== 'normal') { - karyotype = 'unknown'; + antibodyTarget = encodeProject.getAntibodyTarget(target); + $tableHeader.append('<th class="elementType" title="' + + antibodyTarget.description + + '"><div class="verticalText">' + target + '</div></th>'); + // add colgroup element to support cross-hair hover effect + $thead.before('<colgroup class="experimentCol"></colgroup>'); + maxLen = Math.max(maxLen, target.length); + }); + }); + // adjust size of headers based on longest label length + // empirically len/2 em's is right + $('#columnHeaders th').css('height', (String((maxLen/2 + 2)).concat('em'))); + $('#columnHeaders th').css('width', '1em'); } - row = $('<tr><th class="elementType" title="' + - cellTypeHash[cellType].description + - '"><a href="/cgi-bin/hgEncodeVocab?ra=encode/cv.ra&term=' + cellType - + '">' + cellType + '</a><span title="karyotype: ' + karyotype + - '" class="' + karyotype + '">•</span></th>'); + + function rowAddCells($row, antibodyGroups, antibodyTargetExps, matrix, cellType) { + // populate a row in the matrix with cells for antibody target groups and the targets + // null cellType indicates this is a row for a cell group (tier) + + var $td; $.each(antibodyGroups, function (i, group) { // skip group header - row.append('<td></td>'); + $td = $('<td></td>'); + $td.addClass('matrixCell'); + $row.append($td); + $.each(group.targets, function (i, target) { // prune out targets with no experiments - if (targetHash[target] === undefined) { + if (antibodyTargetExps[target] === undefined) { + return true; + } + $td = $('<td></td>'); + $td.addClass('matrixCell'); + $row.append($td); + + if (cellType === null) { return true; } - if (targetHash[target].count === undefined) { + if (!matrix[cellType][target]) { + $td.addClass('todoExperiment'); return true; } - td = $('<td></td>'); - td.addClass('matrixCell'); - if (matrix[cellType][target]) { - td.addClass('experiment'); - td.text(matrix[cellType][target]); - td.data({ + // this cell represents experiments that + // fill in count, mouseover and selection by click + $td.addClass('experiment'); + $td.text(matrix[cellType][target]); + $td.data({ 'target' : target, 'cellType' : cellType }); - td.mouseover(function() { + $td.mouseover(function() { $(this).attr('title', 'Click to select: ' + - $(this).data().target + ' ' + ' in ' + + ($(this).data().target) + ' ' + ' in ' + $(this).data().cellType +' cells'); }); - td.click(function() { - // TODO: base on preview ? - var url = encodeProject.getSearchUrl(assembly); + $td.click(function() { + var url, antibodyTarget; // TODO: encapsulate var names // TODO: search on antibody + url = encodeMatrix.getSearchUrl(encodeProject.getAssembly()); url += - '&hgt_mdbVar1=dataType&hgt_mdbVal1=' + 'ChipSeq' + - '&hgt_mdbVar2=cell&hgt_mdbVal2=' + cellType + - '&hgt_mdbVar3=antibody'; + ('&hgt_mdbVar1=dataType&hgt_mdbVal1=' + 'ChipSeq' + + '&hgt_mdbVar2=cell&hgt_mdbVal2=' + $(this).data().cellType + + '&hgt_mdbVar3=antibody'); // TODO: html encode ? - $.each(targetHash[target].antibodies, function (i, antibody) { - url += '&hgt_mdbVal3=' + antibody; + antibodyTarget = encodeProject.getAntibodyTarget($(this).data().target); + $.each(antibodyTarget.antibodies, function (i, antibody) { + url += ('&hgt_mdbVal3=' + antibody); }); url += '&hgt_mdbVar4=view&hgt_mdbVal4=Any'; // TODO: open search window - //window.open(url, "searchWindow"); - window.location = url; - }); - } - row.append(td); + window.open(url, "searchWindow"); }); - table.append(row); }); - table.append(row); - }); - }); - $("body").append(table); - - // use floating-table-header plugin - table.floatHeader({ - cbFadeIn: function (header) { - // hide axis labels -- a bit tricky to do so - // as special handling needed for X axis label - $(".floatHeader #headerLabelRow").remove(); - $(".floatHeader #searchTypePanel").remove(); - $(".floatHeader #cellHeaderLabel").html(''); - - // Note: user-defined callback requires - // default actions from floatHeader plugin - // implementation (stop+fadeIn) - header.stop(true, true); - header.fadeIn(100); - } }); } - function handleServerData(responses) { - // main actions, called when loading data from server is complete - var experiments = responses[0], cellTypes = responses[1], - antibodies = responses[2], expIds = responses[3]; - var antibodyGroups, cellTiers, expIdHash; - var matrix = {}; - - hideLoadingImage(spinner); - $('#matrixTable').show(); - - // set up structures for antibodies and their groups - $.each(antibodies, function (i, antibody) { - antibodyHash[antibody.term] = antibody; - target = antibody.target; - if (targetHash[target] === undefined) { - targetHash[target] = { - count: 0, // experiments - description: antibody.targetDescription, - antibodies: [] - }; - } - targetHash[target].antibodies.push(antibody.term) - }); - antibodyGroups = encodeProject.getAntibodyGroups(antibodies); - - // set up structures for cell types and their tiers - $.each(cellTypes, function (i, cellType) { - cellTypeHash[cellType.term] = cellType; - }); - cellTiers = encodeProject.getCellTiers(cellTypes); + function tableMatrixOut($table, matrix, cellTiers, antibodyGroups, antibodyTargetExps) { + // Fill in matrix -- + // add rows with cell type labels (column 1) and cells for experiments + // add sections for each Tier of cell type - // use to filter out experiments not in this assembly - expIdHash = encodeProject.getExpIdHash(expIds); + var maxLen, karyotype, cellType; + var $row; - // gather experiments into matrix - $.each(experiments, function (i, exp) { - // todo: filter out with arg to hgApi - if (exp.organism !== organism) { - return true; - } - // exclude ref genome annotations - if (exp.cellType === 'None') { + // add sections for each Tier of cells + $.each(cellTiers, function (i, tier) { + //skip bogus 4th tier (not my property ?) + if (tier === undefined) { return true; } - if (expIdHash[exp.ix] === undefined) { + + $row = $('<tr class="matrix"><th class="groupType">' + + "Tier " + tier.term + '</th></td></tr>'); + rowAddCells($row, antibodyGroups, antibodyTargetExps, matrix, null); + $table.append($row); + maxLen = 0; + + $.each(tier.cellTypes, function (i, term) { + if (!term) { return true; } - // todo: filter out with arg to hgApi ? - if (exp.dataType !== 'ChipSeq') { + if (!matrix[term]) { return true; } - // count experiments per target so we can prune those having none - // (the matrix[cellType] indicates this for cell types - // so don't need hash for those - antibody = encodeProject.antibodyFromExp(exp); - target = encodeProject.targetFromAntibody(antibody, antibodyHash); - if (targetHash[target] !== undefined) { - targetHash[target].count++; + cellType = encodeProject.getCellType(term); + karyotype = cellType.karyotype; + if (karyotype !== 'cancer' && karyotype !== 'normal') { + karyotype = 'unknown'; } + // note karyotype bullet layout requires non-intuitive placement + // in code before the span that shows to it's left + $row = $('<tr>' + + '<th class="elementType">' + + '<span style="float:right; text-align: right;" title="karyotype: ' + karyotype + '" class="karyotype ' + karyotype + '">•</span>' + + '<span title="' + cellType.description + '"><a href="/cgi-bin/hgEncodeVocab?ra=encode/cv.ra&term=' + cellType.term + '">' + cellType.term + '</a>' + + '</th>' + ); + maxLen = Math.max(maxLen, cellType.term.length); - cellType = exp.cellType; - if (!matrix[cellType]) { - matrix[cellType] = {}; - } - if (!matrix[cellType][target]) { - matrix[cellType][target] = 0; - } - matrix[cellType][target]++; + rowAddCells($row, antibodyGroups, antibodyTargetExps, matrix, cellType.term); + $table.append($row); }); - - // fill in table - tableOut(matrix, cellTiers, antibodyGroups); - } - - // initialize - - // get server from calling web page (intended for genome-preview) - if ('encodeDataMatrix_server' in window) { - server = encodeDataMatrix_server; - } else { - server = document.location.hostname; + // adjust size of row headers based on longest label length + $('tbody th').css('height', '1em'); + $('tbody th').css('width', (String((maxLen/2 + 2)).concat('em'))); + }); + $('body').append($table); } - // variables passed from calling page - organism = encodeChipMatrix_organism; - assembly = encodeChipMatrix_assembly; - $("#assemblyLabel").text(assembly); - header = encodeChipMatrix_pageHeader; - $("#pageHeader").text(header); - document.title = 'ENCODE ' + header; + function tableOut($table, matrix, cellTiers, antibodyGroups, antibodyTargetExps) { + // Create table with rows for each cell type and columns for each antibody target - encodeProject.setup({ - server: server, - assembly: assembly - }); + tableHeaderOut($table, antibodyGroups, antibodyTargetExps); + tableMatrixOut($table, matrix, cellTiers, antibodyGroups, antibodyTargetExps); - // show only spinner until data is retrieved - $('#matrixTable').hide(); - spinner = showLoadingImage("spinner", true); + encodeMatrix.addTableFloatingHeader($table); + encodeMatrix.rotateTableCells($table); + encodeMatrix.hoverTableCrossHair($table); + } - // add radio buttons for search type to specified div on page - encodeProject.addSearchPanel('#searchTypePanel'); + // initialize + encodeMatrix.start($matrixTable); + // load data from server and set up callback encodeProject.loadAllFromServer(requests, handleServerData); });