/* 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/. */ "use strict"; var EXPORTED_SYMBOLS = ["DateTimePickerPanel"]; const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); var DateTimePickerPanel = class { constructor(element) { this.element = element; this.TIME_PICKER_WIDTH = "12em"; this.TIME_PICKER_HEIGHT = "21em"; this.DATE_PICKER_WIDTH = "23.1em"; this.DATE_PICKER_HEIGHT = "20.7em"; } 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) { this.type = type; this.pickerState = {}; // TODO: Resize picker according to content zoom level this.element.style.fontSize = "10px"; switch (type) { case "time": { this.detail = detail; this.dateTimePopupFrame.addEventListener("load", this, true); this.dateTimePopupFrame.setAttribute( "src", "chrome://global/content/timepicker.xhtml" ); this.dateTimePopupFrame.style.width = this.TIME_PICKER_WIDTH; this.dateTimePopupFrame.style.height = this.TIME_PICKER_HEIGHT; break; } case "date": { this.detail = detail; this.dateTimePopupFrame.addEventListener("load", this, true); this.dateTimePopupFrame.setAttribute( "src", "chrome://global/content/datepicker.xhtml" ); this.dateTimePopupFrame.style.width = this.DATE_PICKER_WIDTH; this.dateTimePopupFrame.style.height = this.DATE_PICKER_HEIGHT; break; } } this.element.hidden = false; this.element.openPopupAtScreenRect( "after_start", rect.left, rect.top, rect.width, rect.height, false, false ); } closePicker() { this.setInputBoxValue(true); this.pickerState = {}; this.type = undefined; this.dateTimePopupFrame.removeEventListener("load", this, true); this.dateTimePopupFrame.contentDocument.removeEventListener( "message", this ); this.dateTimePopupFrame.setAttribute("src", ""); this.element.hidden = true; } setPopupValue(data) { switch (this.type) { case "time": { this.postMessageToPicker({ name: "PickerSetValue", detail: data.value, }); break; } case "date": { const { year, month, day } = data.value; this.postMessageToPicker({ name: "PickerSetValue", detail: { year, // Month value from input box starts from 1 instead of 0 month: month == undefined ? undefined : month - 1, day, }, }); break; } } } 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"; switch (this.type) { case "time": { const { hour, minute } = detail.value; const format = detail.format || "12"; this.postMessageToPicker({ name: "PickerInit", detail: { hour, minute, format, locale, min: detail.min, max: detail.max, step: detail.step, }, }); break; } case "date": { const { year, month, day } = detail.value; const { firstDayOfWeek, weekends } = this.getCalendarInfo(locale); const monthStrings = this.getDisplayNames( locale, [ "dates/gregorian/months/january", "dates/gregorian/months/february", "dates/gregorian/months/march", "dates/gregorian/months/april", "dates/gregorian/months/may", "dates/gregorian/months/june", "dates/gregorian/months/july", "dates/gregorian/months/august", "dates/gregorian/months/september", "dates/gregorian/months/october", "dates/gregorian/months/november", "dates/gregorian/months/december", ], "short" ); const weekdayStrings = this.getDisplayNames( locale, [ "dates/gregorian/weekdays/sunday", "dates/gregorian/weekdays/monday", "dates/gregorian/weekdays/tuesday", "dates/gregorian/weekdays/wednesday", "dates/gregorian/weekdays/thursday", "dates/gregorian/weekdays/friday", "dates/gregorian/weekdays/saturday", ], "short" ); this.postMessageToPicker({ name: "PickerInit", detail: { year, // Month value from input box starts from 1 instead of 0 month: month == undefined ? undefined : month - 1, day, firstDayOfWeek, weekends, monthStrings, weekdayStrings, locale, dir, min: detail.min, max: detail.max, step: detail.step, stepBase: detail.stepBase, }, }); break; } } } /** * @param {Boolean} passAllValues: Pass spinner values regardless if they've been set/changed or not */ setInputBoxValue(passAllValues) { switch (this.type) { case "time": { const { hour, minute, isHourSet, isMinuteSet, isDayPeriodSet, } = this.pickerState; const isAnyValueSet = isHourSet || isMinuteSet || isDayPeriodSet; if (passAllValues && isAnyValueSet) { this.sendPickerValueChanged({ hour, minute }); } else { this.sendPickerValueChanged({ hour: isHourSet || isDayPeriodSet ? hour : undefined, minute: isMinuteSet ? minute : undefined, }); } break; } case "date": { this.sendPickerValueChanged(this.pickerState); break; } } } sendPickerValueChanged(value) { switch (this.type) { case "time": { this.element.dispatchEvent( new CustomEvent("DateTimePickerValueChanged", { detail: { hour: value.hour, minute: value.minute, }, }) ); break; } case "date": { this.element.dispatchEvent( new CustomEvent("DateTimePickerValueChanged", { 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; } } } getCalendarInfo(locale) { const calendarInfo = Services.intl.getCalendarInfo(locale); // Day of week from calendarInfo starts from 1 as Sunday to 7 as Saturday, // so they need to be mapped to JavaScript convention with 0 as Sunday // and 6 as Saturday let firstDayOfWeek = calendarInfo.firstDayOfWeek - 1, weekendStart = calendarInfo.weekendStart - 1, weekendEnd = calendarInfo.weekendEnd - 1; let weekends = []; // Make sure weekendEnd is greater than weekendStart if (weekendEnd < weekendStart) { weekendEnd += 7; } // We get the weekends by incrementing weekendStart up to weekendEnd. // If the start and end is the same day, then weekends only has one day. for (let day = weekendStart; day <= weekendEnd; day++) { weekends.push(day % 7); } return { firstDayOfWeek, weekends, }; } getDisplayNames(locale, keys, style) { const displayNames = Services.intl.getDisplayNames(locale, { keys, style }); return keys.map(key => displayNames.values[key]); } 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.element.hidePopup(); this.closePicker(); break; } } } postMessageToPicker(data) { if ( this.dateTimePopupFrame.contentDocument.nodePrincipal.isSystemPrincipal ) { this.dateTimePopupFrame.contentWindow.postMessage(data, "*"); } } };