'
- . ' '
- . ' '
- . '
';
+ return JLayoutHelper::render('joomla.form.field.calendar', $data, null, null);
}
/**
diff --git a/libraries/joomla/form/fields/calendar.php b/libraries/joomla/form/fields/calendar.php
index 610ca29cc6044..31b8d37a3b6e7 100644
--- a/libraries/joomla/form/fields/calendar.php
+++ b/libraries/joomla/form/fields/calendar.php
@@ -51,6 +51,30 @@ class JFormFieldCalendar extends JFormField implements JFormDomfieldinterface
*/
protected $filter;
+ /**
+ * The minimum year number to subtract/add from the current year
+ *
+ * @var integer
+ * @since __DEPLOY_VERSION__
+ */
+ protected $minyear;
+
+ /**
+ * The maximum year number to subtract/add from the current year
+ *
+ * @var integer
+ * @since __DEPLOY_VERSION__
+ */
+ protected $maxyear;
+
+ /**
+ * Name of the layout being used to render the field
+ *
+ * @var string
+ * @since __DEPLOY_VERSION__
+ */
+ protected $layout = 'joomla.form.field.calendar';
+
/**
* Method to get certain otherwise inaccessible properties from the form field object.
*
@@ -67,6 +91,14 @@ public function __get($name)
case 'maxlength':
case 'format':
case 'filter':
+ case 'timeformat':
+ case 'todaybutton':
+ case 'singleheader':
+ case 'weeknumbers':
+ case 'showtime':
+ case 'filltable':
+ case 'minyear':
+ case 'maxyear':
return $this->$name;
}
@@ -88,9 +120,18 @@ public function __set($name, $value)
switch ($name)
{
case 'maxlength':
- $value = (int) $value;
+ case 'timeformat':
+ $this->$name = (int) $value;
+ break;
+ case 'todaybutton':
+ case 'singleheader':
+ case 'weeknumbers':
+ case 'showtime':
+ case 'filltable':
case 'format':
case 'filter':
+ case 'minyear':
+ case 'maxyear':
$this->$name = (string) $value;
break;
@@ -119,9 +160,17 @@ public function setup(SimpleXMLElement $element, $value, $group = null)
if ($return)
{
- $this->maxlength = (int) $this->element['maxlength'] ? (int) $this->element['maxlength'] : 45;
- $this->format = (string) $this->element['format'] ? (string) $this->element['format'] : '%Y-%m-%d';
- $this->filter = (string) $this->element['filter'] ? (string) $this->element['filter'] : 'USER_UTC';
+ $this->maxlength = (int) $this->element['maxlength'] ? (int) $this->element['maxlength'] : 45;
+ $this->format = (string) $this->element['format'] ? (string) $this->element['format'] : '%Y-%m-%d';
+ $this->filter = (string) $this->element['filter'] ? (string) $this->element['filter'] : 'USER_UTC';
+ $this->todaybutton = (string) $this->element['todaybutton'] ? (string) $this->element['todaybutton'] : "true";
+ $this->weeknumbers = (string) $this->element['weeknumbers'] ? (string) $this->element['weeknumbers'] : "false";
+ $this->showtime = (string) $this->element['showtime'] ? (string) $this->element['showtime'] : "true";
+ $this->filltable = (string) $this->element['filltable'] ? (string) $this->element['filltable'] : "true";
+ $this->timeformat = (int) $this->element['timeformat'] ? (int) $this->element['timeformat'] : 24;
+ $this->singleheader = (string) $this->element['singleheader'] ? (string) $this->element['singleheader'] : "false";
+ $this->minyear = (string) $this->element['minyear'] ? (string) $this->element['minyear'] : null;
+ $this->maxyear = (string) $this->element['maxyear'] ? (string) $this->element['maxyear'] : null;
}
return $return;
@@ -136,59 +185,45 @@ public function setup(SimpleXMLElement $element, $value, $group = null)
*/
protected function getInput()
{
- // Translate placeholder text
- $hint = $this->translateHint ? JText::_($this->hint) : $this->hint;
+ return $this->getRenderer($this->layout)->render($this->getLayoutData());
+ }
- // Initialize some field attributes.
- $translateFormat = (string) $this->element['translateformat'];
+ /**
+ * Method to get the data to be passed to the layout for rendering.
+ *
+ * @return array
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ protected function getLayoutData()
+ {
+ $data = parent::getLayoutData();
+ $tag = JFactory::getLanguage()->getTag();
+ $calendar = JFactory::getLanguage()->getCalendar();
+ $config = JFactory::getConfig();
+ $user = JFactory::getUser();
+ $direction = strtolower(JFactory::getDocument()->getDirection());
- if ($translateFormat && $translateFormat != 'false')
- {
- $showTime = (string) $this->element['showtime'];
-
- if ($showTime && $showTime != 'false')
- {
- $format = JText::_('DATE_FORMAT_CALENDAR_DATETIME');
- }
- else
- {
- $format = JText::_('DATE_FORMAT_CALENDAR_DATE');
- }
- }
- else
+ // Get the appropriate file for the current language date helper
+ $helperPath = 'system/fields/calendar-locales/date/gregorian/date-helper.min.js';
+
+ if (!empty($calendar) && is_dir(JPATH_ROOT . '/media/system/js/fields/calendar-locales/date/' . strtolower($calendar)))
{
- $format = $this->format;
+ $helperPath = 'system/fields/calendar-locales/date/' . strtolower($calendar) . '/date-helper.min.js';
}
- // Build the attributes array.
- $attributes = array();
-
- empty($this->size) ? null : $attributes['size'] = $this->size;
- empty($this->maxlength) ? null : $attributes['maxlength'] = $this->maxlength;
- empty($this->class) ? null : $attributes['class'] = $this->class;
- !$this->readonly ? null : $attributes['readonly'] = 'readonly';
- !$this->disabled ? null : $attributes['disabled'] = 'disabled';
- empty($this->onchange) ? null : $attributes['onchange'] = $this->onchange;
- !strlen($hint) ? null : $attributes['placeholder'] = $hint;
- $this->autocomplete ? null : $attributes['autocomplete'] = 'off';
- !$this->autofocus ? null : $attributes['autofocus'] = '';
+ // Get the appropriate locale file for the current language
+ $localesPath = 'system/fields/calendar-locales/en.js';
- if ($this->required)
+ if (is_file(JPATH_ROOT . '/media/system/js/fields/calendar-locales/' . strtolower($tag) . '.js'))
{
- $attributes['required'] = '';
- $attributes['aria-required'] = 'true';
+ $localesPath = 'system/fields/calendar-locales/' . strtolower($tag) . '.js';
}
-
- // Handle the special case for "now".
- if (strtoupper($this->value) == 'NOW')
+ elseif (is_file(JPATH_ROOT . '/media/system/js/fields/calendar-locales/' . strtolower(substr($tag, 0, -3)) . '.js'))
{
- $this->value = JFactory::getDate()->format('Y-m-d H:i:s');
+ $localesPath = 'system/fields/calendar-locales/' . strtolower(substr($tag, 0, -3)) . '.js';
}
- // Get some system objects.
- $config = JFactory::getConfig();
- $user = JFactory::getUser();
-
// If a known filter is given use it.
switch (strtoupper($this->filter))
{
@@ -203,29 +238,39 @@ protected function getInput()
// Transform the date string.
$this->value = $date->format('Y-m-d H:i:s', true, false);
}
-
break;
-
case 'USER_UTC':
// Convert a date to UTC based on the user timezone.
if ($this->value && $this->value != JFactory::getDbo()->getNullDate())
{
// Get a date object based on the correct timezone.
$date = JFactory::getDate($this->value, 'UTC');
-
$date->setTimezone(new DateTimeZone($user->getParam('timezone', $config->get('offset'))));
// Transform the date string.
$this->value = $date->format('Y-m-d H:i:s', true, false);
}
-
break;
}
- // Including fallback code for HTML5 non supported browsers.
- JHtml::_('jquery.framework');
- JHtml::_('script', 'system/html5fallback.js', array('version' => 'auto', 'relative' => true));
+ $extraData = array(
+ 'value' => $this->value,
+ 'maxLength' => $this->maxlength,
+ 'format' => $this->format,
+ 'filter' => $this->filter,
+ 'todaybutton' => ($this->todaybutton === "true") ? 1 : 0,
+ 'weeknumbers' => ($this->weeknumbers === "true") ? 1 : 0,
+ 'showtime' => ($this->showtime === "true") ? 1 : 0,
+ 'filltable' => ($this->filltable === "true") ? 1 : 0,
+ 'timeformat' => $this->timeformat,
+ 'singleheader' => ($this->singleheader === "true") ? 1 : 0,
+ 'helperPath' => $helperPath,
+ 'localesPath' => $localesPath,
+ 'minYear' => $this->minyear,
+ 'maxYear' => $this->maxyear,
+ 'direction' => $direction,
+ );
- return JHtml::_('calendar', $this->value, $this->name, $this->id, $format, $attributes);
+ return array_merge($data, $extraData);
}
}
diff --git a/libraries/joomla/language/language.php b/libraries/joomla/language/language.php
index 948594a2ff6dc..4d15f3a8004a0 100644
--- a/libraries/joomla/language/language.php
+++ b/libraries/joomla/language/language.php
@@ -1085,6 +1085,25 @@ public function getTag()
return $this->metadata['tag'];
}
+ /**
+ * Getter for the calendar type
+ *
+ * @return string The calendar type.
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function getCalendar()
+ {
+ if (isset($this->metadata['calendar']))
+ {
+ return $this->metadata['calendar'];
+ }
+ else
+ {
+ return 'gregorian';
+ }
+ }
+
/**
* Get the RTL property.
*
diff --git a/media/system/css/fields/calendar-vanilla-rtl.css b/media/system/css/fields/calendar-vanilla-rtl.css
new file mode 100644
index 0000000000000..c3f8991d420a1
--- /dev/null
+++ b/media/system/css/fields/calendar-vanilla-rtl.css
@@ -0,0 +1,129 @@
+/**
+ * @copyright Copyright (C) 2005 - 2016 Open Source Matters, Inc. All rights reserved.
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+.calendar-container {
+ float: left;
+ min-width: 160px;
+ padding: 5px 0;
+ list-style: none;
+ border: 1px solid #ccc;
+ border-radius: 8px;
+ background-color: #ffffff !important;
+ z-index: 1100 !important;
+}
+.calendar-container table {
+ background-color: #ffffff !important;
+ z-index: 1100 !important;
+}
+/* The main calendar widget. DIV containing a table. */
+div.calendar-container table th, .calendar-container table td {
+ padding: 7px;
+ line-height: 1.8em;
+ background-color: #fff;
+}
+
+div.calendar-container table td {
+ line-height: 2em;
+}
+
+div.calendar-container table td.title { /* This holds the current "month, year" */
+ vertical-align: middle;
+ font-weight: bold;
+ text-align: center;
+ font-size: 1.1em;
+ background: #fff;
+ color: #000;
+ padding: 2px;
+}
+
+.calendar-container table thead td.headrow { /* Row containing the day names */
+ background: #fff;
+}
+
+/* The body part -- contains all the days in month. */
+
+.calendar-container table tbody td.day { /* Cells containing month days dates */
+ text-align: right;
+ padding: 2px 4px 2px 2px;
+}
+
+.calendar-container table tbody td.wn {
+ padding: 2px 3px 2px 2px;
+ background: #fff;
+}
+
+.calendar-container table tbody td.active { /* Active (pressed) cells */
+ background: #ffffff;
+ color: #000000;
+}
+
+.calendar-container table tbody td.weekend { /* Cells showing weekend days */
+ color: #999;
+}
+
+.calendar-container table tbody td.hilite { /* Hovered cells */
+ background: #999999;
+ color: #ffffff;
+}
+
+.calendar-container table tbody td.day-name {
+ width: 14.28%;
+}
+
+.calendar-container table tbody td.day-name.day-name-week {
+ width: 12.5%;
+}
+
+.calendar-container table thead td.day-name.wn {
+ text-align: center;
+ background-color: #f4f4f4;
+}
+
+.calendar-container table tbody td.day {
+ border: none;
+ cursor : pointer;
+}
+
+.calendar-container table tbody td.day.wn {
+ text-align: center;
+ background-color: #f4f4f4;
+}
+
+.calendar-container table tbody td.selected { /* Cell showing today date */
+ font-weight: bold;
+ background: #3071a9;
+ color: #fff;
+}
+
+.calendar-container table tbody td.today:before {
+ font-weight: bold;
+ content: " ";
+ position: relative;
+ top: .25em;
+ right: -1.6em;
+ width: 0;
+ height: 0;
+ border-top: 0.8em solid lime;
+ border-left: .8em solid transparent;
+}
+
+.calendar-container table tbody td.day:hover {
+ cursor: pointer;
+ background: #3d8fd7;
+ color: #fff;
+}
+
+.calendar-container table tbody .disabled {
+ color: #999;
+}
+
+.calendar-container table tbody .emptycell { /* Empty cells (the best is to hide them) */
+ visibility: hidden;
+}
+
+.calendar-container table tbody .emptyrow { /* Empty row (some months need less than 6 rows) */
+ display: none;
+}
+
+a.js-btn.btn.btn-danger.btn-exit, a.js-btn.btn.btn-primary.btn-today, a.js-btn.btn.btn-clear {
+ cursor : pointer;
+ text-decoration: none;
+}
\ No newline at end of file
diff --git a/media/system/js/core-uncompressed.js b/media/system/js/core-uncompressed.js
index 856abdf5dcfb5..2fbe3ba04e298 100644
--- a/media/system/js/core-uncompressed.js
+++ b/media/system/js/core-uncompressed.js
@@ -30,7 +30,7 @@ Joomla.editors.instances = Joomla.editors.instances || {};
// Toggle HTML5 validation
form.noValidate = !validate;
- form.setAttribute('novalidate', !validate);
+ form.setAttribute('novalidate', !validate)
// Submit the form.
// Create the input type="submit"
diff --git a/media/system/js/fields/calendar-locales/date/gregorian/date-helper.js b/media/system/js/fields/calendar-locales/date/gregorian/date-helper.js
new file mode 100644
index 0000000000000..bda6be163b7d3
--- /dev/null
+++ b/media/system/js/fields/calendar-locales/date/gregorian/date-helper.js
@@ -0,0 +1,380 @@
+!(function(Date){
+ 'use strict';
+
+ /****************** Gregorian dates ********************/
+ /** Constants used for time computations */
+ Date.SECOND = 1000 /* milliseconds */;
+ Date.MINUTE = 60 * Date.SECOND;
+ Date.HOUR = 60 * Date.MINUTE;
+ Date.DAY = 24 * Date.HOUR;
+ Date.WEEK = 7 * Date.DAY;
+
+ /** MODIFY ONLY THE MARKED PARTS OF THE METHODS **/
+ /************ START *************/
+ /** INTERFACE METHODS FOR THE CALENDAR PICKER **/
+
+ /********************** *************************/
+ /**************** SETTERS ***********************/
+ /********************** *************************/
+
+ /** Sets the date for the current date without h/m/s. */
+ Date.prototype.setLocalDateOnly = function (dateType, date) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return '';
+ } else {
+ var tmp = new Date(date);
+ this.setDate(1);
+ this.setFullYear(tmp.getFullYear());
+ this.setMonth(tmp.getMonth());
+ this.setDate(tmp.getDate());
+ }
+ };
+
+ /** Sets the full date for the current date. */
+ Date.prototype.setLocalDate = function (dateType, d) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return '';
+ } else {
+ return this.setDate(d);
+ }
+ };
+
+ /** Sets the month for the current date. */
+ Date.prototype.setLocalMonth = function (dateType, m, d) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return '';
+ } else {
+ if (d == undefined) this.getDate();
+ return this.setMonth(m);
+ }
+ };
+
+ /** Sets the year for the current date. */
+ Date.prototype.setOtherFullYear = function(dateType, y) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return '';
+ } else {
+ var date = new Date(this);
+ date.setFullYear(y);
+ if (date.getMonth() != this.getMonth()) this.setDate(28);
+ return this.setUTCFullYear(y);
+ }
+ };
+
+ /** Sets the year for the current date. */
+ Date.prototype.setLocalFullYear = function (dateType, y) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return '';
+ } else {
+ var date = new Date(this);
+ date.setFullYear(y);
+ if (date.getMonth() != this.getMonth()) this.setDate(28);
+ return this.setFullYear(y);
+ }
+ };
+
+ /********************** *************************/
+ /**************** GETTERS ***********************/
+ /********************** *************************/
+
+ /** The number of days per week **/
+ Date.prototype.getLocalWeekDays = function (dateType, y) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return 6;
+ } else {
+ return 6; // 7 days per week
+ }
+ };
+
+ /** Returns the year for the current date. */
+ Date.prototype.getOtherFullYear = function (dateType) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return '';
+ } else {
+ return this.getFullYear();
+ }
+ };
+
+ /** Returns the year for the current date. */
+ Date.prototype.getLocalFullYear = function (dateType) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return '';
+ } else {
+ return this.getFullYear();
+ }
+ };
+
+ /** Returns the month the date. */
+ Date.prototype.getLocalMonth = function (dateType) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return '';
+ } else {
+ return this.getMonth();
+ }
+ };
+
+ /** Returns the date. */
+ Date.prototype.getLocalDate = function (dateType) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return '';
+ } else {
+ return this.getDate();
+ }
+ };
+
+ /** Returns the number of day in the year. */
+ Date.prototype.getLocalDay = function(dateType) {
+ if (dateType != 'gregorian') {
+ return '';
+ } else {
+ return this.getDay();
+ }
+ };
+
+ /** Returns the number of days in the current month */
+ Date.prototype.getLocalMonthDays = function(dateType, month) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return '';
+ } else {
+ var year = this.getFullYear();
+ if (typeof month == "undefined") {
+ month = this.getMonth();
+ }
+ if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) {
+ return 29;
+ } else {
+ return [31,28,31,30,31,30,31,31,30,31,30,31][month];
+ }
+ }
+ };
+
+ /** Returns the week number for the current date. */
+ Date.prototype.getLocalWeekNumber = function(dateType) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return '';
+ } else {
+ var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
+ var DoW = d.getDay();
+ d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu
+ var ms = d.valueOf(); // GMT
+ d.setMonth(0);
+ d.setDate(4); // Thu in Week 1
+ return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
+ }
+ };
+
+ /** Returns the number of day in the year. */
+ Date.prototype.getLocalDayOfYear = function(dateType) {
+ if (dateType != 'gregorian') {
+ return '';
+ } else {
+ var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
+ var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0);
+ var time = now - then;
+ return Math.floor(time / Date.DAY);
+ }
+ };
+
+ /** Checks date and time equality */
+ Date.prototype.equalsTo = function(date) {
+ return ((this.getFullYear() == date.getFullYear()) &&
+ (this.getMonth() == date.getMonth()) &&
+ (this.getDate() == date.getDate()) &&
+ (this.getHours() == date.getHours()) &&
+ (this.getMinutes() == date.getMinutes()));
+ };
+
+ /** Converts foreign date to gregorian date. */
+ Date.localCalToGregorian = function(y, m, d) {
+ /** Modify to match the current calendar when overriding **/
+ return'';
+ };
+
+ /** Converts gregorian date to foreign date. */
+ Date.gregorianToLocalCal = function(y, m, d) {
+ /** Modify to match the current calendar when overriding **/
+ return '';
+ };
+
+ /** INTERFACE METHODS FOR THE CALENDAR PICKER **/
+ /************* END **************/
+
+ /** Method to parse a string and return a date. **/
+ Date.parseFieldDate = function(str, fmt, dateType) {
+ if (dateType != 'gregorian')
+ str = Date.toEnglish(str);
+
+ var today = new Date();
+ var y = 0;
+ var m = -1;
+ var d = 0;
+ var a = str.split(/\W+/);
+ var b = fmt.match(/%./g);
+ var i = 0, j = 0;
+ var hr = 0;
+ var min = 0;
+ var sec = 0;
+ for (i = 0; i < a.length; ++i) {
+ if (!a[i])
+ continue;
+ switch (b[i]) {
+ case "%d":
+ case "%e":
+ d = parseInt(a[i], 10);
+ break;
+
+ case "%m":
+ m = parseInt(a[i], 10) - 1;
+ break;
+
+ case "%Y":
+ case "%y":
+ y = parseInt(a[i], 10);
+ (y < 100) && (y += (y > 29) ? 1900 : 2000);
+ break;
+
+ case "%b":
+ case "%B":
+ for (j = 0; j < 12; ++j) {
+ if (JoomlaCalLocale.months[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { m = j; break; }
+ }
+ break;
+
+ case "%H":
+ case "%I":
+ case "%k":
+ case "%l":
+ hr = parseInt(a[i], 10);
+ break;
+
+ case "%P":
+ case "%p":
+ if (/pm/i.test(a[i]) && hr < 12)
+ hr += 12;
+ else if (/am/i.test(a[i]) && hr >= 12)
+ hr -= 12;
+ break;
+
+ case "%M":
+ min = parseInt(a[i], 10);
+ break;
+ }
+ }
+ if (isNaN(y)) y = today.getFullYear();
+ if (isNaN(m)) m = today.getMonth();
+ if (isNaN(d)) d = today.getDate();
+ if (isNaN(hr)) hr = today.getHours();
+ if (isNaN(min)) min = today.getMinutes();
+ if (isNaN(sec)) sec = today.getSeconds();
+ if (y != 0 && m != -1 && d != 0)
+ return new Date(y, m, d, hr, min, sec);
+ y = 0; m = -1; d = 0;
+ for (i = 0; i < a.length; ++i) {
+ if (a[i].search(/[a-zA-Z]+/) != -1) {
+ var t = -1;
+ for (j = 0; j < 12; ++j) {
+ if (JoomlaCalLocale.months[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { t = j; break; }
+ }
+ if (t != -1) {
+ if (m != -1) {
+ d = m+1;
+ }
+ m = t;
+ }
+ } else if (parseInt(a[i], 10) <= 12 && m == -1) {
+ m = a[i]-1;
+ } else if (parseInt(a[i], 10) > 31 && y == 0) {
+ y = parseInt(a[i], 10);
+ (y < 100) && (y += (y > 29) ? 1900 : 2000);
+ } else if (d == 0) {
+ d = a[i];
+ }
+ }
+ if (y == 0)
+ y = today.getFullYear();
+ if (m != -1 && d != 0)
+ return new Date(y, m, d, hr, min, sec);
+ return today;
+ };
+
+ /** Prints the date in a string according to the given format. */
+ Date.prototype.print = function (str, dateType, translate) {
+ /** Handle calendar type **/
+ if (typeof dateType !== 'string') str = '';
+ if (!dateType) dateType = 'gregorian';
+
+ /** Handle wrong format **/
+ if (typeof str !== 'string') str = '';
+ if (!str) return '';
+
+ if (this.getLocalDate(dateType) == 'NaN' || !this.getLocalDate(dateType)) return '';
+ var m = this.getLocalMonth(dateType);
+ var d = this.getLocalDate(dateType);
+ var y = this.getLocalFullYear(dateType);
+ var wn = this.getLocalWeekNumber(dateType);
+ var w = this.getDay();
+ var s = {};
+ var hr = this.getHours();
+ var pm = (hr >= 12);
+ var ir = (pm) ? (hr - 12) : hr;
+ var dy = this.getLocalDayOfYear(dateType);
+ if (ir == 0)
+ ir = 12;
+ var min = this.getMinutes();
+ var sec = this.getSeconds();
+ s["%a"] = JoomlaCalLocale.shortDays[w]; // abbreviated weekday name
+ s["%A"] = JoomlaCalLocale.days[w]; // full weekday name
+ s["%b"] = JoomlaCalLocale.shortMonths[m]; // abbreviated month name
+ s["%B"] = JoomlaCalLocale.months[m]; // full month name
+ // FIXME: %c : preferred date and time representation for the current locale
+ s["%C"] = 1 + Math.floor(y / 100); // the century number
+ s["%d"] = (d < 10) ? ("0" + d) : d; // the day of the month (range 01 to 31)
+ s["%e"] = d; // the day of the month (range 1 to 31)
+ // FIXME: %D : american date style: %m/%d/%y
+ // FIXME: %E, %F, %G, %g, %h (man strftime)
+ s["%H"] = (hr < 10) ? ("0" + hr) : hr; // hour, range 00 to 23 (24h format)
+ s["%I"] = (ir < 10) ? ("0" + ir) : ir; // hour, range 01 to 12 (12h format)
+ s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy; // day of the year (range 001 to 366)
+ s["%k"] = hr; // hour, range 0 to 23 (24h format)
+ s["%l"] = ir; // hour, range 1 to 12 (12h format)
+ s["%m"] = (m < 9) ? ("0" + (1+m)) : (1+m); // month, range 01 to 12
+ s["%M"] = (min < 10) ? ("0" + min) : min; // minute, range 00 to 59
+ s["%n"] = "\n"; // a newline character
+ s["%p"] = pm ? JoomlaCalLocale.PM : JoomlaCalLocale.AM;
+ s["%P"] = pm ? JoomlaCalLocale.pm : JoomlaCalLocale.am;
+ // FIXME: %r : the time in am/pm notation %I:%M:%S %p
+ // FIXME: %R : the time in 24-hour notation %H:%M
+ s["%s"] = Math.floor(this.getTime() / 1000);
+ s["%S"] = (sec < 10) ? ("0" + sec) : sec; // seconds, range 00 to 59
+ s["%t"] = "\t"; // a tab character
+ // FIXME: %T : the time in 24-hour notation (%H:%M:%S)
+ s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn;
+ s["%u"] = w + 1; // the day of the week (range 1 to 7, 1 = MON)
+ s["%w"] = w; // the day of the week (range 0 to 6, 0 = SUN)
+ // FIXME: %x : preferred date representation for the current locale without the time
+ // FIXME: %X : preferred time representation for the current locale without the date
+ s["%y"] = ('' + y).substr(2, 2); // year without the century (range 00 to 99)
+ s["%Y"] = y; // year with the century
+ s["%%"] = "%"; // a literal '%' character
+
+ var re = /%./g;
+
+ var tmpDate = str.replace(re, function (par) { return s[par] || par; });
+ if (Object.prototype.toString.call(JoomlaCalLocale.localLangNumbers) === '[object Array]' && dateType != 'gregorian' && translate)
+ tmpDate = Date.convertNumbers(tmpDate);
+
+ return tmpDate;
+ };
+})(Date);
\ No newline at end of file
diff --git a/media/system/js/fields/calendar-locales/date/gregorian/date-helper.min.js b/media/system/js/fields/calendar-locales/date/gregorian/date-helper.min.js
new file mode 100644
index 0000000000000..09de8ca993328
--- /dev/null
+++ b/media/system/js/fields/calendar-locales/date/gregorian/date-helper.min.js
@@ -0,0 +1 @@
+!function(a){"use strict";a.SECOND=1e3,a.MINUTE=60*a.SECOND,a.HOUR=60*a.MINUTE,a.DAY=24*a.HOUR,a.WEEK=7*a.DAY,a.prototype.setLocalDateOnly=function(b,c){if("gregorian"!=b)return"";var d=new a(c);this.setDate(1),this.setFullYear(d.getFullYear()),this.setMonth(d.getMonth()),this.setDate(d.getDate())},a.prototype.setLocalDate=function(a,b){return"gregorian"!=a?"":this.setDate(b)},a.prototype.setLocalMonth=function(a,b,c){return"gregorian"!=a?"":(void 0==c&&this.getDate(),this.setMonth(b))},a.prototype.setOtherFullYear=function(b,c){if("gregorian"!=b)return"";var d=new a(this);return d.setFullYear(c),d.getMonth()!=this.getMonth()&&this.setDate(28),this.setUTCFullYear(c)},a.prototype.setLocalFullYear=function(b,c){if("gregorian"!=b)return"";var d=new a(this);return d.setFullYear(c),d.getMonth()!=this.getMonth()&&this.setDate(28),this.setFullYear(c)},a.prototype.getLocalWeekDays=function(a,b){return 6},a.prototype.getOtherFullYear=function(a){return"gregorian"!=a?"":this.getFullYear()},a.prototype.getLocalFullYear=function(a){return"gregorian"!=a?"":this.getFullYear()},a.prototype.getLocalMonth=function(a){return"gregorian"!=a?"":this.getMonth()},a.prototype.getLocalDate=function(a){return"gregorian"!=a?"":this.getDate()},a.prototype.getLocalDay=function(a){return"gregorian"!=a?"":this.getDay()},a.prototype.getLocalMonthDays=function(a,b){if("gregorian"!=a)return"";var c=this.getFullYear();return"undefined"==typeof b&&(b=this.getMonth()),0!=c%4||0==c%100&&0!=c%400||1!=b?[31,28,31,30,31,30,31,31,30,31,30,31][b]:29},a.prototype.getLocalWeekNumber=function(b){if("gregorian"!=b)return"";var c=new a(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0),d=c.getDay();c.setDate(c.getDate()-(d+6)%7+3);var e=c.valueOf();return c.setMonth(0),c.setDate(4),Math.round((e-c.valueOf())/6048e5)+1},a.prototype.getLocalDayOfYear=function(b){if("gregorian"!=b)return"";var c=new a(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0),d=new a(this.getFullYear(),0,0,0,0,0),e=c-d;return Math.floor(e/a.DAY)},a.prototype.equalsTo=function(a){return this.getFullYear()==a.getFullYear()&&this.getMonth()==a.getMonth()&&this.getDate()==a.getDate()&&this.getHours()==a.getHours()&&this.getMinutes()==a.getMinutes()},a.localCalToGregorian=function(a,b,c){return""},a.gregorianToLocalCal=function(a,b,c){return""},a.parseFieldDate=function(b,c,d){"gregorian"!=d&&(b=a.toEnglish(b));var e=new a,f=0,g=-1,h=0,i=b.split(/\W+/),j=c.match(/%./g),k=0,l=0,m=0,n=0,o=0;for(k=0;k29?1900:2e3);break;case"%b":case"%B":for(l=0;l<12;++l)if(JoomlaCalLocale.months[l].substr(0,i[k].length).toLowerCase()==i[k].toLowerCase()){g=l;break}break;case"%H":case"%I":case"%k":case"%l":m=parseInt(i[k],10);break;case"%P":case"%p":/pm/i.test(i[k])&&m<12?m+=12:/am/i.test(i[k])&&m>=12&&(m-=12);break;case"%M":n=parseInt(i[k],10)}if(isNaN(f)&&(f=e.getFullYear()),isNaN(g)&&(g=e.getMonth()),isNaN(h)&&(h=e.getDate()),isNaN(m)&&(m=e.getHours()),isNaN(n)&&(n=e.getMinutes()),isNaN(o)&&(o=e.getSeconds()),0!=f&&g!=-1&&0!=h)return new a(f,g,h,m,n,o);for(f=0,g=-1,h=0,k=0;k31&&0==f?(f=parseInt(i[k],10),f<100&&(f+=f>29?1900:2e3)):0==h&&(h=i[k]);return 0==f&&(f=e.getFullYear()),g!=-1&&0!=h?new a(f,g,h,m,n,o):e},a.prototype.print=function(b,c,d){if("string"!=typeof c&&(b=""),c||(c="gregorian"),"string"!=typeof b&&(b=""),!b)return"";if("NaN"==this.getLocalDate(c)||!this.getLocalDate(c))return"";var e=this.getLocalMonth(c),f=this.getLocalDate(c),g=this.getLocalFullYear(c),h=this.getLocalWeekNumber(c),i=this.getDay(),j={},k=this.getHours(),l=k>=12,m=l?k-12:k,n=this.getLocalDayOfYear(c);0==m&&(m=12);var o=this.getMinutes(),p=this.getSeconds();j["%a"]=JoomlaCalLocale.shortDays[i],j["%A"]=JoomlaCalLocale.days[i],j["%b"]=JoomlaCalLocale.shortMonths[e],j["%B"]=JoomlaCalLocale.months[e],j["%C"]=1+Math.floor(g/100),j["%d"]=f<10?"0"+f:f,j["%e"]=f,j["%H"]=k<10?"0"+k:k,j["%I"]=m<10?"0"+m:m,j["%j"]=n<100?n<10?"00"+n:"0"+n:n,j["%k"]=k,j["%l"]=m,j["%m"]=e<9?"0"+(1+e):1+e,j["%M"]=o<10?"0"+o:o,j["%n"]="\n",j["%p"]=l?JoomlaCalLocale.PM:JoomlaCalLocale.AM,j["%P"]=l?JoomlaCalLocale.pm:JoomlaCalLocale.am,j["%s"]=Math.floor(this.getTime()/1e3),j["%S"]=p<10?"0"+p:p,j["%t"]="\t",j["%U"]=j["%W"]=j["%V"]=h<10?"0"+h:h,j["%u"]=i+1,j["%w"]=i,j["%y"]=(""+g).substr(2,2),j["%Y"]=g,j["%%"]="%";var q=/%./g,r=b.replace(q,function(a){return j[a]||a});return"[object Array]"===Object.prototype.toString.call(JoomlaCalLocale.localLangNumbers)&&"gregorian"!=c&&d&&(r=a.convertNumbers(r)),r}}(Date);
\ No newline at end of file
diff --git a/media/system/js/fields/calendar-locales/date/jalali/date-helper.js b/media/system/js/fields/calendar-locales/date/jalali/date-helper.js
new file mode 100644
index 0000000000000..7cce0687f4c40
--- /dev/null
+++ b/media/system/js/fields/calendar-locales/date/jalali/date-helper.js
@@ -0,0 +1,605 @@
+/** BEGIN: DATE OBJECT PATCHES **/
+/** Adds the number of days array to the Date object. */
+Date.gregorian_MD = [31,28,31,30,31,30,31,31,30,31,30,31];
+Date.local_MD = [31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29];
+
+/** Constants used for time computations */
+Date.SECOND = 1000 /* milliseconds */;
+Date.MINUTE = 60 * Date.SECOND;
+Date.HOUR = 60 * Date.MINUTE;
+Date.DAY = 24 * Date.HOUR;
+Date.WEEK = 7 * Date.DAY;
+
+/** MODIFY ONLY THE MARKED PARTS OF THE METHODS **/
+/************ START *************/
+/** INTERFACE METHODS FOR THE CALENDAR PICKER **/
+
+/********************** *************************/
+/**************** SETTERS ***********************/
+/********************** *************************/
+
+/** Sets the date for the current date without h/m/s. */
+Date.prototype.setLocalDateOnly = function (dateType, date) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return '';
+ } else {
+ var tmp = new Date(date);
+ this.setDate(1);
+ this.setFullYear(tmp.getFullYear());
+ this.setMonth(tmp.getMonth());
+ this.setDate(tmp.getDate());
+ }
+};
+
+/** Sets the full date for the current date. */
+Date.prototype.setLocalDate = function (dateType, d) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return this.setJalaliDate(d);
+ } else {
+ return this.setDate(d);
+ }
+};
+
+/** Sets the month for the current date. */
+Date.prototype.setLocalMonth = function (dateType, m, d) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return this.setJalaliMonth(m, d);
+ } else {
+ if (d == undefined) this.getDate();
+ return this.setMonth(m);
+ }
+};
+
+/** Sets the year for the current date. */
+Date.prototype.setOtherFullYear = function(dateType, y) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ var date = new Date(this);
+ date.setLocalFullYear(y);
+ if (date.getLocalMonth('jalali') != this.getLocalMonth('jalali')) this.setLocalDate('jalali', 29);
+ return this.setLocalFullYear('jalali', y);
+ } else {
+ var date = new Date(this);
+ date.setFullYear(y);
+ if (date.getMonth() != this.getMonth()) this.setDate(28);
+ return this.setUTCFullYear(y);
+ }
+};
+
+/** Sets the year for the current date. */
+Date.prototype.setLocalFullYear = function (dateType, y) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return this.setJalaliFullYear(y);
+ } else {
+ var date = new Date(this);
+ date.setFullYear(y);
+ if (date.getMonth() != this.getMonth()) this.setDate(28);
+ return this.setFullYear(y);
+ }
+};
+
+/********************** *************************/
+/**************** GETTERS ***********************/
+/********************** *************************/
+
+/** The number of days per week **/
+Date.prototype.getLocalWeekDays = function (dateType, y) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return 6;
+ } else {
+ return 6; // 7 days per week
+ }
+};
+
+/** Returns the year for the current date. */
+Date.prototype.getOtherFullYear = function (dateType) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return this.getJalaliFullYear();
+ } else {
+ return this.getFullYear();
+ }
+};
+
+/** Returns the year for the current date. */
+Date.prototype.getLocalFullYear = function (dateType) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return this.getJalaliFullYear();
+ } else {
+ return this.getFullYear();
+ }
+};
+
+/** Returns the month the date. */
+Date.prototype.getLocalMonth = function (dateType) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return this.getJalaliMonth();
+ } else {
+ return this.getMonth();
+ }
+};
+
+/** Returns the date. */
+Date.prototype.getLocalDate = function (dateType) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ return this.getJalaliDate();
+ } else {
+ return this.getDate();
+ }
+};
+
+/** Returns the number of day in the year. */
+Date.prototype.getLocalDay = function(dateType) {
+ if (dateType != 'gregorian') {
+ return this.getJalaliDay();
+ } else {
+ return this.getDay();
+ }
+};
+
+/** Returns the number of days in the current month */
+Date.prototype.getLocalMonthDays = function(dateType, month) {
+ if (dateType != 'gregorian') {
+ /** Modify to match the current calendar when overriding **/
+ var year = this.getLocalFullYear('jalali');
+ if (typeof month == "undefined") {
+ month = this.getLocalMonth('jalali');
+ }
+ if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) {
+ return 29;
+ } else {
+ Date.local_MD[month];
+ }
+ } else {
+ var year = this.getFullYear();
+ if (typeof month == "undefined") {
+ month = this.getMonth();
+ }
+ if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) {
+ return 29;
+ } else {
+ return Date.gregorian_MD[month];
+ }
+ }
+};
+
+/** Returns the week number for the current date. */
+Date.prototype.getLocalWeekNumber = function(dateType) {
+ if (dateType != 'gregorian') {
+ var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
+ var DoW = d.getDay();
+ d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu
+ var ms = d.valueOf(); // GMT
+ d.setMonth(0);
+ d.setDate(4); // Thu in Week 1
+ return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
+ } else {
+ var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
+ var DoW = d.getDay();
+ d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu
+ var ms = d.valueOf(); // GMT
+ d.setMonth(0);
+ d.setDate(4); // Thu in Week 1
+ return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
+ }
+};
+
+/** Returns the number of day in the year. */
+Date.prototype.getLocalDayOfYear = function(dateType) {
+ if (dateType != 'gregorian') {
+ var now = new Date(this.getOtherFullYear(dateType), this.getLocalMonth(dateType), this.getLocalDate(dateType), 0, 0, 0);
+ var then = new Date(this.getOtherFullYear(dateType), 0, 0, 0, 0, 0);
+ var time = now - then;
+ return Math.floor(time / Date.DAY);
+ } else {
+ var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
+ var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0);
+ var time = now - then;
+ return Math.floor(time / Date.DAY);
+ }
+};
+
+/** Returns the number of days in the current month */
+Date.prototype.getMonthDays = function(month) {
+ var year = this.getFullYear();
+ if (typeof month == "undefined") {
+ month = this.getMonth();
+ }
+ if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) {
+ return 29;
+ } else {
+ if (Date.dateType != 'gregorian') {
+ Date.local_MD[month];
+ } else {
+ return Date.gregorian_MD[month];
+ }
+ }
+};
+
+/** Checks date and time equality */
+Date.prototype.equalsTo = function(date) {
+ return ((this.getFullYear() == date.getFullYear()) &&
+ (this.getMonth() == date.getMonth()) &&
+ (this.getDate() == date.getDate()) &&
+ (this.getHours() == date.getHours()) &&
+ (this.getMinutes() == date.getMinutes()));
+};
+
+/** Converts foreign date to gregorian date. */
+Date.localCalToGregorian = function(y, m, d) {
+ /** Modify to match the current calendar when overriding **/
+ return JalaliDate.jalaliToGregorian(y, m, d);
+};
+
+/** Converts gregorian date to foreign date. */
+Date.gregorianToLocalCal = function(y, m, d) {
+ /** Modify to match the current calendar when overriding **/
+ return JalaliDate.gregorianToJalali(y, m, d);
+};
+/** INTERFACE METHODS FOR THE CALENDAR PICKER **/
+/************* END **************/
+
+/** Prints the date in a string according to the given format. */
+Date.prototype.print = function (str, dateType, translate) {
+ /** Handle calendar type **/
+ if (typeof dateType !== 'string') str = '';
+ if (!dateType) dateType = 'gregorian';
+
+ /** Handle wrong format **/
+ if (typeof str !== 'string') str = '';
+ if (!str) return '';
+
+
+ if (this.getLocalDate(dateType) == 'NaN' || !this.getLocalDate(dateType)) return '';
+ var m = this.getLocalMonth(dateType);
+ var d = this.getLocalDate(dateType);
+ var y = this.getLocalFullYear(dateType);
+ var wn = this.getLocalWeekNumber(dateType);
+ var w = this.getLocalDay(dateType);
+ var s = {};
+ var hr = this.getHours();
+ var pm = (hr >= 12);
+ var ir = (pm) ? (hr - 12) : hr;
+ var dy = this.getLocalDayOfYear(dateType);
+ if (ir == 0)
+ ir = 12;
+ var min = this.getMinutes();
+ var sec = this.getSeconds();
+ s["%a"] = JoomlaCalLocale.shortDays[w]; // abbreviated weekday name
+ s["%A"] = JoomlaCalLocale.days[w]; // full weekday name
+ s["%b"] = JoomlaCalLocale.shortMonths[m]; // abbreviated month name
+ s["%B"] = JoomlaCalLocale.months[m]; // full month name
+ // FIXME: %c : preferred date and time representation for the current locale
+ s["%C"] = 1 + Math.floor(y / 100); // the century number
+ s["%d"] = (d < 10) ? ("0" + d) : d; // the day of the month (range 01 to 31)
+ s["%e"] = d; // the day of the month (range 1 to 31)
+ // FIXME: %D : american date style: %m/%d/%y
+ // FIXME: %E, %F, %G, %g, %h (man strftime)
+ s["%H"] = (hr < 10) ? ("0" + hr) : hr; // hour, range 00 to 23 (24h format)
+ s["%I"] = (ir < 10) ? ("0" + ir) : ir; // hour, range 01 to 12 (12h format)
+ s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy; // day of the year (range 001 to 366)
+ s["%k"] = hr; // hour, range 0 to 23 (24h format)
+ s["%l"] = ir; // hour, range 1 to 12 (12h format)
+ s["%m"] = (m < 9) ? ("0" + (1+m)) : (1+m); // month, range 01 to 12
+ s["%M"] = (min < 10) ? ("0" + min) : min; // minute, range 00 to 59
+ s["%n"] = "\n"; // a newline character
+ s["%p"] = pm ? JoomlaCalLocale.PM : JoomlaCalLocale.AM;
+ s["%P"] = pm ? JoomlaCalLocale.pm : JoomlaCalLocale.am;
+ // FIXME: %r : the time in am/pm notation %I:%M:%S %p
+ // FIXME: %R : the time in 24-hour notation %H:%M
+ s["%s"] = Math.floor(this.getTime() / 1000);
+ s["%S"] = (sec < 10) ? ("0" + sec) : sec; // seconds, range 00 to 59
+ s["%t"] = "\t"; // a tab character
+ // FIXME: %T : the time in 24-hour notation (%H:%M:%S)
+ s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn;
+ s["%u"] = w + 1; // the day of the week (range 1 to 7, 1 = MON)
+ s["%w"] = w; // the day of the week (range 0 to 6, 0 = SUN)
+ // FIXME: %x : preferred date representation for the current locale without the time
+ // FIXME: %X : preferred time representation for the current locale without the date
+ s["%y"] = ('' + y).substr(2, 2); // year without the century (range 00 to 99)
+ s["%Y"] = y; // year with the century
+ s["%%"] = "%"; // a literal '%' character
+
+ var re = /%./g;
+
+ var tmpDate = str.replace(re, function (par) { return s[par] || par; });
+ if (Object.prototype.toString.call(JoomlaCalLocale.localLangNumbers) === '[object Array]' && translate)
+ tmpDate = Date.convertNumbers(tmpDate);
+
+ return tmpDate;
+};
+
+Date.parseFieldDate = function(str, fmt, dateType) {
+ var today = new Date();
+ var y = 0;
+ var m = -1;
+ var d = 0;
+ var a = str.split(/\W+/);
+ var b = fmt.match(/%./g);
+ var i = 0, j = 0;
+ var hr = 0;
+ var min = 0;
+ for (i = 0; i < a.length; ++i) {
+ if (!a[i])
+ continue;
+ switch (b[i]) {
+ case "%d":
+ case "%e":
+ d = parseInt(a[i], 10);
+ break;
+
+ case "%m":
+ m = parseInt(a[i], 10) - 1;
+ break;
+
+ case "%Y":
+ case "%y":
+ y = parseInt(a[i], 10);
+ (y < 100) && (y += (y > 29) ? 1900 : 2000);
+ break;
+
+ case "%b":
+ case "%B":
+ for (j = 0; j < 12; ++j) {
+ if (dateType != 'gregorian') {
+ if (JoomlaCalLocale.months[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { m = j; break; }
+ } else {
+ if (JoomlaCalLocale.months[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { m = j; break; }
+ }
+ }
+ break;
+
+ case "%H":
+ case "%I":
+ case "%k":
+ case "%l":
+ hr = parseInt(a[i], 10);
+ break;
+
+ case "%P":
+ case "%p":
+ if (/pm/i.test(a[i]) && hr < 12)
+ hr += 12;
+ else if (/am/i.test(a[i]) && hr >= 12)
+ hr -= 12;
+ break;
+
+ case "%M":
+ min = parseInt(a[i], 10);
+ break;
+ }
+ }
+ if (isNaN(y)) y = today.getFullYear();
+ if (isNaN(m)) m = today.getMonth();
+ if (isNaN(d)) d = today.getDate();
+ if (isNaN(hr)) hr = today.getHours();
+ if (isNaN(min)) min = today.getMinutes();
+ if (y != 0 && m != -1 && d != 0)
+ return new Date(y, m, d, hr, min, 0);
+ y = 0; m = -1; d = 0;
+ for (i = 0; i < a.length; ++i) {
+ if (a[i].search(/[a-zA-Z]+/) != -1) {
+ var t = -1;
+ for (j = 0; j < 12; ++j) {
+ if (dateType != 'gregorian') {
+ if (JoomlaCalLocale.months[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { t = j; break; }
+ } else {
+ if (JoomlaCalLocale.months[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { t = j; break; }
+ }
+ }
+ if (t != -1) {
+ if (m != -1) {
+ d = m+1;
+ }
+ m = t;
+ }
+ } else if (parseInt(a[i], 10) <= 12 && m == -1) {
+ m = a[i]-1;
+ } else if (parseInt(a[i], 10) > 31 && y == 0) {
+ y = parseInt(a[i], 10);
+ (y < 100) && (y += (y > 29) ? 1900 : 2000);
+ } else if (d == 0) {
+ d = a[i];
+ }
+ }
+ if (y == 0)
+ y = today.getFullYear();
+ if (m != -1 && d != 0)
+ return new Date(y, m, d, hr, min, 0);
+ return today;
+};
+
+/*
+ * JalaliJSCalendar - Jalali Extension for Date Object
+ * Copyright (c) 2008 Ali Farhadi (http://farhadi.ir/)
+ * Released under the terms of the GNU General Public License.
+ * See the GPL for details (http://www.gnu.org/licenses/gpl.html).
+ *
+ * Based on code from http://farsiweb.info
+ */
+
+JalaliDate = {
+ g_days_in_month: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+ j_days_in_month: [31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29]
+};
+
+JalaliDate.jalaliToGregorian = function(j_y, j_m, j_d)
+{
+ j_y = parseInt(j_y);
+ j_m = parseInt(j_m);
+ j_d = parseInt(j_d);
+ var jy = j_y-979;
+ var jm = j_m-1;
+ var jd = j_d-1;
+
+ var j_day_no = 365*jy + parseInt(jy / 33)*8 + parseInt((jy%33+3) / 4);
+ for (var i=0; i < jm; ++i) j_day_no += JalaliDate.j_days_in_month[i];
+
+ j_day_no += jd;
+
+ var g_day_no = j_day_no+79;
+
+ var gy = 1600 + 400 * parseInt(g_day_no / 146097); /* 146097 = 365*400 + 400/4 - 400/100 + 400/400 */
+ g_day_no = g_day_no % 146097;
+
+ var leap = true;
+ if (g_day_no >= 36525) /* 36525 = 365*100 + 100/4 */
+ {
+ g_day_no--;
+ gy += 100*parseInt(g_day_no/ 36524); /* 36524 = 365*100 + 100/4 - 100/100 */
+ g_day_no = g_day_no % 36524;
+
+ if (g_day_no >= 365)
+ g_day_no++;
+ else
+ leap = false;
+ }
+
+ gy += 4*parseInt(g_day_no/ 1461); /* 1461 = 365*4 + 4/4 */
+ g_day_no %= 1461;
+
+ if (g_day_no >= 366) {
+ leap = false;
+
+ g_day_no--;
+ gy += parseInt(g_day_no/ 365);
+ g_day_no = g_day_no % 365;
+ }
+
+ for (var i = 0; g_day_no >= JalaliDate.g_days_in_month[i] + (i == 1 && leap); i++)
+ g_day_no -= JalaliDate.g_days_in_month[i] + (i == 1 && leap);
+ var gm = i+1;
+ var gd = g_day_no+1;
+
+ return [gy, gm, gd];
+};
+
+JalaliDate.checkDate = function(j_y, j_m, j_d)
+{
+ return !(j_y < 0 || j_y > 32767 || j_m < 1 || j_m > 12 || j_d < 1 || j_d >
+ (JalaliDate.j_days_in_month[j_m-1] + (j_m == 12 && !((j_y-979)%33%4))));
+};
+
+JalaliDate.gregorianToJalali = function(g_y, g_m, g_d)
+{
+ g_y = parseInt(g_y);
+ g_m = parseInt(g_m);
+ g_d = parseInt(g_d);
+ var gy = g_y-1600;
+ var gm = g_m-1;
+ var gd = g_d-1;
+
+ var g_day_no = 365*gy+parseInt((gy+3) / 4)-parseInt((gy+99)/100)+parseInt((gy+399)/400);
+
+ for (var i=0; i < gm; ++i)
+ g_day_no += JalaliDate.g_days_in_month[i];
+ if (gm>1 && ((gy%4==0 && gy%100!=0) || (gy%400==0)))
+ /* leap and after Feb */
+ ++g_day_no;
+ g_day_no += gd;
+
+ var j_day_no = g_day_no-79;
+
+ var j_np = parseInt(j_day_no/ 12053);
+ j_day_no %= 12053;
+
+ var jy = 979+33*j_np+4*parseInt(j_day_no/1461);
+
+ j_day_no %= 1461;
+
+ if (j_day_no >= 366) {
+ jy += parseInt((j_day_no-1)/ 365);
+ j_day_no = (j_day_no-1)%365;
+ }
+
+ for (var i = 0; i < 11 && j_day_no >= JalaliDate.j_days_in_month[i]; ++i) {
+ j_day_no -= JalaliDate.j_days_in_month[i];
+ }
+ var jm = i+1;
+ var jd = j_day_no+1;
+
+
+ return [jy, jm, jd];
+};
+
+Date.prototype.setJalaliFullYear = function(y, m, d) {
+ var gd = this.getDate();
+ var gm = this.getMonth();
+ var gy = this.getFullYear();
+ var j = JalaliDate.gregorianToJalali(gy, gm+1, gd);
+ if (y < 100) y += 1300;
+ j[0] = y;
+ if (m != undefined) {
+ if (m > 11) {
+ j[0] += Math.floor(m / 12);
+ m = m % 12;
+ }
+ j[1] = m + 1;
+ }
+ if (d != undefined) j[2] = d;
+ var g = JalaliDate.jalaliToGregorian(j[0], j[1], j[2]);
+ return this.setFullYear(g[0], g[1]-1, g[2]);
+};
+
+Date.prototype.setJalaliMonth = function(m, d) {
+ var gd = this.getDate();
+ var gm = this.getMonth();
+ var gy = this.getFullYear();
+ var j = JalaliDate.gregorianToJalali(gy, gm+1, gd);
+ if (m > 11) {
+ j[0] += Math.floor(m / 12);
+ m = m % 12;
+ }
+ j[1] = m+1;
+ if (d != undefined) j[2] = d;
+ var g = JalaliDate.jalaliToGregorian(j[0], j[1], j[2]);
+ return this.setFullYear(g[0], g[1]-1, g[2]);
+};
+
+Date.prototype.setJalaliDate = function(d) {
+ var gd = this.getDate();
+ var gm = this.getMonth();
+ var gy = this.getFullYear();
+ var j = JalaliDate.gregorianToJalali(gy, gm+1, gd);
+ j[2] = d;
+ var g = JalaliDate.jalaliToGregorian(j[0], j[1], j[2]);
+ return this.setFullYear(g[0], g[1]-1, g[2]);
+};
+
+Date.prototype.getJalaliFullYear = function() {
+ var gd = this.getDate();
+ var gm = this.getMonth();
+ var gy = this.getFullYear();
+ var j = JalaliDate.gregorianToJalali(gy, gm+1, gd);
+ return j[0];
+};
+
+Date.prototype.getJalaliMonth = function() {
+ var gd = this.getDate();
+ var gm = this.getMonth();
+ var gy = this.getFullYear();
+ var j = JalaliDate.gregorianToJalali(gy, gm+1, gd);
+ return j[1]-1;
+};
+
+Date.prototype.getJalaliDate = function() {
+ var gd = this.getDate();
+ var gm = this.getMonth();
+ var gy = this.getFullYear();
+ var j = JalaliDate.gregorianToJalali(gy, gm+1, gd);
+ return j[2];
+};
+
+Date.prototype.getJalaliDay = function() {
+ var day = this.getDay();
+ day = (day + 1) % 7;
+ return day;
+};
\ No newline at end of file
diff --git a/media/system/js/fields/calendar-locales/date/jalali/date-helper.min.js b/media/system/js/fields/calendar-locales/date/jalali/date-helper.min.js
new file mode 100644
index 0000000000000..673f00a139c30
--- /dev/null
+++ b/media/system/js/fields/calendar-locales/date/jalali/date-helper.min.js
@@ -0,0 +1 @@
+Date.gregorian_MD=[31,28,31,30,31,30,31,31,30,31,30,31],Date.local_MD=[31,31,31,31,31,31,30,30,30,30,30,29],Date.SECOND=1e3,Date.MINUTE=60*Date.SECOND,Date.HOUR=60*Date.MINUTE,Date.DAY=24*Date.HOUR,Date.WEEK=7*Date.DAY,Date.prototype.setLocalDateOnly=function(a,b){if("gregorian"!=a)return"";var c=new Date(b);this.setDate(1),this.setFullYear(c.getFullYear()),this.setMonth(c.getMonth()),this.setDate(c.getDate())},Date.prototype.setLocalDate=function(a,b){return"gregorian"!=a?this.setJalaliDate(b):this.setDate(b)},Date.prototype.setLocalMonth=function(a,b,c){return"gregorian"!=a?this.setJalaliMonth(b,c):(void 0==c&&this.getDate(),this.setMonth(b))},Date.prototype.setOtherFullYear=function(a,b){if("gregorian"!=a){var c=new Date(this);return c.setLocalFullYear(b),c.getLocalMonth("jalali")!=this.getLocalMonth("jalali")&&this.setLocalDate("jalali",29),this.setLocalFullYear("jalali",b)}var c=new Date(this);return c.setFullYear(b),c.getMonth()!=this.getMonth()&&this.setDate(28),this.setUTCFullYear(b)},Date.prototype.setLocalFullYear=function(a,b){if("gregorian"!=a)return this.setJalaliFullYear(b);var c=new Date(this);return c.setFullYear(b),c.getMonth()!=this.getMonth()&&this.setDate(28),this.setFullYear(b)},Date.prototype.getLocalWeekDays=function(a,b){return 6},Date.prototype.getOtherFullYear=function(a){return"gregorian"!=a?this.getJalaliFullYear():this.getFullYear()},Date.prototype.getLocalFullYear=function(a){return"gregorian"!=a?this.getJalaliFullYear():this.getFullYear()},Date.prototype.getLocalMonth=function(a){return"gregorian"!=a?this.getJalaliMonth():this.getMonth()},Date.prototype.getLocalDate=function(a){return"gregorian"!=a?this.getJalaliDate():this.getDate()},Date.prototype.getLocalDay=function(a){return"gregorian"!=a?this.getJalaliDay():this.getDay()},Date.prototype.getLocalMonthDays=function(a,b){if("gregorian"==a){var c=this.getFullYear();return"undefined"==typeof b&&(b=this.getMonth()),0!=c%4||0==c%100&&0!=c%400||1!=b?Date.gregorian_MD[b]:29}var c=this.getLocalFullYear("jalali");return"undefined"==typeof b&&(b=this.getLocalMonth("jalali")),0!=c%4||0==c%100&&0!=c%400||1!=b?void Date.local_MD[b]:29},Date.prototype.getLocalWeekNumber=function(a){if("gregorian"!=a){var b=new Date(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0),c=b.getDay();b.setDate(b.getDate()-(c+6)%7+3);var d=b.valueOf();return b.setMonth(0),b.setDate(4),Math.round((d-b.valueOf())/6048e5)+1}var b=new Date(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0),c=b.getDay();b.setDate(b.getDate()-(c+6)%7+3);var d=b.valueOf();return b.setMonth(0),b.setDate(4),Math.round((d-b.valueOf())/6048e5)+1},Date.prototype.getLocalDayOfYear=function(a){if("gregorian"!=a){var b=new Date(this.getOtherFullYear(a),this.getLocalMonth(a),this.getLocalDate(a),0,0,0),c=new Date(this.getOtherFullYear(a),0,0,0,0,0),d=b-c;return Math.floor(d/Date.DAY)}var b=new Date(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0),c=new Date(this.getFullYear(),0,0,0,0,0),d=b-c;return Math.floor(d/Date.DAY)},Date.prototype.getMonthDays=function(a){var b=this.getFullYear();return"undefined"==typeof a&&(a=this.getMonth()),0!=b%4||0==b%100&&0!=b%400||1!=a?"gregorian"==Date.dateType?Date.gregorian_MD[a]:void Date.local_MD[a]:29},Date.prototype.equalsTo=function(a){return this.getFullYear()==a.getFullYear()&&this.getMonth()==a.getMonth()&&this.getDate()==a.getDate()&&this.getHours()==a.getHours()&&this.getMinutes()==a.getMinutes()},Date.localCalToGregorian=function(a,b,c){return JalaliDate.jalaliToGregorian(a,b,c)},Date.gregorianToLocalCal=function(a,b,c){return JalaliDate.gregorianToJalali(a,b,c)},Date.prototype.print=function(a,b,c){if("string"!=typeof b&&(a=""),b||(b="gregorian"),"string"!=typeof a&&(a=""),!a)return"";if("NaN"==this.getLocalDate(b)||!this.getLocalDate(b))return"";var d=this.getLocalMonth(b),e=this.getLocalDate(b),f=this.getLocalFullYear(b),g=this.getLocalWeekNumber(b),h=this.getLocalDay(b),i={},j=this.getHours(),k=j>=12,l=k?j-12:j,m=this.getLocalDayOfYear(b);0==l&&(l=12);var n=this.getMinutes(),o=this.getSeconds();i["%a"]=JoomlaCalLocale.shortDays[h],i["%A"]=JoomlaCalLocale.days[h],i["%b"]=JoomlaCalLocale.shortMonths[d],i["%B"]=JoomlaCalLocale.months[d],i["%C"]=1+Math.floor(f/100),i["%d"]=e<10?"0"+e:e,i["%e"]=e,i["%H"]=j<10?"0"+j:j,i["%I"]=l<10?"0"+l:l,i["%j"]=m<100?m<10?"00"+m:"0"+m:m,i["%k"]=j,i["%l"]=l,i["%m"]=d<9?"0"+(1+d):1+d,i["%M"]=n<10?"0"+n:n,i["%n"]="\n",i["%p"]=k?JoomlaCalLocale.PM:JoomlaCalLocale.AM,i["%P"]=k?JoomlaCalLocale.pm:JoomlaCalLocale.am,i["%s"]=Math.floor(this.getTime()/1e3),i["%S"]=o<10?"0"+o:o,i["%t"]="\t",i["%U"]=i["%W"]=i["%V"]=g<10?"0"+g:g,i["%u"]=h+1,i["%w"]=h,i["%y"]=(""+f).substr(2,2),i["%Y"]=f,i["%%"]="%";var p=/%./g,q=a.replace(p,function(a){return i[a]||a});return"[object Array]"===Object.prototype.toString.call(JoomlaCalLocale.localLangNumbers)&&c&&(q=Date.convertNumbers(q)),q},Date.parseFieldDate=function(a,b,c){var d=new Date,e=0,f=-1,g=0,h=a.split(/\W+/),i=b.match(/%./g),j=0,k=0,l=0,m=0;for(j=0;j29?1900:2e3);break;case"%b":case"%B":for(k=0;k<12;++k)if("gregorian"!=c){if(JoomlaCalLocale.months[k].substr(0,h[j].length).toLowerCase()==h[j].toLowerCase()){f=k;break}}else if(JoomlaCalLocale.months[k].substr(0,h[j].length).toLowerCase()==h[j].toLowerCase()){f=k;break}break;case"%H":case"%I":case"%k":case"%l":l=parseInt(h[j],10);break;case"%P":case"%p":/pm/i.test(h[j])&&l<12?l+=12:/am/i.test(h[j])&&l>=12&&(l-=12);break;case"%M":m=parseInt(h[j],10)}if(isNaN(e)&&(e=d.getFullYear()),isNaN(f)&&(f=d.getMonth()),isNaN(g)&&(g=d.getDate()),isNaN(l)&&(l=d.getHours()),isNaN(m)&&(m=d.getMinutes()),0!=e&&f!=-1&&0!=g)return new Date(e,f,g,l,m,0);for(e=0,f=-1,g=0,j=0;j31&&0==e?(e=parseInt(h[j],10),e<100&&(e+=e>29?1900:2e3)):0==g&&(g=h[j]);return 0==e&&(e=d.getFullYear()),f!=-1&&0!=g?new Date(e,f,g,l,m,0):d},JalaliDate={g_days_in_month:[31,28,31,30,31,30,31,31,30,31,30,31],j_days_in_month:[31,31,31,31,31,31,30,30,30,30,30,29]},JalaliDate.jalaliToGregorian=function(a,b,c){a=parseInt(a),b=parseInt(b),c=parseInt(c);for(var d=a-979,e=b-1,f=c-1,g=365*d+8*parseInt(d/33)+parseInt((d%33+3)/4),h=0;h=36525&&(i--,j+=100*parseInt(i/36524),i%=36524,i>=365?i++:k=!1),j+=4*parseInt(i/1461),i%=1461,i>=366&&(k=!1,i--,j+=parseInt(i/365),i%=365);for(var h=0;i>=JalaliDate.g_days_in_month[h]+(1==h&&k);h++)i-=JalaliDate.g_days_in_month[h]+(1==h&&k);var l=h+1,m=i+1;return[j,l,m]},JalaliDate.checkDate=function(a,b,c){return!(a<0||a>32767||b<1||b>12||c<1||c>JalaliDate.j_days_in_month[b-1]+(12==b&&!((a-979)%33%4)))},JalaliDate.gregorianToJalali=function(a,b,c){a=parseInt(a),b=parseInt(b),c=parseInt(c);for(var d=a-1600,e=b-1,f=c-1,g=365*d+parseInt((d+3)/4)-parseInt((d+99)/100)+parseInt((d+399)/400),h=0;h1&&(d%4==0&&d%100!=0||d%400==0)&&++g,g+=f;var i=g-79,j=parseInt(i/12053);i%=12053;var k=979+33*j+4*parseInt(i/1461);i%=1461,i>=366&&(k+=parseInt((i-1)/365),i=(i-1)%365);for(var h=0;h<11&&i>=JalaliDate.j_days_in_month[h];++h)i-=JalaliDate.j_days_in_month[h];var l=h+1,m=i+1;return[k,l,m]},Date.prototype.setJalaliFullYear=function(a,b,c){var d=this.getDate(),e=this.getMonth(),f=this.getFullYear(),g=JalaliDate.gregorianToJalali(f,e+1,d);a<100&&(a+=1300),g[0]=a,void 0!=b&&(b>11&&(g[0]+=Math.floor(b/12),b%=12),g[1]=b+1),void 0!=c&&(g[2]=c);var h=JalaliDate.jalaliToGregorian(g[0],g[1],g[2]);return this.setFullYear(h[0],h[1]-1,h[2])},Date.prototype.setJalaliMonth=function(a,b){var c=this.getDate(),d=this.getMonth(),e=this.getFullYear(),f=JalaliDate.gregorianToJalali(e,d+1,c);a>11&&(f[0]+=Math.floor(a/12),a%=12),f[1]=a+1,void 0!=b&&(f[2]=b);var g=JalaliDate.jalaliToGregorian(f[0],f[1],f[2]);return this.setFullYear(g[0],g[1]-1,g[2])},Date.prototype.setJalaliDate=function(a){var b=this.getDate(),c=this.getMonth(),d=this.getFullYear(),e=JalaliDate.gregorianToJalali(d,c+1,b);e[2]=a;var f=JalaliDate.jalaliToGregorian(e[0],e[1],e[2]);return this.setFullYear(f[0],f[1]-1,f[2])},Date.prototype.getJalaliFullYear=function(){var a=this.getDate(),b=this.getMonth(),c=this.getFullYear(),d=JalaliDate.gregorianToJalali(c,b+1,a);return d[0]},Date.prototype.getJalaliMonth=function(){var a=this.getDate(),b=this.getMonth(),c=this.getFullYear(),d=JalaliDate.gregorianToJalali(c,b+1,a);return d[1]-1},Date.prototype.getJalaliDate=function(){var a=this.getDate(),b=this.getMonth(),c=this.getFullYear(),d=JalaliDate.gregorianToJalali(c,b+1,a);return d[2]},Date.prototype.getJalaliDay=function(){var a=this.getDay();return a=(a+1)%7};
\ No newline at end of file
diff --git a/media/system/js/fields/calendar-locales/en.js b/media/system/js/fields/calendar-locales/en.js
new file mode 100644
index 0000000000000..4280fefcf8fe8
--- /dev/null
+++ b/media/system/js/fields/calendar-locales/en.js
@@ -0,0 +1,19 @@
+window.JoomlaCalLocale = {
+ today : "Today",
+ weekend : [0, 6],
+ wk : "wk",
+ time : "Time:",
+ days : ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ shortDays : ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ months : ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ shortMonths : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ AM : "AM",
+ PM : "PM",
+ am : "am",
+ pm : "pm",
+ dateType : "gregorian",
+ minYear : 1900,
+ maxYear : 2100,
+ exit: "Close",
+ save: "Clear"
+};
\ No newline at end of file
diff --git a/media/system/js/fields/calendar-locales/fa-ir.js b/media/system/js/fields/calendar-locales/fa-ir.js
new file mode 100644
index 0000000000000..1ffca6583ca30
--- /dev/null
+++ b/media/system/js/fields/calendar-locales/fa-ir.js
@@ -0,0 +1,20 @@
+window.JoomlaCalLocale = {
+ today : "امروز",
+ weekend : [5],
+ wk : "هفته",
+ time : "زمان",
+ days : ["یکشنبه","دوشنبه","سه شنبه","چهارشنبه","پنجشنبه","جمعه","شنبه"],
+ shortDays : ["یک","دو","سه","چهار","پنج","جمعه","شنبه"],
+ months : ["فروردین","اردیبهشت","خرداد","تیر","مرداد","شهریور","مهر","آبان","آذر","دی","بهمن","اسفند"],
+ shortMonths : ["فروردین","اردیبهشت","خرداد","تیر","مرداد","شهریور","مهر","آبان","آذر","دی","بهمن","اسفند"],
+ AM : "ق.ظ.",
+ PM : "ب.ظ.",
+ am : "ق.ظ.",
+ pm : "ب.ظ.",
+ dateType : "jalali",
+ minYear : 1268,
+ maxYear : 1458,
+ exit: "ستن",
+ save: "پاک",
+ localLangNumbers: ["۰","۱","۲","۳","۴","۵","۶","۷","۸","۹"]
+};
\ No newline at end of file
diff --git a/media/system/js/fields/calendar-locales/fr.js b/media/system/js/fields/calendar-locales/fr.js
new file mode 100644
index 0000000000000..9fd1189b9e61c
--- /dev/null
+++ b/media/system/js/fields/calendar-locales/fr.js
@@ -0,0 +1,19 @@
+window.JoomlaCalLocale = {
+ today : "Aujourd'hui",
+ weekend : [0, 6],
+ wk : "wk",
+ time : "Heure :",
+ days : ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"],
+ shortDays : ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"],
+ months : ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"],
+ shortMonths : ["Jan", "Fév", "Mar", "Avr", "Mai", "Jui", "Juol", "Aoû", "Sep", "Oct", "Nov", "Déc"],
+ AM : "AM",
+ PM : "PM",
+ am : "am",
+ pm : "pm",
+ dateType : "gregorian",
+ minYear : 1900,
+ maxYear : 2100,
+ exit: "Fermer",
+ save: "Effacer"
+};
\ No newline at end of file
diff --git a/media/system/js/fields/calendar-locales/prs-af.js b/media/system/js/fields/calendar-locales/prs-af.js
new file mode 100644
index 0000000000000..1ffca6583ca30
--- /dev/null
+++ b/media/system/js/fields/calendar-locales/prs-af.js
@@ -0,0 +1,20 @@
+window.JoomlaCalLocale = {
+ today : "امروز",
+ weekend : [5],
+ wk : "هفته",
+ time : "زمان",
+ days : ["یکشنبه","دوشنبه","سه شنبه","چهارشنبه","پنجشنبه","جمعه","شنبه"],
+ shortDays : ["یک","دو","سه","چهار","پنج","جمعه","شنبه"],
+ months : ["فروردین","اردیبهشت","خرداد","تیر","مرداد","شهریور","مهر","آبان","آذر","دی","بهمن","اسفند"],
+ shortMonths : ["فروردین","اردیبهشت","خرداد","تیر","مرداد","شهریور","مهر","آبان","آذر","دی","بهمن","اسفند"],
+ AM : "ق.ظ.",
+ PM : "ب.ظ.",
+ am : "ق.ظ.",
+ pm : "ب.ظ.",
+ dateType : "jalali",
+ minYear : 1268,
+ maxYear : 1458,
+ exit: "ستن",
+ save: "پاک",
+ localLangNumbers: ["۰","۱","۲","۳","۴","۵","۶","۷","۸","۹"]
+};
\ No newline at end of file
diff --git a/media/system/js/fields/calendar-vanilla.js b/media/system/js/fields/calendar-vanilla.js
new file mode 100644
index 0000000000000..e01f441143226
--- /dev/null
+++ b/media/system/js/fields/calendar-vanilla.js
@@ -0,0 +1,1070 @@
+/**
+ * @copyright Copyright (C) 2005 - 2016 Open Source Matters, Inc. All rights reserved.
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+!(function(window, document){
+ 'use strict';
+
+ /** Method to convert numbers to local symbols. */
+ Date.convertNumbers = function(str) {
+ var str = str.toString();
+
+ if (Object.prototype.toString.call(JoomlaCalLocale.localLangNumbers) === '[object Array]') {
+ for (var i = 0; i < JoomlaCalLocale.localLangNumbers.length; i++) {
+ str = str.replace(new RegExp(i, 'g'), JoomlaCalLocale.localLangNumbers[i]);
+ }
+ }
+ return str;
+ };
+
+ /** Traslates to english numbers a string. */
+ Date.toEnglish = function(str) {
+ str = this.toString();
+ var nums = [0,1,2,3,4,5,6,7,8,9];
+ for (var i = 0; i < 10; i++) {
+ str = str.replace(new RegExp(nums[i], 'g'), i);
+ }
+ return str;
+ };
+
+ var JoomlaCalendar = function (element) {
+
+ // Initialize only if the element exists
+ if (!element) {
+ throw new Error("Calendar setup failed:\n No valid element found, Please check your code");
+ }
+
+ if (typeof Date.parseFieldDate !== 'function') {
+ throw new Error("Calendar setup failed:\n No valid date helper, Please check your code");
+ }
+
+ if (element._joomlaCalendar) {
+ throw new Error('JoomlaCalendar instance already exists for the element');
+ }
+
+ element._joomlaCalendar = this;
+
+ this.writable = true;
+ this.params = {};
+ this.element = element;
+ this.inputField = element.getElementsByTagName('input')[0];
+ this.button = element.getElementsByTagName('button')[0];
+
+ if (!this.inputField) {
+ throw new Error("Calendar setup failed:\n No valid input found, Please check your code");
+ }
+
+ // Prepare the parameters
+ this.params = {
+ debug: false,
+ clicked: false,
+ element: {style: {display: "none"}},
+ writable: true
+ };
+
+ var self = this,
+ btn = this.button,
+ instanceParams = {
+ inputField : this.inputField,
+ dateType : JoomlaCalLocale.dateType ? JoomlaCalLocale.dateType : 'gregorian',
+ direction : (document.dir != undefined) ? document.dir : document.getElementsByTagName("html")[0].getAttribute("dir"),
+ firstDayOfWeek : btn.getAttribute("data-firstday") ? parseInt(btn.getAttribute("data-firstday")) : 0,
+ dateFormat : "%Y-%m-%d %H:%M:%S",
+ weekend : JoomlaCalLocale.weekend ? JoomlaCalLocale.weekend : [0,6],
+ minYear : JoomlaCalLocale.minYear ? JoomlaCalLocale.minYear : 1900,
+ maxYear : JoomlaCalLocale.maxYear ? JoomlaCalLocale.maxYear : 2100,
+ minYearTmp : btn.getAttribute("data-min-year"),
+ maxYearTmp : btn.getAttribute("data-max-year"),
+ weekendTmp : btn.getAttribute("data-weekend"),
+ time24 : true,
+ showsOthers : (parseInt(btn.getAttribute("data-show-others")) === 1) ? true : false,
+ showsTime : true,
+ weekNumbers : (parseInt(btn.getAttribute("data-week-numbers")) === 1) ? true : false,
+ showsTodayBtn : true,
+ compressedHeader: (parseInt(btn.getAttribute("data-only-months-nav")) === 1) ? true : false,
+ };
+
+ // Keep B/C
+ if (btn.getAttribute("data-dayformat")) {
+ instanceParams.dateFormat = btn.getAttribute("data-dayformat") ? btn.getAttribute("data-dayformat") : "%Y-%m-%d %H:%M:%S";
+ }
+
+ if (btn.getAttribute("data-time-24")) {
+ instanceParams.time24 = parseInt(btn.getAttribute("data-time-24")) === 24 ? true : false;
+ }
+
+ if (btn.getAttribute("data-show-time")) {
+ instanceParams.showsTime = parseInt(btn.getAttribute("data-show-time")) === 1 ? true : false;
+ }
+
+ if (btn.getAttribute("data-today-btn")) {
+ instanceParams.showsTodayBtn = parseInt(btn.getAttribute("data-today-btn")) === 1 ? true : false;
+ }
+
+ // Merge the parameters
+ for (var param in instanceParams) {
+ this.params[param] = instanceParams[param];
+ }
+
+ // Evaluate the min year
+ if (isInt(self.params.minYearTmp)) {
+ self.params.minYear = getBoundary(parseInt(self.params.minYearTmp), self.params.dateType);
+ }
+ // Evaluate the max year
+ if (isInt(self.params.maxYearTmp)) {
+ self.params.maxYear = getBoundary(parseInt(self.params.maxYearTmp), self.params.dateType);
+ }
+ // Evaluate the weekend days
+ if (self.params.weekendTmp !== "undefined") {
+ self.params.weekend = self.params.weekendTmp.split(',').map(function(item) { return parseInt(item, 10); });
+ }
+
+ // Event handler need to define here, to be able access in current context
+ this._dayMouseDown = function(event) {
+ return self._handleDayMouseDown(event);
+ };
+ this._calKeyEvent = function(event) {
+ return self._handleCalKeyEvent(event);
+ };
+ this._documentClick = function(event) {
+ return self._handleDocumentClick(event);
+ };
+
+ // Set it up
+ this.checkInputs();
+
+ // For the fields with readonly tag calendar will not initiate fully
+ if (this.inputField.getAttribute('readonly'))
+ return;
+
+ this._create();
+ this._bindEvents();
+ };
+
+ JoomlaCalendar.prototype.checkInputs = function () {
+ // Get the date from the input
+ var inputAltValueDate = Date.parseFieldDate(this.inputField.getAttribute('data-alt-value'), this.params.dateFormat, 'gregorian');
+
+ if (this.inputField.value.length) {
+ this.date = inputAltValueDate;
+ this.inputField.value = inputAltValueDate.print(this.params.dateFormat, this.params.dateType, true);
+ } else {
+ this.date = new Date();
+ }
+ };
+
+ /** Time Control */
+ JoomlaCalendar.prototype.updateTime = function (hours, mins, secs) {
+ var self = this,
+ date = self.date;
+
+ var d = self.date.getLocalDate(self.params.dateType),
+ m = self.date.getLocalMonth(self.params.dateType),
+ y = self.date.getLocalFullYear(self.params.dateType),
+ ampm = this.inputField.parentNode.parentNode.querySelectorAll('.time-ampm')[0];
+
+ if (!self.params.time24) {
+ if (/pm/i.test(ampm.value) && hours < 12)
+ hours = parseInt(hours) + 12;
+ else if (/am/i.test(ampm.value) && hours == 12)
+ hours = 0;
+ }
+
+ date.setHours(hours);
+ date.setMinutes(parseInt(mins, 10));
+ date.setSeconds(date.getSeconds());
+ date.setLocalFullYear(self.params.dateType, y);
+ date.setLocalMonth(self.params.dateType, m);
+ date.setLocalDate(self.params.dateType, d);
+ self.dateClicked = false;
+
+ this.callHandler();
+ };
+
+ /** Method to set the date to the given date object */
+ JoomlaCalendar.prototype.setDate = function (date) {
+ if (!date.equalsTo(this.date)) {
+ this.date = date;
+ this.processCalendar(this.params.firstDayOfWeek, date);
+ }
+ };
+
+ /** Method to set the current date by a number, step */
+ JoomlaCalendar.prototype.moveCursorBy = function (step) {
+ var date = new Date(this.date);
+ date.setDate(date.getDate() - step);
+ this.setDate(date);
+ };
+
+ /** Reset select element */
+ JoomlaCalendar.prototype.resetSelected = function (element) {
+ var options = element.options;
+ var i = options.length;
+ while (i--) {
+ var current = options[i];
+ if (current.selected) {
+ current.selected = false;
+ }
+ }
+ };
+
+ /** Method to set the value for the input field */
+ JoomlaCalendar.prototype.callHandler = function () {
+ /** Output the date **/
+ if (this.params.dateType == 'gregorian') {
+ this.inputField.setAttribute('data-alt-value', this.date.print(this.params.dateFormat, this.params.dateType, false));
+ if (this.inputField.getAttribute('data-alt-value') && this.inputField.getAttribute('data-alt-value') != '0000-00-00 00:00:00') {
+ this.inputField.value = this.date.print(this.params.dateFormat, this.params.dateType, true);
+ }
+ } else {
+ this.inputField.setAttribute('data-alt-value', this.date.print(this.params.dateFormat, 'gregorian', false));
+ this.inputField.setAttribute('data-local-value', this.date.print(this.params.dateFormat, this.params.dateType, false));
+ this.inputField.value = this.date.print(this.params.dateFormat, this.params.dateType, true);
+ }
+
+ if (typeof this.inputField.onchange == "function") {
+ this.inputField.onchange();
+ }
+
+ if (this.dateClicked && typeof this.params.onUpdate == "function") {
+ this.params.onUpdate(this);
+ }
+
+ if (this.dateClicked) {
+ this.close();
+ } else {
+ this.processCalendar();
+ }
+ };
+
+ /** Method to close/hide the calendar */
+ JoomlaCalendar.prototype.close = function () {
+ document.activeElement.blur();
+ this.hide();
+ };
+
+ /** Method to show the calendar. */
+ JoomlaCalendar.prototype.show = function () {
+ /** This is needed for IE8 */
+ if(navigator.appName.indexOf("Internet Explorer")!=-1){
+ var badBrowser=(
+ navigator.appVersion.indexOf("MSIE 9")==-1 &&
+ navigator.appVersion.indexOf("MSIE 1")==-1
+ );
+
+ if(badBrowser){
+ if (window.jQuery && jQuery().chosen) {
+ var selItems = this.element.getElementsByTagName('select');
+ for (var i = 0; i < selItems.length; i++) {
+ jQuery(selItems[i]).chosen('destroy');
+ }
+ }
+ }
+ }
+
+ this.checkInputs();
+ this.inputField.focus();
+ var rows = this.table.getElementsByTagName("tr");
+ for (var i = rows.length; i > 0;) {
+ var row = rows[--i];
+ var cells = row.getElementsByTagName("td");
+ for (var j = cells.length; j > 0;) {
+ var cell = cells[--j];
+ }
+ }
+
+ this.dropdownElement.style.display = "block";
+ this.hidden = false;
+
+ document.addEventListener("keydown", this._calKeyEvent, true);
+ document.addEventListener("keypress", this._calKeyEvent, true);
+ document.addEventListener("mousedown", this._documentClick, true);
+
+ /** Move the calendar to top position if it doesn't fit below. */
+ var containerTmp = this.element.querySelector('.js-calendar');
+
+ if ((window.innerHeight + window.scrollY) < containerTmp.getBoundingClientRect().bottom + 20) {
+ containerTmp.style.marginTop = - (containerTmp.getBoundingClientRect().height + this.inputField.getBoundingClientRect().height) + "px";
+ }
+
+ this.processCalendar();
+ };
+
+ /** Method to hide the calendar. */
+ JoomlaCalendar.prototype.hide = function () {
+ document.removeEventListener("keydown", this._calKeyEvent, true);
+ document.removeEventListener("keypress", this._calKeyEvent, true);
+ document.removeEventListener("mousedown", this._documentClick, true);
+
+ this.dropdownElement.style.display = "none";
+ this.hidden = true;
+ };
+
+ /** Method to catch clicks outside of the calendar (used as close call) */
+ JoomlaCalendar.prototype._handleDocumentClick = function (ev) {
+ var el = ev.target;
+
+ if (el !== null && !el.classList.contains('time')) {
+ for (; el !== null && el !== this.element; el = el.parentNode);
+ }
+
+ if (el === null) {
+ document.activeElement.blur();
+ this.hide();
+ return stopCalEvent(ev);
+ }
+ };
+
+ /** Method to handle mouse click events (menus, buttons) **/
+ JoomlaCalendar.prototype._handleDayMouseDown = function (ev) {
+ var self = this,
+ el = ev.currentTarget,
+ target = ev.target || ev.srcElement;
+
+ if (el.nodeName !== 'TD') { // A bootstrap inner button was pressed?
+ var testel = el.getParent('TD');
+ if (testel.nodeName === 'TD') { // Yes so use that element's td
+ el = testel;
+ } else { // No - try to find the table this way
+ el = el.getParent('TD');
+ if (el.classList.contains('js-calendar')) { el = el.getElementsByTagName('table')[0]; }
+ }
+ } else { // Check that doesn't have a button and is not a day td
+ if (!(target.classList.contains('js-btn')) && !el.classList.contains('day') && !el.classList.contains('title')) { return; }
+ }
+
+ if (!el || el.disabled)
+ return false;
+
+ if (typeof el.navtype === "undefined" || el.navtype != 300) {
+ if (el.navtype == 50) { el._current = el.innerHTML; }
+
+ if (target == el || target.parentNode == el) { self.cellClick(el, ev); }
+
+ var mon = null;
+ if (typeof el.month != "undefined") mon = el;
+ if (typeof el.parentNode.month != "undefined") mon = el.parentNode;
+ var date = null;
+ if (mon) {
+ date = new Date(self.date);
+ if (mon.month != date.getLocalMonth(self.params.dateType)) {
+ date.setLocalMonth(self.params.dateType, mon.month);
+ self.setDate(date);
+ self.dateClicked = false;
+ this.callHandler();
+ }
+ } else {
+ var year = null;
+ if (typeof el.year != "undefined") year = target;
+ if (typeof el.parentNode.year != "undefined") year = target.parentNode;
+ if (year) {
+ date = new Date(self.date);
+ if (year.year != date.getLocalFullYear(self.params.dateType)) {
+ date.setFullYear(self.params.dateType, year.year);
+ self.setDate(date);
+ self.dateClicked = false;
+ this.callHandler();
+ }
+ }
+ }
+ }
+
+ return stopCalEvent(ev);
+ };
+
+ /** Method to handle mouse click events (dates) **/
+ JoomlaCalendar.prototype.cellClick = function (el, ev) {
+ var self = this,
+ closing = false,
+ newdate = false,
+ date = null;
+
+ if (typeof el.navtype == "undefined") {
+ if (self.currentDateEl) {
+ el.classList.add("selected");
+ self.currentDateEl = el.caldate;
+ closing = (self.currentDateEl == el.caldate);
+ if (!closing) { self.currentDateEl = el.caldate; }
+ }
+ self.date.setLocalDateOnly('gregorian', el.caldate);
+ date = self.date;
+ var other_month = !(self.dateClicked = !el.otherMonth);
+ if (self.currentDateEl) { newdate = !el.disabled; }
+ if (other_month) {
+ this.processCalendar();
+ }
+ } else {
+ if (el.navtype == 100) {
+ el.classList.remove("hilite");
+ this.inputField.setAttribute('data-alt-value', "0000-00-00 00:00:00");
+ this.inputField.setAttribute('value', '');
+ this.inputField.value = '';
+ this.close();
+ return;
+ }
+ if (el.navtype == 200) {
+ this.close();
+ return;
+ }
+ date = new Date(self.date);
+ if (el.navtype == 0) {
+ self.date.setLocalDateOnly('gregorian', new Date()); // TODAY
+ self.dateClicked = true;
+ this.callHandler();
+ this.close();
+ return;
+ }
+
+ self.dateClicked = false;
+ var year = date.getOtherFullYear(self.params.dateType), mon = date.getLocalMonth(self.params.dateType);
+ switch (el.navtype) {
+ case 400:
+ break;
+ case -2: // Prev year
+ if (!self.params.compressedHeader)
+ if (year > self.params.minYear) {
+ date.setOtherFullYear(self.params.dateType, year - 1);
+ }
+ break;
+ case -1: // Prev month
+ var day = date.getLocalDate(self.params.dateType);
+ if (mon > 0) {
+ var max = date.getLocalMonthDays(self.params.dateType, mon - 1);
+ if (day > max) date.setLocalDate(self.params.dateType, max);
+ date.setLocalMonth(self.params.dateType, mon - 1);
+ } else if (year-- > self.params.minYear) {
+ date.setOtherFullYear(self.params.dateType, year);
+ var max = date.getLocalMonthDays(self.params.dateType, 11);
+ if (day > max) date.setLocalDate(self.params.dateType, max);
+ date.setLocalMonth(self.params.dateType, 11);
+ }
+ break;
+ case 1: // Next month
+ var day = date.getLocalDate(self.params.dateType);
+ if (mon < 11) {
+ var max = date.getLocalMonthDays(self.params.dateType, mon + 1);
+ if (day > max) date.setLocalDate(self.params.dateType, max);
+ date.setLocalMonth(self.params.dateType, mon + 1);
+ } else if (year < self.params.maxYear) {
+ date.setOtherFullYear(self.params.dateType, year + 1);
+ var max = date.getLocalMonthDays(self.params.dateType, 0);
+ if (day > max) date.setLocalDate(self.params.dateType, max);
+ date.setLocalMonth(self.params.dateType, 0);
+ }
+ break;
+ case 2: // Next year
+ if (!self.params.compressedHeader)
+ if (year < self.params.maxYear) {
+ date.setOtherFullYear(self.params.dateType, year + 1);
+ }
+ break;
+ case 0: // Today
+ break;
+ }
+
+ if (!date.equalsTo(self.date)) {
+ this.setDate(date);
+ newdate = true;
+ } else if (el.navtype == 0) {
+ newdate = closing = true;
+ }
+ }
+
+ if (newdate) {
+ if (self.params.showsTime)
+ this.dateClicked = false;
+ ev && this.callHandler();
+ }
+
+ el.classList.remove("hilite");
+
+ if (closing && !self.params.showsTime) {
+ self.dateClicked = false;
+ ev && this.close();
+ }
+ };
+
+ /** Method to handle keyboard click events **/
+ JoomlaCalendar.prototype._handleCalKeyEvent = function (ev) {
+ var self = this,
+ K = parseInt(ev.keyCode);
+
+ // Get value from input
+ if (ev.target === this.inputField && (K === 13 || K === 9) && !ev.shiftKey) {
+ if (this.inputField.value != '') {
+ self.date = Date.parseFieldDate(this.inputField.value, self.params.dateFormat, self.params.dateType);
+ this.processCalendar(self.params.firstDayOfWeek, self.date);
+ this.cellClick(self.currentDateEl, ev);
+ } else {
+ this.inputField.value = '';
+ this.inputField.setAttribute('value', '');
+ this.inputField.setAttribute('data-alt-value', '0000-00-00 00:00:00');
+ }
+ this.close();
+ }
+
+ if (self.params.direction == 'rtl') {
+ if (K == 37) K = 39;
+ else if (K == 39) K = 37;
+ }
+
+ if (ev.shiftKey && K === 32) { // KEY Shift + space (now)
+ this.cellClick(self._nav_now, ev);
+ }
+ if (K === 27) { // KEY esc (close);
+ this.close();
+ }
+ if (ev.shiftKey && K === 13) { // KEY enter (select and close)
+ if (this.params.showsTime)
+ this.dateClicked = false;
+ this.cellClick(self.currentDateEl, ev);
+ }
+ if (K === 38) { // KEY up (previous week)
+ this.moveCursorBy(7);
+ }
+ if (K === 40) { // KEY down (next week)
+ this.moveCursorBy( -7);
+ }
+ if (K === 37) { // KEY left (previous day)
+ this.moveCursorBy(1);
+ }
+ if (K === 39) { // KEY right (next day)
+ this.moveCursorBy( -1);
+ }
+ if (ev.target === this.inputField && !(K>48 || K<57 || K===186 || K===189 || K===190 || K === 32))
+ return stopCalEvent(ev);
+ };
+
+ /** Method to create the html stracture of the calendar */
+ JoomlaCalendar.prototype._create = function () {
+ var self = this,
+ parent = this.element,
+ table = createElement("table"),
+ div = createElement("div");
+
+ this.table = table;
+ table.className = 'table';
+ table.cellSpacing = 0;
+ table.cellPadding = 0;
+ table.style.marginBottom = 0;
+
+ this.dropdownElement = div;
+ parent.appendChild(div);
+
+ if (this.params.direction) {
+ div.style.direction = this.params.direction;
+ }
+
+ div.className = 'js-calendar';
+ div.style.position = "absolute";
+ div.style.boxShadow = "0px 0px 70px 0px rgba(0,0,0,0.67)";
+ div.style.minWidth = this.inputField.width;
+ div.style.padding = '0';
+ div.style.display = "none";
+ div.style.left = "auto";
+ div.style.top = "auto";
+ div.style.zIndex = 1060;
+ div.style.borderRadius = "20px";
+
+ this.wrapper = createElement('div');
+ this.wrapper.className = 'calendar-container';
+ div.appendChild(this.wrapper);
+ this.wrapper.appendChild(table);
+
+ var thead = createElement("thead", table);
+ thead.className = 'calendar-header';
+
+ var cell = null,
+ row = null,
+ cal = this,
+ hh = function (text, cs, navtype, node, styles, classes) {
+ node = node ? node : "td";
+ classes = classes ? 'class="' + classes + '"' : '';
+ styles = styles ? styles : {};
+ cell = createElement(node, row);
+ cell.colSpan = cs;
+ for (var key in styles) {
+ cell.style[key] = styles[key];
+ }
+ if (navtype != 0 && Math.abs(navtype) <= 2) { cell.className += " nav"; }
+
+ cell.addEventListener("mousedown", self._dayMouseDown, true);
+
+ cell.calendar = cal;
+ cell.navtype = navtype;
+ if (navtype != 0 && Math.abs(navtype) <= 2) {
+ cell.innerHTML = "" + text + " ";
+ } else {
+ cell.innerHTML = "" + text + "
";
+ }
+ return cell;
+ };
+
+ if (this.params.compressedHeader === false) { // Head - year
+ row = createElement("tr", thead);
+ row.className = "calendar-head-row";
+ this._nav_py = hh("‹", 1, -2, '', {"text-align": "center", "font-size": "2em", "line-height": "1em"}, 'js-btn btn-prev-year'); // Previous year button
+ this.title = hh('
', this.params.weekNumbers ? 6 : 5, 300);
+ this.title.className = "title";
+ this._nav_ny = hh(" ›", 1, 2, '', {"text-align": "center", "font-size": "2em", "line-height": "1em"}, 'js-btn btn-next-year'); // Next year button
+ }
+
+ row = createElement("tr", thead); // Head - month
+ row.className = "calendar-head-row";
+ this._nav_pm = hh("‹", 1, -1, '', {"text-align": "center", "font-size": "2em", "line-height": "1em"}, 'js-btn btn-prev-month'); // Previous month button
+ this._nav_month = hh('
', this.params.weekNumbers ? 6 : 5, 888, 'td', {'textAlign': 'center'});
+ this._nav_month.className = "title";
+ this._nav_nm = hh(" ›", 1, 1, '', {"text-align": "center", "font-size": "2em", "line-height": "1em"}, 'js-btn btn-next-month'); // Next month button
+
+ row = createElement("tr", thead); // day names
+ row.className = "daynames";
+ if (this.params.weekNumbers) {
+ cell = createElement("td", row);
+ cell.className = "day-name wn";
+ cell.innerHTML = JoomlaCalLocale.wk;
+ }
+ for (var i = 7; i > 0; --i) {
+ cell = createElement("td", row);
+ if (!i) { cell.calendar = self; }
+ }
+ this.firstdayname = (this.params.weekNumbers) ? row.firstChild.nextSibling : row.firstChild;
+
+ var fdow = this.params.firstDayOfWeek,
+ cell = this.firstdayname,
+ weekend = JoomlaCalLocale.weekend;
+
+ for (var i = 0; i < 7; ++i) {
+ var realday = (i + fdow) % 7;
+ cell.classList.add("day-name");
+ this.params.weekNumbers ? cell.classList.add('day-name-week') : '';
+
+ if (i) {
+ cell.calendar = self;
+ cell.fdow = realday;
+ }
+ if (weekend.indexOf(weekend) != -1) { cell.classList.add("weekend"); }
+
+ cell.innerHTML = JoomlaCalLocale.shortDays[(i + fdow) % 7];
+ cell = cell.nextSibling;
+ }
+
+ var tbody = createElement("tbody", table);
+ this.tbody = tbody;
+ for (i = 6; i > 0; --i) {
+ row = createElement("tr", tbody);
+ if (this.params.weekNumbers) { cell = createElement("td", row); }
+
+ for (var j = 7; j > 0; --j) {
+ cell = createElement("td", row);
+ cell.calendar = this;
+ cell.addEventListener("mousedown", this._dayMouseDown, true);
+ }
+ }
+
+ if (this.params.showsTime) {
+ row = createElement("tr", tbody);
+ row.className = "time";
+
+ cell = createElement("td", row);
+ cell.className = "time time-title";
+ cell.colSpan = 1;
+ cell.style.verticalAlign = 'middle';
+ cell.innerHTML = JoomlaCalLocale.time || " ";
+
+ var cell1 = createElement("td", row);
+ cell1.className = "time hours-select";
+ cell1.colSpan = 2;
+
+ var cell2 = createElement("td", row);
+ cell2.className = "time minutes-select";
+ cell2.colSpan = 2;
+
+ (function () {
+ function makeTimePart(className, selected, range_start, range_end, cellTml) {
+ var part = createElement("select", cellTml), num;
+ part.calendar = self;
+ part.className = className;
+ part.setAttribute('data-chosen', true); // avoid Chosen, hack
+ part.style.width = '100%';
+ part.navtype = 50;
+ part._range = [];
+ for (var i = range_start; i <= range_end; ++i) {
+ var txt, selAttr = '';
+ if (i == selected)
+ selAttr = true;
+ if (i < 10 && range_end >= 10) {
+ num = '0' + i;
+ txt = Date.convertNumbers('0') + Date.convertNumbers(i);
+ } else {
+ num = '' + i;
+ txt = '' + Date.convertNumbers(i);
+ }
+ part.options.add(new Option(txt, num, selAttr, selAttr));
+ }
+ return part;
+ }
+ var hrs = self.date.getHours(),
+ mins = self.date.getMinutes(),
+ t12 = !self.params.time24,
+ pm = (self.date.getHours() > 12);
+
+ if (t12 && pm) {
+ hrs -= 12;
+ }
+
+ var H = makeTimePart("time time-hours", hrs, t12 ? 1 : 0, t12 ? 12 : 23, cell1),
+ M = makeTimePart("time time-minutes", mins, 0, 59, cell2),
+ AP = null;
+
+ cell = createElement("td", row);
+ cell.className = "time ampm-select";
+ cell.colSpan = self.params.weekNumbers ? 1 : 2;
+
+ if (t12) {
+ var selAttr = true,
+ altDate = Date.parseFieldDate(self.inputField.getAttribute('data-alt-value'), self.params.dateFormat, 'gregorian');
+ pm = (altDate.getHours() > 12);
+
+ var part = createElement("select", cell);
+ part.className = "time-ampm";
+ part.style.width = '100%';
+ part.options.add(new Option(JoomlaCalLocale.PM, "pm", pm ? selAttr : '', pm ? selAttr : ''));
+ part.options.add(new Option(JoomlaCalLocale.AM, "am", pm ? '' : selAttr, pm ? '' : selAttr));
+ AP = part;
+
+ // Event listener for the am/pm select
+ AP.addEventListener("change", function (event) {
+ self.updateTime(event.target.parentNode.parentNode.childNodes[1].childNodes[0].value,
+ event.target.parentNode.parentNode.childNodes[2].childNodes[0].value,
+ event.target.parentNode.parentNode.childNodes[3].childNodes[0].value);
+ }, false);
+ } else {
+ cell.innerHTML = " ";
+ cell.colSpan = self.params.weekNumbers ? 4 : 3;
+ }
+
+ H.addEventListener("change", function (event) {
+ self.updateTime(event.target.parentNode.parentNode.childNodes[1].childNodes[0].value,
+ event.target.parentNode.parentNode.childNodes[2].childNodes[0].value,
+ event.target.parentNode.parentNode.childNodes[3].childNodes[0].value);
+ }, false);
+ M.addEventListener("change", function (event) {
+ self.updateTime(event.target.parentNode.parentNode.childNodes[1].childNodes[0].value,
+ event.target.parentNode.parentNode.childNodes[2].childNodes[0].value,
+ event.target.parentNode.parentNode.childNodes[3].childNodes[0].value);
+ }, false);
+ })();
+ }
+
+ row = createElement("tr", tbody);
+ row.className = "btn-row";
+ var clearAttr = this.inputField.hasAttribute('required') ? 'none;' : 'block;';
+ this._nav_save = hh(''
+ + JoomlaCalLocale.save + ' ', 2, 100, 'td', {'textAlign': 'center'});
+
+ if (!this.inputField.hasAttribute('required')) {
+ var savea = row.querySelector('a[data-action="clear"]');
+ savea.addEventListener("click",
+ function (e) {
+ var el = savea.parentNode.parentNode;
+ if (el.tagName === 'TD') {
+ self.cellClick(self._nav_save, e);
+ }
+ }, true);
+ }
+
+ if (this.params.showsTodayBtn) { // Head - today
+
+ this._nav_now = hh(''
+ + JoomlaCalLocale.today + ' ', this.params.weekNumbers ? 4 : 3, 0, 'td', {'textAlign': 'center'});
+ var todaya = row.querySelector('a[data-action="today"]');
+ todaya.addEventListener('click', function (e) {
+ var el = todaya.parentNode.parentNode;
+ if (el.tagName === 'TD') { self.cellClick(self._nav_now, e); }
+ }, true);
+ } else {
+ cell = createElement("td", row);
+ cell.innerHTML = " ";
+ cell.colSpan = this.params.weekNumbers ? 4 : 3;
+ }
+
+ this._nav_exit = hh(''
+ + JoomlaCalLocale.exit + ' ', 2, 200, 'td', {'textAlign': 'center'});
+ var exita = row.querySelector('a[data-action="exit"]');
+ exita.addEventListener('click', function (e) {
+ var el = exita.parentNode.parentNode;
+ if (el.tagName === 'TD') { self.cellClick(self._nav_exit, e); }
+ }, true);
+
+ this.processCalendar();
+ };
+
+ /** Method to append numbers to the calendar table */
+ JoomlaCalendar.prototype.processCalendar = function () {
+ this.table.style.visibility = "hidden";
+
+ var firstDayOfWeek = this.params.firstDayOfWeek,
+ date = this.date,
+ today = new Date(),
+ TY = today.getLocalFullYear(this.params.dateType),
+ TM = today.getLocalMonth(this.params.dateType),
+ TD = today.getLocalDate(this.params.dateType),
+ year = date.getOtherFullYear(this.params.dateType),
+ hrs = date.getHours(),
+ mins = date.getMinutes(),
+ secs = date.getSeconds(),
+ t12 = !this.params.time24;
+
+ if (year < this.params.minYear) { // Check min,max year
+ year = this.params.minYear;
+ date.getOtherFullYear(this.params.dateType, year);
+ } else if (year > this.params.maxYear) {
+ year = this.params.maxYear;
+ date.getOtherFullYear(this.params.dateType, year);
+ }
+
+ this.params.firstDayOfWeek = firstDayOfWeek;
+ this.date = new Date(date);
+
+ var month = date.getLocalMonth(this.params.dateType);
+ var mday = date.getLocalDate(this.params.dateType);
+
+ // Compute the first day that would actually be displayed in the calendar, even if it's from the previous month.
+ date.setLocalDate(this.params.dateType, 1);
+ var day1 = (date.getLocalDay(this.params.dateType) - this.params.firstDayOfWeek) % 7;
+
+ if (day1 < 0) {
+ day1 += 7;
+ }
+
+ date.setLocalDate(this.params.dateType, - day1);
+ date.setLocalDate(this.params.dateType, date.getLocalDate(this.params.dateType) + 1);
+
+ var row = this.tbody.firstChild,
+ ar_days = this.ar_days = new Array(),
+ weekend = JoomlaCalLocale.weekend,
+ monthDays = parseInt(date.getLocalWeekDays(this.params.dateType));
+
+ /** Fill the table **/
+ for (var i = 0; i < monthDays; ++i, row = row.nextSibling) {
+ var cell = row.firstChild;
+ if (this.params.weekNumbers) {
+ cell.className = "day wn";
+ cell.innerHTML = date.getLocalWeekNumber(this.params.dateType); //date.convertNumbers();
+ cell = cell.nextSibling;
+ }
+
+ row.className = "daysrow";
+ var hasdays = false, iday,
+ dpos = ar_days[i] = [],
+ totalDays = monthDays + 1;
+
+ for (var j = 0; j < totalDays; ++j, cell = cell.nextSibling, date.setLocalDate(this.params.dateType, iday + 1)) {
+ cell.className = "day";
+ cell.style['textAlign'] = 'center';
+ iday = date.getLocalDate(this.params.dateType);
+ var wday = date.getLocalDay(this.params.dateType);
+ cell.pos = i << 4 | j;
+ dpos[j] = cell;
+ var current_month = (date.getLocalMonth(this.params.dateType) == month);
+ if (!current_month) {
+ if (this.params.showsOthers) {
+ cell.className += " disabled othermonth ";
+ cell.otherMonth = true;
+ } else {
+ cell.className += " emptycell";
+ cell.innerHTML = " ";
+ cell.disabled = true;
+ continue;
+ }
+ } else {
+ cell.otherMonth = false;
+ hasdays = true;
+ cell.style.cursor = "pointer";
+ }
+ cell.disabled = false;
+ cell.innerHTML = this.params.debug ? iday : Date.convertNumbers(iday); // translated day number for each cell
+ if (!cell.disabled) {
+ cell.caldate = new Date(date);
+ if (current_month && iday == mday) {
+ cell.className += " selected";
+ this.currentDateEl = cell;
+ }
+ if (date.getLocalFullYear(this.params.dateType) == TY && date.getLocalMonth(this.params.dateType) == TM && iday == TD) {
+ cell.className += " today";
+ }
+ if (weekend.indexOf(wday) != -1)
+ cell.className += " weekend";
+ }
+ }
+ if (!(hasdays || this.params.showsOthers)) {
+ row.style.display = 'none';
+ row.className = "emptyrow";
+ } else {
+ row.style.display = '';
+ }
+ }
+
+ /* Set the time */
+ if (this.params.showsTime) {
+ if (hrs > 12 && t12) hrs -= 12;
+
+ hrs = (hrs < 10) ? "0" + hrs : hrs;
+ mins = (mins < 10) ? "0" + mins : mins;
+
+ var hoursEl = this.table.querySelector('.time-hours'),
+ minsEl = this.table.querySelector('.time-minutes');
+
+ /* remove the selected class for the hours*/
+ this.resetSelected(hoursEl);
+ hoursEl.value = hrs;
+
+ /* remove the selected class for the minutes*/
+ this.resetSelected(minsEl);
+ minsEl.value = mins;
+
+ if (!this.params.time24)
+ {
+ var dateAlt = new Date(this.inputField.getAttribute('data-alt-value')),
+ ampmEl = this.table.querySelector('.time-ampm'),
+ hrsAlt = dateAlt.getHours();
+
+ if (hrsAlt > 12) {
+ /* remove the selected class for the am-pm*/
+ this.resetSelected(ampmEl);
+ ampmEl.value = 'pm';
+ }
+ }
+ }
+
+ if (!this.params.compressedHeader) {
+ this._nav_month.getElementsByTagName('span')[0].innerHTML = this.params.debug ? month + ' ' + JoomlaCalLocale.months[month] : JoomlaCalLocale.months[month];
+ this.title.getElementsByTagName('span')[0].innerHTML = this.params.debug ? year + ' ' + Date.convertNumbers(year.toString()) : Date.convertNumbers(year.toString());
+ } else {
+ var tmpYear = Date.convertNumbers(year.toString());
+ this._nav_month.getElementsByTagName('span')[0].innerHTML = !this.params.monthBefore ? JoomlaCalLocale.months[month] + ' - ' + tmpYear : tmpYear + ' - ' + JoomlaCalLocale.months[month] ;
+ }
+ this.table.style.visibility = "visible";
+ };
+
+ /** Method to listen for the click event on the input button. **/
+ JoomlaCalendar.prototype._bindEvents = function () {
+ var self = this;
+ this.inputField.addEventListener('focus', function() {
+ self.show();
+ }, true);
+ this.inputField.addEventListener('blur', function(event) {
+ if (event.relatedTarget != null && (event.relatedTarget.classList.contains('time-hours') || event.relatedTarget.classList.contains('time-minutes') || event.relatedTarget.classList.contains('time-ampm'))) return;
+ var elem = event.target;
+ while (elem.parentNode) {
+ elem = elem.parentNode;
+ if (elem.classList.contains('field-calendar')) return;
+ }
+ self.close();
+ }, true);
+ this.button.addEventListener('click', function() {
+ self.show();
+ }, false);
+ };
+
+ /** Helpers **/
+ var stopCalEvent = function (ev) { ev || (ev = window.event); ev.preventDefault(); ev.stopPropagation(); return false; };
+ var createElement = function (type, parent) { var el = null; el = document.createElement(type); if (typeof parent != "undefined") { parent.appendChild(el); } return el; };
+ var isInt = function (input) { return !isNaN(input) && (function(x) { return (x | 0) === x; })(parseFloat(input)) };
+ var getBoundary = function (input, type) { var date = new Date(); var y = date.getLocalFullYear(type); return y + input; };
+ /**
+ * IE8 polyfill for indexOf()
+ */
+ if (!Array.prototype.indexOf) {
+ Array.prototype.indexOf = function(elt) {
+ var len = this.length >>> 0,
+ from = Number(arguments[1]) || 0;
+
+ from = (from < 0) ? Math.ceil(from) : Math.floor(from);
+
+ if (from < 0) {
+ from += len;
+ }
+
+ for (; from < len; from++) {
+ if (from in this && this[from] === elt) {
+ return from;
+ }
+ }
+ return -1;
+ };
+ }
+
+ /** Method to get the active calendar element through any descendant element. */
+ JoomlaCalendar.getCalObject = function(element) {
+ if (!element) {
+ return false;
+ }
+ while (element.parentNode) {
+ element = element.parentNode;
+ if (element.classList.contains('field-calendar')) {
+ return element;
+ }
+ }
+ return false;
+ };
+
+ /** Method to change input values with the data-alt-value values. **/
+ JoomlaCalendar.prototype.setAltValue = function() {
+ var input = this.inputField;
+
+ if (input.getAttribute("data-alt-value") && input.getAttribute("data-alt-value")) {
+ input.value = input.getAttribute("data-alt-value");
+ } else {
+ input.value = '';
+ }
+ };
+
+ /** Init the Calendars on the page */
+ JoomlaCalendar.init = function (className) {
+ var elements, i, instance;
+
+ elements = document.querySelectorAll(className);
+
+ // Fall back for translation strings
+ window.JoomlaCalLocale = window.JoomlaCalLocale ? JoomlaCalLocale : {};
+ JoomlaCalLocale.today = JoomlaCalLocale.today ? JoomlaCalLocale.today : 'today';
+ JoomlaCalLocale.weekend = JoomlaCalLocale.weekend ? JoomlaCalLocale.weekend : [0,6];
+ JoomlaCalLocale.localLangNumbers = JoomlaCalLocale.localLangNumbers ? JoomlaCalLocale.localLangNumbers : [0,1,2,3,4,5,6,7,8,9];
+ JoomlaCalLocale.wk = JoomlaCalLocale.wk ? JoomlaCalLocale.wk : 'wk';
+ JoomlaCalLocale.AM = JoomlaCalLocale.AM ? JoomlaCalLocale.AM : 'AM';
+ JoomlaCalLocale.PM = JoomlaCalLocale.PM ? JoomlaCalLocale.PM : 'PM';
+ JoomlaCalLocale.am = JoomlaCalLocale.am ? JoomlaCalLocale.am : 'am';
+ JoomlaCalLocale.pm = JoomlaCalLocale.pm ? JoomlaCalLocale.pm : 'pm';
+ JoomlaCalLocale.dateType = JoomlaCalLocale.dateType ? JoomlaCalLocale.dateType : 'gregorian';
+ JoomlaCalLocale.time = JoomlaCalLocale.time ? JoomlaCalLocale.time : 'time';
+ JoomlaCalLocale.days = JoomlaCalLocale.days ? JoomlaCalLocale.days : '["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]';
+ JoomlaCalLocale.shortDays = JoomlaCalLocale.shortDays ? JoomlaCalLocale.shortDays : ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
+ JoomlaCalLocale.months = JoomlaCalLocale.months ? JoomlaCalLocale.months : ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
+ JoomlaCalLocale.shortMonths = JoomlaCalLocale.shortMonths ? JoomlaCalLocale.shortMonths : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
+ JoomlaCalLocale.minYear = JoomlaCalLocale.minYear ? JoomlaCalLocale.minYear : 1900;
+ JoomlaCalLocale.maxYear = JoomlaCalLocale.maxYear ? JoomlaCalLocale.maxYear : 2100;
+ JoomlaCalLocale.exit = JoomlaCalLocale.exit ? JoomlaCalLocale.exit : 'Cancel';
+ JoomlaCalLocale.clear = JoomlaCalLocale.clear ? JoomlaCalLocale.clear : 'Clear';
+
+ for (i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ instance = element._joomlaCalendar;
+
+ if (!instance) {
+ instance = new JoomlaCalendar(element);
+ instance.inputField.form.addEventListener('submit', function () {
+ instance.setAltValue();
+ });
+ }
+ }
+ };
+
+ window.JoomlaCalendar = JoomlaCalendar;
+
+ /** Instantiate all the calendar fields when the document is ready */
+ document.addEventListener("DOMContentLoaded", function() {
+ JoomlaCalendar.init(".field-calendar");
+ });
+
+})(window, document);
diff --git a/media/system/js/fields/calendar-vanilla.min.js b/media/system/js/fields/calendar-vanilla.min.js
new file mode 100644
index 0000000000000..b0cd29a6ec09e
--- /dev/null
+++ b/media/system/js/fields/calendar-vanilla.min.js
@@ -0,0 +1 @@
+!function(a,b){"use strict";Date.convertNumbers=function(a){var a=a.toString();if("[object Array]"===Object.prototype.toString.call(JoomlaCalLocale.localLangNumbers))for(var b=0;b0;)for(var g=f[--e],h=g.getElementsByTagName("td"),i=h.length;i>0;){h[--i]}this.dropdownElement.style.display="block",this.hidden=!1,b.addEventListener("keydown",this._calKeyEvent,!0),b.addEventListener("keypress",this._calKeyEvent,!0),b.addEventListener("mousedown",this._documentClick,!0);var k=this.element.querySelector(".js-calendar");a.innerHeight+a.scrollYc.params.minYear&&f.setOtherFullYear(c.params.dateType,h-1);break;case-1:var j=f.getLocalDate(c.params.dateType);if(i>0){var k=f.getLocalMonthDays(c.params.dateType,i-1);j>k&&f.setLocalDate(c.params.dateType,k),f.setLocalMonth(c.params.dateType,i-1)}else if(h-- >c.params.minYear){f.setOtherFullYear(c.params.dateType,h);var k=f.getLocalMonthDays(c.params.dateType,11);j>k&&f.setLocalDate(c.params.dateType,k),f.setLocalMonth(c.params.dateType,11)}break;case 1:var j=f.getLocalDate(c.params.dateType);if(i<11){var k=f.getLocalMonthDays(c.params.dateType,i+1);j>k&&f.setLocalDate(c.params.dateType,k),f.setLocalMonth(c.params.dateType,i+1)}else if(hk&&f.setLocalDate(c.params.dateType,k),f.setLocalMonth(c.params.dateType,0)}break;case 2:c.params.compressedHeader||h48||c<57||186===c||189===c||190===c||32===c))return d(a)},c.prototype._create=function(){var a=this,b=this.element,c=e("table"),d=e("div");this.table=c,c.className="table",c.cellSpacing=0,c.cellPadding=0,c.style.marginBottom=0,this.dropdownElement=d,b.appendChild(d),this.params.direction&&(d.style.direction=this.params.direction),d.className="js-calendar",d.style.position="absolute",d.style.boxShadow="0px 0px 70px 0px rgba(0,0,0,0.67)",d.style.minWidth=this.inputField.width,d.style.padding="0",d.style.display="none",d.style.left="auto",d.style.top="auto",d.style.zIndex=1060,d.style.borderRadius="20px",this.wrapper=e("div"),this.wrapper.className="calendar-container",d.appendChild(this.wrapper),this.wrapper.appendChild(c);var f=e("thead",c);f.className="calendar-header";var g=null,h=null,i=this,j=function(b,c,d,f,j,k){f=f?f:"td",k=k?'class="'+k+'"':"",j=j?j:{},g=e(f,h),g.colSpan=c;for(var l in j)g.style[l]=j[l];return 0!=d&&Math.abs(d)<=2&&(g.className+=" nav"),g.addEventListener("mousedown",a._dayMouseDown,!0),g.calendar=i,g.navtype=d,0!=d&&Math.abs(d)<=2?g.innerHTML=""+b+" ":g.innerHTML=""+b+"
",g};this.params.compressedHeader===!1&&(h=e("tr",f),h.className="calendar-head-row",this._nav_py=j("‹",1,-2,"",{"text-align":"center","font-size":"2em","line-height":"1em"},"js-btn btn-prev-year"),this.title=j('
',this.params.weekNumbers?6:5,300),this.title.className="title",this._nav_ny=j(" ›",1,2,"",{"text-align":"center","font-size":"2em","line-height":"1em"},"js-btn btn-next-year")),h=e("tr",f),h.className="calendar-head-row",this._nav_pm=j("‹",1,-1,"",{"text-align":"center","font-size":"2em","line-height":"1em"},"js-btn btn-prev-month"),this._nav_month=j('
',this.params.weekNumbers?6:5,888,"td",{textAlign:"center"}),this._nav_month.className="title",this._nav_nm=j(" ›",1,1,"",{"text-align":"center","font-size":"2em","line-height":"1em"},"js-btn btn-next-month"),h=e("tr",f),h.className="daynames",this.params.weekNumbers&&(g=e("td",h),g.className="day-name wn",g.innerHTML=JoomlaCalLocale.wk);for(var k=7;k>0;--k)g=e("td",h),k||(g.calendar=a);this.firstdayname=this.params.weekNumbers?h.firstChild.nextSibling:h.firstChild;for(var l=this.params.firstDayOfWeek,g=this.firstdayname,m=JoomlaCalLocale.weekend,k=0;k<7;++k){var n=(k+l)%7;g.classList.add("day-name"),this.params.weekNumbers?g.classList.add("day-name-week"):"",k&&(g.calendar=a,g.fdow=n),m.indexOf(m)!=-1&&g.classList.add("weekend"),g.innerHTML=JoomlaCalLocale.shortDays[(k+l)%7],g=g.nextSibling}var o=e("tbody",c);for(this.tbody=o,k=6;k>0;--k){h=e("tr",o),this.params.weekNumbers&&(g=e("td",h));for(var p=7;p>0;--p)g=e("td",h),g.calendar=this,g.addEventListener("mousedown",this._dayMouseDown,!0)}if(this.params.showsTime){h=e("tr",o),h.className="time",g=e("td",h),g.className="time time-title",g.colSpan=1,g.style.verticalAlign="middle",g.innerHTML=JoomlaCalLocale.time||" ";var q=e("td",h);q.className="time hours-select",q.colSpan=2;var r=e("td",h);r.className="time minutes-select",r.colSpan=2,function(){function b(b,c,d,f,g){var i,h=e("select",g);h.calendar=a,h.className=b,h.setAttribute("data-chosen",!0),h.style.width="100%",h.navtype=50,h._range=[];for(var j=d;j<=f;++j){var k,l="";j==c&&(l=!0),j<10&&f>=10?(i="0"+j,k=Date.convertNumbers("0")+Date.convertNumbers(j)):(i=""+j,k=""+Date.convertNumbers(j)),h.options.add(new Option(k,i,l,l))}return h}var c=a.date.getHours(),d=a.date.getMinutes(),f=!a.params.time24,i=a.date.getHours()>12;f&&i&&(c-=12);var j=b("time time-hours",c,f?1:0,f?12:23,q),k=b("time time-minutes",d,0,59,r),l=null;if(g=e("td",h),g.className="time ampm-select",g.colSpan=a.params.weekNumbers?1:2,f){var m=!0,n=Date.parseFieldDate(a.inputField.getAttribute("data-alt-value"),a.params.dateFormat,"gregorian");i=n.getHours()>12;var o=e("select",g);o.className="time-ampm",o.style.width="100%",o.options.add(new Option(JoomlaCalLocale.PM,"pm",i?m:"",i?m:"")),o.options.add(new Option(JoomlaCalLocale.AM,"am",i?"":m,i?"":m)),l=o,l.addEventListener("change",function(b){a.updateTime(b.target.parentNode.parentNode.childNodes[1].childNodes[0].value,b.target.parentNode.parentNode.childNodes[2].childNodes[0].value,b.target.parentNode.parentNode.childNodes[3].childNodes[0].value)},!1)}else g.innerHTML=" ",g.colSpan=a.params.weekNumbers?4:3;j.addEventListener("change",function(b){a.updateTime(b.target.parentNode.parentNode.childNodes[1].childNodes[0].value,b.target.parentNode.parentNode.childNodes[2].childNodes[0].value,b.target.parentNode.parentNode.childNodes[3].childNodes[0].value)},!1),k.addEventListener("change",function(b){a.updateTime(b.target.parentNode.parentNode.childNodes[1].childNodes[0].value,b.target.parentNode.parentNode.childNodes[2].childNodes[0].value,b.target.parentNode.parentNode.childNodes[3].childNodes[0].value)},!1)}()}h=e("tr",o),h.className="btn-row";var s=this.inputField.hasAttribute("required")?"none;":"block;";if(this._nav_save=j(''+JoomlaCalLocale.save+" ",2,100,"td",{textAlign:"center"}),!this.inputField.hasAttribute("required")){var t=h.querySelector('a[data-action="clear"]');t.addEventListener("click",function(b){var c=t.parentNode.parentNode;"TD"===c.tagName&&a.cellClick(a._nav_save,b)},!0)}if(this.params.showsTodayBtn){this._nav_now=j(''+JoomlaCalLocale.today+" ",this.params.weekNumbers?4:3,0,"td",{textAlign:"center"});var u=h.querySelector('a[data-action="today"]');u.addEventListener("click",function(b){var c=u.parentNode.parentNode;"TD"===c.tagName&&a.cellClick(a._nav_now,b)},!0)}else g=e("td",h),g.innerHTML=" ",g.colSpan=this.params.weekNumbers?4:3;this._nav_exit=j(''+JoomlaCalLocale.exit+" ",2,200,"td",{textAlign:"center"});var v=h.querySelector('a[data-action="exit"]');v.addEventListener("click",function(b){var c=v.parentNode.parentNode;"TD"===c.tagName&&a.cellClick(a._nav_exit,b)},!0),this.processCalendar()},c.prototype.processCalendar=function(){this.table.style.visibility="hidden";var a=this.params.firstDayOfWeek,b=this.date,c=new Date,d=c.getLocalFullYear(this.params.dateType),e=c.getLocalMonth(this.params.dateType),f=c.getLocalDate(this.params.dateType),g=b.getOtherFullYear(this.params.dateType),h=b.getHours(),i=b.getMinutes(),k=(b.getSeconds(),!this.params.time24);gthis.params.maxYear&&(g=this.params.maxYear,b.getOtherFullYear(this.params.dateType,g)),this.params.firstDayOfWeek=a,this.date=new Date(b);var l=b.getLocalMonth(this.params.dateType),m=b.getLocalDate(this.params.dateType);b.setLocalDate(this.params.dateType,1);var n=(b.getLocalDay(this.params.dateType)-this.params.firstDayOfWeek)%7;n<0&&(n+=7),b.setLocalDate(this.params.dateType,-n),b.setLocalDate(this.params.dateType,b.getLocalDate(this.params.dateType)+1);for(var o=this.tbody.firstChild,p=this.ar_days=new Array,q=JoomlaCalLocale.weekend,r=parseInt(b.getLocalWeekDays(this.params.dateType)),s=0;s12&&k&&(h-=12),h=h<10?"0"+h:h,i=i<10?"0"+i:i;var B=this.table.querySelector(".time-hours"),C=this.table.querySelector(".time-minutes");if(this.resetSelected(B),B.value=h,this.resetSelected(C),C.value=i,!this.params.time24){var D=new Date(this.inputField.getAttribute("data-alt-value")),E=this.table.querySelector(".time-ampm"),F=D.getHours();F>12&&(this.resetSelected(E),E.value="pm")}}if(this.params.compressedHeader){var G=Date.convertNumbers(g.toString());this._nav_month.getElementsByTagName("span")[0].innerHTML=this.params.monthBefore?G+" - "+JoomlaCalLocale.months[l]:JoomlaCalLocale.months[l]+" - "+G}else this._nav_month.getElementsByTagName("span")[0].innerHTML=this.params.debug?l+" "+JoomlaCalLocale.months[l]:JoomlaCalLocale.months[l],this.title.getElementsByTagName("span")[0].innerHTML=this.params.debug?g+" "+Date.convertNumbers(g.toString()):Date.convertNumbers(g.toString());this.table.style.visibility="visible"},c.prototype._bindEvents=function(){var a=this;this.inputField.addEventListener("focus",function(){a.show()},!0),this.inputField.addEventListener("blur",function(b){if(null==b.relatedTarget||!(b.relatedTarget.classList.contains("time-hours")||b.relatedTarget.classList.contains("time-minutes")||b.relatedTarget.classList.contains("time-ampm"))){for(var c=b.target;c.parentNode;)if(c=c.parentNode,c.classList.contains("field-calendar"))return;a.close()}},!0),this.button.addEventListener("click",function(){a.show()},!1)};var d=function(b){return b||(b=a.event),b.preventDefault(),b.stopPropagation(),!1},e=function(a,c){var d=null;return d=b.createElement(a),"undefined"!=typeof c&&c.appendChild(d),d},f=function(a){return!isNaN(a)&&function(a){return(0|a)===a}(parseFloat(a))},g=function(a,b){var c=new Date,d=c.getLocalFullYear(b);return d+a};Array.prototype.indexOf||(Array.prototype.indexOf=function(a){var b=this.length>>>0,c=Number(arguments[1])||0;for(c=c<0?Math.ceil(c):Math.floor(c),c<0&&(c+=b);c
+
+
diff --git a/tests/javascript/calendar/spec-setup.js b/tests/javascript/calendar/spec-setup.js
new file mode 100644
index 0000000000000..e734ecce38828
--- /dev/null
+++ b/tests/javascript/calendar/spec-setup.js
@@ -0,0 +1,14 @@
+/**
+ * @copyright Copyright (C) 2005 - 2016 Open Source Matters, Inc. All rights reserved.
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ * @package Joomla
+ * @subpackage JavaScript Tests
+ * @since __DEPLOY_VERSION__
+ * @version 1.0.0
+ */
+
+define(['jquery', 'text!testsRoot/calendar/fixtures/fixture.html', 'libs/fields/calendar-vanilla'], function ($, fixture) {
+
+ $('body').append(fixture);
+
+});
diff --git a/tests/javascript/calendar/spec.js b/tests/javascript/calendar/spec.js
new file mode 100644
index 0000000000000..36756db353383
--- /dev/null
+++ b/tests/javascript/calendar/spec.js
@@ -0,0 +1,77 @@
+/**
+ * @copyright Copyright (C) 2005 - 2016 Open Source Matters, Inc. All rights reserved.
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ * @package Joomla
+ * @subpackage JavaScript Tests
+ * @since __DEPLOY_VERSION__
+ * @version 1.0.0
+ */
+
+define(['jquery', 'testsRoot/calendar/spec-setup', 'jasmineJquery'], function ($) {
+
+ describe('Calendar set for the input element', function () {
+ beforeAll(function () {
+ JoomlaCalendar.init(".field-calendar");
+ });
+
+ it('Should have calendar element under the input element', function () {
+ expect($('body')).toContainElement('.js-calendar');
+ });
+
+ it('Should appear on button click', function () {
+ $('.field-calendar').find('button').click();
+
+ expect($('.js-calendar').css('display')).toEqual('block');
+ });
+
+ it('Should have the correct date', function () {
+ expect($('.field-calendar').find('input').val()).toEqual('2016-09-01 05:17:00');
+ });
+
+ // it('Should have the correct date clicking on previous year button', function () {
+ //
+ // console.info($('.field-calendar').find('.btn-prev-year'));
+ // $('.field-calendar').find('.btn-prev-year').trigger('click');
+ //
+ // expect($('.field-calendar').find('input').val()).toEqual('2015-09-01 00:17:15');
+ // });
+ //
+ // it('Should have the correct date clicking on next year button', function () {
+ //
+ // console.info($('.field-calendar').find('.btn-next-year'));
+ // $('.field-calendar').find('.btn-next-year').trigger('click');
+ //
+ // expect($('.field-calendar').find('input').val()).toEqual('2016-09-01 00:17:15');
+ // });
+ //
+ // it('Should have the correct date clicking on previous month button', function () {
+ //
+ // console.info($('.field-calendar').find('.btn-prev-month'));
+ // $('.field-calendar').find('.btn-prev-moth').trigger('click');
+ //
+ // expect($('.field-calendar').find('input').val()).toEqual('2016-08-01 00:17:15');
+ // });
+ //
+ // it('Should have the correct date clicking on next month button', function () {
+ //
+ // console.info($('.field-calendar').find('.btn-next-month'));
+ // $('.field-calendar').find('.btn-next-moth').trigger('click');
+ //
+ // expect($('.field-calendar').find('input').val()).toEqual('2016-09-01 00:17:15');
+ // });
+ //
+ // it('Should have the correct date clicking on today button', function () {
+ //
+ // console.info($('.field-calendar').find('.btn-today'));
+ // $('.field-calendar').find('.btn-today').trigger('click');
+ //
+ // expect($('.field-calendar').find('input').val()).toEqual('2016-09-01 00:17:15');
+ // });
+ //
+ // it('Should hide on other body element click/focus', function () {
+ // $('#cal-close-btn').focus();
+ //
+ // expect($('.j-calendar').css('display')).not.toEqual('block');
+ // });
+ });
+});
diff --git a/tests/javascript/test-main.js b/tests/javascript/test-main.js
index 0de1ff533994c..96b5acd50b9ca 100644
--- a/tests/javascript/test-main.js
+++ b/tests/javascript/test-main.js
@@ -24,7 +24,9 @@ require.config({
'jasmineJquery': 'tests/javascript/node_modules/jasmine-jquery/lib/jasmine-jquery',
'libs': 'media/system/js',
'testsRoot': 'tests/javascript',
- 'text': 'tests/javascript/node_modules/text/text'
+ 'text': 'tests/javascript/node_modules/text/text',
+ 'calLang': 'media/system/js/fields/calendar-locales/en',
+ 'calDate': 'media/system/js/fields/calendar-locales/date/gregorian/date-helper'
},
shim: {
@@ -45,6 +47,9 @@ require.config({
},
'libs/combobox': {
deps: ['jquery']
+ },
+ 'libs/fields/calendar-vanilla': {
+ deps: ['calLang', 'calDate']
}
},
diff --git a/tests/unit/suites/libraries/cms/html/JHtmlBehaviorTest.php b/tests/unit/suites/libraries/cms/html/JHtmlBehaviorTest.php
index 4721a044d1304..fd52415e79469 100644
--- a/tests/unit/suites/libraries/cms/html/JHtmlBehaviorTest.php
+++ b/tests/unit/suites/libraries/cms/html/JHtmlBehaviorTest.php
@@ -441,24 +441,6 @@ public function testTree($expected, $id, $params = array(), $root = array())
);
}
- /**
- * Tests the calendar method.
- *
- * @return void
- *
- * @since 3.1
- */
- public function testCalendar()
- {
- JHtmlBehavior::calendar();
-
- $this->assertEquals(
- array('JHtmlBehavior::calendar' => true),
- JHtmlBehaviorInspector::getLoaded(),
- 'The calendar behavior is not loaded with all dependencies'
- );
- }
-
/**
* Tests the colorpicker method.
*
diff --git a/tests/unit/suites/libraries/cms/html/JHtmlTest.php b/tests/unit/suites/libraries/cms/html/JHtmlTest.php
index 5e27d719d918d..7f9267ecb1b75 100644
--- a/tests/unit/suites/libraries/cms/html/JHtmlTest.php
+++ b/tests/unit/suites/libraries/cms/html/JHtmlTest.php
@@ -1439,35 +1439,38 @@ public function testCalendar()
strlen($input),
'Line:' . __LINE__ . ' The calendar method should return something without error.'
);
+ $readonly = isset($data['attribs']['readonly']) ? 'readonly="readonly"' : '';
+
+ $xml = new SimpleXMLElement(' ');
- $xml = new SimpleXMLElement('' . $input . ' ');
$this->assertEquals(
- 'text',
- (string) $xml->div->input['type'],
- 'Line:' . __LINE__ . ' The calendar input should have `type == "text"`'
+ 'calendar',
+ (string) $xml->attributes()->type,
+ 'Line:' . __LINE__ . ' The calendar input should have `type == "calendar"`'
);
$this->assertEquals(
$data['friendly_date'],
- (string) $xml->div->input['title'],
+ (string) $xml->attributes()->title,
'Line:'.__LINE__.' The calendar input should have `title == "' . $data['friendly_date'] . '"`'
);
$this->assertEquals(
$data['name'],
- (string) $xml->div->input['name'],
+ (string) $xml->attributes()->name,
'Line:'.__LINE__.' The calendar input should have `name == "' . $data['name'] . '"`'
);
$this->assertEquals(
$data['id'],
- (string) $xml->div->input['id'],
+ (string) $xml->attributes()->id,
'Line:'.__LINE__.' The calendar input should have `id == "' . $data['id'] . '"`'
);
$this->assertEquals(
$data['formattedDate'],
- (string) $xml->div->input['value'],
+ (string) $xml->attributes()->value,
'Line:' . __LINE__ . ' The calendar input should have `value == "' . $data['formattedDate'] . '"`'
);
@@ -1475,69 +1478,28 @@ public function testCalendar()
{
$this->assertEquals(
$data['attribs']['readonly'],
- (string) $xml->div->input['readonly'],
- 'Line:' . __LINE__ . ' The readonly calendar input should have `readonly == "' . $data['attribs']['readonly'] . '"`'
- );
-
- $this->assertTrue(
- isset($xml->div->button['style']),
- 'Line:' . __LINE__ . ' The calendar input should not have a visible button'
- );
-
- $this->assertArrayNotHasKey(
- '/media/system/js/calendar.js',
- JFactory::getDocument()->_scripts,
- 'Line:' . __LINE__ . ' JS file "calendar.js" shouldn\'t be loaded'
- );
-
- $this->assertArrayNotHasKey(
- '/media/system/js/calendar-setup.js',
- JFactory::getDocument()->_scripts,
- 'Line:' . __LINE__ . ' JS file "calendar-setup.js" shouldn\'t be loaded'
- );
-
- $this->assertArrayNotHasKey(
- 'text/javascript',
- JFactory::getDocument()->_script,
- 'Line:' . __LINE__ . ' Inline JS for the calendar shouldn\'t be loaded'
+ $xml->attributes()->readonly,
+ 'Line:' . __LINE__ . ' The calendar input should have readonly attribute'
);
}
- else
- {
- $this->assertFalse(
- isset($xml->div->input['readonly']),
- 'Line:' . __LINE__ . ' The calendar input shouldn\'t have readonly attribute'
- );
- $this->assertFalse(
- isset($xml->div->button['style']),
- 'Line:' . __LINE__ . ' The calendar input should visible button'
- );
-
- $this->assertEquals(
- $data['id'] . '_img',
- (string) $xml->div->button['id'],
- 'Line:' . __LINE__ . ' The calendar button should have `id == "' . $data['id'] . '_img' . '"`'
- );
-
- $this->assertArrayHasKey(
- '/media/system/js/calendar.js',
- JFactory::getDocument()->_scripts,
- 'Line:'.__LINE__.' JS file "calendar.js" should be loaded'
- );
+ $this->assertArrayHasKey(
+ '/media/system/js/fields/calendar-locales/en.js',
+ JFactory::getDocument()->_scripts,
+ 'Line:'.__LINE__.' JS file "calendar-vanilla.min.js" should be loaded'
+ );
- $this->assertArrayHasKey(
- '/media/system/js/calendar-setup.js',
- JFactory::getDocument()->_scripts,
- 'Line:'.__LINE__.' JS file "calendar-setup.js" should be loaded'
- );
+ $this->assertArrayHasKey(
+ '/media/system/js/fields/calendar-locales/date/gregorian/date-helper.min.js',
+ JFactory::getDocument()->_scripts,
+ 'Line:'.__LINE__.' JS file "date.js" should be loaded'
+ );
- $this->assertContains(
- 'jQuery(document).ready(function($) {Calendar.setup({',
- JFactory::getDocument()->_script['text/javascript'],
- 'Line:' . __LINE__ . ' Inline JS for the calendar should be loaded'
- );
- }
+ $this->assertArrayHasKey(
+ '/media/system/js/fields/calendar-vanilla.min.js',
+ JFactory::getDocument()->_scripts,
+ 'Line:'.__LINE__.' JS file "calendar-vanilla.min.js" should be loaded'
+ );
}
}