1770 lines
69 KiB
PHP
1770 lines
69 KiB
PHP
<?php
|
||
|
||
/**
|
||
* Class for date controls.
|
||
*
|
||
* @author Stefan Gabos <contact@stefangabos.ro>
|
||
* @copyright (c) 2006 - 2016 Stefan Gabos
|
||
* @package Controls
|
||
*/
|
||
class Zebra_Form_Date extends Zebra_Form_Control
|
||
{
|
||
|
||
/**
|
||
* Adds a date control to the form.
|
||
*
|
||
* <b>Do not instantiate this class directly! Use the {@link Zebra_Form::add() add()} method instead!</b>
|
||
*
|
||
* The output of this control will be a {@link Zebra_Form_Text textbox} control with an icon to the right of it.<br>
|
||
* Clicking the icon will open an inline JavaScript date picker.<br>
|
||
*
|
||
* <code>
|
||
* // create a new form
|
||
* $form = new Zebra_Form('my_form');
|
||
*
|
||
* // add a date control to the form
|
||
* $mydate = $form->add('date', 'my_date', date('Y-m-d'));
|
||
*
|
||
* // you *have* to set the "date" rule
|
||
* $mydate->set_rule(array(
|
||
* 'date' => array('error', 'Invalid date specified!'),
|
||
* ));
|
||
*
|
||
* // set the date's format
|
||
* $mydate->format('M d, Y');
|
||
*
|
||
* // don't forget to always call this method before rendering the form
|
||
* if ($form->validate()) {
|
||
*
|
||
* // get the date in YYYY-MM-DD format so you can play with is easily
|
||
* $date = $mydate->get_date();
|
||
*
|
||
* }
|
||
*
|
||
* // output the form using an automatically generated template
|
||
* $form->render();
|
||
* </code>
|
||
*
|
||
* @param string $id Unique name to identify the control in the form.
|
||
*
|
||
* The control's <b>name</b> attribute will be the same as the <b>id</b> attribute!
|
||
*
|
||
* This is the name to be used when referring to the control's value in the
|
||
* POST/GET superglobals, after the form is submitted.
|
||
*
|
||
* This is also the name of the variable to be used in custom template files, in
|
||
* order to display the control.
|
||
*
|
||
* <code>
|
||
* // in a template file, in order to print the generated HTML
|
||
* // for a control named "my_date", one would use:
|
||
* echo $my_date;
|
||
* </code>
|
||
*
|
||
* @param string $default (Optional) Default date, formatted according to {@link format() format}.
|
||
*
|
||
* @param array $attributes (Optional) An array of attributes valid for
|
||
* {@link http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.4 input}
|
||
* controls (size, readonly, style, etc)
|
||
*
|
||
* Must be specified as an associative array, in the form of <i>attribute => value</i>.
|
||
* <code>
|
||
* // setting the "readonly" attribute
|
||
* $obj = $form->add(
|
||
* 'date',
|
||
* 'my_date',
|
||
* '',
|
||
* array(
|
||
* 'readonly' => 'readonly'
|
||
* )
|
||
* );
|
||
* </code>
|
||
*
|
||
* See {@link Zebra_Form_Control::set_attributes() set_attributes()} on how to set
|
||
* attributes, other than through the constructor.
|
||
*
|
||
* The following attributes are automatically set when the control is created and
|
||
* should not be altered manually:<br>
|
||
*
|
||
* <b>type</b>, <b>id</b>, <b>name</b>, <b>value</b>, <b>class</b>
|
||
*
|
||
* @return void
|
||
*/
|
||
function __construct($id, $default = '', $attributes = '')
|
||
{
|
||
|
||
// call the constructor of the parent class
|
||
parent::__construct();
|
||
|
||
// set the private attributes of this control
|
||
// these attributes are private for this control and are for internal use only
|
||
// and will not be rendered by the _render_attributes() method
|
||
$this->private_attributes = array(
|
||
|
||
'locked',
|
||
'disable_xss_filters',
|
||
'disable_zebra_datepicker',
|
||
'date',
|
||
'always_visible',
|
||
'container',
|
||
'custom_classes',
|
||
'days',
|
||
'days_abbr',
|
||
'default_position',
|
||
'direction',
|
||
'disabled_dates',
|
||
'enabled_dates',
|
||
'first_day_of_week',
|
||
'format',
|
||
'header_captions',
|
||
'header_navigation',
|
||
'icon_position',
|
||
'inside_icon',
|
||
'lang_clear_date',
|
||
'months',
|
||
'months_abbr',
|
||
'offset',
|
||
'open_icon_only',
|
||
'pair',
|
||
'readonly_element',
|
||
'show_clear_date',
|
||
'show_icon',
|
||
'show_other_months',
|
||
'show_select_today',
|
||
'show_week_number',
|
||
'select_other_months',
|
||
'start_date',
|
||
'strict',
|
||
'view',
|
||
'weekend_days',
|
||
'zero_pad',
|
||
|
||
);
|
||
|
||
// set the javascript attributes of this control
|
||
// these attributes will be used by the JavaScript date picker object
|
||
$this->javascript_attributes = array(
|
||
|
||
'always_visible',
|
||
'container',
|
||
'custom_classes',
|
||
'days',
|
||
'days_abbr',
|
||
'default_position',
|
||
'direction',
|
||
'disabled_dates',
|
||
'enabled_dates',
|
||
'first_day_of_week',
|
||
'format',
|
||
'header_captions',
|
||
'header_navigation',
|
||
'icon_position',
|
||
'inside_icon',
|
||
'lang_clear_date',
|
||
'months',
|
||
'months_abbr',
|
||
'offset',
|
||
'pair',
|
||
'readonly_element',
|
||
'show_clear_date',
|
||
'show_icon',
|
||
'show_other_months',
|
||
'show_select_today',
|
||
'show_week_number',
|
||
'select_other_months',
|
||
'start_date',
|
||
'strict',
|
||
'view',
|
||
'weekend_days',
|
||
'zero_pad',
|
||
|
||
);
|
||
|
||
// set the default attributes for the text control
|
||
// put them in the order you'd like them rendered
|
||
$this->set_attributes(
|
||
|
||
array(
|
||
|
||
'type' => 'text',
|
||
'name' => $id,
|
||
'id' => $id,
|
||
'value' => $default,
|
||
'class' => 'control text date',
|
||
|
||
'always_visible' => null,
|
||
'days' => null,
|
||
'days_abbr' => null,
|
||
'direction' => null,
|
||
'disable_zebra_datepicker' => false,
|
||
'disabled_dates' => null,
|
||
'enabled_dates' => null,
|
||
'first_day_of_week' => null,
|
||
'format' => 'Y-m-d',
|
||
'header_captions' => null,
|
||
'header_navigation' => null,
|
||
'inside_icon' => null,
|
||
'months' => null,
|
||
'months_abbr' => null,
|
||
'offset' => null,
|
||
'pair' => null,
|
||
'readonly_element' => null,
|
||
'show_clear_date' => null,
|
||
'show_other_months' => null,
|
||
'show_select_today' => null,
|
||
'show_week_number' => null,
|
||
'select_other_months' => null,
|
||
'start_date' => null,
|
||
'strict' => null,
|
||
'view' => null,
|
||
'weekend_days' => null,
|
||
'zero_pad' => null,
|
||
|
||
)
|
||
|
||
);
|
||
|
||
// if "class" is amongst user specified attributes
|
||
if (is_array($attributes) && isset($attributes['class'])) {
|
||
|
||
// we need to set the "class" attribute like this, so it doesn't overwrite previous values
|
||
$this->set_attributes(array('class' => $attributes['class']), false);
|
||
|
||
// make sure we don't set it again below
|
||
unset($attributes['class']);
|
||
|
||
}
|
||
|
||
// sets user specified attributes for the control
|
||
$this->set_attributes($attributes);
|
||
|
||
}
|
||
|
||
/**
|
||
* Should the date picker be always visible?
|
||
*
|
||
* Setting this property to a jQuery element will result in the date picker being always visible, the indicated
|
||
* element acting as the date picker's container;
|
||
*
|
||
* Note that when this property is set to TRUE, the {@link always_show_clear()} will be automatically set to TRUE.
|
||
*
|
||
* <code>
|
||
* $date = $form->add('date', 'my_date');
|
||
*
|
||
* // an element having the ID "container"
|
||
* // will be the date picker's container
|
||
* $date->always_visible('$("#container")');
|
||
* </code>
|
||
*
|
||
* @param string $element A jQuery selector pointing to an existing element from the page to be used as the
|
||
* date picker's container.
|
||
*
|
||
* @return void
|
||
*/
|
||
function always_visible($element)
|
||
{
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('always_visible' => $element));
|
||
|
||
}
|
||
|
||
/**
|
||
* Use this method to instruct the library to open a date picker inside a specific element - useful when you want
|
||
* the date picker to open at a specific position.
|
||
*
|
||
* <code>
|
||
* $date = $form->add('date', 'my_date');
|
||
*
|
||
* // the date picker will open inside this element
|
||
* $date->container('$("#container")');
|
||
* </code>
|
||
*
|
||
* @param string $element A jQuery selector pointing to an existing element from the page to be used as the
|
||
* date picker's container.
|
||
*
|
||
* By default, all date pickers are placed at the end of the <body> element
|
||
*
|
||
* @since 2.9.8
|
||
*
|
||
* @return void
|
||
*/
|
||
function container($element)
|
||
{
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('container' => $element));
|
||
|
||
}
|
||
|
||
/**
|
||
* Dates that should have custom classes applied to them.
|
||
*
|
||
* <code>
|
||
* $date = $form->add('date', 'my_date');
|
||
*
|
||
* // apply "myclass1" custom class to the first day, of every month, of every year
|
||
* $date->custom_classes(array(
|
||
* 'myclass1' => array('01 * *'),
|
||
* ));
|
||
* </code>
|
||
*
|
||
* @param array $custom_classes An array in the form of
|
||
*
|
||
* <code>
|
||
* array(
|
||
* 'myclass1': array(dates_to_apply_myclass1_to),
|
||
* 'myclass2': array(dates_to_apply_myclass2_to),
|
||
* )
|
||
* </code>
|
||
*
|
||
* ...where "dates_to_apply_myclassx_to" is an array of dates in the same format as
|
||
* required for {@link disabled_dates} property.
|
||
*
|
||
* Custom classes will be applied <b>only</b> in the day picker view and not on
|
||
* month/year views! Also note that the class name will have the “_disabled” suffix
|
||
* added if the day the class is applied to is disabled.
|
||
*
|
||
* In order for the styles in your custom classes to be applied, make sure you are
|
||
* using the following syntax:
|
||
*
|
||
* <code>
|
||
* .Zebra_DatePicker .dp_daypicker td.myclass1 { .. }
|
||
* .Zebra_DatePicker .dp_daypicker td.myclass1_disabled { .. }
|
||
* </code>
|
||
*
|
||
* Default is FALSE, no custom classes
|
||
*
|
||
* @since 2.9.8
|
||
*
|
||
* @return void
|
||
*/
|
||
function custom_classes($custom_classes)
|
||
{
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('custom_classes' => $custom_classes));
|
||
|
||
}
|
||
|
||
/**
|
||
* The position of the date picker relative to the element it is attached to.
|
||
*
|
||
* Note that, regardless of this setting, the date picker's position will be automatically adjusted to fit in the
|
||
* view port, if needed.
|
||
*
|
||
* <i>This property will be ignored if {@link always_visible} or {@link container} properties are set</i>
|
||
*
|
||
* <code>
|
||
* $date = $form->add('date', 'my_date');
|
||
*
|
||
* // the date picker will open *below* the element is attached to
|
||
* $date->default_position('below');
|
||
* </code>
|
||
*
|
||
* @param string $position The position of the date picker relative to the element it is attached to.
|
||
*
|
||
* Possible values are "above" and "below".
|
||
*
|
||
* Default is "above"
|
||
*
|
||
* @since 2.9.8
|
||
*
|
||
* @return void
|
||
*/
|
||
function default_position($position)
|
||
{
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('default_position' => $position));
|
||
|
||
}
|
||
|
||
/**
|
||
* Direction of the calendar.
|
||
*
|
||
* <code>
|
||
* $obj = $form->add('date', 'mydate')
|
||
*
|
||
* // calendar starts tomorrow and seven days after that are selectable
|
||
* $obj->direction(array(1, 7));
|
||
*
|
||
* // calendar starts today and seven days after that are selectable
|
||
* $obj->direction(array(true, 7));
|
||
*
|
||
* // calendar starts on January 1st 2013 and has no ending date
|
||
* // (assuming "format" is YYYY-MM-DD)
|
||
* $obj->direction(array('2013-01-01', false));
|
||
*
|
||
* // calendar ends today and starts on January 1st 2012
|
||
* // assuming "format" is YYYY-MM-DD)
|
||
* $obj->direction(array(false, '2012-01-01'));
|
||
* </code>
|
||
*
|
||
* @param mixed $direction A positive or negative integer:
|
||
*
|
||
* - n (a positive integer) creates a future-only calendar beginning at n days
|
||
* after today;
|
||
*
|
||
* - -n (a negative integer) creates a past-only calendar ending at n days
|
||
* before today;
|
||
*
|
||
* - if n is 0, the calendar has no restrictions.
|
||
*
|
||
* Use boolean TRUE for a future-only calendar starting with today and use boolean
|
||
* FALSE for a past-only calendar ending today.
|
||
*
|
||
* You may also set this property to an array with two elements in the following
|
||
* combinations:
|
||
*
|
||
* - first item is boolean TRUE (calendar starts today), an integer > 0 (calendar
|
||
* starts n days after today), or a valid date given in the format defined by
|
||
* the "format" attribute (calendar starts at the specified date), and the second
|
||
* item is boolean FALSE (the calendar has no ending date), an integer > 0 (calendar
|
||
* ends n days after the starting date), or a valid date given in the format
|
||
* defined by the "format" attribute and which occurs after the starting date
|
||
* (calendar ends at the specified date)
|
||
*
|
||
* - first item is boolean FALSE (calendar ends today), an integer < 0 (calendar
|
||
* ends n days before today), or a valid date given in the format defined by the
|
||
* "format" attribute (calendar ends at the specified date), and the second item
|
||
* is an integer > 0 (calendar ends n days before the ending date), or a valid
|
||
* date given in the format defined by the "format" attribute and which occurs
|
||
* before the starting date (calendar starts at the specified date)
|
||
*
|
||
*
|
||
* Note that {@link disabled_dates()} will still apply!
|
||
*
|
||
* Default is 0 (no restrictions).
|
||
*
|
||
* @return void
|
||
*/
|
||
function direction($direction)
|
||
{
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('direction' => $direction));
|
||
|
||
}
|
||
|
||
/**
|
||
* Disables selection of specific dates or range of dates in the calendar.
|
||
*
|
||
* <code>
|
||
* $obj = $form->add('date', 'mydate')
|
||
*
|
||
* // disable January 1, 2012
|
||
* $obj->disabled_dates(array('1 1 2012'));
|
||
*
|
||
* // disable all days in January 2012
|
||
* $obj->disabled_dates(array('* 1 2012'));
|
||
*
|
||
* // disable January 1 through 10 in 2012
|
||
* $obj->disabled_dates(array('1-10 1 2012'));
|
||
*
|
||
* // disable January 1 and 10 in 2012
|
||
* $obj->disabled_dates(array('1,10 1 2012'));
|
||
*
|
||
* // disable 1 through 10, and then 20th, 22nd and 24th
|
||
* // of January through March for every year
|
||
* $obj->disabled_dates(array('1-10,20,22,24 1-3 *'));
|
||
*
|
||
* // disable all Saturdays and Sundays
|
||
* $obj->disabled_dates(array('* * * 0,6'));
|
||
*
|
||
* // disable 1st and 2nd of July 2012,
|
||
* // and all of August of 2012;
|
||
* $obj->disabled_dates(array('01 07 2012', '02 07 2012', '* 08 2012'));
|
||
* </code>
|
||
*
|
||
* @param array $disabled_dates An array of strings representing disabled dates. Values in the string have
|
||
* to be in the following format: "day month year weekday" where "weekday" is
|
||
* optional and can be 0-6 (Saturday to Sunday); The syntax is similar to
|
||
* cron's syntax: the values are separated by spaces and may contain * (asterisk)
|
||
* - (dash) and , (comma) delimiters.
|
||
*
|
||
* Default is FALSE, no disabled dates.
|
||
*
|
||
* @return void
|
||
*/
|
||
function disabled_dates($disabled_dates) {
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('disabled_dates' => $disabled_dates));
|
||
|
||
}
|
||
|
||
/**
|
||
* By default, Zebra_Form relies on {@link http://stefangabos.ro/jquery/zebra-datepicker/ Zebra_DatePicker} for
|
||
* {@link Zebra_Form_Date Date} controls. If you want to use a different date picker, you have to disable the
|
||
* built-in one by calling this method.
|
||
*
|
||
* <samp>Make sure the language used by the custom date picker is the same as the {@link language() language} of the
|
||
* library, or validation of the date will fail!
|
||
*
|
||
* Also, note that {@link format() format}, {@link direction() direction} and {@link disabled_dates() disabled dates}
|
||
* will still apply and will be taken into account when validating the date, but the other properties will be ignored
|
||
* as are specific to Zebra_DatePicker!</samp>
|
||
*
|
||
* @since 2.8.7
|
||
*
|
||
* @return void
|
||
*/
|
||
function disable_zebra_datepicker() {
|
||
|
||
$this->set_attributes(array('disable_zebra_datepicker' => true));
|
||
|
||
}
|
||
|
||
/**
|
||
* Enables selection of specific dates or range of dates in the calendar, after dates have been previously disabled
|
||
* via {@link disabled_dates()}.
|
||
*
|
||
* @param array $enabled_dates An array of enabled dates in the same format as required for as argument for
|
||
* the {@link disabled_dates()} method. To be used together with
|
||
* {@link disabled_dates()} by first setting "disabled_dates" to something like
|
||
* array('* * * *') (which will disable everything) and then setting "enabled_dates"
|
||
* to, say, array('* * * 0,6') to enable just weekends.
|
||
*
|
||
* Default is FALSE, all dates are enabled (unless, specificaly disabled via
|
||
* {@link disabled_dates()}).
|
||
*
|
||
* @since 2.9.3
|
||
*
|
||
* @return void
|
||
*/
|
||
function enabled_dates($enabled_dates) {
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('enabled_dates' => $enabled_dates));
|
||
|
||
}
|
||
|
||
/**
|
||
* Week's starting day.
|
||
*
|
||
* @param integer $day Valid values are 0 to 6, Sunday to Saturday.
|
||
*
|
||
* Default is 1, Monday.
|
||
*
|
||
* @return void
|
||
*/
|
||
function first_day_of_week($day)
|
||
{
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('first_day_of_week' => $day));
|
||
|
||
}
|
||
|
||
/**
|
||
* Sets the format of the returned date.
|
||
*
|
||
* @param string $format Format of the returned date.
|
||
*
|
||
* Accepts the following characters for date formatting: d, D, j, l, N, w, S, F, m, M,
|
||
* n, Y, y borrowing syntax from ({@link http://www.php.net/manual/en/function.date.php
|
||
* PHP's date function})
|
||
*
|
||
* Note that when setting a date format without days (‘d’, ‘j’), the users will be able
|
||
* to select only years and months, and when setting a format without months and days
|
||
* (‘F’, ‘m’, ‘M’, ‘n’, ‘t’, ‘d’, ‘j’), the users will be able to select only years.
|
||
*
|
||
* Also note that the value of the "view" property (see below) may be overridden if it
|
||
* is the case: a value of "days" for the "view" property makes no sense if the date
|
||
* format doesn’t allow the selection of days.
|
||
*
|
||
* Default format is <b>Y-m-d</b>
|
||
*
|
||
* @return void
|
||
*/
|
||
function format($format) {
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('format' => $format));
|
||
|
||
}
|
||
|
||
/**
|
||
* <b>To be used after the form is submitted!</b>
|
||
*
|
||
* Returns submitted date in the YYYY-MM-DD format so that it's directly usable with a database engine or with
|
||
* PHP's {@link http://php.net/manual/en/function.strtotime.php strtotime} function.
|
||
*
|
||
* @return string Returns submitted date in the YYYY-MM-DD format, or <b>an empty string</b> if control was
|
||
* submitted with no value (empty).
|
||
*/
|
||
function get_date()
|
||
{
|
||
|
||
$result = $this->get_attributes('date');
|
||
|
||
// if control had a value return it, or return an empty string otherwise
|
||
return (isset($result['date'])) ? $result['date'] : '';
|
||
|
||
}
|
||
|
||
/**
|
||
* Captions in the datepicker's header, for the 3 possible views: days, months, years.
|
||
*
|
||
* For each of the 3 views the following special characters may be used borrowing from PHP's "date" function's
|
||
* syntax: m, n, F, M, y and Y; any of these will be replaced at runtime with the appropriate date fragment, depending
|
||
* on the currently viewed date. two more special characters are also available Y1 and Y2 (upper case representing
|
||
* years with 4 digits, lowercase representing years with 2 digits) which represent "currently selected year - 7"
|
||
* and "currently selected year + 4" and which only make sense used in the "years" view.
|
||
*
|
||
* Even though any of these special characters may be used in any of the 3 views, you should use m, n, F, M for the
|
||
* "days" view and y, Y, Y1, Y2, y1, y2 for the "months" and "years" view or you may get unexpected results!
|
||
*
|
||
* Text and HTML can also be used, and will be rendered as it is, as in the example below (the library is smart
|
||
* enough to not replace special characters when used in words or HTML tags):
|
||
*
|
||
* <code>
|
||
* header_captions(array(
|
||
* 'days' => 'Departure:<br>F, Y',
|
||
* 'months' => 'Departure:<br>Y',
|
||
* 'years' => 'Departure:<br>Y1 - Y2'
|
||
* ));
|
||
* </code>
|
||
*
|
||
* Default is
|
||
*
|
||
* <code>
|
||
* header_captions(array(
|
||
* 'days' => 'F, Y',
|
||
* 'months' => 'Y',
|
||
* 'years' => 'Y1 - Y2'
|
||
* ));
|
||
* </code>
|
||
*
|
||
* @param $captions An associative array containing captions in the datepicker's header, for the 3 possible
|
||
* views: days, months, years.
|
||
*
|
||
* @return void
|
||
*/
|
||
function header_captions($captions)
|
||
{
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('header_captions' => $captions));
|
||
|
||
}
|
||
|
||
/**
|
||
* Sets the HTML to be used for the previous month/next month buttons.
|
||
*
|
||
* @param $navigation An array with 2 elements containing the HTML to be used for the previous month/next month
|
||
* buttons.
|
||
*
|
||
* Default is array('«','»')
|
||
*
|
||
* @return void
|
||
*/
|
||
function header_navigation($navigation)
|
||
{
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('header_navigation' => $navigation));
|
||
|
||
}
|
||
|
||
/**
|
||
* The position of the date picker's inside the element it is attached to.
|
||
*
|
||
* <code>
|
||
* $date = $form->add('date', 'my_date');
|
||
*
|
||
* // position the date picker's icon to the left
|
||
* $date->icon_position('left');
|
||
* </code>
|
||
*
|
||
* @param string $position The position of the date picker's inside the element it is attached to.
|
||
*
|
||
* Possible values are "left" and "right".
|
||
*
|
||
* Default is "right"
|
||
*
|
||
* @since 2.9.8
|
||
*
|
||
* @return void
|
||
*/
|
||
function icon_position($position)
|
||
{
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('icon_position' => $position));
|
||
|
||
}
|
||
|
||
/**
|
||
* Sets whether the icon for opening the datepicker should be inside or outside the element.
|
||
*
|
||
* @param boolean $value If set to FALSE, the icon will be placed to the right of the parent element, while
|
||
* if set to TRUE it will be placed to the right of the parent element, but *inside* the
|
||
* element itself.
|
||
*
|
||
* Default is TRUE.
|
||
*
|
||
* @return void
|
||
*/
|
||
function inside($value) {
|
||
|
||
// set the date picker's attribute
|
||
// ("inside" is a "reserved" attribute so we'll pick something else)
|
||
$this->set_attributes(array('inside_icon' => $value));
|
||
|
||
}
|
||
|
||
/**
|
||
* Sets the offset, in pixels (x, y), to shift the date picker’s position relative to the top-left of the icon that
|
||
* toggles the date picker.
|
||
*
|
||
* @param array $value An array indicating the offset, in pixels (x, y), to shift the date picker’s position
|
||
* relative to the top-left of the icon that toggles the date picker.
|
||
*
|
||
* Default is array(5, -5).
|
||
*
|
||
* @return void
|
||
*/
|
||
function offset($value) {
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('offset' => $value));
|
||
|
||
}
|
||
|
||
/**
|
||
* Sets whether the date picker should be shown *only* when clicking the icon.
|
||
*
|
||
* @param array $value An array indicating the offset, in pixels (x, y), to shift the date picker’s position
|
||
* relative to the top-left of the icon that toggles the date picker.
|
||
*
|
||
* Default is FALSE.
|
||
*
|
||
* @return void
|
||
*/
|
||
function open_icon_only($value) {
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('open_icon_only' => $value));
|
||
|
||
}
|
||
|
||
/**
|
||
* Pairs the date element with another date element from the page, so that the other date element will use the current
|
||
* date element’s value as starting date.
|
||
*
|
||
* <code>
|
||
* // let's assume this will be the starting date
|
||
* $date1 = $form->add('date', 'starting_date');
|
||
*
|
||
* // dates are selectable in the future, starting with today
|
||
* $date1->direction(true);
|
||
*
|
||
* // indicate another date element that will use this
|
||
* // element's value as starting date
|
||
* $date1->pair('ending_date');
|
||
*
|
||
* // the other date element
|
||
* $date2 = $form->add('date', 'ending_date');
|
||
*
|
||
* // start one day after the reference date
|
||
* // (that is, one day after whaterver is selected in the first element)
|
||
* $date2->direction(1);
|
||
* </code>
|
||
*
|
||
* @param string $value The ID of another "date" element which will use the current date element's value as
|
||
* starting date.
|
||
*
|
||
* Note that the rules set in the "direction" property will still apply, only that the
|
||
* reference date will not be the current system date but the value selected in the
|
||
* current date picker.
|
||
*
|
||
* Default is FALSE (not paired with another date picker)
|
||
*
|
||
* @return void
|
||
*/
|
||
function pair($value) {
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('pair' => '$(\'#' . $value . '\')'));
|
||
|
||
}
|
||
|
||
/**
|
||
* Sets whether the element the calendar is attached to should be read-only.
|
||
*
|
||
* @param boolean $value The setting's value
|
||
*
|
||
* If set to TRUE, a date can be set only through the date picker and cannot be enetered
|
||
* manually.
|
||
*
|
||
* Default is TRUE.
|
||
*
|
||
* @return void
|
||
*/
|
||
function readonly_element($value) {
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('readonly_element' => $value));
|
||
|
||
}
|
||
|
||
/**
|
||
* Should days from previous and/or next month be selectable when visible?
|
||
*
|
||
* @param string $value The setting's value
|
||
*
|
||
* Note that if set to TRUE, the value of {@link show_other_months()} will be considered
|
||
* TRUE regardless of the actual value!
|
||
*
|
||
* Default is TRUE.
|
||
*
|
||
* @since 2.9.3
|
||
*
|
||
* @return void
|
||
*/
|
||
function select_other_months($value) {
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('select_other_months' => $value));
|
||
|
||
}
|
||
|
||
/**
|
||
* Should the "Clear date" button be visible?
|
||
*
|
||
* @param string $value The setting's value
|
||
*
|
||
* Accepted values are:
|
||
*
|
||
* - 0 (zero) – the button for clearing a previously selected date is shown only if a
|
||
* previously selected date already exists; this means that if the input the date
|
||
* picker is attached to is empty, and the user selects a date for the first time,
|
||
* this button will not be visible; once the user picked a date and opens the date
|
||
* picker again, this time the button will be visible.
|
||
*
|
||
* - TRUE will make the button visible all the time
|
||
*
|
||
* - FALSE will disable the button
|
||
*
|
||
* Default is "0" (without quotes)
|
||
*
|
||
* @return void
|
||
*/
|
||
function show_clear_date($value = 0)
|
||
{
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('show_clear_date' => $value));
|
||
|
||
}
|
||
|
||
/**
|
||
* Should a calendar icon be added to the elements the plugin is attached to?
|
||
*
|
||
* <code>
|
||
* $date = $form->add('date', 'my_date');
|
||
*
|
||
* // do not show the icon
|
||
* $date->show_icon(false);
|
||
* </code>
|
||
*
|
||
* @param boolean $visible When set to TRUE the plugin will attach a calendar icon to the elements the plugin
|
||
* is attached to.
|
||
*
|
||
* Default is TRUE
|
||
*
|
||
* @since 2.9.8
|
||
*
|
||
* @return void
|
||
*/
|
||
function show_icon($visible)
|
||
{
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('show_icon' => $visible));
|
||
|
||
}
|
||
|
||
/**
|
||
* Should days from previous and/or next month be visible?
|
||
*
|
||
* @param string $value The setting's value
|
||
*
|
||
* Default is TRUE.
|
||
*
|
||
* @since 2.9.3
|
||
*
|
||
* @return void
|
||
*/
|
||
function show_other_months($value = true) {
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('show_other_months' => $value));
|
||
|
||
}
|
||
|
||
/**
|
||
* Should the "Today" button be visible?
|
||
*
|
||
* @param string $value The setting's value
|
||
*
|
||
* Setting this property to anything but a boolean FALSE will enable the button and
|
||
* will use the property's value as caption for the button; setting it to FALSE will
|
||
* disable the button.
|
||
*
|
||
* Default is "Today"
|
||
*
|
||
* @since 2.9.4
|
||
*
|
||
* @return void
|
||
*/
|
||
function show_select_today($value = 'Today')
|
||
{
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('show_select_today' => $value));
|
||
|
||
}
|
||
|
||
/**
|
||
* Sets whether an extra column should be shown, showing the number of each week.
|
||
*
|
||
* @param string $value Anything other than FALSE will enable this feature, and use the given value as column
|
||
* title. For example, show_week_number: ‘Wk’ would enable this feature and have "Wk" as
|
||
* the column’s title.
|
||
*
|
||
* Default is FALSE.
|
||
*
|
||
* @return void
|
||
*/
|
||
function show_week_number($value) {
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('show_week_number' => $value));
|
||
|
||
}
|
||
|
||
/**
|
||
* Sets a default date to start the date picker with.
|
||
*
|
||
* @param date $value A default date to start the date picker with,
|
||
*
|
||
* Must be specified in the format defined by the "format" property, or it will be
|
||
* ignored!
|
||
*
|
||
* Note that this value is used only if there is no value in the field the date picker
|
||
* is attached to!
|
||
*
|
||
* Default is FALSE.
|
||
*
|
||
* @return void
|
||
*/
|
||
function start_date($value) {
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('start_date' => $value));
|
||
|
||
}
|
||
|
||
/**
|
||
* Sets whether default values, in the input field the date picker is attached to, be deleted if they are not valid
|
||
* according to {@link direction() direction} and/or {@link disabled_dates() disabled_dates}.
|
||
*
|
||
* @param boolean $value If set to TRUE, default values, in the input field the date picker is attached to,
|
||
* will be deleted if they are not valid according to {@link direction() direction}
|
||
* and/or {@link disabled_dates() disabled_dates}.
|
||
*
|
||
* Default is FALSE.
|
||
*
|
||
* @return void
|
||
*/
|
||
function strict($value) {
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('strict' => $value));
|
||
|
||
}
|
||
|
||
/**
|
||
* Sets how should the date picker start.
|
||
*
|
||
* @param string $view How should the date picker start.
|
||
*
|
||
* Valid values are "days", "months" and "years".
|
||
*
|
||
* Note that the date picker is always cycling days-months-years when clicking in the
|
||
* date picker's header, and years-months-days when selecting dates (unless one or more
|
||
* of the views are missing due to the date's format)
|
||
*
|
||
* Also note that the value of the "view" property may be overridden if the date's format
|
||
* requires so! (i.e. "days" for the "view" property makes no sense if the date format
|
||
* doesn't allow the selection of days)
|
||
*
|
||
* Default is "days".
|
||
*
|
||
* @return void
|
||
*/
|
||
function view($view) {
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('view' => $view));
|
||
|
||
}
|
||
|
||
/**
|
||
* Sets the days of the week that are to be considered as "weekend days".
|
||
*
|
||
* @param array $days An array of days of the week that are to be considered as "weekend days".
|
||
*
|
||
* Valid values are 0 to 6 (Sunday to Saturday).
|
||
*
|
||
* Default is array(0,6) (Saturday and Sunday).
|
||
*
|
||
* @return void
|
||
*/
|
||
function weekend_days($days) {
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('weekend_days' => $days));
|
||
|
||
}
|
||
|
||
/**
|
||
* Should day numbers < 10 be padded with zero?
|
||
*
|
||
* @param boolean $state When set to TRUE, day numbers < 10 will be prefixed with 0.
|
||
*
|
||
* Default is FALSE.
|
||
*
|
||
* @return void
|
||
*/
|
||
function zero_pad($state) {
|
||
|
||
// set the date picker's attribute
|
||
$this->set_attributes(array('zero_pad' => $state));
|
||
|
||
}
|
||
|
||
/**
|
||
* Generates the control's HTML code.
|
||
*
|
||
* <i>This method is automatically called by the {@link Zebra_Form::render() render()} method!</i>
|
||
*
|
||
* @return string The control's HTML code
|
||
*/
|
||
function toHTML()
|
||
{
|
||
|
||
// all date controls must have the "date" rule set or we trigger an error
|
||
if (!isset($this->rules['date'])) _zebra_form_show_error('The control named <strong>"' . $this->attributes['name'] . '"</strong> in form <strong>"' . $this->form_properties['name'] . '"</strong> must have the <em>"date"</em> rule set', E_USER_ERROR);
|
||
|
||
return '
|
||
<div>
|
||
<input ' . $this->_render_attributes() . ($this->form_properties['doctype'] == 'xhtml' ? '/' : '') . '>
|
||
<div class="clear"></div>
|
||
</div>
|
||
';
|
||
|
||
}
|
||
|
||
/**
|
||
* Initializes the datepicker's data so calculations for disabled dates can be done.
|
||
*
|
||
* Returns an array with two values: the first and the last selectable dates, as UNIX timestamps.
|
||
*
|
||
* If a value does not apply (i.e. no starting or no ending date), the value will be "0".
|
||
*
|
||
* @return array Returns an array with two values: the first and the last selectable dates,
|
||
* as UNIX timestamps.
|
||
*
|
||
* @access private
|
||
*/
|
||
function _init()
|
||
{
|
||
|
||
// do these calculations only once
|
||
if (!isset($this->limits)) {
|
||
|
||
// get current system date
|
||
$system_date = time();
|
||
|
||
// check if the calendar has any restrictions
|
||
|
||
// calendar is future-only, starting today
|
||
// it means we have a starting date (the current system date), but no ending date
|
||
if ($this->attributes['direction'] === true) $this->first_selectable_date = $system_date;
|
||
|
||
// calendar is past only, ending today
|
||
// it means we have an ending date (the reference date), but no starting date
|
||
else if ($this->attributes['direction'] === false) $this->last_selectable_date = $system_date;
|
||
|
||
else if (
|
||
|
||
// if direction is not given as an array and the value is an integer > 0
|
||
(!is_array($this->attributes['direction']) && (int)($this->attributes['direction']) > 0) ||
|
||
|
||
// or direction is given as an array
|
||
(is_array($this->attributes['direction']) && (
|
||
|
||
// and first entry is boolean TRUE
|
||
$this->attributes['direction'][0] === true ||
|
||
// or a valid date
|
||
($tmp_start_date = $this->_is_format_valid($this->attributes['direction'][0])) ||
|
||
// or an integer > 0
|
||
(is_numeric($this->attributes['direction'][0]) && $this->attributes['direction'][0] > 0)
|
||
|
||
) && (
|
||
|
||
// and second entry is boolean FALSE
|
||
$this->attributes['direction'][1] === false ||
|
||
// or a valid date
|
||
($tmp_end_date = $this->_is_format_valid($this->attributes['direction'][1])) ||
|
||
// or integer >= 0
|
||
(is_numeric($this->attributes['direction'][1]) && $this->attributes['direction'][1] >= 0)
|
||
|
||
))
|
||
|
||
) {
|
||
|
||
// if an exact starting date was given, use that as a starting date
|
||
if (isset($tmp_start_date)) $this->first_selectable_date = $tmp_start_date;
|
||
|
||
// otherwise
|
||
else
|
||
|
||
// figure out the starting date
|
||
$this->first_selectable_date = strtotime('+' . (!is_array($this->attributes['direction']) ? (int)($this->attributes['direction']) : (int)($this->attributes['direction'][0] === true ? 0 : $this->attributes['direction'][0])) . ' day', $system_date);
|
||
|
||
// if an exact ending date was given and the date is after the starting date, use that as a ending date
|
||
if (isset($tmp_end_date) && $tmp_end_date >= $this->first_selectable_date) $this->last_selectable_date = $tmp_end_date;
|
||
|
||
// if have information about the ending date
|
||
else if (!isset($tmp_end_date) && $this->attributes['direction'][1] !== false && is_array($this->attributes['direction']))
|
||
|
||
// figure out the ending date
|
||
$this->last_selectable_date = strtotime('+' . (int)($this->attributes['direction'][1]) . ' day', $system_date);
|
||
|
||
} else if (
|
||
|
||
// if direction is not given as an array and the value is an integer < 0
|
||
(!is_array($this->attributes['direction']) && is_numeric($this->attributes['direction']) && $this->attributes['direction'] < 0) ||
|
||
|
||
// or direction is given as an array
|
||
(is_array($this->attributes['direction']) && (
|
||
|
||
// and first entry is boolean FALSE
|
||
$this->attributes['direction'][0] === false ||
|
||
// or an integer < 0
|
||
(is_numeric($this->attributes['direction'][0]) && $this->attributes['direction'][0] < 0)
|
||
|
||
) && (
|
||
|
||
// and second entry is integer >= 0
|
||
(is_numeric($this->attributes['direction'][1]) && $this->attributes['direction'][1] >= 0) ||
|
||
// or a valid date
|
||
($tmp_start_date = $this->_is_format_valid($this->attributes['direction'][1]))
|
||
|
||
))
|
||
|
||
) {
|
||
|
||
// figure out the ending date
|
||
$this->last_selectable_date = strtotime('+' . (!is_array($this->attributes['direction']) ? (int)($this->attributes['direction']) : (int)($this->attributes['direction'][0] === false ? 0 : $this->attributes['direction'][0])) . ' day', $system_date);
|
||
|
||
// if an exact starting date was given, and the date is before the ending date, use that as a starting date
|
||
if (isset($tmp_start_date) && $tmp_start_date < $this->last_selectable_date) $this->first_selectable_date = $tmp_start_date;
|
||
|
||
// if have information about the starting date
|
||
else if (!isset($tmp_start_date) && is_array($this->attributes['direction']))
|
||
|
||
// figure out the staring date
|
||
$this->first_selectable_date = strtotime('-' . (int)($this->attributes['direction'][1]) . ' day');
|
||
|
||
}
|
||
|
||
// if a first selectable date exists
|
||
if (isset($this->first_selectable_date)) {
|
||
|
||
// extract the date parts
|
||
$first_selectable_year = date('Y', $this->first_selectable_date);
|
||
$first_selectable_month = date('m', $this->first_selectable_date);
|
||
$first_selectable_day = date('d', $this->first_selectable_date);
|
||
|
||
}
|
||
|
||
// if a last selectable date exists
|
||
if (isset($this->last_selectable_date)) {
|
||
|
||
// extract the date parts
|
||
$last_selectable_year = date('Y', $this->last_selectable_date);
|
||
$last_selectable_month = date('m', $this->last_selectable_date);
|
||
$last_selectable_day = date('d', $this->last_selectable_date);
|
||
|
||
}
|
||
|
||
// if a first selectable date exists but is disabled, find the actual first selectable date
|
||
if (isset($this->first_selectable_date) && $this->_is_disabled($first_selectable_year, $first_selectable_month, $first_selectable_day)) {
|
||
|
||
// loop until we find the first selectable year
|
||
while ($this->_is_disabled($first_selectable_year)) {
|
||
|
||
// if calendar is past-only, decrement the year
|
||
if ($this->first_selectable_date < 0 || $this->first_selectable_date === false) $first_selectable_year--;
|
||
|
||
// otherwise, increment the year
|
||
else $first_selectable_year++;
|
||
|
||
// because we've changed years, reset the month to January
|
||
$first_selectable_month = 1;
|
||
|
||
}
|
||
|
||
// loop until we find the first selectable month
|
||
while ($this->_is_disabled($first_selectable_year, $first_selectable_month)) {
|
||
|
||
// if calendar is past-only, decrement the month
|
||
if ($this->first_selectable_date < 0 || $this->first_selectable_date === false) $first_selectable_month--;
|
||
|
||
// otherwise, increment the month
|
||
else $first_selectable_month++;
|
||
|
||
// if we moved to a following year
|
||
if ($first_selectable_month > 12) {
|
||
|
||
// increment the year
|
||
$first_selectable_year++;
|
||
|
||
// reset the month to January
|
||
$first_selectable_month = 1;
|
||
|
||
// if we moved to a previous year
|
||
} else if ($first_selectable_month < 1) {
|
||
|
||
// decrement the year
|
||
$first_selectable_year--;
|
||
|
||
// reset the month to January
|
||
$first_selectable_month = 1;
|
||
|
||
}
|
||
|
||
// because we've changed months, reset the day to the first day of the month
|
||
$first_selectable_day = 1;
|
||
|
||
}
|
||
|
||
// loop until we find the first selectable day
|
||
while ($this->_is_disabled($first_selectable_year, $first_selectable_month, $first_selectable_day))
|
||
|
||
// if calendar is past-only, decrement the day
|
||
if ($this->first_selectable_date < 0 || $this->first_selectable_date === false) $first_selectable_day--;
|
||
|
||
// otherwise, increment the day
|
||
else $first_selectable_day++;
|
||
|
||
// use mktime() to normalize the date
|
||
// for example, 2011 05 33 will be transformed to 2011 06 02
|
||
$this->first_selectable_date = mktime(12, 0, 0, $first_selectable_month, $first_selectable_day, $first_selectable_year);
|
||
|
||
// re-extract date parts from the normalized date
|
||
// as we use them in the current loop
|
||
// extract the date parts
|
||
$first_selectable_year = date('Y', $this->first_selectable_date);
|
||
$first_selectable_month = date('m', $this->first_selectable_date);
|
||
$first_selectable_day = date('d', $this->first_selectable_date);
|
||
|
||
}
|
||
|
||
// save first and last selectable dates, as UNIX timestamps (or "0" if does not apply)
|
||
$this->limits = array(isset($this->first_selectable_date) ? $this->first_selectable_date : 0, isset($this->last_selectable_date) ? $this->last_selectable_date : 0);
|
||
|
||
}
|
||
|
||
// return first and last selectable dates, as UNIX timestamps (or "0" if does not apply)
|
||
return $this->limits;
|
||
|
||
}
|
||
|
||
/**
|
||
* Checks if the enetered value is a valid date in the right format.
|
||
*
|
||
* @return mixed Returns the UNIX timestamp of the checked date, if the date has the correct format,
|
||
* or FALSE otherwise.
|
||
*
|
||
* @access private
|
||
*/
|
||
function _is_format_valid($date)
|
||
{
|
||
|
||
// the format we expect the date to be
|
||
// escape characters that would make sense as regular expression
|
||
$format = preg_quote($this->attributes['format']);
|
||
|
||
// parse the format and extract the characters that define the format
|
||
// (note that we're also capturing the offsets)
|
||
preg_match_all('/[dDjlNSwFmMnYyGHghaAisU]{1}/', $format, $matches, PREG_OFFSET_CAPTURE);
|
||
|
||
$regexp = array();
|
||
|
||
// iterate through the found characters
|
||
// and create the regular expression that we will use to see if the entered date is ok
|
||
foreach ($matches[0] as $match) {
|
||
|
||
switch ($match[0]) {
|
||
|
||
// day of the month, 2 digits with leading zeros, 01 to 31
|
||
case 'd': $regexp[] = '0[1-9]|[12][0-9]|3[01]'; break;
|
||
|
||
// a textual representation of a day, three letters, mon through sun
|
||
case 'D': $regexp[] = '[a-z]{3}'; break;
|
||
|
||
// day of the month without leading zeros, 1 to 31
|
||
case 'j': $regexp[] = '[1-9]|[12][0-9]|3[01]'; break;
|
||
|
||
// a full textual representation of the day of the week, sunday through saturday
|
||
case 'l': $regexp[] = '[a-z]+'; break;
|
||
|
||
// ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0), 1 (for Monday) through 7 (for Sunday)
|
||
case 'N': $regexp[] = '[1-7]'; break;
|
||
|
||
// english ordinal suffix for the day of the month, 2 characters: st, nd, rd or th. works well with j
|
||
case 'S': $regexp[] = 'st|nd|rd|th'; break;
|
||
|
||
// numeric representation of the day of the week, 0 (for sunday) through 6 (for saturday)
|
||
case 'w': $regexp[] = '[0-6]'; break;
|
||
|
||
// a full textual representation of a month, such as january or march
|
||
case 'F': $regexp[] = '[a-z]+'; break;
|
||
|
||
// numeric representation of a month, with leading zeros, 01 through 12
|
||
case 'm': $regexp[] = '0[1-9]|1[012]+'; break;
|
||
|
||
// a short textual representation of a month, three letters, jan through dec
|
||
case 'M': $regexp[] = '[a-z]{3}'; break;
|
||
|
||
// numeric representation of a month, without leading zeros, 1 through 12
|
||
case 'n': $regexp[] = '[1-9]|1[012]'; break;
|
||
|
||
// a full numeric representation of a year, 4 digits examples: 1999 or 2003
|
||
case 'Y': $regexp[] = '[0-9]{4}'; break;
|
||
|
||
// a two digit representation of a year examples: 99 or 03
|
||
case 'y': $regexp[] = '[0-9]{2}'; break;
|
||
|
||
// 24-hour format of an hour without leading zeros, 0 through 23
|
||
case 'G': $regexp[] = '[0-9]|1[0-9]|2[0-3]'; break;
|
||
|
||
// 24-hour format of an hour with leading zeros, 00 through 23
|
||
case 'H': $regexp[] = '0[0-9]|1[0-9]|2[0-3]'; break;
|
||
|
||
// 12-hour format of an hour without leading zeros, 1 through 12
|
||
case 'g': $regexp[] = '[0-9]|1[0-2]'; break;
|
||
|
||
// 12-hour format of an hour with leading zeros, 01 through 12
|
||
case 'h': $regexp[] = '0[0-9]|1[0-2]'; break;
|
||
|
||
// lowercase ante meridiem and post meridiem am or pm
|
||
case 'a':
|
||
case 'A': $regexp[] = '(am|pm)'; break;
|
||
|
||
// minutes with leading zeros, 00 to 59
|
||
case 'i': $regexp[] = '[0-5][0-9]'; break;
|
||
|
||
// seconds, with leading zeros 00 through 59
|
||
case 's': $regexp[] = '[0-5][0-9]'; break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
// if format is defined
|
||
if (!empty($regexp)) {
|
||
|
||
// we will replace every format-related character in the format expression with
|
||
// the appropriate regular expression in order to see that valid data was entered
|
||
// as required by the character
|
||
// we are replacing from finish to start so that we don't mess up the offsets
|
||
// therefore, we need to reverse the array first
|
||
$matches[0] = array_reverse($matches[0]);
|
||
|
||
// how many characters to replace
|
||
$chars = count($matches[0]);
|
||
|
||
// iterate through the characters
|
||
foreach ($matches[0] as $index => $char)
|
||
|
||
// and replace them with the appropriate regular expression
|
||
$format = substr_replace($format, '(' . $regexp[$chars - $index - 1] . ')', $matches[0][$index][1], 1);
|
||
|
||
// the final regular expression to math the date against
|
||
$format = '/^' . str_replace('/', '\/', $format) . '$/i';
|
||
|
||
// if entered value seems to be ok
|
||
if (preg_match($format, $date, $segments)) {
|
||
|
||
$original_day = $original_month = $original_year = 0;
|
||
$original_hour = $original_minute = $original_second = $original_format = -1;
|
||
|
||
// english names for days and months
|
||
$english_days = array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday');
|
||
$english_months = array('January','February','March','April','May','June','July','August','September','October','November','December');
|
||
|
||
// reverse the characters in the format (remember that we reversed them above)
|
||
$matches[0] = array_reverse($matches[0]);
|
||
|
||
$valid = true;
|
||
|
||
// iterate through the characters in the format
|
||
// to see if years, months, days, hours, minutes and seconds are correct
|
||
// i.e. if for month we entered "abc" it would pass our regular expression but
|
||
// now we will check if the three letter text is an actual month
|
||
foreach ($matches[0] as $index => $match) {
|
||
|
||
switch ($match[0]) {
|
||
|
||
// numeric representation of a month, with leading zeros, 01 through 12
|
||
case 'm':
|
||
// numeric representation of a month, without leading zeros, 1 through 12
|
||
case 'n':
|
||
|
||
$original_month = (int)($segments[$index + 1] - 1);
|
||
|
||
break;
|
||
|
||
// day of the month, 2 digits with leading zeros, 01 to 31
|
||
case 'd':
|
||
// day of the month without leading zeros, 1 to 31
|
||
case 'j':
|
||
|
||
$original_day = (int)($segments[$index + 1]);
|
||
|
||
break;
|
||
|
||
// a textual representation of a day, three letters, mon through sun
|
||
case 'D':
|
||
// a full textual representation of the day of the week, sunday through saturday
|
||
case 'l':
|
||
// a full textual representation of a month, such as january or march
|
||
case 'F':
|
||
// a short textual representation of a month, three letters, jan through dec
|
||
case 'M':
|
||
|
||
// by default, we assume that the text is invalid
|
||
$valid = false;
|
||
|
||
// iterate through the values in the language file
|
||
foreach ($this->form_properties['language'][($match[0] == 'F' || $match[0] == 'M' ? 'months' : 'days')] as $key => $value) {
|
||
|
||
// if value matches the value from the language file
|
||
if (strtolower($segments[$index + 1]) == strtolower(substr($value, 0, ($match[0] == 'D' || $match[0] == 'M' ? 3 : strlen($value))))) {
|
||
|
||
// replace with the english value
|
||
// this is because later on we'll run strtotime of the entered value and strtotime parses english dates
|
||
switch ($match[0]) {
|
||
case 'D': $segments[$index + 1] = substr($english_days[$key], 0, 3); break;
|
||
case 'l': $segments[$index + 1] = $english_days[$key]; break;
|
||
case 'F': $segments[$index + 1] = $english_months[$key]; $original_month = $key; break;
|
||
case 'M': $segments[$index + 1] = substr($english_months[$key], 0, 3); $original_month = $key; break;
|
||
}
|
||
|
||
// flag the value as valid
|
||
$valid = true;
|
||
|
||
// don't look further
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
// if an invalid was found don't look any further
|
||
if (!$valid) break 2;
|
||
|
||
break;
|
||
|
||
// a full numeric representation of a year, 4 digits examples: 1999 or 2003
|
||
case 'Y':
|
||
|
||
$original_year = (int)($segments[$index + 1]);
|
||
|
||
break;
|
||
|
||
// a two digit representation of a year examples: 99 or 03
|
||
case 'y':
|
||
|
||
$original_year = (int)('19' . $segments[$index + 1]);
|
||
|
||
break;
|
||
|
||
// 24-hour format of an hour without leading zeros, 0 through 23
|
||
case 'G':
|
||
// 24-hour format of an hour with leading zeros, 00 through 23
|
||
case 'H':
|
||
// 12-hour format of an hour without leading zeros, 1 through 12
|
||
case 'g':
|
||
// 12-hour format of an hour with leading zeros, 01 through 12
|
||
case 'h':
|
||
|
||
$original_hour = (int)($segments[$index + 1]);
|
||
|
||
break;
|
||
|
||
// lowercase ante meridiem and post meridiem am or pm
|
||
case 'a':
|
||
case 'A':
|
||
|
||
$original_format = $segments[$index + 1];
|
||
|
||
break;
|
||
|
||
// minutes with leading zeros, 00 to 59
|
||
case 'i':
|
||
|
||
$original_minute = (int)($segments[$index + 1]);
|
||
|
||
break;
|
||
|
||
// seconds, with leading zeros 00 through 59
|
||
case 's':
|
||
|
||
$original_second = (int)($segments[$index + 1]);
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
// if entered value seems valid
|
||
if ($valid) {
|
||
|
||
// if date format does not include day, make day = 1
|
||
if ($original_day == 0) $original_day = 1;
|
||
|
||
// if date format does not include month, make month = 0 (January)
|
||
if ($original_month == 0) $original_month = 0;
|
||
|
||
// if date format does not include year, use the current year
|
||
if ($original_year == 0) $original_year = date('Y');
|
||
|
||
// if date is still valid after we process it with strtotime
|
||
// (we do this because, so far, a date like "Feb 31 2010" would be valid
|
||
// but strtotime would turn that to "Mar 03 2010")
|
||
if (
|
||
|
||
$english_months[$original_month] . ' ' . str_pad($original_day, 2, '0', STR_PAD_LEFT) . ', ' . $original_year ==
|
||
date('F d, Y', strtotime($english_months[$original_month] . ' ' . $original_day . ', ' . $original_year))
|
||
|
||
) {
|
||
|
||
// make sure we also return the date as YYYY-MM-DD so that it can be
|
||
// easily used with a database or with PHP's strtotime function
|
||
$this->attributes['date'] = $original_year . '-' . str_pad($original_month + 1, 2, '0', STR_PAD_LEFT) . '-' . str_pad($original_day, 2, '0', STR_PAD_LEFT);
|
||
|
||
return strtotime($original_year . '-' . ($original_month + 1) . '-' . $original_day);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
// if script gets this far, return FALSE as something must've been wrong
|
||
return false;
|
||
|
||
}
|
||
|
||
/**
|
||
* Checks if, according to the restrictions of the calendar and/or the values defined by the "disabled_dates"
|
||
* property, a day, a month or a year needs to be disabled.
|
||
*
|
||
* @param integer $year The year to check
|
||
* @param integer $month The month to check
|
||
* @param integer $day The day to check
|
||
*
|
||
* @return boolean Returns TRUE if the given value is not disabled or FALSE otherwise
|
||
*
|
||
* @access private
|
||
*/
|
||
function _is_disabled($year, $month = '', $day = '')
|
||
{
|
||
|
||
// parse the rules for disabling dates and turn them into arrays of arrays
|
||
if (!isset($this->disabled_dates)) {
|
||
|
||
// array that will hold the rules for disabling dates
|
||
$this->disabled_dates = array();
|
||
|
||
// if disabled dates is an array and is not empty
|
||
if (is_array($this->attributes['disabled_dates']) && !empty($this->attributes['disabled_dates']))
|
||
|
||
// iterate through the rules for disabling dates
|
||
foreach ($this->attributes['disabled_dates'] as $value) {
|
||
|
||
// split the values in rule by white space
|
||
$rules = explode(' ', $value);
|
||
|
||
// there can be a maximum of 4 rules (days, months, years and, optionally, day of the week)
|
||
for ($i = 0; $i < 4; $i++) {
|
||
|
||
// if one of the values is not available
|
||
// replace it with a * (wildcard)
|
||
if (!isset($rules[$i])) $rules[$i] = '*';
|
||
|
||
// if rule contains a comma, create a new array by splitting the rule by commas
|
||
// if there are no commas create an array containing the rule's string
|
||
$rules[$i] = (strpos($rules[$i], ',') !== false ? explode(',', $rules[$i]) : (array)$rules[$i]);
|
||
|
||
// iterate through the items in the rule
|
||
for ($j = 0; $j < count($rules[$i]); $j++)
|
||
|
||
// if item contains a dash (defining a range)
|
||
if (strpos($rules[$i][$j], '-') !== false) {
|
||
|
||
// get the lower and upper limits of the range
|
||
// if range is valid
|
||
if (preg_match('/^([0-9]+)\-([0-9]+)/', $rules[$i][$j], $limits) > 0) {
|
||
|
||
// remove the range indicator
|
||
array_splice($rules[$i], $j, 1);
|
||
|
||
// iterate through the range
|
||
for ($k = $limits[1]; $k <= $limits[2]; $k++)
|
||
|
||
// if value is not already among the values of the rule
|
||
// add it to the rule
|
||
if (!in_array($k, $rules[$i])) $rules[$i][] = (int)$k;
|
||
|
||
}
|
||
|
||
// make sure to convert things like "01" to "1"
|
||
} elseif ($rules[$i][$j] != '*') $rules[$i][$j] = (int)$rules[$i][$j];
|
||
|
||
}
|
||
|
||
// add to the list of processed rules
|
||
$this->disabled_dates[] = $rules;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
// if calendar has direction restrictions
|
||
if (!(!is_array($this->attributes['direction']) && (int)($this->attributes['direction']) === 0)) {
|
||
|
||
// if a first selectable date exists
|
||
if (isset($this->first_selectable_date)) {
|
||
|
||
// extract the date parts
|
||
$first_selectable_year = date('Y', $this->first_selectable_date);
|
||
$first_selectable_month = date('m', $this->first_selectable_date);
|
||
$first_selectable_day = date('d', $this->first_selectable_date);
|
||
|
||
}
|
||
|
||
// if a last selectable date exists
|
||
if (isset($this->last_selectable_date)) {
|
||
|
||
// extract the date parts
|
||
$last_selectable_year = date('Y', $this->last_selectable_date);
|
||
$last_selectable_month = date('m', $this->last_selectable_date);
|
||
$last_selectable_day = date('d', $this->last_selectable_date);
|
||
|
||
}
|
||
|
||
// normalize and merge arguments then transform the result to an integer
|
||
$now = $year . ($month != '' ? str_pad($month, 2, '0', STR_PAD_LEFT) : '') . ($day != '' ? str_pad($day, 2, '0', STR_PAD_LEFT) : '');
|
||
|
||
// if we're checking days
|
||
if (strlen($now) == 8 && (
|
||
|
||
// day is before the first selectable date
|
||
(isset($this->first_selectable_date) && $now < $first_selectable_year . $first_selectable_month . $first_selectable_day) ||
|
||
|
||
// or day is after the last selectable date
|
||
(isset($this->last_selectable_date) && $now > $last_selectable_year . $last_selectable_month . $last_selectable_day)
|
||
|
||
// day needs to be disabled
|
||
)) return true;
|
||
|
||
// if we're checking months
|
||
else if (strlen($now) == 6 && (
|
||
|
||
// month is before the first selectable month
|
||
(isset($this->first_selectable_date) && $now < $first_selectable_year . $first_selectable_month) ||
|
||
|
||
// or month is after the last selectable month
|
||
(isset($this->last_selectable_date) && $now > $last_selectable_year . $last_selectable_month)
|
||
|
||
// month needs to be disabled
|
||
)) return true;
|
||
|
||
// if we're checking years
|
||
else if (strlen($now) == 4 && (
|
||
|
||
// year is before the first selectable year
|
||
(isset($this->first_selectable_date) && $now < $first_selectable_year) ||
|
||
|
||
// or year is after the last selectable year
|
||
(isset($this->last_selectable_date) && $now > $last_selectable_year)
|
||
|
||
// year needs to be disabled
|
||
)) return true;
|
||
|
||
}
|
||
|
||
// if there are rules for disabling dates
|
||
if (isset($this->disabled_dates)) {
|
||
|
||
// by default, we assume the day/month/year is not to be disabled
|
||
$disabled = false;
|
||
|
||
// iterate through the rules for disabling dates
|
||
foreach ($this->disabled_dates as $rule) {
|
||
|
||
// if the date is to be disabled, don't look any further
|
||
if ($disabled) return;
|
||
|
||
// if the rules apply for the current year
|
||
if (in_array($year, $rule[2]) || in_array('*', $rule[2], true))
|
||
|
||
// if the rules apply for the current month
|
||
if (($month != '' && in_array($month, $rule[1])) || in_array('*', $rule[1], true))
|
||
|
||
// if the rules apply for the current day
|
||
if (($day != '' && in_array($day, $rule[0])) || in_array('*', $rule[0], true)) {
|
||
|
||
|
||
// if day is to be disabled whatever the day
|
||
// don't look any further
|
||
if (in_array('*', $rule[3], true)) return ($disabled = true);
|
||
|
||
// get the weekday
|
||
$weekday = date('w', mktime(12, 0, 0, $month, $day, $year));
|
||
|
||
// if weekday is to be disabled
|
||
// don't look any further
|
||
if (in_array($weekday, $rule[3])) return ($disabled = true);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
// if the day/month/year needs to be disabled
|
||
if ($disabled) return true;
|
||
|
||
}
|
||
|
||
// if script gets this far it means that the day/month/year doesn't need to be disabled
|
||
return false;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
?>
|