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 + '">&bull;</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 + '">&bull;</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);
 });