Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/apps/system/js/value_selector/spin_date_picker.js
diff options
context:
space:
mode:
Diffstat (limited to 'apps/system/js/value_selector/spin_date_picker.js')
-rw-r--r--apps/system/js/value_selector/spin_date_picker.js341
1 files changed, 341 insertions, 0 deletions
diff --git a/apps/system/js/value_selector/spin_date_picker.js b/apps/system/js/value_selector/spin_date_picker.js
new file mode 100644
index 0000000..feb0602
--- /dev/null
+++ b/apps/system/js/value_selector/spin_date_picker.js
@@ -0,0 +1,341 @@
+/**
+ * SpinDatePicker is a html/js "widget" which enables users
+ * pick a specific date. It display the date in the way based
+ * on the language setting.
+ *
+ * The SpinDatePicker itself contains no UI for the controls.
+ *
+ * Example usage:
+ *
+ * // All necessary UI elements are contained in the root element.
+ * var picker = new SpinDatePicker(root);
+ * picker.value = new Date();
+ * // after users pick a date
+ * var newDate = picker.value;
+ */
+var SpinDatePicker = (function SpinDatePicker() {
+ 'use strict';
+
+ var FIRST_YEAR = 1900;
+ var LAST_YEAR = 2099;
+
+ function getYearText() {
+ var yearText = [];
+ var dateTimeFormat = navigator.mozL10n.DateTimeFormat();
+
+ for (var i = FIRST_YEAR; i <= LAST_YEAR; i++) {
+ var date = new Date(i, 0, 1);
+ yearText.push(dateTimeFormat.localeFormat(date, '%Y'));
+ }
+
+ return yearText;
+ }
+
+ function getMonthText() {
+ var monthText = [];
+ var date = new Date(0);
+ var dateTimeFormat = navigator.mozL10n.DateTimeFormat();
+
+ for (var i = 0; i < 12; i++) {
+ date.setMonth(i);
+ monthText.push(dateTimeFormat.localeFormat(date, '%B'));
+ }
+
+ return monthText;
+ }
+
+ function getDateText(days) {
+ var dateText = [];
+ var date = new Date(0);
+ var dateTimeFormat = navigator.mozL10n.DateTimeFormat();
+
+ for (var i = 1; i <= days; i++) {
+ date.setDate(i);
+ dateText.push(dateTimeFormat.localeFormat(date, '%d'));
+ }
+
+ return dateText;
+ }
+
+ function getDaysInMonth(year, month) {
+ var date = new Date(year, month + 1, 0);
+ return date.getDate();
+ }
+
+ /**
+ * Get the order of date components.
+ *
+ * @param {String} date format.
+ */
+ function getDateComponentOrder(format) {
+ var format = navigator.mozL10n.get('dateTimeFormat_%x');
+ var order = '';
+ var tokens = format.match(/(%E.|%O.|%.)/g);
+
+ if (tokens) {
+ tokens.forEach(function(token) {
+ switch (token) {
+ case '%Y':
+ case '%y':
+ case '%Oy':
+ case 'Ey':
+ case 'EY':
+ order += 'Y';
+ break;
+ case '%B':
+ case '%b':
+ case '%m':
+ case '%Om':
+ order += 'M';
+ break;
+ case '%d':
+ case '%e':
+ case '%Od':
+ case '%Oe':
+ order += 'D';
+ break;
+ }
+ });
+ }
+
+ if (order.length != 3)
+ order = 'DMY';
+
+ return order;
+ }
+
+ /**
+ * Initialize a date picker widget.
+ *
+ * @param {HTMLELement} element target of widget creation.
+ */
+ function SpinDatePicker(element) {
+ this.element = element;
+
+ this.yearPicker = null;
+ this.monthPicker = null;
+ this.datePickers = {
+ '28': null,
+ '29': null,
+ '30': null,
+ '31': null
+ };
+
+ //XXX: When the document is localized again
+ // we must also re-render the month because
+ // the week days may have changed?
+ // This will only happen when we change timezones
+ // unless we add this information to the locales.
+
+ var pickerContainer =
+ element.querySelector('.picker-container');
+ var yearPickerContainer =
+ element.querySelector('.value-picker-year');
+ var monthPickerContainer =
+ element.querySelector('.value-picker-month');
+ var tmpDatePickerContainers =
+ element.querySelectorAll('.value-picker-date');
+ var datePickerContainers = {
+ '28': tmpDatePickerContainers[0],
+ '29': tmpDatePickerContainers[1],
+ '30': tmpDatePickerContainers[2],
+ '31': tmpDatePickerContainers[3]
+ };
+
+ var updateCurrentValue = function spd_updateCurrentValue() {
+ var selectedYear = this.yearPicker.getSelectedIndex() + FIRST_YEAR;
+ var selectedMonth = this.monthPicker.getSelectedIndex();
+ var days = getDaysInMonth(selectedYear, selectedMonth);
+ var datePicker = this.datePickers[days];
+ var selectedDate = datePicker.getSelectedIndex() + 1;
+
+ this._value = new Date(selectedYear, selectedMonth, selectedDate);
+ };
+
+ var updateDatePickerVisibility =
+ function spd_updateDatePickerVisibility() {
+ var days = getDaysInMonth(this.yearPicker.getSelectedIndex() +
+ FIRST_YEAR, this.monthPicker.getSelectedIndex());
+ for (var i = 28; i <= 31; i++) {
+ datePickerContainers[i].hidden = true;
+ this.datePickers[i].setSelectedIndex(this._currentSelectedDateIndex);
+ }
+ datePickerContainers[days].hidden = false;
+ };
+
+ var onvaluechangeInternal =
+ function spd_onvaluechangeInternal(newDateValue) {
+ this.yearPicker.setSelectedIndex(newDateValue.getFullYear() - FIRST_YEAR);
+ this.monthPicker.setSelectedIndex(newDateValue.getMonth());
+ for (var i = 28; i <= 31; i++) {
+ this.datePickers[i].setSelectedIndex(newDateValue.getDate() - 1);
+ }
+ updateDatePickerVisibility.apply(this);
+ updateCurrentValue.apply(this);
+ };
+
+ var onSelectedYearChanged =
+ function spd_onSelectedYearChanged(selectedYear) {
+ updateDatePickerVisibility.apply(this);
+ updateCurrentValue.apply(this);
+ };
+
+ var onSelectedMonthChanged =
+ function spd_onSelectedMonthChanged(selectedMonth) {
+ updateDatePickerVisibility.apply(this);
+ updateCurrentValue.apply(this);
+ };
+
+ var onSelectedDateChanged =
+ function spd_onSelectedDateChanged(selectedDate) {
+ this._currentSelectedDateIndex = selectedDate;
+ updateCurrentValue.apply(this);
+ };
+
+ var unitClassName = 'picker-unit';
+
+ // year value picker
+ var yearUnitStyle = {
+ valueDisplayedText: getYearText(),
+ className: unitClassName
+ };
+ if (this.yearPicker)
+ this.yearPicker.uninit();
+ this.yearPicker = new ValuePicker(yearPickerContainer, yearUnitStyle);
+ this.yearPicker.onselectedindexchange = onSelectedYearChanged.bind(this);
+
+ // month value picker
+ var monthUnitStyle = {
+ valueDisplayedText: getMonthText(),
+ className: unitClassName
+ };
+ if (this.monthPicker)
+ this.monthPicker.uninit();
+ this.monthPicker =
+ new ValuePicker(monthPickerContainer, monthUnitStyle);
+ this.monthPicker.onselectedindexchange = onSelectedMonthChanged.bind(this);
+
+ // date value picker
+ for (var i = 28; i <= 31; i++) {
+ var datePickerContainer = datePickerContainers[i];
+ var dateUnitStyle = {
+ valueDisplayedText: getDateText(i),
+ className: unitClassName
+ };
+ var datePicker = this.datePickers[i];
+
+ if (datePicker)
+ datePicker.uninit();
+ datePickerContainer.hidden = false;
+ this.datePickers[i] = new ValuePicker(datePickerContainer, dateUnitStyle);
+ this.datePickers[i].onselectedindexchange =
+ onSelectedDateChanged.bind(this);
+ }
+
+ // set component order
+ var dateComponentOrder = getDateComponentOrder();
+ var pickerClassList = pickerContainer.classList;
+ pickerClassList.remove('YMD');
+ pickerClassList.remove('DMY');
+ pickerClassList.remove('MDY');
+ pickerClassList.add(dateComponentOrder);
+
+ // Prevent focus being taken away by us for time picker.
+ // The event listener on outer box will not be triggered cause
+ // there is a evt.stopPropagation() in value_picker.js
+ this.pickerElements = [monthPickerContainer, yearPickerContainer];
+ for (var i = 28; i <= 31; i++) {
+ this.pickerElements.push(datePickerContainers[i]);
+ }
+
+ this.pickerElements.forEach((function pickerElements_forEach(picker) {
+ picker.addEventListener('mousedown', this);
+ }).bind(this));
+
+ this.onvaluechangeInternal = onvaluechangeInternal.bind(this);
+ }
+
+ SpinDatePicker.prototype = {
+
+ /**
+ * Internal value not exposed so we can fire events
+ * when the getter/setter's are used.
+ *
+ * @type Date
+ */
+ _value: null,
+
+ /**
+ * Gets current value
+ *
+ * @return {Null|Date} date or null.
+ */
+ get value() {
+ return this._value;
+ },
+
+ /**
+ * Sets the current value of the date picker.
+ * When value differs from the currently set the
+ * `onvaluechange` event will be fired with the new/old value.
+ */
+ set value(value) {
+ var old = this._value;
+ if (old !== value) {
+ this._value = value;
+ this.onvaluechangeInternal(value);
+ }
+ },
+
+ /**
+ * Getter is used for date normalization.
+ */
+ get year() {
+ return this._value.getFullYear();
+ },
+
+ /**
+ * Getter is used for date normalization.
+ */
+ get month() {
+ return this._value.getMonth();
+ },
+
+ get date() {
+ return this._value.getDate();
+ },
+
+ handleEvent: function vs_handleEvent(evt) {
+ switch (evt.type) {
+ case 'mousedown':
+ // Prevent focus being taken away by us.
+ evt.preventDefault();
+ break;
+ }
+ },
+
+ uninit: function() {
+ if (this.yearPicker)
+ this.yearPicker.uninit();
+ if (this.monthPicker)
+ this.monthPicker.uninit();
+ if (this.datePickers) {
+ for (var i = 28; i <= 31; i++) {
+ var datePicker = this.datePickers[i];
+ datePicker.uninit();
+ }
+ }
+
+ this.pickerElements.forEach((function pickerElements_forEach(picker) {
+ picker.removeEventListener('mousedown', this);
+ }).bind(this));
+ },
+
+ /**
+ * Called when the selected date changes.
+ */
+ onvaluechangeInternal: function(date) {}
+ };
+
+ return SpinDatePicker;
+}());