<?php
/*******************************************************************************
 *
 * LEIDEN OPEN VARIATION DATABASE (LOVD)
 *
 * Created     : 2007-06-28
 * Modified    : 2010-07-29
 * For LOVD    : 2.0-28
 *
 * Access      : Public
 * Purpose     : Allow overview of all variants in all genes based on a search
 *               on the Patient/Origin/* fields.
 *
 * Copyright   : 2004-2010 Leiden University Medical Center; http://www.LUMC.nl/
 * Programmers : Ing. Ivo F.A.C. Fokkema <I.F.A.C.Fokkema@LUMC.nl>
 *               Ir. Gerard C.P. Schaafsma <G.C.P.Schaafsma@LUMC.nl>
 * Last edited : Ing. Ivo F.A.C. Fokkema <I.F.A.C.Fokkema@LUMC.nl>
 *
 *
 * This file is part of LOVD.
 *
 * LOVD is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * LOVD is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LOVD; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *************/

define('ROOT_PATH', './');
require ROOT_PATH . 'inc-init.php';

if (HAS_AUTH) {
    // If authorized, check for updates.
    require ROOT_PATH . 'inc-upgrade.php';
}

// 2010-01-12; 2.0-24 Need a table for hiding columns, just select all variant and selected patient columns
$aTable = array('pathogenic_' => 'Path.');
$sQ = 'SELECT c.colid, c.head_column, c.col_order FROM ' . TABLE_COLS . ' AS c WHERE c.colid LIKE "Variant/%" UNION SELECT pc.colid, c.head_column, pc.col_order FROM ' . TABLE_PATIENTS_COLS . ' AS pc LEFT JOIN ' . TABLE_COLS . ' AS c USING (colid) ' . ($_AUTH && $_AUTH['level'] >= LEVEL_MANAGER? '' : 'WHERE pc.public=1 ') . 'ORDER BY LEFT(colid, 3) DESC, (col_order = 0) ASC, col_order ASC, colid';
$q = mysql_query($sQ);
while ($z = mysql_fetch_assoc($q)) {
    $aTable[$z['colid']] = $z['head_column'];
}

// Additional columns for authorized users.
if ($_AUTH && $_AUTH['level'] >= LEVEL_MANAGER) {
    $aTable['status_'] = 'Status';
}

// 2008-09-17; 2.0-12; Column hiding also introduced in this variant overview.
if (isset($_COOKIE[$_SETT['cookie_id'] . '_hidden_cols'])) {
    $aHiddenColumns = preg_split('/[;,]+/', $_COOKIE[$_SETT['cookie_id'] . '_hidden_cols']); // Support both , and ; as a separator for backwards compatibility to LOVD 2.0-22 and before.
} else {
    $aHiddenColumns = array();
}

if (!empty($_GET['hide_col'])) {
    // 2009-11-06; 2.0-23; Received strings are now converted into arrays to be processed the same way.
    if (is_string($_GET['hide_col'])) {
        $_GET['hide_col'] = explode(',', $_GET['hide_col']);
    }
    foreach ($_GET['hide_col'] as $sCol) {
        // We don't check the availability of the column here, because some other gene may have it.
        if ($sCol == 'all') {
            foreach ($aTable as $key => $value) {
                if (!in_array($key, $aHiddenColumns)) {
                    $aHiddenColumns[] = $key;
                }
            }
        } elseif (!in_array($sCol, $aHiddenColumns)) {
            $aHiddenColumns[] = $sCol;
        }
    }
}
if (!empty($_GET['show_col'])) {
    // 2009-11-06; 2.0-23; Received strings are now converted into arrays to be prosessed the same way.
    if (is_string($_GET['show_col'])) {
        $_GET['show_col'] = explode(',', $_GET['show_col']);
    }
    foreach ($_GET['show_col'] as $sCol) {
        if ($sCol == 'all') {
            $aHiddenColumns = array();
        } else {
            $nKey = array_search($sCol, $aHiddenColumns);
            if ($nKey !== false) {
                unset($aHiddenColumns[$nKey]);
            }
        }
    }
}

// Now, store the settings in the cookie.
if ((!isset($_COOKIE[$_SETT['cookie_id'] . '_hidden_cols']) && count($aHiddenColumns)) || (isset($_COOKIE[$_SETT['cookie_id'] . '_hidden_cols']) && $_COOKIE[$_SETT['cookie_id'] . '_hidden_cols'] != $aHiddenColumns)) {
    setcookie($_SETT['cookie_id'] . '_hidden_cols', implode(',', $aHiddenColumns), time() + 60*60*24*31);
}

// Then, clean $aHiddenCols so that it will only contain columns that are actually going to be on the screen. This is for counting purposes, to see how much has been hidden.
foreach ($aHiddenColumns as $nKey => $sCol) {
    if (!isset($aTable[$sCol])) {
        unset($aHiddenColumns[$nKey]);
    }
}

require ROOT_PATH . 'inc-top.php';
require ROOT_PATH . 'inc-lib-form.php';
require ROOT_PATH . 'inc-lib-list.php';
require ROOT_PATH . 'class/currdb.php';
lovd_printHeader('variant_search', 'LOVD - Search variants');





// Do we even have the origin fields enabled?
// Sounds stupid, but I need a gene here...
if ($_SESSION['currdb']) {
    $_CURRDB = new CurrDB();
} elseif (!GENE_COUNT) {
    // Standard situation.
    $_CURRDB = new CurrDB(false);
} else {
    // Fetch a gene. Any gene!
    $aGenes = lovd_getGeneList();
    $sSymbol = $aGenes[0];
    $_CURRDB = new CurrDB(false, $sSymbol);
}

// Hide non-public columns for all curators.
// FIXME; Actually, a curator who owns every single gene should be included... Hmm...
$_CURRDB->hideCols('public', ($_AUTH['level'] >= LEVEL_MANAGER));
$aColList = $_CURRDB->getColList();
$aOriginCols = array();
foreach ($aColList as $sCol) {
    if (substr($sCol, 0, 15) == 'Patient/Origin/') {
        $aOriginCols[] = $sCol;
    }
}

$nOriginCols = count($aOriginCols);
if (!$nOriginCols) {
    // No origin columns enabled... goodbye!
    lovd_showInfoTable('There are no Patient/Origin columns enabled. Therefore this overview is non functional.', 'stop');
    require ROOT_PATH . 'inc-bot.php';
    exit;
}





// Show form in all cases.
print('      Fill in at least one of the origin fields to search the gene databases.<BR>' . "\n" .
      '      <BR>' . "\n\n");

// 2010-07-23; 2.0-28; Added information table that explains the current view.
lovd_includeJS(ROOT_PATH . 'inc-js-toggle-visibility.js');
$sInfoText = 'You can use this overview to view the variants and patients from the gene databases you select, based on the patient\'s origin fields. Examples are geographic origin or ethnic origin. More or less fields may be available, depending on which patient origin columns are activated.';
if (HAS_AUTH) {
    $sInfoText .= ' To change the available search fields, you need to enable or disable patient columns whose IDs start with "Patient/Origin/".';
    if ($_AUTH['level'] < LEVEL_MANAGER) {
        $sInfoText .= ' This requires ' . $_SETT['user_levels'][LEVEL_MANAGER] . ' level access.';
    }
}
print('      <TABLE border="0" cellpadding="2" cellspacing="0" width="525" class="info" style="font-size : 11px;">' . "\n" .
      '        <TH>About this overview [<A href="#" id="moreinfo_link" onClick="lovd_toggleVisibility(\'moreinfo\'); return false;">Show</A>]</TH>' . "\n" .
      '        <TR id="moreinfo" style="display : none;">' . "\n" .
      '          <TD>' . $sInfoText . '</TD>' . "\n" .
      '        </TR>' . "\n" .
      '      </TABLE><BR>' . "\n\n");



// Select current gene, at least.
if (empty($_GET['genes']) || !is_array($_GET['genes'])) {
    $_GET['genes'] = array();
    if ($_SESSION['currdb']) {
        $_GET['genes'][] = $_SESSION['currdb'];
    }
}

// 2008-07-31; 2.0-10; Implement XSS check on search terms.
foreach ($_GET as $key => $val) {
    if (!is_array($val) && $val != strip_tags($val)) {
        $_GET[$key] = '';
    }
}

// Fetch genes.
$qGenes = mysql_query('SELECT symbol, gene FROM ' . TABLE_DBS . ' ORDER BY symbol');
$nGenes = mysql_num_rows($qGenes);

// Table.
// 2008-09-17; 2.0-12; Allow form to store info for hiding/showing columns.
print('      <FORM action="' . $_SERVER['PHP_SELF'] . '" method="get">' . "\n" .
      '        <INPUT type="hidden" name="order" value="' . (!empty($_GET['order'])? $_GET['order'] : '') . '">' . "\n" .
      '        <INPUT type="hidden" name="hide_col" value="">' . "\n" .
      '        <INPUT type="hidden" name="show_col" value="">' . "\n" .
      '        <TABLE border="0" cellpadding="0" cellspacing="1" width="950">' . "\n" .
      '          <TR>' . "\n" .
      '            <TD valign="top" rowspan="' . ($nOriginCols + 3) . '" width="33%" style="padding-right : 10px;">' . "\n" .
      '              <B>Select gene(s) you wish to search in</B><BR>' . "\n" .
      '              <SELECT name="genes[]" size="' . ($nGenes > 5? 5 : $nGenes) . '" multiple>' . "\n");
while ($zGenes = mysql_fetch_assoc($qGenes)) {
    print('              <OPTION value="' . $zGenes['symbol'] . '"' . (in_array($zGenes['symbol'], $_GET['genes'])? ' selected' : '') . '>' . $zGenes['symbol'] . ' (' . lovd_shortenString($zGenes['gene'], 50) . ')</OPTION>' . "\n");
}
print('              </SELECT><BR>' . "\n" .
      '              <A href="#" onclick="var list = document.forms[0][\'genes[]\']; for (i=0;i<list.options.length;i++) { list.options[i].selected = true; }; return false">Select all</A>' . "\n" .
      '            </TD>' . "\n" .
      '            <TH colspan="2">Please fill in the search terms in the field' . ($nOriginCols == 1? '' : 's') . ' below</TH></TR>');

// Rest will be generated automatically.
// Array which will make up the form table.
$aForm = array(
                array('GET', '', '', '33%', '33%'),
              );

foreach ($aOriginCols as $sCol) {
    $aForm = array_merge($aForm, $_CURRDB->buildFormTable('Patient', $sCol));
}

// Modify form so it can be used for searching.
foreach ($aForm as $key => $aField) {
    // 2008-01-28; 2.0-04; Added this check to ensure this does not modify the $aForm[0] element or any comments.
    if ($key && $aField[0] != 'print' && $aField[1] != 'print') {
        $aField[2] = 'search_' . $aField[2];
        $aForm[$key] = $aField;
    }
}
$aForm[] = 'skip';
$aForm[] = array('', 'submit', 'Search sequence variants');
$_MODULES->processForm('VariantsOverviewOriginSearch', $aForm);
lovd_viewForm($aForm);

print('</TABLE></FORM><BR>' . "\n\n");

// That's it? Or do we need to search?
$bSearched = false;
foreach ($aOriginCols as $sCol) {
    if (array_key_exists('search_' . $sCol, $_GET) && $_GET['search_' . $sCol]) {
        $bSearched = true;
        break;
    }
}

if (!$bSearched || !count($_GET['genes'])) {
    // Can't search with this.
    require ROOT_PATH . 'inc-bot.php';
    exit;
}

// 2010-01-13; 2.0-24; Show link at all times. Simply activate only when needed.
$sNavigation = '';
// "Unhide all columns".
if (count($aHiddenColumns)) {
    $sNavigation = '<A href="#" onclick="document.forms[0].show_col.value=\'all\';document.forms[0].submit();">';
} else {
    $sNavigation = '<A style="color : #999999;">';
}
$sNavigation .= 'Unhide all columns</A>';
// 2010-01-13; 2.0-24; Added "Hide specific columns" and "Hide all columns" links.
// "Hide specific columns".
// "Hide all columns".
if (count($aHiddenColumns) < count($aTable)-1) {
    $sNavigation .= ' | <A href="#" onclick="lovd_openWindow(\'views_columns.php' . '?action=full_all&amp;hide=true' . lovd_showSID(true, true) . '\', \'HideColumns\', 300, 500); return false;">Hide Specific Columns</A>' .
                    ' | <A href="#" onclick="document.forms[0].hide_col.value=\'all\';document.forms[0].submit();">Hide all columns</A>';
} else {
    $sNavigation .= ' | <A style="color : #999999;">Hide Specific Columns</A>' .
                    ' | <A style="color : #999999;">Hide all columns</A>';
}
print('      <TABLE border="0" cellpadding="0" cellspacing="0" width="960">' . "\n" .
      '        <TR>' . "\n" .
      '          <TD valign="top">' . "\n");
lovd_viewNavigation($sNavigation, 6);
print('          </TD>' . "\n" .
        '      </TR></TABLE><BR>' . "\n\n");



// Per gene, show the variants connected to the patients found by the search terms.
foreach ($_GET['genes'] as $sSymbol) {
    // Whether or not a user can see non-public data.
    $bCurator = lovd_isCurator($sSymbol);

    $_CURRDB = new CurrDB(true, $sSymbol);
    $_CURRDB->hideCols('public', $bCurator);

    // Standard query, will be extended later on.
    $sQ = 'SELECT v.*, p.*, p2v.allele, p2v.status' . ($bCurator? ', stat.status_text AS status_' : '') . ', path.pathogenic_text AS pathogenic_, count(p2v_2.variantid) AS mutations, s.reference AS subs_reference FROM ' . TABLEPREFIX . '_' . $sSymbol . '_variants AS v LEFT JOIN ' . TABLE_PAT2VAR . ' AS p2v ON (p2v.symbol = "' . $sSymbol . '" AND v.variantid = p2v.variantid) LEFT JOIN ' . TABLE_PATIENTS . ' AS p USING (patientid) LEFT OUTER JOIN ' . TABLE_PAT2VAR . ' AS p2v_2 ON (p.patientid = p2v_2.patientid' . ($bCurator? '' : ' AND p2v_2.status >= ' . STATUS_MARKED) . ')' . ($bCurator? ' LEFT OUTER JOIN ' . TABLE_VAR_STATUS . ' AS stat ON (p2v.status = stat.status)' : '') . ' LEFT OUTER JOIN ' . TABLE_PATHOGENIC . ' AS path ON (p2v.pathogenic = path.pathogenic) LEFT OUTER JOIN ' . TABLE_SUBS . ' AS s ON (p.submitterid = s.submitterid) WHERE ' . ($bCurator? '1=1' : 'p2v.status >= ' . STATUS_MARKED . ' AND p2v_2.status >= ' . STATUS_MARKED);

    // Guess the column used to describe the mutation.
    $sMutationCol = $_CURRDB->getMutationCol();

    // Here starts the actual building of the query.
    // SEARCH: Advanced text search.
    // FIXME; just the origin columns? Simple change to $_CURRDB->buildSearchList() needed, like buildFormTable().
    // 2009-06-26; 2.0-19; Now actually implement a proper selection-list search implementation, like variants_search.php. Separated from this part.
    $aSearchText = array_merge(
        $_CURRDB->buildSearchList('text'),
        $_CURRDB->buildSearchList('textarea'),
        $_CURRDB->buildSearchList('checkbox'));
    foreach ($aSearchText as $key => $val) {
        if (isset($_GET[$key]) && trim($_GET[$key])) {
            // 2010; 2.0-28; Allow for searches where the order of words is forced by enclosing the values with double quotes; ["Fokkema et al 2005"] will only
            // return hits with these words in exactly this order while [Fokkema et al 2005] will return hits with all of these words, in whatever order.
            $sSearch = preg_replace_callback('/\\\"([^"]+)\\\"/', create_function('$aRegs', 'return str_replace(\' \', \'{{SPACE}}\', $aRegs[1]);'), trim($_GET[$key]));
            $a = explode(' ', $sSearch);
            foreach ($a as $sTerm) {
                if ($sTerm) {
                    if (substr_count($sTerm, '|') && preg_match('/^[^|]+(\|[^|]+)+$/', $sTerm)) {
                        // OR.
                        $aOR = explode('|', $sTerm);
                        $sQ .= ' AND (';
                        foreach ($aOR as $nTerm => $sTerm) {
                            // 2009-03-03; 2.0-17; Advanced searching did not allow to combine NOT and OR searches.
                            if (substr($sTerm, 0, 1) == '!') {
                                // NOT.
                                $sQ .= ($nTerm? ' OR ' : '') . $val . ' NOT LIKE "%' . lovd_escapeSearchTerm(substr($sTerm, 1)) . '%"';
                            } else {
                                // Common search term.
                                $sQ .= ($nTerm? ' OR ' : '') . $val . ' LIKE "%' . lovd_escapeSearchTerm($sTerm) . '%"';
                            }
                        }
                        $sQ .= ')';
                    } elseif (substr($sTerm, 0, 1) == '!') {
                        // NOT.
                        $sQ .= ' AND ' . $val . ' NOT LIKE "%' . lovd_escapeSearchTerm(substr($sTerm, 1)) . '%"';
                    } else {
                        // Common search term.
                        $sQ .= ' AND ' . $val . ' LIKE "%' . lovd_escapeSearchTerm($sTerm) . '%"';
                    }
                }
            }
        }
    }

    // 2009-04-21; 2.0-18; Aargh, we need to implement a select search here, because we build the form elements on this page.
    // 2009-06-26; 2.0-19; Now actually implement a proper selection-list search implementation, like variants_search.php.
    // SEARCH: List search.
    $aSearchSelect = $_CURRDB->buildSearchList('select');
    foreach ($aSearchSelect as $key => $val) {
        if (!empty($_GET[$key])) {
            if (!is_array($_GET[$key])) {
                // Single select.
                $sQ .= ' AND CONCAT(";", ' . $val . ', ";") LIKE "%;' . $_GET[$key] . ';%"';
            } else {
                // Multiple select.
                $sQ .= ' AND CONCAT(";", ' . $val . ', ";") LIKE "%;' . implode('" AND CONCAT(";", ' . $val . ', ";") LIKE "%', $_GET[$key]) . ';%"';
            }
        }
    }

    // Add p2v.allele here to show homozygous mutations twice in the list.
    $sQ .= ' GROUP BY v.variantid, p.patientid';

    $nResults = mysql_num_rows(mysql_query($sQ));

    // SORT: Current settings.
    if (isset($_GET['order']) && $_GET['order']) {
        $aOrder = explode(',', $_GET['order']);
    } else {
        $aOrder = array('', '');
    }

    // SORT: Column data.
    $aOrderList = array_merge(
             array('pathogenic_' => array('path.pathogenic', 'ASC')),
             array('sort' => array('v.sort', 'ASC')),
             $_CURRDB->buildOrderList());
    if ($bCurator) {
        // Add status.
        $aOrderList['status_'] = array('p2v.status', 'ASC');
    }
    if (!array_key_exists($aOrder[0], $aOrderList)) {
        // 2007-10-19; 2.0-01; Changed default sorting column from Exon to Variant/DNA.
        $aOrder[0] = $sMutationCol;
    }
    if ($aOrder[1] != 'ASC' && $aOrder[1] != 'DESC') {
        $aOrder[1] = $aOrderList[$aOrder[0]][1];
    }

    // 2007-10-19; 2.0-01; Fixed bug #18 - Variants do not sort correctly when selecting DNA column to sort on.
    // 2009-06-12; 2.0-19; Changed again, since the sort column now also contains the Exon information.
    // SORT: Additional sort columns (faking a perfect sort).
    $sOrderPre = '';
    $sOrderPost = '';
    if ($aOrder[0] == 'Variant/DNA') {
        // 1) In case of the mutation column, sort FIRST on sort.
        $sOrderPre .= 'v.sort ' . $aOrder[1] . ', ';
    } else {
        // 2) In case of anything else, sort on sort AFTER.
        $sOrderPost .= ', v.sort ' . $aOrder[1];
    }

    $sQ .= ' ORDER BY ' . $sOrderPre . $aOrderList[$aOrder[0]][0] . ' ' . $aOrder[1] . $sOrderPost . ', p.patientid';



    if (!$nResults) {
        // Searched, but no results.
        // We actually didn't check, if you're not a curator for all genes, if variants were public.
        print('      No variants found in the ' . $sSymbol . ' gene that match your search terms.<BR><BR>' . "\n\n");
        continue;
    }

    $q = mysql_query($sQ);
    if (!$q) {
        lovd_dbFout('VariantsOverviewOrigin', $sQ, mysql_error());
    }

    $n = (isset($nResults)? $nResults : $nTotal);
    print('      <SPAN class="S13"><B>' . $n . ($bCurator? '' : ' public') . ' entr' . ($n == 1? 'y' : 'ies') . ' in ' . $sSymbol . '</B></SPAN><BR>' . "\n");

    // Array which will make up the table (header and data).
    $aTable = array('pathogenic_' => array('Path.', 50));
    $aTable = array_merge($aTable, $_CURRDB->buildTable('list'));

    // Additional columns for authorized users.
    if ($bCurator) {
        $aTable['status_'] = array('Status', 75);
    }

    // Stretch it up, cause we're adding data to it...
    if ($aTable[$sMutationCol][1] < 200) {
        $aTable[$sMutationCol][1] = 200;
    }

    // Total width.
    $nTotalWidth = 2;
    $nHiddenColWidth = 5;
    $bPrevHidden = false; // 2009-12-17; 2.0-24; To remember if the previous column was hidden. We now group hidden columns into one.
    foreach ($aTable as $sCol => $aCol) {
        // 2008-02-15; 2.0-04; Hidden columns are smaller!
        if (in_array($sCol, $aHiddenColumns) && $sCol != $sMutationCol) {
            // We don't allow the MutationCol to be hidden!
            // 2009-12-17; 2.0-24; We now group hidden columns into one.
            if (!$bPrevHidden) {
                $nTotalWidth += $nHiddenColWidth + 8;
                $bPrevHidden = true;
            }
        } else {
            $nTotalWidth += $aCol[1] + 8;
            $bPrevHidden = false;
        }
    }



    // Stretch table up, to respect column widths.
    print('      <IMG src="' . ROOT_PATH . 'gfx/trans.png" alt="" width="' . $nTotalWidth . '" height="1"><BR>' . "\n\n");

    // Table.
    print('      <TABLE border="0" cellpadding="0" cellspacing="1" class="data">' . "\n" .
          '        <TR>');

    $bPrevHidden = false; // 2009-12-17; 2.0-24; To remember if the previous column was hidden. We now group hidden columns into one.
    // Print the column headers.
    foreach ($aTable as $sField => $aCol) {
        if (in_array($sField, $aHiddenColumns) && $sField != $sMutationCol) {
            // We don't allow the MutationCol to be hidden!
            // 2009-12-17; 2.0-24; We now group hidden columns into one.
            if (!$bPrevHidden) {
                print("\n" . '          <TH width="' . $nHiddenColWidth . '"><A href="#" onclick="lovd_openWindow(\'views_columns.php?action=full_all' . lovd_showSID(true, true) . '\', \'ShowColumns\', 300, 500);"><IMG src="' . ROOT_PATH . 'gfx/order_arrow_show.png" alt="Unhide specific columns" title="Unhide specific columns" width="5" height="13" border="0"></A></TH>');
                $bPrevHidden = true;
            }
            continue;
        } else {
            $bPrevHidden = false;
        }

        // Location onclick changed because of the search fields within the same <TH>. Clicking on the field triggers a sort.
        print("\n" . '          <TH valign="top"' . (!empty($aCol[2])? ' ' . $aCol[2] : '') . (is_numeric($aCol[1])? ' width="' . $aCol[1] . '"' : '') . (array_key_exists($sField, $aOrderList)? ' class="order' . ($aOrder[0] == $sField? 'ed' : '') . '"' : '') . '>' .
              (array_key_exists($sField, $aOrderList)? "\n" .
                                                           '            <TABLE border="0" cellpadding="0" cellspacing="0" width="100%" class="S11">' . "\n" .
                                                           '              <TR' . (array_key_exists($sField, $aOrderList)? ' onclick="document.forms[0].order.value=\'' . $sField . ',' . ($aOrder[0] == $sField? ($aOrder[1] == 'ASC'? 'DESC' : 'ASC') : $aOrderList[$sField][1]) . '\';document.forms[0].submit();"' : '') . '>' . "\n" .
                                                           '                <TH>' . str_replace(' ', '&nbsp;', $aCol[0]) . '</TH>' . "\n" .
                                                           '                <TD align="right" width="13">' . ($sField == $sMutationCol? '&nbsp;' : '<IMG src="' . ROOT_PATH . 'gfx/order_arrow_hide.png" alt="Hide ' . $aCol[0] . ' column" title="Hide ' . $aCol[0] . ' column" width="11" height="11" onclick="document.forms[0].hide_col.value=\'' . $sField . '\';document.forms[0].submit();" style="margin : 1px;">') . '</TD>' . "\n" .
                                                           '                <TD align="right" width="13"><IMG src="' . ROOT_PATH . 'gfx/order_arrow_desc' . ($aOrder[0] == $sField && $aOrder[1] == 'DESC'? '_sel' : '') . '.png" alt="Descending" title="Descending" width="13" height="6"><BR><IMG src="' . ROOT_PATH . 'gfx/order_arrow_asc' . ($aOrder[0] == $sField && $aOrder[1] == 'ASC'? '_sel' : '') . '.png" alt="Ascending" title="Ascending" width="13" height="6"></TD></TR>' .
                                                           '</TABLE>' : $aCol[0]) . '</TH>');
    }
    print('</TR>');

    $i = 0;
    $nRepeat = 25;
    while ($zData = mysql_fetch_assoc($q)) {
        // Print header repeatedly.
        $i ++;
        if (!($i%($nRepeat+1))) {
            print("\n" .
                  '        <TR>');
            $bPrevHidden = false; // 2009-12-17; 2.0-24; To remember if the previous column was hidden. We now group hidden columns into one.
            // Print the repeated column headers.
            foreach ($aTable as $sField => $aCol) {
                if (in_array($sField, $aHiddenColumns) && $sField != $sMutationCol) {
                    // We don't allow the MutationCol to be hidden!
                    // 2009-12-17; 2.0-24; We now group hidden columns into one.
                    if (!$bPrevHidden) {
                        print("\n" . '          <TH width="' . $nHiddenColWidth . '"><A href="#" onclick="lovd_openWindow(\'views_columns.php?action=' . $_GET['action'] . lovd_showSID(true, true) . '\', \'ShowColumns\', 300, 500);"><IMG src="' . ROOT_PATH . 'gfx/order_arrow_show.png" alt="Unhide specific columns" title="Unhide specific columns" width="5" height="13" border="0"></A></TH>');
                        $bPrevHidden = true;
                    }
                    continue;
                } else {
                   $bPrevHidden = false;
                }

                print("\n" . '          <TH valign="top"' . (!empty($aCol[2])? ' ' . $aCol[2] : '') . (is_numeric($aCol[1])? ' width="' . $aCol[1] . '"' : '') . (array_key_exists($sField, $aOrderList)? ' class="order' . ($aOrder[0] == $sField? 'ed' : '') . '"' : '') . '>' .
                      (array_key_exists($sField, $aOrderList)? "\n" .
                                                                   '            <TABLE border="0" cellpadding="0" cellspacing="0" width="100%" class="S11">' . "\n" .
                                                                   '              <TR' . (array_key_exists($sField, $aOrderList)? ' onclick="document.forms[0].order.value=\'' . $sField . ',' . ($aOrder[0] == $sField? ($aOrder[1] == 'ASC'? 'DESC' : 'ASC') : $aOrderList[$sField][1]) . '\';document.forms[0].submit();"' : '') . '>' . "\n" .
                                                                   '                <TH>' . str_replace(' ', '&nbsp;', $aCol[0]) . '</TH>' . "\n" .
                                                                   '                <TD align="right" width="13">' . ($sField == $sMutationCol? '&nbsp;' : '<IMG src="' . ROOT_PATH . 'gfx/order_arrow_hide.png" alt="Hide ' . $aCol[0] . ' column" title="Hide ' . $aCol[0] . ' column" width="11" height="11" onclick="document.forms[0].hide_col.value=\'' . $sField . '\';document.forms[0].submit();" style="margin : 1px;">') . '</TD>' . "\n" .
                                                                   '                <TD align="right" width="13"><IMG src="' . ROOT_PATH . 'gfx/order_arrow_desc' . ($aOrder[0] == $sField && $aOrder[1] == 'DESC'? '_sel' : '') . '.png" alt="Descending" title="Descending" width="13" height="6"><BR><IMG src="' . ROOT_PATH . 'gfx/order_arrow_asc' . ($aOrder[0] == $sField && $aOrder[1] == 'ASC'? '_sel' : '') . '.png" alt="Ascending" title="Ascending" width="13" height="6"></TD></TR>' .
                                                                   '</TABLE>' : $aCol[0]) . '</TH>');
            }
            print('</TR>');
            $i = 1;
        }

        print("\n" .
              '        <TR valign="top"' . ($bCurator && $zData['status'] < STATUS_OK? ' class="' . ($zData['status'] < STATUS_MARKED? 'del' : 'marked') . '"' : '') . ' style="cursor : pointer; cursor : hand;" onmouseover="this.className = \'' . ($bCurator && $zData['status'] < STATUS_OK? ($zData['status'] < STATUS_MARKED? 'del_' : 'marked_') : '') . 'hover\';" onmouseout="this.className = \'' . ($bCurator && $zData['status'] < STATUS_OK? ($zData['status'] < STATUS_MARKED? 'del' : 'marked') : '') . '\';" onclick="window.location.href = \'' . lovd_cleanDirName(dirname($_SERVER['PHP_SELF']) . '/' . ROOT_PATH) . 'variants.php?select_db=' . $sSymbol . '&amp;action=view&amp;view=' . $zData['patientid'] . '%2C' . $zData['variantid'] . '%2C' . $zData['allele'] . lovd_showSID(true, true) . '\';">');

        // Create normal anchor link as well.
        $sLink = '<A href="' . ROOT_PATH . 'variants.php?select_db=' . $sSymbol . '&amp;action=view&amp;view=' . $zData['patientid'] . '%2C' . $zData['variantid'] . '%2C' . $zData['allele'] . lovd_showSID(true, true) . '" class="data"' . ($zData['status'] < STATUS_MARKED? ' style="color : #AAAAAA;"' : '') . '>';
        if ($sMutationCol) {
            $zData[$sMutationCol] = $sLink . $zData[$sMutationCol] . '</A>';
        }

        // Very unlikely not to function; only if none of the HGVS standard DNA, RNA or Protein fields are enabled, this will fail.
        if ($zData['mutations'] > 1) {
            // 2008-03-27; 2.0-05; Show only a maximum number of other mutations associated with this patient. Otherwise, hide them all.
            if ($zData['mutations'] > 10) {
                // Too many to fetch; wouldn't look good.
                // FIXME; make a setting out of this.
                $zData[$sMutationCol] .= '<BR>&nbsp;&nbsp;&nbsp;&nbsp;+ ' . ($zData['mutations'] - 1) . ' others';
            } else {
                // Fetch info on other mutations.
                // FIXME; this may be quite a performance lag with a high $_GET['limit'] value and lots of associated mutations.
                // Backwards compatible with MySQL 4.0 and earlier; if we rely on MySQL 4.1, we could do a GROUP_CONCAT in top mutation fetch query that save us these queries.
                $sQOther = 'SELECT p2v.*, v.`' . $sMutationCol . '` FROM ' . TABLE_PAT2VAR . ' AS p2v LEFT OUTER JOIN ' . TABLEPREFIX . '_' . $sSymbol . '_variants AS v ON (p2v.symbol = "' . $sSymbol . '" AND v.variantid = p2v.variantid) WHERE ' . ($bCurator? '' : 'p2v.status >= ' . STATUS_MARKED . ' AND ') . 'p2v.patientid = "' . $zData['patientid'] . '" AND !(p2v.symbol = "' . $sSymbol . '" AND v.variantid = "' . $zData['variantid'] . '" AND p2v.allele = "' . $zData['allele'] . '") ORDER BY v.sort ASC';

                $qOther = mysql_query($sQOther);
                $sOther = '';
                $aOther = array();

                // Loop other variants found in the same patient.
                while ($zOther = mysql_fetch_assoc($qOther)) {
                    if ($zOther['symbol'] == $sSymbol) {
                        // Other variant in same gene.
                        $sOther .= ($sOther? ', ' : '') . $zOther[$sMutationCol];
                    } else {
                        // Other variant in different gene.
                        if (!isset($aOther[$zOther['symbol']])) {
                            $aOther[$zOther['symbol']] = 0;
                        }
                        $aOther[$zOther['symbol']] ++;
                    }
                }

                if (count($aOther)) {
                    foreach ($aOther as $sGene => $nOther) {
                        $sOther .= ($sOther? ', ' : '') . $sGene . ' (' . $nOther . ')';
                    }
                }

                $zData[$sMutationCol] .= '<BR>&nbsp;&nbsp;&nbsp;&nbsp;+ ' . $sOther;
            }
        }

        // Parse and build Custom Links.
        lovd_buildLinks($zData);

        // Transform select lists' values.
        $_CURRDB->transformSelectValues($zData);

        // FIXME;
        // Shorten strings for TEXT that surely doesn't contain HTML tags... HOW?

        // Submitter reference...
        if ($_CURRDB->colExists('Patient/Reference') && $zData['subs_reference']) {
            // 2008-03-28; 2.0-05; Show full submitter information directly from this link.
            $zData['Patient/Reference'] .= (!empty($zData['Patient/Reference'])? ', ' : '') . '<A href="#" onclick="lovd_openWindow(\'' . ROOT_PATH . 'submitters.php?action=view&amp;view=' . $zData['submitterid'] . lovd_showSID(true, true) . '\', \'SubmitterView\', 400, 175, event.screenX - 50, event.screenY - 50); return false;">' . $zData['subs_reference'] . '</A>';
        }

        $bPrevHidden = false; // 2009-12-17; 2.0-24; To remember if the previous column was hidden. We now group hidden columns into one.
        // Print the data.
        foreach ($aTable as $sField => $aCol) {
            // 2008-02-15; 2.0-04; Hidden columns are smaller!
            if (in_array($sField, $aHiddenColumns) && $sField != $sMutationCol) {
                // We don't allow the MutationCol to be hidden!
                // 2009-12-17; 2.0-24; We now group hidden columns into one.
                if (!$bPrevHidden) {
                    print("\n" . '          <TD width="' . $nHiddenColWidth . '">&nbsp;</TD>');
                    $bPrevHidden = true;
                }
                continue;
            } else {
               $bPrevHidden = false;
            }
            print("\n" . '          <TD' . (!empty($aCol[2])? ' ' . $aCol[2] : '') . (is_numeric($aCol[1])? ' width="' . $aCol[1] . '"' : '') . ($aOrder[0] == $sField? ' class="ordered"' : '') . '>' . (!$zData[$sField]? '-' : $zData[$sField]) . '</TD>');
        }
        print('</TR>');
    }
    print('</TABLE><BR>' . "\n\n");
}

require ROOT_PATH . 'inc-bot.php';
exit;
?>