* @copyright (c) 2006 - 2016 Stefan Gabos
* @package Controls
*/
class Zebra_Form_Date extends Zebra_Form_Control
{
/**
* Adds a date control to the form.
*
* Do not instantiate this class directly! Use the {@link Zebra_Form::add() add()} method instead!
*
* The output of this control will be a {@link Zebra_Form_Text textbox} control with an icon to the right of it.
* Clicking the icon will open an inline JavaScript date picker.
*
*
* // 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();
*
*
* @param string $id Unique name to identify the control in the form.
*
* The control's name attribute will be the same as the id 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.
*
*
* // in a template file, in order to print the generated HTML
* // for a control named "my_date", one would use:
* echo $my_date;
*
*
* @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 attribute => value.
*
* // setting the "readonly" attribute
* $obj = $form->add(
* 'date',
* 'my_date',
* '',
* array(
* 'readonly' => 'readonly'
* )
* );
*
*
* 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:
*
* type, id, name, value, class
*
* @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.
*
*
* $date = $form->add('date', 'my_date');
*
* // an element having the ID "container"
* // will be the date picker's container
* $date->always_visible('$("#container")');
*
*
* @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.
*
*
* $date = $form->add('date', 'my_date');
*
* // the date picker will open inside this element
* $date->container('$("#container")');
*
*
* @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
* $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 * *'),
* ));
*
*
* @param array $custom_classes An array in the form of
*
*
* array(
* 'myclass1': array(dates_to_apply_myclass1_to),
* 'myclass2': array(dates_to_apply_myclass2_to),
* )
*
*
* ...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 only 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:
*
*
* .Zebra_DatePicker .dp_daypicker td.myclass1 { .. }
* .Zebra_DatePicker .dp_daypicker td.myclass1_disabled { .. }
*
*
* 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.
*
* This property will be ignored if {@link always_visible} or {@link container} properties are set
*
*
* $date = $form->add('date', 'my_date');
*
* // the date picker will open *below* the element is attached to
* $date->default_position('below');
*
*
* @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.
*
*
* $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'));
*
*
* @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.
*
*
* $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'));
*
*
* @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.
*
* 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!
*
* @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 Y-m-d
*
* @return void
*/
function format($format) {
// set the date picker's attribute
$this->set_attributes(array('format' => $format));
}
/**
* To be used after the form is submitted!
*
* 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 an empty string 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):
*
*
* header_captions(array(
* 'days' => 'Departure:
F, Y',
* 'months' => 'Departure:
Y',
* 'years' => 'Departure:
Y1 - Y2'
* ));
*
*
* Default is
*
*
* header_captions(array(
* 'days' => 'F, Y',
* 'months' => 'Y',
* 'years' => 'Y1 - Y2'
* ));
*
*
* @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.
*
*
* $date = $form->add('date', 'my_date');
*
* // position the date picker's icon to the left
* $date->icon_position('left');
*
*
* @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.
*
*
* // 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);
*
*
* @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?
*
*
* $date = $form->add('date', 'my_date');
*
* // do not show the icon
* $date->show_icon(false);
*
*
* @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.
*
* This method is automatically called by the {@link Zebra_Form::render() render()} method!
*
* @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 "' . $this->attributes['name'] . '" in form "' . $this->form_properties['name'] . '" must have the "date" rule set', E_USER_ERROR);
return '