| <?php
namespace Jackbooted\Html;
use \Jackbooted\Util\Invocation;
/**
 * @copyright Confidential and copyright (c) 2016 Jackbooted Software. All rights reserved.
 *
 * Written by Brett Dutton of Jackbooted Software
 * brett at brettdutton dot com
 *
 * This software is written and distributed under the GNU General Public
 * License which means that its source code is freely-distributed and
 * available to the general public.
 */
/**
 * Generates the javascript validation based on the required directives.
 *
 * After you initialise the
 * class with the form name you call methods to direct the style of validation. Then call to method
 * <i>toHtml</i> to generate the javascript needed for the validation.
 *
 * Current validation functions are:
 * <ul>
 * <li>Exists</li>
 * <li>String Length</li>
 * <li>Numerical Range</li>
 * <li>Is Integer</li>
 * <li>Valid Email</li>
 * <li>2 fields are equal (password)</li>
 * <li>Duplicate Field (prefered name)</li>
 * <li>24Hr Time Check</li>
 * </ul>
 *
 * <b>PLease Note. This dependends on jquery!</b>
 *
 * <h4>Example of use</h4>
 * <pre>
 * <?php
 * require_once ( dirname ( dirname ( dirname (  __FILE__ ) ) ) . "/config.php" );
 *
 * $val = new Validator( 'testFormName' );
 * $val->setMissingAlert( true );
 *
 * $val->addExists( 'Exists', 'Exists Description' );
 * $val->addExists( 'Length1', 'Length1 Description' );
 * $val->addLength( 'Length1', 'Length1 Description', 5, 5 );
 * $val->addExists( 'Length2', 'Length2 Description' );
 * $val->addLength( 'Length2', 'Length2 Description', 2, 6 );
 * $val->addExists( 'Length3', 'Length3 Description' );
    * $val->addLength( 'Length3', 'Length3 Description', 2 );
 * $val->addExists( 'Length4', 'Length4 Description' );
 * $val->addLength( 'Length4', 'Length4 Description', null, 5 );
 * $val->addExists( 'Range1', 'Range1 Description' );
 * $val->addRange( 'Range1', 'Range1 Description', 5, 5 );
 * $val->addExists( 'Range2', 'Range2 Description' );
 * $val->addRange( 'Range2', 'Range2 Description', 2, 6 );
 * $val->addExists( 'Range3', 'Range3 Description' );
 * $val->addRange( 'Range3', 'Range3 Description', 2 );
 * $val->addExists( 'Range4', 'Range4 Description' );
 * $val->addRange( 'Range4', 'Range4 Description', null, 5 );
 * $val->addExists( 'Integer', 'Integer Description' );
 * $val->addInteger( 'Integer', 'Integer Description' );
 * $val->addExists( 'Email', 'Email Description' );
 * $val->addEmail( 'Email', 'Email Description' );
 * $val->addExists( 'Equal1', 'Equal Description' );
 * $val->addEqual( 'Equal1', 'Equal2', 'Equal Description' );
 * $val->addCopy( 'Copy1', 'Copy2' );
 * $val->addExists( 'Copy1', 'Copy 1 descr' );
 * $val->add24HrTime( '24HR', 'Time needs to be HH:MM' );
 * $html = $val->toHtml();
 * echo '<a href="javascript:void(0)" onclick="$(\'#code\').toggle()">Hide/Show Javascript Output</a><br/>';
 * echo '<div id="code" style="display:none"><pre>' . htmlspecialchars( $html ) . '</pre></div>';
 * echo '<a href="javascript:void(0)" onclick="$(\'#vars\').toggle()">Hide/Show GET Variables</a><br/>';
 * if ( count ( $_GET ) > 0 ) {
 *     echo '<div id="vars" style="display:none"><pre>';
 *     print_r( $_GET );
 *     echo '</pre></div>';
 * }
 *
 * echo $html;
 * echo '<form name="testFormName" onSubmit="' . $val->onSubmit() . '">';
 * echo 'Exists'  . Tag::text( 'Exists',  array ( 'value' => 'xyz' ) ) . '<br>';
 * echo 'Length1' . Tag::text( 'Length1', array ( 'value' => '12345' ) ) . '<br>';
 * echo 'Length2' . Tag::text( 'Length2', array ( 'value' => '123' ) ) . '<br>';
 * echo 'Length3' . Tag::text( 'Length3', array ( 'value' => '12345' ) ) . '<br>';
 * echo 'Length4' . Tag::text( 'Length4', array ( 'value' => '1234' ) ) . '<br>';
 * echo 'Range1'  . Tag::text( 'Range1',  array ( 'value' => '5' ) ) . '<br>';
 * echo 'Range2'  . Tag::text( 'Range2',  array ( 'value' => '5' ) ) . '<br>';
 * echo 'Range3'  . Tag::text( 'Range3',  array ( 'value' => '4' ) ) . '<br>';
 * echo 'Range4'  . Tag::text( 'Range4',  array ( 'value' => '4' ) ) . '<br>';
 * echo 'Integer' . Tag::text( 'Integer', array ( 'value' => '10' ) ) . '<br>';
 * echo 'Email'   . Tag::text( 'Email',   array ( 'value' => '[email protected] ' ) ) . '<br>';
 * echo 'Equal1'  . Tag::text( 'Equal1',  array ( 'value' => 'A1' ) ) . '<br>';
 * echo 'Equal2'  . Tag::text( 'Equal2',  array ( 'value' => 'A1' ) ) . '<br>';
 * echo 'Copy1'   . Tag::text( 'Copy1',   array ( 'value' => 'BB' ) ) . '<br>';
 * echo 'Copy2'   . Tag::text( 'Copy2' ) . '<br>';
 * echo '24Hr'    . Tag::text( '24HR',    array ( 'value' => '01:02' ) ) . '<br>';
 * echo Tag::submit( 'Submit', 'submit' ) . '<br>';
 * echo Response::factory()->toHidden(false);
 * echo '</form>';
 * </pre>
 */
class Validator extends \Jackbooted\Util\JB {
    /**
     * Enumerated list of functions that are available.
     */
    const FN_EXISTS  = 'EXISTS';
    /**
     * Function to check a range.
     */
    const FN_RANGE   = 'RANGE';
    /**
     * Function to check if the field is a valid integer.
     */
    const FN_INTEGER = 'INTEGER';
    /**
     * Check to see if the field is a valid email.
     */
    const FN_EMAIL   = 'EMAIL';
    /**
     * Check to see if 2 fields are the same.
     */
    const FN_EQUAL   = 'EQUAL';
    /**
     * Copies one field to another if the second one is empty.
     */
    const FN_COPY    = 'COPY';
    /**
     * Validates if the field is a particular length or range.
     */
    const FN_LENGTH  = 'LENGTH';
    /**
     * Validates if the field is 24hr time.
     */
    const FN_24HRTIME  = '24HRTIME';
    public static function factory ( $formName, $suffix='' ) {
        return new Validator ( $formName, $suffix );
    }
    /**
     * Name of the form in this document.
     * @var String
     */
    private $formName;
    /**
     * An array of all the form variables that will be tested.
     * @see $add
     * @var array
     */
    private $testCases =  [];
    /**
     * List of used functions.
     *
     * This is used so we
     * do not generate javascript that we don't need.
     * @var array
     */
    private $usedFunct =  [];
    /**
     * This variable will alert the user to any form variables that are missing.
     *
     * This is not used in anything other than testing.
     * @var array
     */
    private $alertOnMissingFormVar = FALSE;
    /**
     * @var int is a unique name for the javascript. Automatically generated.
     */
    private $id;
    private $headerJS;
    private $existsJS;
    private $emailJS;
    private $integerJS;
    private $validateHeaderJS;
    private $testCaseHeaderJS;
    private $caseExistsJS;
    private $caseLenEqJS;
    private $caseLenBetweenJS;
    private $caseLenGTJS;
    private $caseLenLTJS;
    private $caseIntegerJS;
    private $caseRangeBetweenJS;
    private $caseRangeGTJS;
    private $caseRangeLTJS;
    private $caseEmailJS;
    private $caseEqualJS;
    private $caseCopyJS;
    private $caseAlertMissingJS;
    private $validateFooterJS;
    private $validateFunctionJS;
    private $case24HrTimeJS;
    private $t24HrTimeJS;
    /**
     * Creates the Validation object.
     *
     * Requires the form name.
     *
     * @param string $formName The name of the form that will be validated.
     * @param string $suffix The suffix will give the unique identifier if there are a number of
     * validators on a page. The uniquie suffix is automatically generated based on number of
     * invokations of the form. This does not work on ajax late generated forms
     * so for ajax, supply a unique suffix
     *
     * @since 1.0
     */
    public function  __construct ( $formName, $suffix='' ) {
        parent::__construct();
        if ( $suffix == '' ) {
            $suffix = Invocation::next();
        }
        $this->formName = $formName;
        $this->id = '_' . $suffix;
        $this->setUpJavaScriptFunctions ();
    }
    /**
     * Sets the variable alertOnMissingFormVar.
     *
     * This variable controlles
     * is there is a message displayed if the javascript is given an
     * invalid form variable name to test.
     *
     * @param boolean $state The state of this variable.
     *
     * @since 1.0
     * @return void
     */
    public function setMissingAlert ( $state ) {
        $this->alertOnMissingFormVar = $state;
        return $this;
    }
    /**
     * Add a test for this form variable.
     *
     * Tests for Existance.
     *
     * @param string $fv   Form Variable name.
     * @param string $desc A message if the test fails.
     *
     * @since 1.0
     * @return void
     */
    public function addExists ( $fv, $desc ) {
        return $this->add ( $fv, $desc,  self::FN_EXISTS );
    }
    /**
     *  Add a test for this form variable.
     *
     * Tests for a length.
     * if $minLength is set and $maxLength then the test will ensure that
     * the field is at least $minLength long. The same goes for $maxLength set and
     * $minLength not set. The field can be a maximum of maxLength long
     * If they are both set then the field must ve between the 2 lengths
     * If they are the same then the field must be exactly that length.
     * If both $minLength and $maxLength are null then no tests are performed.
     *
     * @param string  $fv        Form Variable name.
     * @param string  $desc      A message if the test fails.
     * @param integer $minLength Minimum length of string.
     * @param integer $maxLength Maximum length of string.
     *
     * @since 1.0
     * @return void
     */
    public function addLength ( $fv, $desc, $minLength=null, $maxLength=null, $zeroOk='false' ) {
        return $this->add ( $fv, $desc,  self::FN_LENGTH,  [ $minLength, $maxLength, $zeroOk ] );
    }
    /**
     * Add a test that a field is within a particular range.
     *
     * The range works in
     * the same sort of way as the length checking. If both variables are set then the
     * field value must be between the max and min. If the min is set and max not set then
     * the value must be greater than min, and visa-versa
     * If both $mn and $mx are null then no tests are performed.
     *
     * @param string $fv   Form Variable name.
     * @param string $desc A message if the test fails.
     * @param float  $mn   Minimum value of the field.
     * @param float  $mx   Maximum value of the field.
     *
     * @since 1.0
     * @return void
     */
    public function addRange ( $fv, $desc, $mn=null, $mx=null ) {
        return $this->add ( $fv, $desc,  self::FN_RANGE,  [ $mn, $mx ] );
    }
    /**
     * Add a test for this form variable.
     *
     * Tests for value being an integer
     * ensures that the field contains a valid integer. Note that empty is valid as well
     * Usually you check for existance before you test for integer.
     *
     * @param string $fv   Form Variable name.
     * @param string $desc A message if the test fails.
     *
     * @since 1.0
     * @return void
     */
    public function addInteger ( $fv, $desc ) {
        return $this->add ( $fv, $desc,  self::FN_INTEGER );
    }
    /**
     * Add a test for this form variable.
     *
     * Tests for valid email
     * Note that empty is valid. That means that you must check for
     * Existance as well as email.
     *
     * @param string $fv   The form variable name to test.
     * @param string $desc The description of the error.
     *
     * @since 1.0
     * @return void
     */
    public function addEmail ( $fv, $desc ) {
        return $this->add ( $fv, $desc, self::FN_EMAIL   );
    }
    /**
     * Add a test for this form variable.
     *
     * Tests for valid 24 hour time
     *
     * @param string $fv   The form variable name to test.
     * @param string $desc The description of the error.
     *
     * @since 2.0
     * @return void
     */
    public function add24HrTime ( $fv, $desc, $defaultTime='' ) {
        $this->usedFunct[self::FN_INTEGER] = 'YES';
        return $this->add ( $fv, $desc, self::FN_24HRTIME, $defaultTime );
    }
    /**
     * Add a test for this form variable.
     *
     * Tests for 2 form variables
     * are equal. This is particually useful for passwords
     *
     * @param string $fv1  First form variable to check.
     * @param string $fv2  Other form variable to test againse.
     * @param string $desc Message if the variables are not the same.
     *
     * @since 1.0
     * @return void
     */
    public function addEqual ( $fv1, $fv2, $desc ) {
        return $this->add ( $fv1, $desc, self::FN_EQUAL, $fv2 );
    }
    /**
     * Add a test for this form variable.
     *
     * Tests if a form variable
     * is empty. If it is then it copies the value from $fv1 into $fv2
     * This would be useful for say prefered name.
     *
     * @param string $fv1 Source Form Variable.
     * @param string $fv2 Destination Form Variable.
     *
     * @since 1.0
     * @return void
     */
    public function addCopy ( $fv1, $fv2 ) {
        return $this->add ( $fv1, '', self::FN_COPY, $fv2 );
    }
    /**
     * Generic function for adding tests.
     *
     * This is only called internally.
     *
     * @param string $fv   Form Variable.
     * @param string $desc Description for this test.
     * @param enum $t      Test type.
     * @param string $xtra Extra information.
     *
     * @since 1.0
     * @return void
     */
    private function add ( $fv, $desc, $t, $xtra=NULL ) {
        $this->testCases[] =  [ 'NAME' => $fv,
                                     'DESC' => $desc,
                                     'TEST' => $t,
                                     'XTRA' => $xtra ];
        $this->usedFunct[$t] = 'YES';
        return $this;
    }
    /**
     * Generates HTML and Javascript to do the tests on this form.
     *
     * @since 1.0
     * @return string
     */
    public function toHtml () {
        $msg = $this->headerJS;
        foreach ( $this->usedFunct as $key => $val ) {
            $msg .= $this->addJSFunctions ( $key );
        }
        // Then create the validation function that will test all the
        // different form variables
        $msg .= sprintf ( $this->validateHeaderJS, $this->formName );
        foreach ( $this->testCases as $val ) {
            $nam  = $val['NAME'];
            $desc = $val['DESC'];
            $xtra = $val['XTRA'];
            // Test is in java and check if the form variable exists
            // Ensures the Javascript does not crash on
            // missing Form Variables
            $msg .= sprintf ( $this->testCaseHeaderJS, $nam );
            $msg .= $this->createCaseJSTests ( $val, $xtra, $desc );
            // End of the if test that check if the form var exists
            $msg .= sprintf ( $this->validateFooterJS, $this->formName );
            // Check if we are notifying the user on missing form vars
            if ( $this->alertOnMissingFormVar ) {
                $msg .= sprintf ( $this->caseAlertMissingJS, $nam );
            }
        }
        $msg .= sprintf ( $this->validateFunctionJS, $this->formName );
        return JS::library ( JS::JQUERY ) .
               JS::javaScript ( $msg );
    }
    private function createCaseJSTests ( $val, $xtra, $desc ) {
        // Output the javascript to do the different checks
        switch ( $val['TEST'] ) {
        case self::FN_EXISTS:
            return sprintf ( $this->caseExistsJS, $desc );
        case self::FN_LENGTH:
            return $this->lengthJSCases ( $xtra, $desc );
        case self::FN_INTEGER:
            return sprintf ( $this->caseIntegerJS, $desc );
        case self::FN_RANGE:
            return $this->rangeJSCases ( $xtra, $desc );
        case self::FN_EMAIL:
            return sprintf ( $this->caseEmailJS, $desc );
        case self::FN_24HRTIME:
            return sprintf ( $this->case24HrTimeJS, $desc, $val['XTRA'], $val['XTRA'] );
        case self::FN_EQUAL:
            return sprintf ( $this->caseEqualJS, $val['XTRA'], $desc );
        case self::FN_COPY:
            return sprintf ( $this->caseCopyJS, $val['XTRA'], $desc );
        default:
            return '';
        }
    }
    private function rangeJSCases ( $xtra, $desc ) {
        if ( $xtra[0] != NULL && $xtra[1] != NULL ) {
            return sprintf ( $this->caseRangeBetweenJS, $xtra[0], $xtra[1], $desc );
        }
        else if ( $xtra[0] != NULL ) {
            return sprintf ( $this->caseRangeGTJS, $xtra[0], $desc );
        }
        else if ( $xtra[1] != NULL ) {
            return sprintf ( $this->caseRangeLTJS, $xtra[1], $desc );
        }
        else {
            return '';
        }
    }
    private function lengthJSCases ( $xtra, $desc ) {
        if ( $xtra[0] != NULL && $xtra[1] != NULL ) {
            if ( $xtra[0] == $xtra[1] ) {
                return sprintf ( $this->caseLenEqJS, $xtra[2], $xtra[0], $desc );
            }
            else {
                return sprintf ( $this->caseLenBetweenJS, $xtra[2], $xtra[0], $xtra[1], $desc );
            }
        }
        else if ( $xtra[0] != NULL ) {
            return sprintf ( $this->caseLenGTJS, $xtra[2], $xtra[0], $desc );
        }
        else if ( $xtra[1] != NULL ) {
            return sprintf ( $this->caseLenLTJS, $xtra[2], $xtra[1], $desc );
        }
        else {
            return '';
        }
    }
    private function addJSFunctions ( $key ) {
        switch ( $key ) {
            case self::FN_EXISTS:  // Output the javascript functions for Existance
                return $this->existsJS;
            case self::FN_EMAIL: // Output the javascript functions for email
                return $this->emailJS;
            case self::FN_24HRTIME: // Output the javascript functions for time validation
                return $this->t24HrTimeJS;
            case self::FN_INTEGER:
                $ret = $this->integerJS;
                $this->integerJS = '';
                return $ret;
            default:
                return '';
        }
    }
    /**
     * Creates the javascript that can be included in attribute that will validate the form.
     *
     * Typically this would be used in the creation of the form so that it does the validation.
     * <pre>
     * echo '<form onSubmit="' . $val->onSubmit () . '">';
     * </pre>
     *
     * @param string $js Additional javascript.
     *
     * @since 1.0
     * @return string
     */
    public function onSubmit ( $js='' ) {
        return 'if(!' . $this->jsValidate() . ')return false;' . $js .'return true;';
    }
    /**
     * Returns the javascript that is just used for doing the validation.
     *
     * Typically this would be used in the javascript when you have to integrate with other validation.
     * <pre>
     * <script language="JavaScript">
     *     function saveClicked() {
     *         if ( ! <?php echo $valid->jsValidate(); ?> ) {
     *             return;
     *         }
     *         var f = document.forms[0];
     *         if (f['issues[]'].selectedIndex < 0) {
     *             if (!confirm('There is no issue selected.')) {
     *                 return;
     *             }
     *         }
     *         top.restoreSession();
     *         f.submit();
     *     }
     * </script>
     * </pre>
     *
     * @since 2.0
     * @return string
     */
    public function jsValidate ( ) {
        return "validateForm{$this->id}()";
    }
    private function setUpJavaScriptFunctions () {
        $this->headerJS = <<<EOT1
function isEmpty{$this->id}( s ) {
    return ( ( s == null ) || ( s.length == 0 ) );
}
var whitespace{$this->id} = " \\t\\n\\r";
function isWhitespace{$this->id} ( s ) {
    var i;
    if ( isEmpty{$this->id}( s ) ) return true;
    for ( i=0; i<s.length; i++ ) {
        var c = s.charAt( i );
        if ( whitespace{$this->id}.indexOf( c ) == -1 ) return false;
    }
    return true;
}
EOT1;
        $this->existsJS = <<<EOT2
function doesExist{$this->id} ( s ) {
    return ( ! isEmpty{$this->id}( s ) && ! isWhitespace{$this->id} ( s ) );
}
EOT2;
        $this->emailJS = <<<EOT3
function isEmail{$this->id} ( s ) {
    if ( isEmpty{$this->id}( s ) ) return true;
    if ( isWhitespace{$this->id}( s ) ) return false;
    var i = 1;
    var sLength = s.length;
    while ( ( i < sLength ) && ( s.charAt( i ) != "@" ) ) i++;
    if ( ( i >= sLength ) || ( s.charAt( i ) != "@" ) ) return false;
    else i += 2;
    while ( ( i < sLength ) && ( s.charAt( i ) != "." ) ) i++;
    if ( ( i >= sLength - 1 ) || ( s.charAt( i ) != "." ) ) return false;
    else return true;
}
EOT3;
        $this->integerJS = <<<EOT4
function isDigit{$this->id}( num ) {
    if ( num.length > 1 ) return false;
    var string = "1234567890";
    if ( string.indexOf( num ) != -1) return true;
    return false;
}
function isInteger{$this->id}( val ) {
    var ok;
    for ( var i=0; i<val.length; i++ ) {
        var ch = val.charAt( i );
        if ( ( i == 0 ) && ( ch == '+' || ch == '-' ) ) ok = true;
        else if ( isDigit{$this->id} ( ch ) ) ok = true;
        else {
            return false;
        }
    }
    return true;
}
EOT4;
        $this->validateHeaderJS = <<<EOT5
function validateForm{$this->id}() {
    var formName='%s';
EOT5;
        $this->testCaseHeaderJS = <<<EOT6
    var fieldName = '%s';
    var element = $('form[name=' + formName + '] input[name=' + fieldName + ']');
    if ( element.length == 1 ) {
EOT6;
        $this->caseExistsJS = <<<EOT7
        if ( ! doesExist{$this->id} ( element.val() ) ) {
            alert ( "%s" );
            element.focus();
            return false;
        }
EOT7;
        $this->caseLenEqJS = <<<EOT8
        if ( ! ( ( element.val().length == 0 && %s ) || element.val().length == %s ) ) {
            alert ( "%s" );
            element.focus();
            return false;
        }
EOT8;
        $this->caseLenBetweenJS = <<<EOT9
        if ( ! ( ( element.val().length == 0 && %s ) || ( element.val().length >= %s && element.val().length <= %s ) ) ) {
            alert ( "%s" );
            element.focus();
            return false;
        }
EOT9;
        $this->caseLenGTJS = <<<EOT10
        if ( ! ( ( element.val().length == 0 && %s ) || element.val().length >= %s ) ) {
            alert ( "%s" );
            element.focus();
            return false;
        }
EOT10;
        $this->caseLenLTJS = <<<EOT11
        if ( ! ( ( element.val().length == 0 && %s ) || element.val().length <= %s ) ) {
            alert ( "%s" );
            element.focus();
            return false;
        }
EOT11;
        $this->caseIntegerJS = <<<EOT12
        if ( ! isInteger{$this->id} ( element.val() ) ) {
            alert ( "%s" );
            element.focus();
            return false ;
        }
EOT12;
        $this->caseRangeBetweenJS = <<<EOT13
        if ( ! isEmpty{$this->id} ( element.val() ) ) {
            if ( parseInt ( element.val() ) < %s || parseInt ( element.val() ) > %s ) {
                alert ( "%s" );
                element.focus();
                return false;
            }
        }
EOT13;
        $this->caseRangeGTJS = <<<EOT14
        if ( ! isEmpty{$this->id} ( element.val() ) ) {
            if ( parseInt ( element.val() ) < %s ) {
                alert ( "%s" );
                element.focus();
                return ( false );
            }
        }
EOT14;
        $this->caseRangeLTJS = <<<EOT15
        if ( ! isEmpty{$this->id} ( element.val() ) ) {
            if ( parseInt ( element.val() ) > %s ) {
                alert ( "%s" );
                element.focus();
                return false;
            }
        }
EOT15;
        $this->caseEmailJS = <<<EOT16
        if ( ! isEmail{$this->id} ( element.val() ) ) {
            alert ( "%s" );
            element.focus();
            return false;
        }
EOT16;
        $this->caseEqualJS = <<<EOT17
        var fieldName2='%s';
        var element2 = $('form[name=' + formName + '] input[name=' + fieldName2 + ']');
        if ( element.val() != element2.val() ) {
            alert ( "%s" );
            element.focus();
            return false;
        }
EOT17;
        $this->caseCopyJS = <<<EOT18
        var fieldName2 = '%s';
        var element2 = $('form[name=' + formName + '] input[name=' + fieldName2 + ']');
        if ( ! doesExist{$this->id} ( element2.val() ) ) {
            element2.val( element.val() );
        }
EOT18;
        $this->validateFooterJS = <<<EOT20
    }
EOT20;
        $this->caseAlertMissingJS = <<<EOT19
    else {
        alert ( "Form variable '%s' does not exist in this form" );
        return false;
    }
EOT19;
    $this->validateFunctionJS = <<<EOT21
    return true;
}
EOT21;
        $this->case24HrTimeJS = <<<EOT22
        if ( ! is24HrTime{$this->id} ( element.val() ) ) {
            alert ( "%s" );
            if ( "%s" != "" ) element.val ( "%s" );
            element.focus();
            return false;
        }
EOT22;
         $this->t24HrTimeJS = <<<EOT23
function is24HrTime{$this->id} ( s ) {
    s = $.trim ( s );
    if ( s.length == 0 ) return true;
    if ( s.length != 5 ) return false;
    var parts = s.split ( ':' );
    if ( parts.length != 2 ) return false;
    if ( ! isInteger{$this->id} ( parts[0] ) ) return false;
    if ( ! isInteger{$this->id} ( parts[1] ) ) return false;
    var hrs = parseInt ( parts[0] );
    var min = parseInt ( parts[1] );
    if ( hrs < 0 || hrs > 23 || min < 0 || min > 60 ) return false;
    return true;
}
EOT23;
   }
}
 |