/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ export var DateTimePickerPanel = class { constructor(element) { this.element = element; this.TIME_PICKER_WIDTH = "13em"; this.TIME_PICKER_HEIGHT = "22em"; this.DATE_PICKER_WIDTH = "24em"; this.DATE_PICKER_HEIGHT = "27em"; this.DATETIME_PICKER_WIDTH = "40em"; this.DATETIME_PICKER_HEIGHT = "27em"; } get dateTimePopupFrame() { let frame = this.element.querySelector("#dateTimePopupFrame"); if (!frame) { frame = this.element.ownerDocument.createXULElement("iframe"); frame.id = "dateTimePopupFrame"; this.element.appendChild(frame); } return frame; } openPicker(type, rect, detail) { if ( type == "datetime-local" && !Services.prefs.getBoolPref("dom.forms.datetime.timepicker") ) { type = "date"; } this.pickerState = {}; // TODO: Resize picker according to content zoom level this.element.style.fontSize = "10px"; this.type = type; this.detail = detail; this.dateTimePopupFrame.addEventListener("load", this, true); this.dateTimePopupFrame.setAttribute( "src", "chrome://global/content/datetimepicker.xhtml" ); switch (type) { case "time": { this.dateTimePopupFrame.style.width = this.TIME_PICKER_WIDTH; this.dateTimePopupFrame.style.height = this.TIME_PICKER_HEIGHT; break; } case "date": { this.dateTimePopupFrame.style.width = this.DATE_PICKER_WIDTH; this.dateTimePopupFrame.style.height = this.DATE_PICKER_HEIGHT; break; } case "datetime-local": { this.dateTimePopupFrame.style.width = this.DATETIME_PICKER_WIDTH; this.dateTimePopupFrame.style.height = this.DATETIME_PICKER_HEIGHT; break; } } this.element.openPopupAtScreenRect( "after_start", rect.left, rect.top, rect.width, rect.height, false, false ); } closePicker(clear) { if (clear) { this.element.dispatchEvent(new CustomEvent("DateTimePickerValueCleared")); } this.pickerState = {}; this.type = undefined; this.dateTimePopupFrame.removeEventListener("load", this, true); this.dateTimePopupFrame.contentWindow.removeEventListener("message", this); this.dateTimePopupFrame.setAttribute("src", ""); this.element.hidePopup(); } setPopupValue(data) { const detail = data.value; // Month value from input box starts from 1 instead of 0 detail.month = detail.month == undefined ? undefined : detail.month - 1; this.postMessageToPicker({ name: "PickerSetValue", detail, }); } initPicker(detail) { let locale = new Services.intl.Locale( Services.locale.webExposedLocales[0], { calendar: "gregory", } ).toString(); // Workaround for bug 1418061, while we wait for resolution of // http://bugs.icu-project.org/trac/ticket/13592: drop the PT region code, // because it results in "abbreviated" day names that are too long; // the region-less "pt" locale has shorter forms that are better here. locale = locale.replace(/^pt-PT/i, "pt"); const dir = Services.locale.isAppLocaleRTL ? "rtl" : "ltr"; const { year, month, day, hour, minute } = detail.value; const flattenDetail = { type: this.type, year, // Month value from input box starts from 1 instead of 0 month: month == undefined ? undefined : month - 1, day, hour, minute, locale, dir, format: detail.format || "12", min: detail.min, max: detail.max, step: detail.step, stepBase: detail.stepBase, }; if (this.type !== "time") { const { firstDayOfWeek, weekends } = this.getCalendarInfo(locale); const monthDisplayNames = new Services.intl.DisplayNames(locale, { type: "month", style: "short", calendar: "gregory", }); const monthStrings = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map( monthNumber => monthDisplayNames.of(monthNumber) ); const weekdayDisplayNames = new Services.intl.DisplayNames(locale, { type: "weekday", style: "abbreviated", calendar: "gregory", }); const weekdayStrings = [ // Weekdays starting Sunday (7) to Saturday (6). 7, 1, 2, 3, 4, 5, 6, ].map(weekday => weekdayDisplayNames.of(weekday)); Object.assign(flattenDetail, { firstDayOfWeek, weekends, monthStrings, weekdayStrings, }); } this.postMessageToPicker({ name: "PickerInit", detail: flattenDetail, }); } /** * @param {boolean} passAllValues: Pass spinner values regardless if they've been set/changed or not */ setInputBoxValue(passAllValues) { const value = { year: this.pickerState.year, month: this.pickerState.month, day: this.pickerState.day, hour: this.pickerState.hour, minute: this.pickerState.minute, }; if (this.type !== "date") { const isNoValueSet = this.pickerState.isHourSet || this.pickerState.isMinuteSet || this.pickerState.isDayPeriodSet; if (!passAllValues || isNoValueSet) { value.hour = this.pickerState.isHourSet || this.pickerState.isDayPeriodSet ? value.hour : undefined; value.minute = this.pickerState.isMinuteSet ? value.minute : undefined; } } this.sendPickerValueChanged(value); } sendPickerValueChanged(value) { let detail = {}; switch (this.type) { case "time": { detail = { hour: value.hour, minute: value.minute, }; break; } case "date": { detail = { year: value.year, // Month value from input box starts from 1 instead of 0 month: value.month == undefined ? undefined : value.month + 1, day: value.day, }; break; } case "datetime-local": { detail = { year: value.year, // Month value from input box starts from 1 instead of 0 month: value.month == undefined ? undefined : value.month + 1, day: value.day, hour: value.hour, minute: value.minute, }; break; } } this.element.dispatchEvent( new CustomEvent("DateTimePickerValueChanged", { detail, }) ); } getCalendarInfo(locale) { const calendarInfo = Services.intl.getCalendarInfo(locale); // Day of week from calendarInfo starts from 1 as Monday to 7 as Sunday, // so they need to be mapped to JavaScript convention with 0 as Sunday // and 6 as Saturday function toDateWeekday(day) { return day === 7 ? 0 : day; } let firstDayOfWeek = toDateWeekday(calendarInfo.firstDayOfWeek), weekend = calendarInfo.weekend; let weekends = weekend.map(toDateWeekday); return { firstDayOfWeek, weekends, }; } handleEvent(aEvent) { switch (aEvent.type) { case "load": { this.initPicker(this.detail); this.dateTimePopupFrame.contentWindow.addEventListener("message", this); break; } case "message": { this.handleMessage(aEvent); break; } } } handleMessage(aEvent) { if ( !this.dateTimePopupFrame.contentDocument.nodePrincipal.isSystemPrincipal ) { return; } switch (aEvent.data.name) { case "PickerPopupChanged": { this.pickerState = aEvent.data.detail; this.setInputBoxValue(); break; } case "ClosePopup": { this.closePicker(aEvent.data.detail); break; } } } postMessageToPicker(data) { if ( this.dateTimePopupFrame.contentDocument.nodePrincipal.isSystemPrincipal ) { this.dateTimePopupFrame.contentWindow.postMessage(data, "*"); } } };