summaryrefslogtreecommitdiff
path: root/toolkit/content
diff options
context:
space:
mode:
authorjanekptacijarabaci <janekptacijarabaci@seznam.cz>2018-03-30 23:50:34 +0200
committerjanekptacijarabaci <janekptacijarabaci@seznam.cz>2018-03-30 23:50:34 +0200
commite14c686ac0ad5e6cfdd933049c11b80a425283dc (patch)
tree2898de2582c3ea07ac781fafbf079fed790ef888 /toolkit/content
parent77fbaff2faf6fc341e30a309f7e68458f2864ad9 (diff)
downloaduxp-e14c686ac0ad5e6cfdd933049c11b80a425283dc.tar.gz
Bug 1381421 - (Part 1) Handle dates earlier than 0001-01-01 and later than 275760-09-13 correctly
Diffstat (limited to 'toolkit/content')
-rw-r--r--toolkit/content/widgets/calendar.js35
-rw-r--r--toolkit/content/widgets/datekeeper.js86
-rw-r--r--toolkit/content/widgets/datepicker.js16
3 files changed, 75 insertions, 62 deletions
diff --git a/toolkit/content/widgets/calendar.js b/toolkit/content/widgets/calendar.js
index 80c2976e08..44ba675014 100644
--- a/toolkit/content/widgets/calendar.js
+++ b/toolkit/content/widgets/calendar.js
@@ -11,6 +11,9 @@
* @param {Object} options
* {
* {Number} calViewSize: Number of days to appear on a calendar view
+ * {Function} getDayString: Transform day number to string
+ * {Function} getWeekHeaderString: Transform day of week number to string
+ * {Function} setSelection: Set selection for dateKeeper
* }
* @param {Object} context
* {
@@ -24,9 +27,11 @@ function Calendar(options, context) {
this.context = context;
this.state = {
days: [],
- weekHeaders: []
+ weekHeaders: [],
+ setSelection: options.setSelection,
+ getDayString: options.getDayString,
+ getWeekHeaderString: options.getWeekHeaderString
};
- this.props = {};
this.elements = {
weekHeaders: this._generateNodes(DAYS_IN_A_WEEK, context.weekHeader),
daysView: this._generateNodes(options.calViewSize, context.daysView)
@@ -46,34 +51,32 @@ function Calendar(options, context) {
* {Boolean} isVisible: Whether or not the calendar is in view
* {Array<Object>} days: Data for days
* {
- * {Number} dateValue: Date in milliseconds
- * {Number} textContent
+ * {Date} dateObj
+ * {Number} content
* {Array<String>} classNames
+ * {Boolean} enabled
* }
* {Array<Object>} weekHeaders: Data for weekHeaders
* {
- * {Number} textContent
+ * {Number} content
* {Array<String>} classNames
- * {Boolean} enabled
* }
- * {Function} getDayString: Transform day number to string
- * {Function} getWeekHeaderString: Transform day of week number to string
- * {Function} setSelection: Set selection for dateKeeper
* }
*/
setProps(props) {
if (props.isVisible) {
// Transform the days and weekHeaders array for rendering
- const days = props.days.map(({ dateObj, classNames, enabled }) => {
+ const days = props.days.map(({ dateObj, content, classNames, enabled }) => {
return {
- textContent: props.getDayString(dateObj.getUTCDate()),
+ dateObj,
+ textContent: this.state.getDayString(content),
className: classNames.join(" "),
enabled
};
});
- const weekHeaders = props.weekHeaders.map(({ textContent, classNames }) => {
+ const weekHeaders = props.weekHeaders.map(({ content, classNames }) => {
return {
- textContent: props.getWeekHeaderString(textContent),
+ textContent: this.state.getWeekHeaderString(content),
className: classNames.join(" ")
};
});
@@ -92,8 +95,6 @@ function Calendar(options, context) {
this.state.days = days;
this.state.weekHeaders = weekHeaders;
}
-
- this.props = Object.assign(this.props, props);
},
/**
@@ -150,9 +151,9 @@ function Calendar(options, context) {
case "click": {
if (event.target.parentNode == this.context.daysView) {
let targetId = event.target.dataset.id;
- let targetObj = this.props.days[targetId];
+ let targetObj = this.state.days[targetId];
if (targetObj.enabled) {
- this.props.setSelection(targetObj.dateObj);
+ this.state.setSelection(targetObj.dateObj);
}
}
break;
diff --git a/toolkit/content/widgets/datekeeper.js b/toolkit/content/widgets/datekeeper.js
index 4959b96092..5d70416a93 100644
--- a/toolkit/content/widgets/datekeeper.js
+++ b/toolkit/content/widgets/datekeeper.js
@@ -19,9 +19,11 @@ function DateKeeper(props) {
// The min value is 0001-01-01 based on HTML spec:
// https://html.spec.whatwg.org/#valid-date-string
MIN_DATE = -62135596800000,
- // The max value is derived from the ECMAScript spec:
+ // The max value is derived from the ECMAScript spec (275760-09-13):
// http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.1
- MAX_DATE = 8640000000000000;
+ MAX_DATE = 8640000000000000,
+ MAX_YEAR = 275760,
+ MAX_MONTH = 9;
DateKeeper.prototype = {
get year() {
@@ -32,10 +34,6 @@ function DateKeeper(props) {
return this.state.dateObj.getUTCMonth();
},
- get day() {
- return this.state.dateObj.getUTCDate();
- },
-
get selection() {
return this.state.selection;
},
@@ -55,7 +53,6 @@ function DateKeeper(props) {
*/
init({ year, month, day, min, max, step, stepBase, firstDayOfWeek = 0, weekends = [0], calViewSize = 42 }) {
const today = new Date();
- const isDateSet = year != undefined && month != undefined && day != undefined;
this.state = {
step, firstDayOfWeek, weekends, calViewSize,
@@ -66,28 +63,34 @@ function DateKeeper(props) {
today: this._newUTCDate(today.getFullYear(), today.getMonth(), today.getDate()),
weekHeaders: this._getWeekHeaders(firstDayOfWeek, weekends),
years: [],
- months: [],
- days: [],
+ dateObj: new Date(0),
selection: { year, month, day },
};
- this.state.dateObj = isDateSet ?
- this._newUTCDate(year, month, day) :
- new Date(this.state.today);
+ this.setCalendarMonth({
+ year: year === undefined ? today.getFullYear() : year,
+ month: month === undefined ? today.getMonth() : month
+ });
},
/**
- * Set new date. The year is always treated as full year, so the short-form
- * is not supported.
+ * Set new calendar month. The year is always treated as full year, so the
+ * short-form is not supported.
* @param {Object} date parts
* {
* {Number} year [optional]
* {Number} month [optional]
- * {Number} date [optional]
* }
*/
- set({ year = this.year, month = this.month, day = this.day }) {
+ setCalendarMonth({ year = this.year, month = this.month }) {
+ // Make sure the date is valid before setting.
// Use setUTCFullYear so that year 99 doesn't get parsed as 1999
- this.state.dateObj.setUTCFullYear(year, month, day);
+ if (year > MAX_YEAR || year === MAX_YEAR && month >= MAX_MONTH) {
+ this.state.dateObj.setUTCFullYear(MAX_YEAR, MAX_MONTH - 1, 1);
+ } else if (year < 1 || year === 1 && month < 0) {
+ this.state.dateObj.setUTCFullYear(1, 0, 1);
+ } else {
+ this.state.dateObj.setUTCFullYear(year, month, 1);
+ }
},
/**
@@ -107,10 +110,7 @@ function DateKeeper(props) {
* @param {Number} month
*/
setMonth(month) {
- const lastDayOfMonth = this._newUTCDate(this.year, month + 1, 0).getUTCDate();
- this.set({ year: this.year,
- month,
- day: Math.min(this.day, lastDayOfMonth) });
+ this.setCalendarMonth({ year: this.year, month });
},
/**
@@ -118,10 +118,7 @@ function DateKeeper(props) {
* @param {Number} year
*/
setYear(year) {
- const lastDayOfMonth = this._newUTCDate(year, this.month + 1, 0).getUTCDate();
- this.set({ year,
- month: this.month,
- day: Math.min(this.day, lastDayOfMonth) });
+ this.setCalendarMonth({ year, month: this.month });
},
/**
@@ -129,10 +126,7 @@ function DateKeeper(props) {
* @param {Number} offset
*/
setMonthByOffset(offset) {
- const lastDayOfMonth = this._newUTCDate(this.year, this.month + offset + 1, 0).getUTCDate();
- this.set({ year: this.year,
- month: this.month + offset,
- day: Math.min(this.day, lastDayOfMonth) });
+ this.setCalendarMonth({ year: this.year, month: this.month + offset });
},
/**
@@ -178,10 +172,13 @@ function DateKeeper(props) {
currentYear >= lastItem.value - YEAR_BUFFER_SIZE) {
// The year is set in the middle with items on both directions
for (let i = -(YEAR_VIEW_SIZE / 2); i < YEAR_VIEW_SIZE / 2; i++) {
- years.push({
- value: currentYear + i,
- enabled: true
- });
+ const year = currentYear + i;
+ if (year >= 1 && year <= MAX_YEAR) {
+ years.push({
+ value: year,
+ enabled: true
+ });
+ }
}
this.state.years = years;
}
@@ -193,6 +190,7 @@ function DateKeeper(props) {
* @return {Array<Object>}
* {
* {Date} dateObj
+ * {Number} content
* {Array<String>} classNames
* {Boolean} enabled
* }
@@ -206,9 +204,22 @@ function DateKeeper(props) {
const dateObj = this._newUTCDate(firstDayOfMonth.getUTCFullYear(),
firstDayOfMonth.getUTCMonth(),
firstDayOfMonth.getUTCDate() + i);
+
let classNames = [];
let enabled = true;
+ const isValid = dateObj.getTime() >= MIN_DATE && dateObj.getTime() <= MAX_DATE;
+ if (!isValid) {
+ classNames.push("out-of-range");
+ enabled = false;
+
+ days.push({
+ classNames,
+ enabled,
+ });
+ continue;
+ }
+
const isWeekend = this.state.weekends.includes(dateObj.getUTCDay());
const isCurrentMonth = month == dateObj.getUTCMonth();
const isSelection = this.state.selection.year == dateObj.getUTCFullYear() &&
@@ -244,6 +255,7 @@ function DateKeeper(props) {
}
days.push({
dateObj,
+ content: dateObj.getUTCDate(),
classNames,
enabled,
});
@@ -275,7 +287,7 @@ function DateKeeper(props) {
* @param {Array<Number>} weekends
* @return {Array<Object>}
* {
- * {Number} textContent
+ * {Number} content
* {Array<String>} classNames
* }
*/
@@ -285,7 +297,7 @@ function DateKeeper(props) {
for (let i = 0; i < DAYS_IN_A_WEEK; i++) {
headers.push({
- textContent: dayOfWeek % DAYS_IN_A_WEEK,
+ content: dayOfWeek % DAYS_IN_A_WEEK,
classNames: weekends.includes(dayOfWeek % DAYS_IN_A_WEEK) ? ["weekend"] : []
});
dayOfWeek++;
@@ -318,7 +330,7 @@ function DateKeeper(props) {
* @return {Date}
*/
_newUTCDate(...parts) {
- return new Date(Date.UTC(...parts));
- }
+ return new Date(new Date(0).setUTCFullYear(...parts));
+ },
};
}
diff --git a/toolkit/content/widgets/datepicker.js b/toolkit/content/widgets/datepicker.js
index 0c288d9174..f5659b736e 100644
--- a/toolkit/content/widgets/datepicker.js
+++ b/toolkit/content/widgets/datepicker.js
@@ -54,7 +54,7 @@ function DatePicker(context) {
dateKeeper,
locale,
isMonthPickerVisible: false,
- getDayString: new Intl.NumberFormat(locale).format,
+ getDayString: day => day ? new Intl.NumberFormat(locale).format(day) : "",
getWeekHeaderString: weekday => weekdayStrings[weekday],
getMonthString: month => monthStrings[month],
setSelection: date => {
@@ -101,7 +101,10 @@ function DatePicker(context) {
this.components = {
calendar: new Calendar({
calViewSize: CAL_VIEW_SIZE,
- locale: this.state.locale
+ locale: this.state.locale,
+ setSelection: this.state.setSelection,
+ getDayString: this.state.getDayString,
+ getWeekHeaderString: this.state.getWeekHeaderString
}, {
weekHeader: this.context.weekHeader,
daysView: this.context.daysView
@@ -141,10 +144,7 @@ function DatePicker(context) {
this.components.calendar.setProps({
isVisible: !isMonthPickerVisible,
days: this.state.days,
- weekHeaders: dateKeeper.state.weekHeaders,
- setSelection: this.state.setSelection,
- getDayString: this.state.getDayString,
- getWeekHeaderString: this.state.getWeekHeaderString
+ weekHeaders: dateKeeper.state.weekHeaders
});
isMonthPickerVisible ?
@@ -254,8 +254,8 @@ function DatePicker(context) {
set({ year, month, day }) {
const { dateKeeper } = this.state;
- dateKeeper.set({
- year, month, day
+ dateKeeper.setCalendarMonth({
+ year, month
});
dateKeeper.setSelection({
year, month, day