Modified: trunk/Source/WebCore/Resources/pagepopups/calendarPicker.js (133564 => 133565)
--- trunk/Source/WebCore/Resources/pagepopups/calendarPicker.js 2012-11-06 07:16:27 UTC (rev 133564)
+++ trunk/Source/WebCore/Resources/pagepopups/calendarPicker.js 2012-11-06 07:18:12 UTC (rev 133565)
@@ -175,39 +175,126 @@
};
function createUTCDate(year, month, date) {
- var newDate = new Date(Date.UTC(year, month, date));
- if (year < 100)
- newDate.setUTCFullYear(year);
+ var newDate = new Date(0);
+ newDate.setUTCFullYear(year);
+ newDate.setUTCMonth(month);
+ newDate.setUTCDate(date);
return newDate;
};
/**
- * @param {string=} opt_current
- * @return {!Date}
+ * @param {string} dateString
+ * @return {?Day|Month}
*/
-function parseDateString(opt_current) {
- if (opt_current) {
- var result = opt_current.match(/(\d+)-(\d+)-(\d+)/);
- if (result)
- return createUTCDate(Number(result[1]), Number(result[2]) - 1, Number(result[3]));
- }
+function parseDateString(dateString) {
+ var month = Month.parse(dateString);
+ if (month)
+ return month;
+ return Day.parse(dateString);
+}
+
+/**
+ * @param {!number|Day} valueOrDayOrYear
+ * @param {!number=} month
+ * @param {!number=} date
+ */
+function Day(valueOrDayOrYear, month, date) {
+ var dateObject;
+ if (arguments.length == 3)
+ dateObject = createUTCDate(valueOrDayOrYear, month, date);
+ else if (valueOrDayOrYear instanceof Day)
+ dateObject = createUTCDate(valueOrDayOrYear.year, valueOrDayOrYear.month, valueOrDayOrYear.date);
+ else
+ dateObject = new Date(valueOrDayOrYear);
+ this.year = dateObject.getUTCFullYear();
+ this.month = dateObject.getUTCMonth();
+ this.date = dateObject.getUTCDate();
+};
+
+Day.ISOStringRegExp = /^(\d+)-(\d+)-(\d+)$/;
+
+// See WebCore/platform/DateComponents.h.
+Day.Minimum = new Date(-62135596800000.0);
+Day.Maximum = new Date(8640000000000000.0);
+
+/**
+ * @param {!string} str
+ * @return {?Month}
+ */
+Day.parse = function(str) {
+ var match = Day.ISOStringRegExp.exec(str);
+ if (!match)
+ return null;
+ var year = parseInt(match[1], 10);
+ var month = parseInt(match[2], 10) - 1;
+ var date = parseInt(match[3], 10);
+ return new Day(year, month, date);
+};
+/**
+ * @param {!Date} date
+ * @return {!Month}
+ */
+Day.createFromDate = function(date) {
+ return new Day(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
+};
+
+Day.createFromToday = function() {
var now = new Date();
- // Create UTC date with same numbers as local date.
- return createUTCDate(now.getFullYear(), now.getMonth(), now.getDate());
+ return new Day(now.getFullYear(), now.getMonth(), now.getDate());
}
/**
- * @param {!number} year
- * @param {!number} month
- * @param {!number} day
+ * @param {!Day} other
+ * @return {!bool}
+ */
+Day.prototype.equals = function(other) {
+ return this.year === other.year && this.month === other.month && this.date === other.date;
+};
+
+/**
+ * @return {!Day}
+ */
+Day.prototype.previous = function() {
+ return new Day(this.year, this.month, this.date - 1);
+};
+
+/**
+ * @return {!Day}
+ */
+Day.prototype.next = function() {
+ return new Day(this.year, this.month, this.date + 1);
+};
+
+/**
+ * @return {!Date}
+ */
+Day.prototype.startDate = function() {
+ return createUTCDate(this.year, this.month, this.date);
+};
+
+/**
+ * @return {!Date}
+ */
+Day.prototype.endDate = function() {
+ return createUTCDate(this.year, this.month, this.date + 1);
+};
+
+/**
+ * @return {!number}
+ */
+Day.prototype.valueOf = function() {
+ return this.startDate().getTime();
+};
+
+/**
* @return {!string}
*/
-function serializeDate(year, month, day) {
- var yearString = String(year);
+Day.prototype.toString = function() {
+ var yearString = String(this.year);
if (yearString.length < 4)
yearString = ("000" + yearString).substr(-4, 4);
- return yearString + "-" + ("0" + (month + 1)).substr(-2, 2) + "-" + ("0" + day).substr(-2, 2);
-}
+ return yearString + "-" + ("0" + (this.month + 1)).substr(-2, 2) + "-" + ("0" + this.date).substr(-2, 2);
+};
/**
* @param {!number|Month} valueOrMonthOrYear
@@ -221,12 +308,12 @@
this.year = valueOrMonthOrYear.year;
this.month = valueOrMonthOrYear.month;
} else {
- this.year = Math.floor(valueOrMonthOrYear / 12) + 1970;
- this.month = valueOrMonthOrYear % 12;
+ this.year = 1970;
+ this.month = valueOrMonthOrYear;
}
this.year = this.year + Math.floor(this.month / 12);
this.month = this.month < 0 ? this.month % 12 + 12 : this.month % 12;
- if (this.year < 0 || Month.Maximum < this) {
+ if (this.year <= 0 || Month.Maximum < this) {
this.year = NaN;
this.month = NaN;
}
@@ -292,7 +379,7 @@
*/
Month.prototype.endDate = function() {
if (this.equals(Month.Maximum))
- return CalendarPicker.MaximumPossibleDate;
+ return Day.Maximum.startDate();
return this.next().startDate();
};
@@ -418,30 +505,33 @@
this._handleWindowResizeBound = this._handleWindowResize.bind(this);
window.addEventListener("resize", this._handleWindowResizeBound, false);
// We assume this._config.min is a valid date.
- this.minimumDate = (typeof this._config.min !== "undefined") ? parseDateString(this._config.min) : CalendarPicker.MinimumPossibleDate;
+ this._minimumValue = (typeof this._config.min !== "undefined") ? parseDateString(this._config.min).valueOf() : Day.Minimum.valueOf();
// We assume this._config.max is a valid date.
- this.maximumDate = (typeof this._config.max !== "undefined") ? parseDateString(this._config.max) : CalendarPicker.MaximumPossibleDate;
+ this._maximumValue = (typeof this._config.max !== "undefined") ? parseDateString(this._config.max).valueOf() : Day.Maximum.valueOf();
this.step = (typeof this._config.step !== undefined) ? Number(this._config.step) : CalendarPicker.DefaultStepScaleFactor;
this.stepBase = (typeof this._config.stepBase !== "undefined") ? Number(this._config.stepBase) : CalendarPicker.DefaultStepBase;
- this.yearMonthController = new YearMonthController(this);
- this.daysTable = new DaysTable(this);
+ this._minimumMonth = Month.createFromDate(new Date(this._minimumValue));
+ this.maximumMonth = Month.createFromDate(new Date(this._maximumValue));
+ this._currentMonth = new Month(NaN, NaN);
+ this._yearMonthController = new YearMonthController(this);
+ this._daysTable = new DaysTable(this);
this._hadKeyEvent = false;
this._layout();
- var initialDate = parseDateString(this._config.currentValue);
- if (initialDate < this.minimumDate)
- initialDate = this.minimumDate;
- else if (initialDate > this.maximumDate)
- initialDate = this.maximumDate;
- this.daysTable.selectDate(initialDate);
+ var initialSelection = parseDateString(this._config.currentValue);
+ if (!initialSelection)
+ initialSelection = Day.createFromToday();
+ if (initialSelection.valueOf() < this._minimumValue)
+ initialSelection = new Day(this._minimumValue);
+ else if (initialSelection.valueOf() > this._maximumValue)
+ initialSelection = new Day(this._maximumValue);
+ this.showMonth(Month.createFromDate(initialSelection.startDate()), false);
+ this._daysTable.selectRange(initialSelection);
this.fixWindowSize();
this._handleBodyKeyDownBound = this._handleBodyKeyDown.bind(this);
document.body.addEventListener("keydown", this._handleBodyKeyDownBound, false);
}
CalendarPicker.prototype = Object.create(Picker.prototype);
-// Hard limits of type=date. See WebCore/platform/DateComponents.h.
-CalendarPicker.MinimumPossibleDate = new Date(-62135596800000.0);
-CalendarPicker.MaximumPossibleDate = new Date(8640000000000000.0);
// See WebCore/html/DateInputType.cpp.
CalendarPicker.DefaultStepScaleFactor = 86400000;
CalendarPicker.DefaultStepBase = 0.0;
@@ -457,17 +547,17 @@
CalendarPicker.prototype._layout = function() {
if (this._config.isCalendarRTL)
this._element.classList.add("rtl");
- this.yearMonthController.attachTo(this._element);
- this.daysTable.attachTo(this._element);
+ this._yearMonthController.attachTo(this._element);
+ this._daysTable.attachTo(this._element);
this._layoutButtons();
// DaysTable will have focus but we don't want to show its focus ring until the first key event.
this._element.classList.add(ClassNames.NoFocusRing);
};
CalendarPicker.prototype.handleToday = function() {
- var date = new Date();
- this.daysTable.selectDate(date);
- this.submitValue(serializeDate(date.getFullYear(), date.getMonth(), date.getDate()));
+ var today = Day.createFromToday();
+ this._daysTable.selectRange(today);
+ this.submitValue(today.toString());
};
CalendarPicker.prototype.handleClear = function() {
@@ -528,6 +618,38 @@
this.lastFocusableControl = this.clear || this.today;
};
+/**
+ * @param {!Month} month
+ * @return {!bool}
+ */
+CalendarPicker.prototype.shouldShowMonth = function(month) {
+ return this._minimumMonth.valueOf() <= month.valueOf() && this.maximumMonth.valueOf() >= month.valueOf();
+};
+
+/**
+ * @param {!Month} month
+ * @param {!bool=} animate
+ * @param {!bool=} keepSelectionPosition
+ */
+CalendarPicker.prototype.showMonth = function(month, animate, keepSelectionPosition) {
+ if (this._currentMonth.equals(month))
+ return;
+ else if (month.valueOf() < this._minimumMonth.valueOf())
+ month = this._minimumMonth;
+ else if (month.valueOf() > this.maximumMonth.valueOf())
+ month = this.maximumMonth;
+ this._yearMonthController.setMonth(month);
+ this._daysTable.navigateToMonth(month, animate, keepSelectionPosition);
+ this._currentMonth = month;
+};
+
+/**
+ * @return {!Month}
+ */
+CalendarPicker.prototype.currentMonth = function() {
+ return this._currentMonth;
+};
+
// ----------------------------------------------------------------
/**
@@ -536,10 +658,6 @@
*/
function YearMonthController(picker) {
this.picker = picker;
- /**
- * @type {?Month}
- */
- this._currentMonth = null;
}
/**
@@ -576,14 +694,12 @@
this._wall.addEventListener("click", this._closePopup.bind(this), false);
element.appendChild(this._wall);
- var maximumYear = this.picker.maximumDate.getUTCFullYear();
- // Because the maximum possible year is September we should use the year before it.
- if (maximumYear === Month.Maximum.year)
- maximumYear--;
+ var month = this.picker.maximumMonth;
var maxWidth = 0;
for (var m = 0; m < 12; ++m) {
- this._month.textContent = new Month(maximumYear, m).toLocaleString();
+ this._month.textContent = month.toLocaleString();
maxWidth = Math.max(maxWidth, this._month.offsetWidth);
+ month = month.previous();
}
if (getLanguage() == "ja" && ImperialEraLimit < maximumYear) {
for (var m = 0; m < 12; ++m) {
@@ -654,44 +770,30 @@
};
/**
- * @return {?Month}
- */
-YearMonthController.prototype.month = function() {
- return this._currentMonth;
-};
-
-/**
* @param {!Month} month
*/
YearMonthController.prototype.setMonth = function(month) {
- this._currentMonth = new Month(month);
- this._redraw();
-};
-
-YearMonthController.prototype._redraw = function() {
- var min = Month.createFromDate(this.picker.minimumDate).valueOf();
- var max = Month.createFromDate(this.picker.maximumDate).valueOf();
- var current = this._currentMonth.valueOf();
+ var monthValue = month.valueOf();
if (this._left3)
- this._left3.disabled = current - 13 < min;
- this._left2.disabled = current - 2 < min;
- this._left1.disabled = current - 1 < min;
- this._right1.disabled = current + 1 > max;
- this._right2.disabled = current + 2 > max;
+ this._left3.disabled = !this.picker.shouldShowMonth(new Month(monthValue - 13));
+ this._left2.disabled = !this.picker.shouldShowMonth(new Month(monthValue - 2));
+ this._left1.disabled = !this.picker.shouldShowMonth(new Month(monthValue - 1));
+ this._right1.disabled = !this.picker.shouldShowMonth(new Month(monthValue + 1));
+ this._right2.disabled = !this.picker.shouldShowMonth(new Month(monthValue + 2));
if (this._right3)
- this._right3.disabled = current + 13 > max;
- this._month.innerText = this._currentMonth.toLocaleString();
+ this._left3.disabled = !this.picker.shouldShowMonth(new Month(monthValue + 13));
+ this._month.innerText = month.toLocaleString();
while (this._monthPopupContents.hasChildNodes())
this._monthPopupContents.removeChild(this._monthPopupContents.firstChild);
- for (var m = current - 6; m <= current + 6; m++) {
- if (m < min || m > max)
+ for (var m = monthValue - 6; m <= monthValue + 6; m++) {
+ var month = new Month(m);
+ if (!this.picker.shouldShowMonth(month))
continue;
- var month = new Month(m);
var option = createElement("div", ClassNames.MonthSelectorPopupEntry, month.toLocaleString());
option.dataset.value = month.toString();
this._monthPopupContents.appendChild(option);
- if (m == current)
+ if (m == monthValue)
option.classList.add(ClassNames.SelectedMonthYear);
}
};
@@ -810,7 +912,7 @@
var selection = this._getSelection();
if (!selection)
return;
- this.picker.daysTable.navigateToMonthAndKeepSelectionPosition(Month.parse(selection.dataset.value));
+ this.picker.showMonth(Month.parse(selection.dataset.value));
};
/*
@@ -868,17 +970,9 @@
* @param {!number} amount
*/
YearMonthController.prototype.moveRelatively = function(amount) {
- var min = Month.createFromDate(this.picker.minimumDate).valueOf();
- var max = Month.createFromDate(this.picker.maximumDate).valueOf();
- var current = this._currentMonth.valueOf();
- var updated = current;
- if (amount < 0)
- updated = current + amount >= min ? current + amount : min;
- else
- updated = current + amount <= max ? current + amount : max;
- if (updated == current)
- return;
- this.picker.daysTable.navigateToMonthAndKeepSelectionPosition(new Month(updated));
+ var current = this.picker.currentMonth().valueOf();
+ var updated = new Month(current + amount);
+ this.picker.showMonth(updated, true, true);
};
// ----------------------------------------------------------------
@@ -889,10 +983,6 @@
*/
function DaysTable(picker) {
this.picker = picker;
- /**
- * @type {?Month}
- */
- this._currentMonth = null;
}
/**
@@ -947,36 +1037,35 @@
};
/**
- * @param {!number} time date in millisecond.
+ * @param {!number} value
* @return {!boolean}
*/
-CalendarPicker.prototype.stepMismatch = function(time) {
- return (time - this.stepBase) % this.step != 0;
+CalendarPicker.prototype._stepMismatch = function(value) {
+ return (value - this.stepBase) % this.step != 0;
}
/**
- * @param {!number} time date in millisecond.
+ * @param {!number} value
* @return {!boolean}
*/
-CalendarPicker.prototype.outOfRange = function(time) {
- return time < this.minimumDate.getTime() || time > this.maximumDate.getTime();
+CalendarPicker.prototype._outOfRange = function(value) {
+ return value < this._minimumValue || value > this._maximumValue;
}
/**
- * @param {!number} time date in millisecond.
+ * @param {!Month|Day} range
* @return {!boolean}
*/
-CalendarPicker.prototype.isValidDate = function(time) {
- return !this.outOfRange(time) && !this.stepMismatch(time);
+CalendarPicker.prototype.isValidDate = function(range) {
+ var value = range.valueOf();
+ return !this._outOfRange(value) && !this._stepMismatch(value);
}
/**
- * @param {!number} year
- * @param {!number} month
+ * @param {!Month} month
*/
DaysTable.prototype._renderMonth = function(month) {
- this._currentMonth = new Month(month);
- var dayIterator = this._currentMonth.startDate();
+ var dayIterator = month.startDate();
var monthStartDay = dayIterator.getUTCDay();
var weekStartDay = global.params.weekStartDay || 0;
var startOffset = weekStartDay - monthStartDay;
@@ -990,15 +1079,13 @@
var element = this._days[w][d];
element.innerText = localizeNumber(dayIterator.getUTCDate());
element.className = ClassNames.Day;
- element.dataset.submitValue = serializeDate(iterMonth.year, iterMonth.month, dayIterator.getUTCDate());
+ element.dataset.submitValue = Day.createFromDate(dayIterator).toString();
if (isNaN(time)) {
element.innerText = "-";
element.classList.add(ClassNames.Unavailable);
- } else if (this.picker.outOfRange(time))
+ } else if (!this.picker.isValidDate(Day.createFromDate(dayIterator)))
element.classList.add(ClassNames.Unavailable);
- else if (this.picker.stepMismatch(time))
- element.classList.add(ClassNames.Unavailable);
- else if (!iterMonth.equals(this._currentMonth)) {
+ else if (!iterMonth.equals(month)) {
element.classList.add(ClassNames.Available);
element.classList.add(ClassNames.NotThisMonth);
} else
@@ -1007,31 +1094,28 @@
}
}
- this.picker.today.disabled = !this.picker.isValidDate(parseDateString().getTime());
+ this.picker.today.disabled = !this.picker.isValidDate(Day.createFromToday().valueOf());
};
/**
* @param {!Month} month
+ * @param {!bool} animate
+ * @param {!bool} keepSelectionPosition
*/
-DaysTable.prototype._navigateToMonth = function(month) {
- this.picker.yearMonthController.setMonth(month);
- this._renderMonth(month);
-};
-
-/**
- * @param {!Month} month
- */
-DaysTable.prototype._navigateToMonthWithAnimation = function(month) {
- if (this._currentMonth) {
- var delta = this._currentMonth.valueOf() - month.valueOf();
- if (delta === 0)
- return;
+DaysTable.prototype.navigateToMonth = function(month, animate, keepSelectionPosition) {
+ var firstNodeInSelectedRange = this._firstNodeInSelectedRange();
+ if (animate) {
var daysStyle = this._daysContainer.style;
daysStyle.position = "relative";
daysStyle.webkitTransition = "left 0.1s ease";
- daysStyle.left = (delta > 0 ? "" : "-") + this._daysContainer.offsetWidth + "px";
+ daysStyle.left = (this.picker.currentMonth().valueOf() > month.valueOf() ? "" : "-") + this._daysContainer.offsetWidth + "px";
}
- this._navigateToMonth(month);
+ this._renderMonth(month);
+ if (keepSelectionPosition && firstNodeInSelectedRange) {
+ var x = parseInt(firstNodeInSelectedRange.dataset.positionX, 10);
+ var y = parseInt(firstNodeInSelectedRange.dataset.positionY, 10);
+ this._selectRangeAtPosition(x, y);
+ }
};
DaysTable.prototype._moveInDays = function() {
@@ -1046,27 +1130,12 @@
};
/**
- * @param {!Month} month
- */
-DaysTable.prototype.navigateToMonthAndKeepSelectionPosition = function(month) {
- if (this._currentMonth.equals(month))
- return;
- var firstNodeInSelectedRange = this._firstNodeInSelectedRange();
- this._navigateToMonthWithAnimation(month);
- if (firstNodeInSelectedRange) {
- var x = parseInt(firstNodeInSelectedRange.dataset.positionX, 10);
- var y = parseInt(firstNodeInSelectedRange.dataset.positionY, 10);
- this._selectRangeAtPosition(x, y);
- }
-};
-
-/**
* @param {!Date} date
*/
-DaysTable.prototype.selectDate = function(date) {
+DaysTable.prototype.selectRange = function(day) {
this._deselect();
- this._navigateToMonthWithAnimation(Month.createFromDate(date));
- var dateString = serializeDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
+ this.picker.showMonth(Month.createFromDate(day.startDate()), true);
+ var dateString = day.toString();
for (var w = 0; w < DaysTable._Weeks; w++) {
for (var d = 0; d < 7; d++) {
if (this._days[w][d].dataset.submitValue == dateString) {
@@ -1113,10 +1182,10 @@
* @return {!boolean}
*/
DaysTable.prototype._maybeSetPreviousMonth = function() {
- var currentMonth = this.picker.yearMonthController.month();
- if (this.picker.minimumDate >= currentMonth.startDate())
+ var previousMonth = this.picker.currentMonth().previous();
+ if (!this.picker.shouldShowMonth(previousMonth))
return false;
- this._navigateToMonthWithAnimation(currentMonth.previous());
+ this.picker.showMonth(previousMonth, true);
return true;
};
@@ -1124,10 +1193,10 @@
* @return {!boolean}
*/
DaysTable.prototype._maybeSetNextMonth = function() {
- var currentMonth = this.picker.yearMonthController.month();
- if (this.picker.maximumDate < currentMonth.endDate())
+ var nextMonth = this.picker.currentMonth().next();
+ if (!this.picker.shouldShowMonth(nextMonth))
return false;
- this._navigateToMonthWithAnimation(currentMonth.next());
+ this.picker.showMonth(nextMonth, true);
return true;
};
@@ -1237,7 +1306,7 @@
}
} else if (key == "U+0054") { // 't'
- this.selectDate(new Date());
+ this.selectRange(Day.createFromToday());
event.stopPropagation();
event.preventDefault();
}
@@ -1271,11 +1340,11 @@
this.lastFocusableControl.focus();
}
} else if (key == "U+004D") { // 'm'
- this.yearMonthController.moveRelatively(event.shiftKey ? YearMonthController.PreviousMonth : YearMonthController.NextMonth);
+ this._yearMonthController.moveRelatively(event.shiftKey ? YearMonthController.PreviousMonth : YearMonthController.NextMonth);
} else if (key == "U+0059") { // 'y'
- this.yearMonthController.moveRelatively(event.shiftKey ? YearMonthController.PreviousYear : YearMonthController.NextYear);
+ this._yearMonthController.moveRelatively(event.shiftKey ? YearMonthController.PreviousYear : YearMonthController.NextYear);
} else if (key == "U+0044") { // 'd'
- this.yearMonthController.moveRelatively(event.shiftKey ? YearMonthController.PreviousTenYears : YearMonthController.NextTenYears);
+ this._yearMonthController.moveRelatively(event.shiftKey ? YearMonthController.PreviousTenYears : YearMonthController.NextTenYears);
} else if (key == "U+001B") // ESC
this.handleCancel();
}