<?php
/*******************************************************************************
 *
 * LEIDEN OPEN VARIATION DATABASE (LOVD)
 *
 * Created     : 2006-03-13
 * Modified    : 2009-08-27
 * For LOVD    : 2.0-21
 *
 * Access      : Public
 * Purpose     : Declare standard LOVD functions that are needed to build forms.
 *
 * Copyright   : 2004-2009 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
 *
 *************/

function lovd_checkDBID ($sGene, $sVariant, $sMutationCol = 'Variant/DNA', $sDBID, $nIDtoIgnore = 0)
{
    // Checks if given $sVariant and $sDBID match. I.e., whether or not there is
    // already an entry where this variant and DBID come together.
    // NOTE: We're assuming that the DBID field actually exists. Using this
    // function implies you've checked for it's presence.
    // 2008-06-25; 2.0-08; All checks ignore the current variant, if the ID is given.

    // 2009-06-11; 2.0-19; GENE_00000 is always allowed.
    if ($sDBID == substr($sGene, 0, strpos($sGene . '_', '_')) . '_00000') {
        return true;
    }

    $sVariant = str_replace(array('(', ')', '?'), '', $sVariant);
    // Variant/DBID combo already exists?
    list($n) = @mysql_fetch_row(mysql_query('SELECT COUNT(*) FROM `' . TABLEPREFIX . '_' . mysql_real_escape_string($sGene) . '_variants` WHERE REPLACE(REPLACE(REPLACE(`' . $sMutationCol . '`, "(", ""), ")", ""), "?", "") = "' . $sVariant . '" AND `Variant/DBID` LIKE "' . $sDBID . '%"' . ($nIDtoIgnore? ' AND variantid != "' . $nIDtoIgnore . '"' : '')));
    if (!$n) {
        // Check if the chosen ID is empty, then.
        list($n) = @mysql_fetch_row(mysql_query('SELECT COUNT(*) FROM `' . TABLEPREFIX . '_' . mysql_real_escape_string($sGene) . '_variants` WHERE `Variant/DBID` LIKE "' . $sDBID . '%"' . ($nIDtoIgnore? ' AND variantid != "' . $nIDtoIgnore . '"' : '')));
        if ($n) {
            return false;
        }
    }
    return true;
}





function lovd_checkXSS ($aInput = '')
{
    // XSS attack prevention. Simply deny input of HTML, PHP other stuff blocked by strip_tags().

    if ($aInput === '') {
        if (count($_POST)) {
            return lovd_checkXSS($_POST);
        } else {
            return true;
        }
    }

    if (is_array($aInput)) {
        foreach ($aInput as $key => $val) {
            if (is_array($val)) {
                lovd_checkXSS($val);
            } elseif (!empty($val) && preg_match('/<.*>/', $val)) {
                // 2009-04-17; 2.0-18; Changed check from strip_tags() to preg_match(); "<bla" should be allowed; "<bla>" not.
                // Disallowed tag found.
                lovd_errorAdd('Disallowed tag found in form field' . (is_numeric($key)? '.' : ' "' . $key . '".') . ' XSS attack?');
            }
        }
        return true;
    }

    return false;
}





function lovd_error ()
{
    // Function kindly provided by Ileos.nl in the interest of Open Source.
    // Tells the program whether or not we've had an error.
    global $_ERROR;

    return (isset($_ERROR) && $_ERROR);
}





function lovd_errorAdd ($sError)
{
    // Function kindly provided by Ileos.nl in the interest of Open Source.
    // Add error to error variable.
    global $_ERROR;
    $sError = trim($sError);
    
    if (strlen($sError)) {
        $_ERROR .= ($_ERROR? "\n" : '') . $sError;
    }
}





function lovd_errorClean ()
{
    // Function kindly provided by Ileos.nl in the interest of Open Source.
    // Clean error variable.
    global $_ERROR;
    $_ERROR = '';
}





function lovd_errorPrint ()
{
    // Function kindly provided by Ileos.nl in the interest of Open Source.
    // Prints error variable.
    global $_ERROR;

    if (strlen($_ERROR)) {
        print('      <DIV class="err">' . "\n" .
              '        ' . str_replace("\n", '<BR>' . "\n" . '        ', $_ERROR) . '</DIV><BR>' . "\n\n");
    }
}





function lovd_fetchDBID ($sGene, $sVariant, $sMutationCol = 'Variant/DNA')
{
    // Searches through the $sGene variants to fetch lowest DBID belonging to
    // this variant, otherwise returns next variant ID not in use.
    // NOTE: We're assuming that the DBID field actually exists. Using this
    // function implies you've checked for it's presence.

    $sSymb = substr($sGene, 0, strpos($sGene . '_', '_'));
    $lID = strlen($sSymb) + 6;
    $sVariant = str_replace(array('(', ')', '?'), '', $sVariant);
    // 2008-06-27; 2.0-08; Drop the regexp check. The field either contains an ID or not, and the actual regexp is much more extensive anyway!
    list($sVariantDB, $sID) = @mysql_fetch_row(mysql_query('SELECT DISTINCT `' . $sMutationCol . '`, `Variant/DBID` FROM `' . TABLEPREFIX . '_' . mysql_real_escape_string($sGene) . '_variants` WHERE REPLACE(REPLACE(REPLACE(`' . $sMutationCol . '`, "(", ""), ")", ""), "?", "") = "' . $sVariant . '" AND `Variant/DBID` != "" ORDER BY `Variant/DBID`'));
    if (empty($sVariantDB)) {
        // Nieuwe!
        list($sID) = @mysql_fetch_row(mysql_query('SELECT MAX(LEFT(`Variant/DBID`, ' . $lID . ')) FROM `' . TABLEPREFIX . '_' . mysql_real_escape_string($sGene) . '_variants` WHERE LEFT(`Variant/DBID`, ' . $lID . ') REGEXP "^' . $sSymb . '_[0-9]{5}"'));
        if (!$sID) {
            $sID = $sSymb . '_00001';
        } else {
            $nID = substr($sID, -5) + 1;
            $sID = $sSymb . '_' . str_pad($nID, 5, '0', STR_PAD_LEFT);
        }
    } else {
        // 2009-08-26; 2.0-21; Select the first so-called word of the Variant/DBID field
        // We're assuming here that the start of the DBID field will always be the ID, like the column's default RegExp forces.
        preg_match('/^(\w+)\b/', $sID, $aMatches);
        $sID = $aMatches[1];
    }
    return $sID;
}





function lovd_matchDate ($s, $bTime = false)
{
    // Function kindly provided by Ileos.nl in the interest of Open Source.
    // Matches a string to the date pattern, one that MySQL can understand.
    // 2009-06-18; 2.0-19; Added $bTime flag to check DATETIME column types.
    return (preg_match('/^[0-9]{4}[.\/-][0-9]{2}[.\/-][0-9]{2}' . ($bTime? ' [0-2][0-9]\:[0-5][0-9]\:[0-5][0-9]' : '') . '$/', $s));
}





function lovd_matchEmail ($s)
{
    // Function kindly provided by Ileos.nl in the interest of Open Source.
    // Matches a string to the email address pattern.
    return (preg_match('/^[A-Z0-9_.+-]+@(([A-Z0-9][A-Z0-9-]*[A-Z0-9]|[A-Z0-9])\.)+[A-Z]{2,6}$/i', $s));
}





function lovd_matchIPRange ($s, $bThrowError = false)
{
    // Matches a string containing an IP address range.
//FIXME; include check on numbers higher than 255; preg_split on [^0-9] and foreach() through the results.
    $a = split('[;,]', $s);
    $b = true;
    foreach ($a as $val) {
        if (!preg_match('/^(\*|[0-9]{1,3}\.(\*|[0-9]{1,3}(\-[0-9]{1,3})?\.(\*|[0-9]{1,3}(\-[0-9]{1,3})?\.(\*|[0-9]{1,3}(\-[0-9]{1,3})?))))$/', $val)) {
            $b = false;
            if ($bThrowError) {
                lovd_errorAdd('Value "' . $val . '" not understood as a given IP range.');
            }
            return $b;
        }
    }
    return $b;
}





function lovd_matchPassword ($s)
{
    // Matches a string to the password pattern (non standard). This somewhat
    // enforces the choice of a good password. This should be extended with a
    // dictionary search, maybe.
    if (strlen($s) < 4) {
        return false;
    }
    // OK... what if we remove all characters. Anything left?
    $s = preg_replace('/[A-Za-z]+/', '', $s);
    return (strlen($s) > 0);
}





function lovd_matchURL ($s)
{
    // Based on a function provided by Ileos.nl.
    // Matches a string to the standard URL pattern (including those using IP addresses).
    return (preg_match('/^(ht|f)tps?:\/\/([0-9]{1,3}(\.[0-9]{1,3}){3}|(([0-9a-z][-0-9a-z]*[0-9a-z]|[0-9a-z])\.)+[a-z]{2,6})\/?[%&=#0-9a-z\/._+-]*\??.*$/i', $s));
}





function lovd_matchUsername ($s)
{
    // Function kindly provided by Ileos.nl in the interest of Open Source.
    // Matches a string to the username pattern (non standard).
    return (preg_match('/^[A-Z][A-Z0-9_.-]{3,19}$/i', $s));
}





function lovd_setUpdatedDate ($sGene)
{
    // Updates the updated_date field of the indicated gene.
    global $_AUTH;

    // Does this user have rights on this gene? It doesn't really matter that much, but still.
    if (lovd_isCurator($sGene)) {
        // Just update the database and we'll see what happens.
        @mysql_query('UPDATE ' . TABLE_DBS . ' SET updated_by = "' . $_AUTH['userid'] . '", updated_date = NOW() WHERE symbol = "' . $sGene . '"');
        if (mysql_affected_rows() > 0) {
            return true;
        }
    }

    return false;
}





function lovd_sort ($s_dna, $sExon = '0')
{
    // Create the sort code for a correctly written DNA change.
    // Don't fix it if it's not broken; took this code directly from LOVD v.1.1.0; only minor additions.
    /*
    (Since 2.0-19 these codes are preceded by the exon number)
    +---------------------------------+-------------------+-----------------------------------------------+
    | SORTING CODE                    | DNA CHANGE FORMAT | EXAMPLE (theoretic)                           |
    +---------------------------------+-------------------+-----------------------------------------------+
    | -999990_-999950                 | -10-50            | -519-23a>c                                    |
    | -999990_-999950_-999900_-999970 | -10-50_-100-30    | -519-23_-43-67delinscct OR -519-23_-519-13inv |
    | -999990_-999950_-999900_0000000 | -10-50_-100       | -519-23_-43del                                |
    | -999990_0000000                 | -10               | -519A>C                                       |
    | -999990_0000000_-999900_-999970 | -10   _-100-30    | -519_-43-67delinsCCT                          |
    | -999990_0000000_-999900_0000000 | -10   _-100       | -519_-43del                                   |
    +---------------------------------+-------------------+-----------------------------------------------+
    | 0000100_-999970                 | 100-30            | 193-38t>g                                     |
    | 0000100_-999970_0000200_0000000 | 100-30_200        | 193-38_294del                                 |
    | 0000100_-999970_0000200_0000030 | 100-30_200+30     | 193-38_294+63delinsgaaaat                     |
    | 0000100_0000000                 | 100               | 193G>T                                        |
    | 0000100_0000000_0000200_0000000 | 100   _200        | 193_294del OR 193_194insCCT                   |
    | 0000100_0000000_0000200_0000030 | 100   _200+30     | 193_294+63delinsG                             |
    +---------------------------------+-------------------+-----------------------------------------------+
    | 0000200_0000030                 | 200+30            | 294+63g>t                                     |
    | 0000200_0000030_0000300_0000000 | 200+30_300        | 294+63_382del                                 |
    | 0000200_0000030_0000300_0000030 | 200+30_300+30     | 294+63_382+8delinsccgc OR 294+63_294+376del   |
    +---------------------------------+-------------------+-----------------------------------------------+
    | ('-?' is treated as '-1', '+?' as '+1')                                                             |
    +---------------------------------+-------------------+-----------------------------------------------+
    | 9999999_0000010                 | *10               | *69C>T                                        |
    | 9999999_0000010_0000030_0000000 | *10_30            | *69_102del                                    |
    +-----------------------------------------------------------------------------------------------------+
    */

    // 2009-06-11; 2.0-19; Added an extra sort 'field' based on exon number.
    // 2009-07-17; 2.0-20; This method failed miserably; isolate (first) numbers first.
    // Supports 1, 01, 001, 01i, exon 1, intron 1, 01_05 and many other exon notations.
    $sExon = str_pad(substr(preg_replace('/^[^0-9]*([0-9]+).*$/', "$1", $sExon), 0, 4), 4, '0', STR_PAD_LEFT);

    // 2009-04-02; 2.0-17; Prevent notices with empty string as input.
    if (!trim($s_dna)) {
        return $sExon;
    }

    $sort = '';

    //REMOVE POSSIBLE EXON PRECEDING NOTATION; '(01)ex02ex03del' -> 'ex02ex03del'
    $s_dna = ereg_replace("^\([0-9]{1,2}\)([-0-9]{1,7}(\-|\+).+)$","\\1",$s_dna);

    //REMOVE FUNNY SIGNS PRECEDING THE DNA1 COLUMN.
    // 2007-09-06; 2.0-beta-08; Fixed error in creating sorting code with variants starting with "c.(".
    $s_sort = ereg_replace("^(c\.|g\.)?[[(?]*","",$s_dna);

    // 2005-05-04 for LOVDv.1.1.0-06
    // Special notation for nucleotides after the stop codon.
    $after_stop = false;
    if ($s_sort && $s_sort{0} == '*') {
        $after_stop = true;
        $s_sort = substr($s_sort, 1);
    }

    //GET THE FIRST NUMBER, CAN BE NEGATIVE!
    list($n_sort,$s_sort) = sscanf($s_sort,"%d%s");

    // 2009-07-17; 2.0-20; Modified here to properly handle "c.0" notation.
    if (is_numeric($n_sort) && $n_sort < 0) {
        $sort = "-".str_pad(1000000+$n_sort,6,"0",STR_PAD_LEFT);
    } elseif ($n_sort >= 0) {
        if ($after_stop) {
            $sort = '9999999_' . str_pad($n_sort, 7, '0', STR_PAD_LEFT);
        } else {
            $sort = str_pad($n_sort, 7, '0', STR_PAD_LEFT);
        }
    }

    if ($sort && substr($s_sort,0,1) == "-") {
        //INTRONIC BASES, "-"
        $s_sort = substr($s_sort,1);

        if (substr($s_sort,0,1) == "?") {
            //INTRONIC BASE NOT KNOWN, '-?'
            $s_sort = substr($s_sort,1);
            $n_sort = 1;
        } else {
            list($n_sort,$s_sort) = sscanf($s_sort,"%d%s");
        }

        $sort .= "_-".str_pad(1000000-$n_sort,6,"0",STR_PAD_LEFT);

    } elseif ($sort && substr($s_sort,0,1) == "+") {
        //INTRONIC BASES, "+"
        $s_sort = substr($s_sort,1);

        if (substr($s_sort, 0, 1) == "?") {
            //INTRONIC BASE NOT KNOWN, '+?'
            $s_sort = substr($s_sort,1);
            $n_sort = 1;
        } else {
            list($n_sort,$s_sort) = sscanf($s_sort, "%d%s");
        }

        $sort .= "_" . str_pad($n_sort, 7, "0", STR_PAD_LEFT);

    // 2009-06-23; 2.0-19 removed && !after_stop
    } elseif ($sort) {// && !$after_stop) {
        //NO INTRONIC BASE, FILL UP WITH '0000000'
        $sort .= "_0000000";
    }

    //NEXT PART
    if ($sort && substr($s_sort,0,1) == "_") {
        $s_sort = substr($s_sort,1);

        // 2009-06-23; 2.0-19 corrected for presence of *
        // Special notation for nucleotides after the stop codon.
        if ($s_sort{0} == '*') {
            $after_stop = true;
            $s_sort = substr($s_sort, 1);
            $sort .= '_9999999';
        }

        list($n_sort,$s_sort) = sscanf($s_sort,"%d%s");

        if (is_numeric($n_sort) && $n_sort < 1) {
            $sort .= "_-".str_pad(1000000+$n_sort,6,"0",STR_PAD_LEFT);
        } elseif ($n_sort >= 1) {
            $sort .= "_".str_pad($n_sort,7,"0",STR_PAD_LEFT);
        }

        //INTRONIC BASES, "-"
        if ($sort && substr($s_sort,0,1) == "-") {
            $s_sort = substr($s_sort,1);

            //INTRONIC BASE NOT KNOWN, '-?'
            if (substr($s_sort,0,1) == "?") {
                $s_sort = substr($s_sort,1);
                $n_sort = 1;
            } else {
                list($n_sort,$s_sort) = sscanf($s_sort,"%d%s");
            }

            $sort .= "_-".str_pad(1000000-$n_sort,6,"0",STR_PAD_LEFT);

            //INTRONIC BASES, "+"
        } elseif ($sort && substr($s_sort,0,1) == "+") {
            $s_sort = substr($s_sort,1);

            //INTRONIC BASE NOT KNOWN, '+?'
            if (substr($s_sort,0,1) == "?") {
                $s_sort = substr($s_sort,1);
                $n_sort = 1;
            } else {
                list($n_sort,$s_sort) = sscanf($s_sort,"%d%s");
            }

            $sort .= "_".str_pad($n_sort,7,"0",STR_PAD_LEFT);

            //NO INTRONIC BASE, FILL UP WITH '0000000'
        } elseif ($sort) {
            $sort .= "_0000000";
        }
    }

    // Combine generated sort code with generated Exon code here.
    $sort = $sExon . '_' . $sort;
    return ($sort);
}





function lovd_viewForm ($a,
                        $s_h1 = "\n          <TR>\n            <TD valign=\"top\" class=\"{{ CLASS }}\" width=\"{{ WIDTH }}\">",
                        $s_h2 = "</TD>",
                        $s_d1 = "\n            <TD class=\"{{ CLASS }}\" width=\"{{ WIDTH }}\">",
                        $s_d2 = "</TD></TR>",
                        $s_nl = "              ")
{
    // Based on a function provided by Ileos.nl.
    /***************************************************************************
     * Display HTML form according to input array containing settings and field
     * descriptions.
     *
     * Sytax for values of input array :
     *
     * array('GET|POST', 'header_class_name', 'data_class_name', 'header_width', 'data_width'),
     * 'skip',
     * 'hr',
     * array('print', '<comment>'),
     * array('<header>', 'print', '<comment>'),
     * array('<header>', 'text|password|file', '<field_name>', <field_size>),
     * array('<header>', 'textarea', '<field_name>', <field_cols>, <field_rows>),
     * array('<header>', 'select', '<field_name>', <field_size>, <data> (array, key => val|query, [0] => [1]), <select>:true|false|select_text, <multiple>:true|false, <select_all_link>:true|false|link_text),
     * array('<header>', 'checkbox', '<field_name>', <type_checked>:0|1),
     * array('<header>', 'submit', '<button_value>', '<field_name>'),
     * 
     * Version 1.5; 2006-08-16
     **********/

    // Options.
    list($s_method, $s_class_header, $s_class_data, $s_width_header, $s_width_data) = $a[0];

    // Method.
    if (!in_array($s_method, array('GET', 'POST'))) {
        $s_method = 'POST';
    }

    // Header class name.
    if ($s_class_header) {
        $s_h1 = str_replace('{{ CLASS }}', $s_class_header, $s_h1);
    } else {
        $s_h1 = str_replace(' class="{{ CLASS }}"', '', $s_h1);
    }

    // Data class name.
    if ($s_class_data) {
        $s_d1 = str_replace('{{ CLASS }}', $s_class_data, $s_d1);
    } else {
        $s_d1 = str_replace(' class="{{ CLASS }}"', '', $s_d1);
    }

    // Header width.
    if ($s_width_header) {
        $s_h1 = str_replace('{{ WIDTH }}', $s_width_header, $s_h1);
    } else {
        $s_h1 = str_replace(' width="{{ WIDTH }}"', '', $s_h1);
    }

    // Data width.
    if ($s_width_data) {
        $s_d1 = str_replace('{{ WIDTH }}', $s_width_data, $s_d1);
    } else {
        $s_d1 = str_replace(' width="{{ WIDTH }}"', '', $s_d1);
    }



    // Scripts for checkboxes & radiobuttons.
    $s_script = "\n" .
                '      <SCRIPT type="text/javascript">' . "\n" .
                '        // Preload' . "\n" .
                '        var checkbox = new Image; checkbox.src = "' . ROOT_PATH . 'gfx/checkbox.png";' . "\n" .
                '        var checkbox_0 = new Image; checkbox_0.src = "' . ROOT_PATH . 'gfx/checkbox_0.png";' . "\n" .
                '        var checkbox_1 = new Image; checkbox_1.src = "' . ROOT_PATH . 'gfx/checkbox_1.png";' . "\n" .
                "\n" .
                '        function ileos_checkBoxes (var_boxname, var_checked) {' . "\n" .
                '          var_imagename = "form_" + var_boxname;' . "\n" .
                '          obj_box = document.getElementById(var_imagename);' . "\n\n" .
                '          if (document.forms[0][var_boxname].value == 1) {' . "\n" .
                '            obj_box.src = checkbox.src;' . "\n" .
                '            document.forms[0][var_boxname].value = 0;' . "\n" .
                '          } else {' . "\n" .
                '            obj_box.src = eval("checkbox_" + var_checked + ".src");' . "\n" .
                '            document.forms[0][var_boxname].value = 1;' . "\n" .
                '          }' . "\n" .
                '        }' . "\n" .
                '      </SCRIPT>' . "\n";

    $script = false;





    foreach ($a as $n_val => $a_field) {
        if (!$n_val) {
            // Options, already read out.
            continue;

        } elseif (!is_array($a_field)) {
            // Opdracht.

            if ($a_field == 'skip') {
                // Skip line.
                echo $s_h1 . '&nbsp;' . $s_h2 . $s_d1 . '&nbsp;' . $s_d2;
                continue;
            } elseif ($a_field == 'hr') {
                // Horizontal line (ruler).
                echo str_replace(' valign=', ' colspan="2" valign=', $s_h1) . '<IMG src="' . ROOT_PATH . 'gfx/trans.png" alt="" width="100%" height="1" class="form_hr">' . $s_d2;
                continue;
            }

        } else {
            // Build some form content.

            if ($a_field[0] == 'print') {
                // Print text.
                echo $a_field[1] . "\n";
                continue;



            } elseif ($a_field[1] == 'print') {
                // Print text separated in header and field values.
                list($s_header, $s_type, $s_print) = $a_field;

                print($s_h1 . $s_header . $s_h2 . $s_d1 . $s_print . $s_d2);
                continue;



            // 2009-04-03; 2.0-18; Added 'file' form field type.
            } elseif (in_array($a_field[1], array('text', 'password', 'file'))) {
                list($s_header, $s_type, $s_name, $n_size) = $a_field;
                if (!isset($GLOBALS['_' . $s_method][$s_name])) { $GLOBALS['_' . $s_method][$s_name] = ''; }

                // 2009-02-16; 2.0-16; Added htmlspecialchars to handle quotes in values.
                print($s_h1 . $s_header . $s_h2 . $s_d1 . '<INPUT type="' . $s_type . '" name="' . $s_name . '" size="' . $n_size . '" value="' . htmlspecialchars($GLOBALS['_' . $s_method][$s_name]) . '">' . $s_d2);
                continue;



            } elseif ($a_field[1] == 'textarea') {
                list($s_header, $s_type, $s_name, $n_cols, $n_rows) = $a_field;
                if (!isset($GLOBALS['_' . $s_method][$s_name])) { $GLOBALS['_' . $s_method][$s_name] = ''; }

                // 2009-02-16; 2.0-16; Added htmlspecialchars to handle quotes in values.
                print($s_h1 . $s_header . $s_h2 . $s_d1 . '<TEXTAREA name="' . $s_name . '" cols="' . $n_cols . '" rows="' . $n_rows . '">' . htmlspecialchars($GLOBALS['_' . $s_method][$s_name]) . '</TEXTAREA>' . $s_d2);
                continue;



            } elseif ($a_field[1] == 'select') {
                list($s_header, $s_type, $s_name, $n_size, $data, $select, $multiple, $select_all) = $a_field;
                if (!isset($GLOBALS['_' . $s_method][$s_name])) { $GLOBALS['_' . $s_method][$s_name] = ''; }

                print($s_h1 . $s_header . $s_h2 . $s_d1 . '<SELECT name="' . $s_name . ($multiple? '[]' :'') . '"' . ($multiple == true || $n_size != 1? ' size="' . $n_size . '"' : '') . ($multiple? ' multiple' : '') . '>');

                if ($select) {
                    // Print de 'select' kop.
                    $selected = false;
                    if ((!$multiple && !$GLOBALS['_' . $s_method][$s_name]) || ($multiple && is_array($GLOBALS['_' . $s_method][$s_name]) && !count($GLOBALS['_' . $s_method][$s_name]))) {
                        $selected = true;
                    }
                    print("\n" . $s_nl . '  <OPTION value=""' . ($selected? ' selected' : '') . '>' . ($select === "true" || $select === true? '-- select --' : $select) . '</OPTION>');
                }

                if (is_array($data)) {
                    // Array input.
                    foreach ($data as $key => $val) {
                        $selected = false;
                        if ((!$multiple && $GLOBALS['_' . $s_method][$s_name] == $key) || ($multiple && is_array($GLOBALS['_' . $s_method][$s_name]) && in_array($key, $GLOBALS['_' . $s_method][$s_name]))) {
                            $selected = true;
                        }
                        print("\n" . $s_nl . '  <OPTION value="' . $key . '"' . ($selected? ' selected' : '') . '>' . $val . '</OPTION>');
                    }

                } elseif(is_resource($data)) {
                    // Query input.
                    while (list($key, $val) = mysql_fetch_row($data)) {
                        $selected = false;
                        if ((!$multiple && $GLOBALS['_' . $s_method][$s_name] == $key) || ($multiple && is_array($GLOBALS['_' . $s_method][$s_name]) && in_array($key, $GLOBALS['_' . $s_method][$s_name]))) {
                            $selected = true;
                        }
                        print("\n" . $s_nl . '  <OPTION value="' . $key . '"' . ($selected? ' selected' : '') . '>' . $val . '</OPTION>');
                    }
                }

                print('</SELECT>');

                // Select all link.
                if ($select_all) {
                    print('&nbsp;<A href="#" onclick="var list = document.forms[0][\'' . $s_name . '[]\']; for (i=0;i<list.options.length;i++) { list.options[i].selected = true; }; return false">Select all</A>');
                }

                print($s_d2);
                continue;



            } elseif ($a_field[1] == 'checkbox') {
                list($s_header, $s_type, $s_name, $n_checked) = $a_field;
                if (!isset($GLOBALS['_' . $s_method][$s_name])) { $GLOBALS['_' . $s_method][$s_name] = ''; }

                if ((!$n_checked && $n_checked !== 0) || !in_array($n_checked, array(0, 1))) {
                    // Indien niet ingevuld, is de standaard vink de groene.
                    $n_checked = 1;
                }

                if (!$script) {
                    echo str_replace('{{ CHECKED }}', $n_checked, $s_script);
                    $script = true;
                }

                print($s_h1 . $s_header . $s_h2 . $s_d1 . '<INPUT type="hidden" name="' . $s_name . '" value="' . $GLOBALS['_' . $s_method][$s_name] . '"><IMG src="' . ROOT_PATH . 'gfx/checkbox' . ($GLOBALS['_' . $s_method][$s_name]? '_' . $n_checked : '') . '.png" alt="" width="13" height="13" id="form_' . $s_name . '" style="cursor : pointer; cursor : hand;" onclick="ileos_checkBoxes(\'' . $s_name . '\', ' . $n_checked . ');">' . $s_d2);
                continue;












            } elseif ($a_field[1] == 'submit') {
                print($s_h1 . $a_field[0] . $s_h2 . $s_d1 . '<INPUT type="submit"' . (isset($a_field[3])? ' name="' . $a_field[3] . '"' : '') . ' value="' . $a_field[2] . '">' . $s_d2);
                continue;
            }
        }



//automatic form v2.7b // function incompatible with 2.2 input
//$a   = array();
//$a[] = array("radio","description","name_of_field",array_data,array_des));
//$a[] = array("comment","value");


/*




    if ($a[$i+1][0] == "comment" || substr($a[$i][0], -8) == "_no_line") {
      if (substr($a[$i][0], -8) == "_no_line") {
        $a[$i][0] = substr($a[$i][0], 0, strlen($a[$i][0])-8);
      }
      $comm = true;
    }
    if ($a[$i][0] == "comment") {
      print ($s_cf . $a[$i][1] . $s_ca);
      continue;
    }
    if ($a[$i][1]) {
      if ($a[$i][1] == " ") {
        print ($s_df."&nbsp;".$s_da);
      } else {
        print ($s_df.$a[$i][1] . ($a[$i][0]? ":" : "") . $s_da);
      }
    } elseif ($a[$i][0] == "hidden") {
      print ($s_df.$s_da);
    }
    if (!$a[$i][0]) {
      print ($s_if."&nbsp;".$s_ia);
      continue;
    }

    } elseif ($a[$i][0] == "radio") {
      print ($s_if);
      for ($j=0;$j<count($a[$i][3]);$j++) {
        print (($j == 0? "" : "$s_nl  ")."<input type=\"".$a[$i][0]."\" name=\"".$a[$i][2]."\" value=\"".$a[$i][3][$j]."\"".($_POST[$a[$i][2]] == $a[$i][3][$j]? " checked" : "").">".$a[$i][4][$j].($a[$i][3][$j+1]? "<br>\n" : ""));
      }
      print ($s_ia);
      continue;
    }


*/
    }
}





function lovd_wrapText ($s, $l = 80, $sCut = ' ')
{
    // Function kindly provided by Ileos.nl in the interest of Open Source.
    // Wraps a text to a certain length.

    if (empty($sCut)) {
        $sCut = ' ';
    } elseif (strlen($sCut) > 1) {
        $sCut = substr($sCut, 0, 1);
    }
    if ($sCut != ' ') {
        // We add the cut character at the end, so we use extra space.
        $l --;
    }
    $l_regel = 0;
    $a = split("\r?\n", $s);
    $s = '';

    foreach ($a as $key => $val) {
        $a_word = explode($sCut, $val);

        foreach ($a_word as $word_key => $word) {
            $n = strlen($word);

            // If there is only one word, but it does not fit, we need to cut it.
            if (!$s && $n > $l) {
                // We're at the first word, but it's too long!
                $aCutAlt = array(',', ';', ':');
                foreach ($aCutAlt as $sCutAlt) {
                    if (substr_count($word, $sCutAlt)) {
                        // But we found an alternative cutting character.
                        $s .= lovd_wrapText($word, $l, $sCutAlt);
                        $l_regel = strlen(strrchr($s, "\n")) - 1;
                        continue 2;
                    }
                }
            }

            if (!$word_key) {
                // Het eerste woord van de regel
                $s .= $word;
                $l_regel = $n;

            } else {
                // Niet het eerste woord van de regel

                if (($l_regel + 1 + $n) <= $l) {
                    // Past
                    $s .= $sCut . $word;
                    $l_regel += (1 + $n);

                } else {
                    // Past niet!
                    $s .= ($sCut != ' '? $sCut : '') . "\n" . lovd_wrapText($word, $l, $sCut);
                    $l_regel = strlen(strrchr($s, "\n")) - 1;
                }
            }
        }

        // Nog een regel hierna?
        if (count($a) > ($key + 1)) {
            $s .= "\n";
        }
    }

    return $s;
}
?>
