Merge comm-central to ash
This commit is contained in:
Коммит
ab26990946
|
@ -13,6 +13,13 @@
|
|||
* invitation panel when an email contains an embedded invitation.
|
||||
*/
|
||||
const CalInvitationDisplay = {
|
||||
/**
|
||||
* The itipItem currently displayed.
|
||||
*
|
||||
* @type {calIItipItem}
|
||||
*/
|
||||
currentItipItem: null,
|
||||
|
||||
/**
|
||||
* The XUL element that wraps the invitation.
|
||||
*
|
||||
|
@ -91,28 +98,45 @@
|
|||
* @param {calIItipItem} itipItem
|
||||
*/
|
||||
async show(itipItem) {
|
||||
this.currentItipItem = itipItem;
|
||||
this.display.replaceChildren();
|
||||
|
||||
let [, rc, actionFunc, foundItems] = await new Promise(resolve =>
|
||||
cal.itip.processItipItem(itipItem, (targetItipItem, rc, actionFunc, foundItems) =>
|
||||
resolve([targetItipItem, rc, actionFunc, foundItems])
|
||||
)
|
||||
);
|
||||
|
||||
if (this.currentItipItem != itipItem || !Components.isSuccessCode(rc)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let [item] = itipItem.getItemList();
|
||||
let foundItem = await itipItem.targetCalendar.getItem(item.id);
|
||||
let [foundItem] = foundItems;
|
||||
let panel = document.createElement("calendar-invitation-panel");
|
||||
let { mode } = panel;
|
||||
switch (itipItem.receivedMethod) {
|
||||
let method = actionFunc ? actionFunc.method : itipItem.receivedMethod;
|
||||
switch (method) {
|
||||
case "REQUEST:UPDATE":
|
||||
panel.mode = panel.constructor.MODE_UPDATE_MAJOR;
|
||||
break;
|
||||
case "REQUEST:UPDATE-MINOR":
|
||||
panel.mode = panel.constructor.MODE_UPDATE_MINOR;
|
||||
break;
|
||||
case "REQUEST":
|
||||
if (foundItem) {
|
||||
if (cal.itip.compareSequence(foundItem, item) == 1) {
|
||||
mode = panel.MODE_UPDATE_MAJOR;
|
||||
} else if (cal.itip.compareStamp(foundItem, item) == 1) {
|
||||
mode = panel.MODE_UPDATE_MINOR;
|
||||
} else {
|
||||
mode = panel.MODE_ALREADY_PROCESSED;
|
||||
}
|
||||
}
|
||||
panel.mode = foundItem
|
||||
? panel.constructor.MODE_ALREADY_PROCESSED
|
||||
: panel.constructor.MODE_NEW;
|
||||
break;
|
||||
case "CANCEL":
|
||||
mode = foundItem ? panel.MODE_CANCELLED : panel.MODE_CANCELLED_NOT_FOUND;
|
||||
panel.mode = foundItem
|
||||
? panel.constructor.MODE_CANCELLED
|
||||
: panel.constructor.MODE_CANCELLED_NOT_FOUND;
|
||||
break;
|
||||
default:
|
||||
panel.mode = panel.mode = panel.constructor.MODE_NEW;
|
||||
break;
|
||||
}
|
||||
panel.mode = mode;
|
||||
panel.foundItem = foundItem;
|
||||
panel.item = item;
|
||||
this.display.appendChild(panel);
|
||||
this.body.hidden = true;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* 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/. */
|
||||
|
||||
/* globals cal openLinkExternally */
|
||||
/* globals cal, openLinkExternally, MozXULElement, MozElements */
|
||||
|
||||
"use strict";
|
||||
|
||||
|
@ -12,12 +12,20 @@
|
|||
"resource:///modules/calendar/calRecurrenceUtils.jsm"
|
||||
);
|
||||
|
||||
let l10n = new DOMLocalization(["calendar/calendar-invitation-panel.ftl"]);
|
||||
// calendar-invitation-panel.ftl is not globally loaded until now.
|
||||
MozXULElement.insertFTLIfNeeded("calendar/calendar-invitation-panel.ftl");
|
||||
|
||||
/**
|
||||
* Base element providing boilerplate for shadow root initialisation.
|
||||
*/
|
||||
class BaseInvitationElement extends HTMLElement {
|
||||
/**
|
||||
* A previous copy of the event item if found on an existing calendar.
|
||||
*
|
||||
* @type {calIEvent?}
|
||||
*/
|
||||
foundItem;
|
||||
|
||||
/**
|
||||
* The id of the <template> tag to initialize the element with.
|
||||
*
|
||||
|
@ -26,7 +34,7 @@
|
|||
constructor(id) {
|
||||
super();
|
||||
this.attachShadow({ mode: "open" });
|
||||
l10n.connectRoot(this.shadowRoot);
|
||||
document.l10n.connectRoot(this.shadowRoot);
|
||||
|
||||
let link = document.createElement("link");
|
||||
link.rel = "stylesheet";
|
||||
|
@ -39,7 +47,7 @@
|
|||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
l10n.disconnectRoot(this.shadowRoot);
|
||||
document.l10n.disconnectRoot(this.shadowRoot);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,12 +56,12 @@
|
|||
* interactive panel.
|
||||
*/
|
||||
class InvitationPanel extends BaseInvitationElement {
|
||||
MODE_NEW = "New";
|
||||
MODE_ALREADY_PROCESSED = "Processed";
|
||||
MODE_UPDATE_MAJOR = "UpdateMajor";
|
||||
MODE_UPDATE_MINOR = "UpdateMinor";
|
||||
MODE_CANCELLED = "Cancelled";
|
||||
MODE_CANCELLED_NOT_FOUND = "CancelledNotFound";
|
||||
static MODE_NEW = "New";
|
||||
static MODE_ALREADY_PROCESSED = "Processed";
|
||||
static MODE_UPDATE_MAJOR = "UpdateMajor";
|
||||
static MODE_UPDATE_MINOR = "UpdateMinor";
|
||||
static MODE_CANCELLED = "Cancelled";
|
||||
static MODE_CANCELLED_NOT_FOUND = "CancelledNotFound";
|
||||
|
||||
/**
|
||||
* mode determines how the UI should display the received invitation. It
|
||||
|
@ -61,7 +69,7 @@
|
|||
*
|
||||
* @type {string}
|
||||
*/
|
||||
mode = this.MODE_NEW;
|
||||
mode = InvitationPanel.MODE_NEW;
|
||||
|
||||
/**
|
||||
* The event item to be displayed.
|
||||
|
@ -72,78 +80,205 @@
|
|||
|
||||
connectedCallback() {
|
||||
if (this.item && this.mode) {
|
||||
let template = document.getElementById(`calendarInvitationPanel${this.mode}`);
|
||||
let template = document.getElementById(`calendarInvitationPanel`);
|
||||
this.shadowRoot.appendChild(template.content.cloneNode(true));
|
||||
this.shadowRoot.getElementById("wrapper").item = this.item;
|
||||
this.shadowRoot.getElementById("header").item = this.item;
|
||||
|
||||
let title = this.shadowRoot.querySelector("calendar-invitation-panel-title");
|
||||
title.foundItem = this.foundItem;
|
||||
title.item = this.item;
|
||||
|
||||
let statusBar = this.shadowRoot.querySelector("calendar-invitation-panel-status-bar");
|
||||
statusBar.status = this.mode;
|
||||
|
||||
this.shadowRoot.querySelector("calendar-minidate").date = this.item.startDate;
|
||||
let props = this.shadowRoot.querySelector("calendar-invitation-panel-properties");
|
||||
props.foundItem = this.foundItem;
|
||||
props.item = this.item;
|
||||
}
|
||||
}
|
||||
}
|
||||
customElements.define("calendar-invitation-panel", InvitationPanel);
|
||||
|
||||
/**
|
||||
* InvitationPanelWrapper wraps the contents of the panel for formatting and
|
||||
* provides the minidate to the left of the details.
|
||||
* InvitationPanelTitle displays the title of the event in a header element.
|
||||
*/
|
||||
class InvitationPanelWrapper extends BaseInvitationElement {
|
||||
constructor() {
|
||||
super("calendarInvitationPanelWrapper");
|
||||
}
|
||||
|
||||
set item(value) {
|
||||
this.shadowRoot.getElementById("minidate").date = value.startDate;
|
||||
this.shadowRoot.getElementById("properties").item = value;
|
||||
}
|
||||
}
|
||||
customElements.define("calendar-invitation-panel-wrapper", InvitationPanelWrapper);
|
||||
|
||||
/**
|
||||
* InvitationPanelHeader renders the header part of the invitation panel.
|
||||
*/
|
||||
class InvitationPanelHeader extends BaseInvitationElement {
|
||||
constructor() {
|
||||
super("calendarInvitationPanelHeader");
|
||||
}
|
||||
class InvitationPanelTitle extends HTMLElement {
|
||||
/**
|
||||
* A previous copy of the event, if specified it is used to determine if
|
||||
* the title of the invitation has changed.
|
||||
*
|
||||
* @type {calIEvent?}
|
||||
*/
|
||||
foundItem;
|
||||
|
||||
/**
|
||||
* Setting the item will populate the header with information.
|
||||
* Setting this value will render the title as well as a change indicator
|
||||
* if a change in title is detected.
|
||||
*
|
||||
* @type {calIEvent}
|
||||
*/
|
||||
set item(item) {
|
||||
let l10nArgs = JSON.stringify({
|
||||
summary: item.getProperty("SUMMARY") || "",
|
||||
organizer: item.organizer ? item.organizer?.commonName || item.organizer.toString() : "",
|
||||
});
|
||||
|
||||
let action = this.getAttribute("actionType");
|
||||
if (action) {
|
||||
this.shadowRoot
|
||||
.getElementById("intro")
|
||||
.setAttribute("data-l10n-id", `calendar-invitation-panel-intro-${action}`);
|
||||
set item(value) {
|
||||
let node = document.getElementById("calendarInvitationPanelTitle").content.cloneNode(true);
|
||||
if (this.foundItem && this.foundItem.title != value.title) {
|
||||
let indicator = node.querySelector("calendar-invitation-change-indicator");
|
||||
indicator.status = PROPERTY_MODIFIED;
|
||||
indicator.hidden = false;
|
||||
}
|
||||
|
||||
for (let id of ["intro", "title"]) {
|
||||
this.shadowRoot.getElementById(id).setAttribute("data-l10n-args", l10nArgs);
|
||||
node.querySelector("h1").textContent = value.title;
|
||||
this.append(node);
|
||||
}
|
||||
}
|
||||
customElements.define("calendar-invitation-panel-title", InvitationPanelTitle);
|
||||
|
||||
/**
|
||||
* Object used to describe relevant arguments to MozElements.NotificationBox.
|
||||
* appendNotification().
|
||||
* @type {Object} InvitationStatusBarDescriptor
|
||||
* @property {string} label - An l10n id used used to generate the notification
|
||||
* bar text.
|
||||
* @property {number} priority - One of the notification box constants that
|
||||
* indicate the priority of a notification.
|
||||
* @property {object[]} buttons - An array of objects corresponding to the
|
||||
* "buttons" argument of MozElements.NotificationBox.appendNotification().
|
||||
* See that method for details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* InvitationStatusBar generates a notification bar that informs the user about
|
||||
* the status of the received invitation and possible actions they may take.
|
||||
*/
|
||||
class InvitationPanelStatusBar extends HTMLElement {
|
||||
/**
|
||||
* @type {NotificationBox}
|
||||
*/
|
||||
get notificationBox() {
|
||||
if (!this._notificationBox) {
|
||||
this._notificationBox = new MozElements.NotificationBox(element => {
|
||||
this.append(element);
|
||||
});
|
||||
}
|
||||
return this._notificationBox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the value of the title displayed as a string.
|
||||
* Map-like object where each key is an InvitationPanel mode and the values
|
||||
* are descriptors used to generate the notification bar for that mode.
|
||||
*
|
||||
* @type {string}
|
||||
* @type {Object.<string, InvitationStatusBarDescriptor>
|
||||
*/
|
||||
get fullTitle() {
|
||||
return [
|
||||
...this.shadowRoot.querySelectorAll(
|
||||
".calendar-invitation-panel-intro, .calendar-invitation-panel-title"
|
||||
),
|
||||
]
|
||||
.map(node => node.textContent)
|
||||
.join(" ");
|
||||
notices = {
|
||||
[InvitationPanel.MODE_NEW]: {
|
||||
label: "calendar-invitation-panel-status-new",
|
||||
buttons: [
|
||||
{
|
||||
"l10n-id": "calendar-invitation-panel-more-button",
|
||||
callback: (notification, opts, button, event) =>
|
||||
this._showMoreMenu(event, [
|
||||
{
|
||||
l10nId: "calendar-invitation-panel-menu-item-save",
|
||||
},
|
||||
]),
|
||||
},
|
||||
],
|
||||
},
|
||||
[InvitationPanel.MODE_ALREADY_PROCESSED]: {
|
||||
label: "calendar-invitation-panel-status-processed",
|
||||
buttons: [
|
||||
{
|
||||
"l10n-id": "calendar-invitation-panel-view-button",
|
||||
},
|
||||
],
|
||||
},
|
||||
[InvitationPanel.MODE_UPDATE_MINOR]: {
|
||||
label: "calendar-invitation-panel-status-updateminor",
|
||||
priority: this.notificationBox.PRIORITY_WARNING_LOW,
|
||||
buttons: [
|
||||
{ "l10n-id": "calendar-invitation-panel-update-button" },
|
||||
{
|
||||
"l10n-id": "calendar-invitation-panel-more-button",
|
||||
callback: (notification, opts, button, event) =>
|
||||
this._showMoreMenu(event, [
|
||||
{
|
||||
type: "checkbox",
|
||||
l10nId: "calendar-invitation-panel-menu-item-toggle-changes",
|
||||
},
|
||||
]),
|
||||
},
|
||||
],
|
||||
},
|
||||
[InvitationPanel.MODE_UPDATE_MAJOR]: {
|
||||
label: "calendar-invitation-panel-status-updatemajor",
|
||||
priority: this.notificationBox.PRIORITY_WARNING_LOW,
|
||||
buttons: [
|
||||
{
|
||||
"l10n-id": "calendar-invitation-panel-more-button",
|
||||
callback: (notification, opts, button, event) =>
|
||||
this._showMoreMenu(event, [
|
||||
{
|
||||
type: "checkbox",
|
||||
l10nId: "calendar-invitation-panel-menu-item-toggle-changes",
|
||||
},
|
||||
]),
|
||||
},
|
||||
],
|
||||
},
|
||||
[InvitationPanel.MODE_CANCELLED]: {
|
||||
label: "calendar-invitation-panel-status-cancelled",
|
||||
buttons: [{ "l10n-id": "calendar-invitation-panel-delete-button" }],
|
||||
priority: this.notificationBox.PRIORITY_CRITICAL_LOW,
|
||||
},
|
||||
[InvitationPanel.MODE_CANCELLED_NOT_FOUND]: {
|
||||
label: "calendar-invitation-panel-status-cancelled-notfound",
|
||||
priority: this.notificationBox.PRIORITY_CRITICAL_LOW,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* status corresponds to one of the MODE_* constants and will trigger
|
||||
* rendering of the notification box.
|
||||
*
|
||||
* @type {string} status
|
||||
*/
|
||||
set status(value) {
|
||||
let opts = this.notices[value];
|
||||
let priority = opts.priority || this.notificationBox.PRIORITY_INFO_LOW;
|
||||
let buttons = opts.buttons || [];
|
||||
let notification = this.notificationBox.appendNotification(
|
||||
"invitationStatus",
|
||||
{
|
||||
label: { "l10n-id": opts.label },
|
||||
priority,
|
||||
},
|
||||
buttons
|
||||
);
|
||||
notification.removeAttribute("dismissable");
|
||||
}
|
||||
|
||||
_showMoreMenu(event, menuitems) {
|
||||
let menu = document.getElementById("calendarInvitationPanelMoreMenu");
|
||||
menu.replaceChildren();
|
||||
for (let { type, l10nId, command } of menuitems) {
|
||||
let menuitem = document.createXULElement("menuitem");
|
||||
if (type) {
|
||||
menuitem.type = type;
|
||||
}
|
||||
if (command) {
|
||||
menuitem.addEventListener("command", command);
|
||||
}
|
||||
document.l10n.setAttributes(menuitem, l10nId);
|
||||
menu.appendChild(menuitem);
|
||||
}
|
||||
menu.openPopup(event.originalTarget, "after_start", 0, 0, false, false, event);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
customElements.define("calendar-invitation-panel-header", InvitationPanelHeader);
|
||||
customElements.define("calendar-invitation-panel-status-bar", InvitationPanelStatusBar);
|
||||
|
||||
const PROPERTY_REMOVED = -1;
|
||||
const PROPERTY_UNCHANGED = 0;
|
||||
const PROPERTY_ADDED = 1;
|
||||
const PROPERTY_MODIFIED = 2;
|
||||
|
||||
/**
|
||||
* InvitationPanelProperties renders the details of the most useful properties
|
||||
|
@ -154,6 +289,90 @@
|
|||
super("calendarInvitationPanelProperties");
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to retrieve a property value from an event.
|
||||
*
|
||||
* @callback GetValue
|
||||
* @param {calIEvent} event
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
/**
|
||||
* A function used to make a property value visible in to the user.
|
||||
*
|
||||
* @callback PropertyShow
|
||||
* @param {HTMLElement} node - The element responsible for displaying the
|
||||
* value.
|
||||
* @param {string} value - The value of property to display.
|
||||
* @param {string} oldValue - The previous value of the property if the
|
||||
* there is a prior copy of the event.
|
||||
* @param {calIEvent} item - The event item the property belongs to.
|
||||
* @param {string} oldItem - The prior version of the event if there is
|
||||
* one.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} InvitationPropertyDescriptor
|
||||
* @property {string} id - The id of the HTMLElement that displays
|
||||
* the property.
|
||||
* @property {GetValue} getValue - Function used to retrieve the displayed
|
||||
* value of the property from the item.
|
||||
* @property {PropertyShow} show - Function to use to display the property
|
||||
* value.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A static list of objects used in determining how to display each of the
|
||||
* properties.
|
||||
*
|
||||
* @type {PropertyDescriptor[]}
|
||||
*/
|
||||
static propertyDescriptors = [
|
||||
{
|
||||
id: "interval",
|
||||
getValue(item) {
|
||||
let tz = cal.dtz.defaultTimezone;
|
||||
let startDate = item.startDate?.getInTimezone(tz) ?? null;
|
||||
let endDate = item.endDate?.getInTimezone(tz) ?? null;
|
||||
return `${startDate.icalString}-${endDate?.icalString}`;
|
||||
},
|
||||
show(intervalNode, newValue, oldValue, item) {
|
||||
intervalNode.item = item;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "recurrence",
|
||||
getValue(item) {
|
||||
let parent = item.parentItem;
|
||||
if (!parent.recurrenceInfo) {
|
||||
return null;
|
||||
}
|
||||
return recurrenceRule2String(parent.recurrenceInfo, parent.recurrenceStartDate);
|
||||
},
|
||||
show(recurrence, value) {
|
||||
recurrence.appendChild(document.createTextNode(value));
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "location",
|
||||
getValue(item) {
|
||||
return item.getProperty("LOCATION");
|
||||
},
|
||||
show(location, value) {
|
||||
location.appendChild(cal.view.textToHtmlDocumentFragment(value, document));
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "description",
|
||||
getValue(item) {
|
||||
return item.descriptionText;
|
||||
},
|
||||
show(description, value) {
|
||||
description.appendChild(cal.view.textToHtmlDocumentFragment(value, document));
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Setting the item will populate the table that displays the event
|
||||
* properties.
|
||||
|
@ -161,30 +380,59 @@
|
|||
* @type {calIEvent}
|
||||
*/
|
||||
set item(item) {
|
||||
let interval = this.shadowRoot.getElementById("interval");
|
||||
interval.item = item;
|
||||
|
||||
if (item.recurrenceInfo || item.parentItem.recurrenceInfo) {
|
||||
let parent = item.parentItem;
|
||||
this.shadowRoot.getElementById("recurrence").textContent = recurrenceRule2String(
|
||||
parent.recurrenceInfo,
|
||||
parent.recurrenceStartDate
|
||||
);
|
||||
for (let prop of InvitationPanelProperties.propertyDescriptors) {
|
||||
let el = this.shadowRoot.getElementById(prop.id);
|
||||
let value = prop.getValue(item);
|
||||
let oldValue;
|
||||
let result = PROPERTY_UNCHANGED;
|
||||
if (this.foundItem) {
|
||||
oldValue = prop.getValue(this.foundItem);
|
||||
result = this.compare(oldValue, value);
|
||||
if (result) {
|
||||
let indicator = this.shadowRoot.getElementById(`${prop.id}ChangeIndicator`);
|
||||
if (indicator) {
|
||||
indicator.type = result;
|
||||
indicator.hidden = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value) {
|
||||
prop.show(el, value, oldValue, item, this.foundItem, result);
|
||||
}
|
||||
}
|
||||
|
||||
this.shadowRoot
|
||||
.getElementById("location")
|
||||
.appendChild(cal.view.textToHtmlDocumentFragment(item.getProperty("LOCATION"), document));
|
||||
let attendeeValues = item.getAttendees();
|
||||
this.shadowRoot.getElementById("summary").attendees = attendeeValues;
|
||||
|
||||
let attendees = item.getAttendees();
|
||||
this.shadowRoot.getElementById("summary").attendees = attendees;
|
||||
this.shadowRoot.getElementById("attendees").attendees = attendees;
|
||||
let attendees = this.shadowRoot.getElementById("attendees");
|
||||
if (this.foundItem) {
|
||||
attendees.oldValue = this.foundItem.getAttendees();
|
||||
}
|
||||
attendees.value = attendeeValues;
|
||||
|
||||
this.shadowRoot
|
||||
.getElementById("description")
|
||||
.appendChild(cal.view.textToHtmlDocumentFragment(item.descriptionText, document));
|
||||
let attachments = this.shadowRoot.getElementById("attachments");
|
||||
if (this.foundItem) {
|
||||
attachments.oldValue = this.foundItem.getAttachments();
|
||||
}
|
||||
attachments.value = item.getAttachments();
|
||||
}
|
||||
|
||||
this.shadowRoot.getElementById("attachments").attachments = item.getAttachments();
|
||||
/**
|
||||
* Compares two like property values, an old and a new one, to determine
|
||||
* what type of change has been made (if any).
|
||||
*
|
||||
* @param {any} oldValue
|
||||
* @param {any} newValue
|
||||
* @returns {number} - One of the PROPERTY_* constants.
|
||||
*/
|
||||
compare(oldValue, newValue) {
|
||||
if (!oldValue && newValue) {
|
||||
return PROPERTY_ADDED;
|
||||
}
|
||||
if (oldValue && !newValue) {
|
||||
return PROPERTY_REMOVED;
|
||||
}
|
||||
return oldValue != newValue ? PROPERTY_MODIFIED : PROPERTY_UNCHANGED;
|
||||
}
|
||||
}
|
||||
customElements.define("calendar-invitation-panel-properties", InvitationPanelProperties);
|
||||
|
@ -207,7 +455,7 @@
|
|||
let [startDate, endDate] = cal.dtz.formatter.getItemDates(value);
|
||||
let timezone = startDate.timezone.displayName;
|
||||
let parts = cal.dtz.formatter.formatIntervalParts(startDate, endDate);
|
||||
l10n.setAttributes(
|
||||
document.l10n.setAttributes(
|
||||
this.shadowRoot.getElementById("interval"),
|
||||
`calendar-invitation-interval-${parts.type}`,
|
||||
{ ...parts, timezone }
|
||||
|
@ -249,7 +497,7 @@
|
|||
counts.OTHER++;
|
||||
}
|
||||
}
|
||||
l10n.setAttributes(
|
||||
document.l10n.setAttributes(
|
||||
this.shadowRoot.getElementById("total"),
|
||||
"calendar-invitation-panel-partstat-total",
|
||||
{ count: counts.TOTAL }
|
||||
|
@ -265,9 +513,13 @@
|
|||
// calendar-invitation-panel-partstat-declined
|
||||
// calendar-invitation-panel-partstat-tentative
|
||||
// calendar-invitation-panel-partstat-needs-action
|
||||
l10n.setAttributes(span, `calendar-invitation-panel-partstat-${partStat.toLowerCase()}`, {
|
||||
count: counts[partStat],
|
||||
});
|
||||
document.l10n.setAttributes(
|
||||
span,
|
||||
`calendar-invitation-panel-partstat-${partStat.toLowerCase()}`,
|
||||
{
|
||||
count: counts[partStat],
|
||||
}
|
||||
);
|
||||
breakdown.appendChild(span);
|
||||
}
|
||||
}
|
||||
|
@ -275,80 +527,232 @@
|
|||
customElements.define("calendar-invitation-partstat-summary", InvitationPartStatSummary);
|
||||
|
||||
/**
|
||||
* InvitationAttendeeList displays a list of all the attendees on
|
||||
* an event's attendee list.
|
||||
* BaseInvitationChangeList is a <ul> element that can visually show changes
|
||||
* between elements of a list value.
|
||||
*
|
||||
* @template T
|
||||
*/
|
||||
class InvitationAttendeeList extends BaseInvitationElement {
|
||||
constructor() {
|
||||
super("calendarInvitationAttendeesList");
|
||||
class BaseInvitationChangeList extends HTMLUListElement {
|
||||
/**
|
||||
* An array containing the old values to be compared against for changes.
|
||||
*
|
||||
* @type {T[]}
|
||||
*/
|
||||
oldValue = [];
|
||||
|
||||
/**
|
||||
* String indicating the type of list items to create. This is passed
|
||||
* directly to the "is" argument of document.createElement().
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
listItem;
|
||||
|
||||
_createListItem(value, status) {
|
||||
let li = document.createElement("li", { is: this.listItem });
|
||||
li.changeStatus = status;
|
||||
li.value = value;
|
||||
return li;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setting this property will trigger rendering of the attendees list.
|
||||
* Setting this property will trigger rendering of the list. If no prior
|
||||
* values are detected, change indicators are not touched.
|
||||
*
|
||||
* @type {calIAttendee[]}
|
||||
* @type {T[]}
|
||||
*/
|
||||
set attendees(value) {
|
||||
let ul = this.shadowRoot.getElementById("list");
|
||||
for (let att of value) {
|
||||
let li = document.createElement("li");
|
||||
let span = document.createElement("span");
|
||||
span.textContent = att;
|
||||
li.appendChild(span);
|
||||
ul.appendChild(li);
|
||||
set value(list) {
|
||||
if (!this.oldValue.length) {
|
||||
for (let value of list) {
|
||||
this.append(this._createListItem(value));
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (let [value, status] of this.getChanges(this.oldValue, list)) {
|
||||
this.appendChild(this._createListItem(value, status));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implemented by sub-classes to generate a list of changes for each element
|
||||
* of the new list.
|
||||
*
|
||||
* @param {T[]} oldValue
|
||||
* @param {T[]} newValue
|
||||
* @return {[T, number][]}
|
||||
*/
|
||||
getChanges(oldValue, newValue) {
|
||||
throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
|
||||
}
|
||||
}
|
||||
customElements.define("calendar-invitation-attendee-list", InvitationAttendeeList);
|
||||
|
||||
/**
|
||||
* InvitationAttachmentList displays a list of all attachments in the
|
||||
* invitation that have URIs. Binary attachments are not supported.
|
||||
* BaseInvitationChangeListItem is the <li> element used for change lists.
|
||||
*
|
||||
* @template {T}
|
||||
*/
|
||||
class InvitationAttachmentList extends BaseInvitationElement {
|
||||
constructor() {
|
||||
super("calendarInvitationAttachmentList");
|
||||
class BaseInvitationChangeListItem extends HTMLLIElement {
|
||||
/**
|
||||
* Indicates whether the item value has changed and should be displayed as
|
||||
* such. Its value is one of the PROPERTY_* constants.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
changeStatus = PROPERTY_UNCHANGED;
|
||||
|
||||
/**
|
||||
* Settings this property will render the list item including a change
|
||||
* indicator if the changeStatus property != PROPERTY_UNCHANGED.
|
||||
*
|
||||
* @type {T}
|
||||
*/
|
||||
set value(itemValue) {
|
||||
this.build(itemValue);
|
||||
if (this.changeStatus) {
|
||||
let changeIndicator = document.createElement("calendar-invitation-change-indicator");
|
||||
changeIndicator.type = this.changeStatus;
|
||||
this.append(changeIndicator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setting this property will trigger rendering of the attachments list.
|
||||
* Implemented by sub-classes to build the <li> inner DOM structure.
|
||||
*
|
||||
* @type {calIAttachment[]}
|
||||
* @param {T} value
|
||||
* @abstract
|
||||
*/
|
||||
set attachments(value) {
|
||||
let ul = this.shadowRoot.getElementById("list");
|
||||
for (let attachment of value) {
|
||||
if (attachment.uri) {
|
||||
let item = document.createElement("li", { is: "calendar-invitation-attachment-item" });
|
||||
item.attachment = attachment;
|
||||
ul.appendChild(item);
|
||||
build(value) {
|
||||
throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* InvitationAttendeeList displays a list of all the attendees on an event's
|
||||
* attendee list.
|
||||
*/
|
||||
class InvitationAttendeeList extends BaseInvitationChangeList {
|
||||
listItem = "calendar-invitation-panel-attendee-list-item";
|
||||
|
||||
getChanges(oldValue, newValue) {
|
||||
let diff = [];
|
||||
for (let att of newValue) {
|
||||
let oldAtt = oldValue.find(oldAtt => oldAtt.id == att.id);
|
||||
if (!oldAtt) {
|
||||
diff.push([att, PROPERTY_ADDED]); // New attendee.
|
||||
} else if (oldAtt.participationStatus != att.participationStatus) {
|
||||
diff.push([att, PROPERTY_MODIFIED]); // Participation status changed.
|
||||
} else {
|
||||
diff.push([att, PROPERTY_UNCHANGED]); // No change.
|
||||
}
|
||||
}
|
||||
|
||||
// Insert removed attendees into the diff.
|
||||
for (let [idx, att] of oldValue.entries()) {
|
||||
let found = newValue.find(newAtt => newAtt.id == att.id);
|
||||
if (!found) {
|
||||
diff.splice(idx, 0, [att, PROPERTY_REMOVED]);
|
||||
}
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
customElements.define("calendar-invitation-panel-attachment-list", InvitationAttachmentList);
|
||||
customElements.define("calendar-invitation-panel-attendee-list", InvitationAttendeeList, {
|
||||
extends: "ul",
|
||||
});
|
||||
|
||||
/**
|
||||
* InvitationAttachmentItem displays a link to an attachment attached to the
|
||||
* InvitationAttendeeListItem displays a single attendee from the attendee
|
||||
* list.
|
||||
*/
|
||||
class InvitationAttendeeListItem extends BaseInvitationChangeListItem {
|
||||
build(value) {
|
||||
let span = document.createElement("span");
|
||||
if (this.changeStatus == PROPERTY_REMOVED) {
|
||||
span.setAttribute("class", "removed");
|
||||
}
|
||||
span.textContent = value;
|
||||
this.appendChild(span);
|
||||
}
|
||||
}
|
||||
customElements.define(
|
||||
"calendar-invitation-panel-attendee-list-item",
|
||||
InvitationAttendeeListItem,
|
||||
{
|
||||
extends: "li",
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* InvitationAttachmentList displays a list of all attachments in the invitation
|
||||
* that have URIs. Binary attachments are not supported.
|
||||
*/
|
||||
class InvitationAttachmentList extends BaseInvitationChangeList {
|
||||
listItem = "calendar-invitation-panel-attachment-list-item";
|
||||
|
||||
getChanges(oldValue, newValue) {
|
||||
let diff = [];
|
||||
for (let attch of newValue) {
|
||||
if (!attch.uri) {
|
||||
continue;
|
||||
}
|
||||
let oldAttch = oldValue.find(
|
||||
oldAttch => oldAttch.uri && oldAttch.uri.spec == attch.uri.spec
|
||||
);
|
||||
|
||||
if (!oldAttch) {
|
||||
// New attachment.
|
||||
diff.push([attch, PROPERTY_ADDED]);
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
attch.hashId != oldAttch.hashId ||
|
||||
attch.getParameter("FILENAME") != oldAttch.getParameter("FILENAME")
|
||||
) {
|
||||
// Contents changed or renamed.
|
||||
diff.push([attch, PROPERTY_MODIFIED]);
|
||||
continue;
|
||||
}
|
||||
// No change.
|
||||
diff.push([attch, PROPERTY_UNCHANGED]);
|
||||
}
|
||||
|
||||
// Insert removed attachments into the diff.
|
||||
for (let [idx, attch] of oldValue.entries()) {
|
||||
if (!attch.uri) {
|
||||
continue;
|
||||
}
|
||||
let found = newValue.find(newAtt => newAtt.uri && newAtt.uri.spec == attch.uri.spec);
|
||||
if (!found) {
|
||||
diff.splice(idx, 0, [attch, PROPERTY_REMOVED]);
|
||||
}
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
customElements.define("calendar-invitation-panel-attachment-list", InvitationAttachmentList, {
|
||||
extends: "ul",
|
||||
});
|
||||
|
||||
/**
|
||||
* InvitationAttachmentListItem displays a link to an attachment attached to the
|
||||
* event.
|
||||
*/
|
||||
class InvitationAttachmentItem extends HTMLLIElement {
|
||||
class InvitationAttachmentListItem extends BaseInvitationChangeListItem {
|
||||
/**
|
||||
* Settings this property will set up the attachment to be displayed as a
|
||||
* link with appropriate icon. Links are opened externally.
|
||||
* Indicates whether the attachment has changed and should be displayed as
|
||||
* such. Its value is one of the PROPERTY_* constants.
|
||||
*
|
||||
* @type {calIAttachment[]}
|
||||
* @type {number}
|
||||
*/
|
||||
set attachment(value) {
|
||||
let title = value.getParameter("FILENAME") || value.uri.spec;
|
||||
let link = document.createElement("a");
|
||||
link.textContent = title;
|
||||
link.setAttribute("href", value.uri.spec);
|
||||
link.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
openLinkExternally(event.target.href);
|
||||
});
|
||||
changeStatus = PROPERTY_UNCHANGED;
|
||||
|
||||
/**
|
||||
* Sets up the attachment to be displayed as a link with appropriate icon.
|
||||
* Links are opened externally.
|
||||
*
|
||||
* @param {calIAttachment}
|
||||
*/
|
||||
build(value) {
|
||||
let icon = document.createElement("img");
|
||||
let iconSrc = value.uri.spec.length ? value.uri.spec : "dummy.html";
|
||||
if (!value.uri.schemeIs("file")) {
|
||||
|
@ -364,12 +768,59 @@
|
|||
}
|
||||
}
|
||||
icon.setAttribute("src", "moz-icon://" + iconSrc);
|
||||
this.append(icon, link);
|
||||
this.append(icon);
|
||||
|
||||
let title = value.getParameter("FILENAME") || value.uri.spec;
|
||||
if (this.changeStatus == PROPERTY_REMOVED) {
|
||||
let span = document.createElement("span");
|
||||
span.setAttribute("class", "removed");
|
||||
span.textContent = title;
|
||||
this.append(span);
|
||||
} else {
|
||||
let link = document.createElement("a");
|
||||
link.textContent = title;
|
||||
link.setAttribute("href", value.uri.spec);
|
||||
link.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
openLinkExternally(event.target.href);
|
||||
});
|
||||
this.append(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
customElements.define("calendar-invitation-attachment-item", InvitationAttachmentItem, {
|
||||
extends: "li",
|
||||
});
|
||||
customElements.define(
|
||||
"calendar-invitation-panel-attachment-list-item",
|
||||
InvitationAttachmentListItem,
|
||||
{
|
||||
extends: "li",
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* InvitationChangeIndicator is a visual indicator for indicating some piece
|
||||
* of data has changed.
|
||||
*/
|
||||
class InvitationChangeIndicator extends HTMLElement {
|
||||
_typeMap = {
|
||||
[PROPERTY_REMOVED]: "removed",
|
||||
[PROPERTY_ADDED]: "added",
|
||||
[PROPERTY_MODIFIED]: "modified",
|
||||
};
|
||||
|
||||
/**
|
||||
* One of the PROPERTY_* constants that indicates what kind of change we
|
||||
* are indicating (add/modify/delete) etc.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
type = PROPERTY_MODIFIED;
|
||||
|
||||
connectedCallback() {
|
||||
let key = this._typeMap[this.type];
|
||||
document.l10n.setAttributes(this, `calendar-invitation-change-indicator-${key}`);
|
||||
}
|
||||
}
|
||||
customElements.define("calendar-invitation-change-indicator", InvitationChangeIndicator);
|
||||
|
||||
/**
|
||||
* InvitationPanelFooter renders the footer for the details section of
|
||||
|
@ -379,13 +830,6 @@
|
|||
constructor() {
|
||||
super("calendarInvitationPanelFooter");
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
l10n.setAttributes(
|
||||
this.shadowRoot.getElementById("status"),
|
||||
"calendar-invitation-panel-reply-status"
|
||||
);
|
||||
}
|
||||
}
|
||||
customElements.define("calendar-invitation-panel-footer", InvitationPanelFooter);
|
||||
}
|
||||
|
|
|
@ -2,108 +2,25 @@
|
|||
# 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/.
|
||||
|
||||
<!-- Template used by <calendar-invitation-panel/> when an item is first
|
||||
encountered -->
|
||||
<template id="calendarInvitationPanelNew" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<calendar-invitation-panel-wrapper id="wrapper">
|
||||
<calendar-invitation-panel-header id="header" slot="header">
|
||||
<button id="actionButton"
|
||||
class="calendar-invitation-panel-action-button"
|
||||
data-l10n-id="calendar-invitation-panel-save-button"
|
||||
slot="action"></button>
|
||||
</calendar-invitation-panel-header>
|
||||
</calendar-invitation-panel-wrapper>
|
||||
<calendar-invitation-panel-footer/>
|
||||
</template>
|
||||
|
||||
<!-- Template for <calendar-invitation-panel/> when the invitation has already
|
||||
been processed. -->
|
||||
<template id="calendarInvitationPanelProcessed" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<calendar-invitation-panel-wrapper id="wrapper">
|
||||
<calendar-invitation-panel-header id="header" slot="header">
|
||||
<button id="actionButton"
|
||||
class="calendar-invitation-panel-action-button"
|
||||
data-l10n-id="calendar-invitation-panel-view-button"
|
||||
slot="action"></button>
|
||||
</calendar-invitation-panel-header>
|
||||
</calendar-invitation-panel-wrapper>
|
||||
<calendar-invitation-panel-footer/>
|
||||
</template>
|
||||
|
||||
<!-- Template for <calendar-invitation-panel/> when the invitation is a major
|
||||
update. -->
|
||||
<template id="calendarInvitationPanelUpdateMajor" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<calendar-invitation-panel-wrapper id="wrapper">
|
||||
<calendar-invitation-panel-header id="header" slot="header" actionType="update">
|
||||
<button id="actionButton"
|
||||
class="calendar-invitation-panel-action-button"
|
||||
data-l10n-id="calendar-invitation-panel-view-button"
|
||||
slot="action"></button>
|
||||
</calendar-invitation-panel-header>
|
||||
</calendar-invitation-panel-wrapper>
|
||||
<calendar-invitation-panel-footer/>
|
||||
</template>
|
||||
|
||||
<!-- Template for <calendar-invitation-panel/> when the invitation is a minor
|
||||
update. -->
|
||||
<template id="calendarInvitationPanelUpdateMinor" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<calendar-invitation-panel-wrapper id="wrapper">
|
||||
<calendar-invitation-panel-header id="header" slot="header" actionType="update">
|
||||
<button id="actionButton"
|
||||
class="calendar-invitation-panel-action-button"
|
||||
data-l10n-id="calendar-invitation-panel-update-button"
|
||||
slot="action"></button>
|
||||
</calendar-invitation-panel-header>
|
||||
</calendar-invitation-panel-wrapper>
|
||||
<calendar-invitation-panel-footer/>
|
||||
</template>
|
||||
|
||||
<!-- Template for <calendar-invitation-panel/> when the invitation is a
|
||||
cancellation of an existing event. -->
|
||||
<template id="calendarInvitationPanelCancelled" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<calendar-invitation-panel-wrapper id="wrapper">
|
||||
<calendar-invitation-panel-header id="header" slot="header" actionType="cancel">
|
||||
<button id="actionButton"
|
||||
class="calendar-invitation-panel-action-button"
|
||||
data-l10n-id="calendar-invitation-panel-delete-button"
|
||||
slot="action"></button>
|
||||
</calendar-invitation-panel-header>
|
||||
</calendar-invitation-panel-wrapper>
|
||||
<calendar-invitation-panel-footer/>
|
||||
</template>
|
||||
|
||||
<!-- Template for <calendar-invitation-panel/> when the invitation is a
|
||||
cancellation of an unprocessed event. -->
|
||||
<template id="calendarInvitationPanelCancelledNotFound" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<calendar-invitation-panel-wrapper id="wrapper">
|
||||
<calendar-invitation-panel-header id="header" slot="header" actionType="cancel"/>
|
||||
</calendar-invitation-panel-wrapper>
|
||||
<calendar-invitation-panel-footer/>
|
||||
</template>
|
||||
|
||||
<!-- Template for <calendar-invitation-panel-wrapper/> -->
|
||||
<template id="calendarInvitationPanelWrapper" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<!-- Template for <calendar-invitation-panel/> -->
|
||||
<template id="calendarInvitationPanel" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<calendar-invitation-panel-status-bar/>
|
||||
<div class="calendar-invitation-panel-wrapper">
|
||||
<div class="calendar-invitation-panel-preview">
|
||||
<calendar-minidate id="minidate"/>
|
||||
<calendar-minidate/>
|
||||
</div>
|
||||
<div class="calendar-invitation-panel-details">
|
||||
<slot name="header"></slot>
|
||||
<calendar-invitation-panel-properties id="properties"/>
|
||||
<calendar-invitation-panel-title/>
|
||||
<calendar-invitation-panel-properties/>
|
||||
</div>
|
||||
</div>
|
||||
<calendar-invitation-panel-footer/>
|
||||
</template>
|
||||
|
||||
<!-- Template for <calendar-invitation-panel-header/> -->
|
||||
<template id="calendarInvitationPanelHeader" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<div class="calendar-invitation-panel-details-header">
|
||||
<div id="intro"
|
||||
class="calendar-invitation-panel-intro"
|
||||
data-l10n-id="calendar-invitation-panel-intro"></div>
|
||||
<div class="calendar-invitation-panel-summary">
|
||||
<h1 id="title"
|
||||
class="calendar-invitation-panel-title"
|
||||
data-l10n-id="calendar-invitation-panel-title"></h1>
|
||||
<slot name="action"></slot>
|
||||
</div>
|
||||
<!-- Template for <calendar-invitation-panel-title/> -->
|
||||
<template id="calendarInvitationPanelTitle" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<div class="calendar-invitation-panel-title">
|
||||
<calendar-invitation-change-indicator hidden="hidden"/><h1></h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -113,32 +30,45 @@
|
|||
<tbody>
|
||||
<tr>
|
||||
<th data-l10n-id="calendar-invitation-panel-prop-title-when"></th>
|
||||
<td id="when" class="calendar-invitation-when">
|
||||
<td class="calendar-invitation-when">
|
||||
<calendar-invitation-change-indicator id="intervalChangeIndicator" hidden="hidden"/>
|
||||
<calendar-invitation-interval id="interval"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th data-l10n-id="calendar-invitation-panel-prop-title-recurrence"></th>
|
||||
<td id="recurrence" class="calendar-invitation-recurrence"></td>
|
||||
<td id="recurrence" class="calendar-invitation-recurrence">
|
||||
<calendar-invitation-change-indicator id="recurrenceChangeIndicator" hidden="hidden"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th data-l10n-id="calendar-invitation-panel-prop-title-location"></th>
|
||||
<td id="location" class="content"></td>
|
||||
<td id="location" class="content">
|
||||
<calendar-invitation-change-indicator id="locationChangeIndicator" hidden="hidden"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th data-l10n-id="calendar-invitation-panel-prop-title-attendees"></th>
|
||||
<td>
|
||||
<calendar-invitation-partstat-summary id="summary"/>
|
||||
<calendar-invitation-attendee-list id="attendees"/>
|
||||
<ul id="attendees"
|
||||
is="calendar-invitation-panel-attendee-list"
|
||||
class="calendar-invitation-panel-list"></ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th data-l10n-id="calendar-invitation-panel-prop-title-description"></th>
|
||||
<td id="description" class="content"></td>
|
||||
<td id="description" class="content">
|
||||
<calendar-invitation-change-indicator id="descriptionChangeIndicator" hidden="hidden"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th data-l10n-id="calendar-invitation-panel-prop-title-attachments"></th>
|
||||
<td class="content"><calendar-invitation-panel-attachment-list id="attachments"/></td>
|
||||
<td class="content">
|
||||
<ul id="attachments"
|
||||
is="calendar-invitation-panel-attachment-list"
|
||||
class="calendar-invitation-panel-list"></ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -158,21 +88,9 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Template for <calendar-invitation-attendees-list/> -->
|
||||
<template id="calendarInvitationAttendeesList" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<ul id="list" class="calendar-invitation-list"></ul>
|
||||
</template>
|
||||
|
||||
<!-- Template for <calendar-invitation-attachment-list/> -->
|
||||
<template id="calendarInvitationAttachmentList" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<ul id="list" class="calendar-invitation-list"></ul>
|
||||
</template>
|
||||
|
||||
<!-- Template for <calendar-invitation-panel-footer/> -->
|
||||
<template id="calendarInvitationPanelFooter" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<div class="calendar-invitation-panel-details-footer">
|
||||
<div id="status"
|
||||
class="calendar-invitation-panel-reply-status"></div>
|
||||
<div id="actionButtons" class="calendar-invitation-panel-response-buttons">
|
||||
<button id="acceptButton"
|
||||
name="accept"
|
||||
|
@ -187,3 +105,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Menu for the "More" button in the invitation panel. Populated via JavaScript.-->
|
||||
<menupopup id="calendarInvitationPanelMoreMenu"></menupopup>
|
||||
|
|
|
@ -9,7 +9,16 @@
|
|||
border: 1px solid var(--splitter-color);
|
||||
}
|
||||
|
||||
:host(calendar-invitation-panel-wrapper) {
|
||||
:host(calendar-invitation-panel) .notificationbox-stack {
|
||||
width: 100%;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
:host(calendar-invitation-panel) .notificationbox-stack > notification-message {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.calendar-invitation-panel-wrapper {
|
||||
display: flex;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
@ -20,32 +29,22 @@
|
|||
flex-direction: column;
|
||||
}
|
||||
|
||||
.calendar-invitation-panel-details-header {
|
||||
.calendar-invitation-panel-title {
|
||||
margin-bottom: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.calendar-invitation-panel-summary {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.calendar-invitation-panel-title {
|
||||
.calendar-invitation-panel-title > h1 {
|
||||
font-size: 1.4rem;
|
||||
font-weight: 800;
|
||||
margin: 0 1em;
|
||||
flex-grow: 2;
|
||||
align-self: flex-end;
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.calendar-invitation-panel-action-button {
|
||||
margin: 0;
|
||||
.calendar-invitation-panel-response-buttons {
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
|
||||
.calendar-invitation-panel-action-button,
|
||||
.calendar-invitation-panel-response-buttons button {
|
||||
min-height: 2.5em;
|
||||
font-size: .8em;
|
||||
|
@ -58,15 +57,6 @@
|
|||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.calendar-invitation-panel-reply-status {
|
||||
margin-inline-end: 2rem;
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
.calendar-invitation-panel-response-buttons {
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
|
||||
.calendar-invitation-panel-props {
|
||||
margin: 1.5em 0px;
|
||||
}
|
||||
|
@ -78,6 +68,7 @@
|
|||
|
||||
.calendar-invitation-panel-props th, .calendar-invitation-panel-props td {
|
||||
vertical-align: top;
|
||||
padding: 3px 0;
|
||||
}
|
||||
|
||||
.calendar-invitation-panel-props th {
|
||||
|
@ -91,9 +82,8 @@
|
|||
word-break: break-word;
|
||||
}
|
||||
|
||||
.calendar-invitation-when {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
calendar-invitation-interval {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.calendar-invitation-panel-partstat-breakdown:before {
|
||||
|
@ -113,19 +103,33 @@
|
|||
margin-inline-end: 0.5em;
|
||||
}
|
||||
|
||||
.calendar-invitation-list {
|
||||
.calendar-invitation-panel-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.calendar-invitation-list li {
|
||||
.calendar-invitation-panel-list li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.calendar-invitation-list li img {
|
||||
.calendar-invitation-panel-list li img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.calendar-invitation-panel-list li span.removed {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
calendar-invitation-change-indicator:not([hidden]) {
|
||||
border-radius: var(--toolbarbutton-border-radius);
|
||||
margin-inline-end: 3px;
|
||||
margin-block: 2px;
|
||||
padding-inline: 7px;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
box-shadow: inset 0 0 0 1px transparent;
|
||||
display: inline-block;
|
||||
}
|
||||
|
|
|
@ -2,13 +2,17 @@
|
|||
# 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/.
|
||||
|
||||
# Variables:
|
||||
# $organizer (String) - The participant that created the original invitation.
|
||||
calendar-invitation-panel-intro = { $organizer } has invited you to:
|
||||
calendar-invitation-panel-status-new = You have been invited to this event.
|
||||
|
||||
# Variables:
|
||||
# $organizer (String) - The participant that updated the original invitation.
|
||||
calendar-invitation-panel-intro-update = { $organizer } has updated:
|
||||
calendar-invitation-panel-status-processed = This event has already been added your calendar.
|
||||
|
||||
calendar-invitation-panel-status-updateminor = This message contains an update for this event.
|
||||
|
||||
calendar-invitation-panel-status-updatemajor = This message contains an update for this event. You should re-confirm your attendance.
|
||||
|
||||
calendar-invitation-panel-status-cancelled = This message contains a cancellation for this event.
|
||||
|
||||
calendar-invitation-panel-status-cancelled-notfound = This message contains a cancellation for an event not found on your calendar.
|
||||
|
||||
# Variables:
|
||||
# $organizer (String) - The participant that cancelled the invitation.
|
||||
|
@ -18,8 +22,6 @@ calendar-invitation-panel-intro-cancel = { $organizer } has cancelled:
|
|||
# $summary (String) - A short summary or title of the event.
|
||||
calendar-invitation-panel-title = { $summary }
|
||||
|
||||
calendar-invitation-panel-save-button = Save
|
||||
|
||||
calendar-invitation-panel-view-button = View
|
||||
|
||||
calendar-invitation-panel-update-button = Update
|
||||
|
@ -32,7 +34,13 @@ calendar-invitation-panel-decline-button = No
|
|||
|
||||
calendar-invitation-panel-tentative-button = Maybe
|
||||
|
||||
calendar-invitation-panel-reply-status = * You have not decided or responded yet
|
||||
calendar-invitation-panel-more-button = More
|
||||
|
||||
calendar-invitation-panel-menu-item-save =
|
||||
.label = Save to calendar
|
||||
|
||||
calendar-invitation-panel-menu-item-toggle-changes=
|
||||
.label = Show Changes
|
||||
|
||||
calendar-invitation-panel-prop-title-when = When:
|
||||
|
||||
|
@ -114,3 +122,9 @@ calendar-invitation-panel-partstat-needs-action = { $count } pending
|
|||
calendar-invitation-panel-partstat-total = { $count } participants
|
||||
|
||||
calendar-invitation-panel-prop-title-attachments = Attachments:
|
||||
|
||||
calendar-invitation-change-indicator-removed = Removed
|
||||
|
||||
calendar-invitation-change-indicator-added = New
|
||||
|
||||
calendar-invitation-change-indicator-modified = Changed
|
||||
|
|
|
@ -35,13 +35,12 @@ async function checkABrowser(browser) {
|
|||
let picker = doc.getElementById(browser.getAttribute("datetimepicker"));
|
||||
Assert.ok(picker, "date/time picker exists");
|
||||
|
||||
// Click on the input box to open the popup.
|
||||
// Open the popup.
|
||||
let shownPromise = BrowserTestUtils.waitForEvent(picker, "popupshown");
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
`input[type="date"]`,
|
||||
{},
|
||||
browser
|
||||
);
|
||||
await SpecialPowers.spawn(browser, [], function() {
|
||||
content.document.notifyUserGestureActivation();
|
||||
content.document.querySelector(`input[type="date"]`).showPicker();
|
||||
});
|
||||
await shownPromise;
|
||||
|
||||
// Allow the picker time to initialise.
|
||||
|
@ -50,7 +49,12 @@ async function checkABrowser(browser) {
|
|||
// Click in the middle of the picker. This should always land on a date and
|
||||
// close the picker.
|
||||
let hiddenPromise = BrowserTestUtils.waitForEvent(picker, "popuphidden");
|
||||
EventUtils.synthesizeMouseAtCenter(picker, {}, win);
|
||||
let frame = picker.querySelector("#dateTimePopupFrame");
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
frame.contentDocument.querySelector(".days-view td"),
|
||||
{},
|
||||
frame.contentWindow
|
||||
);
|
||||
await hiddenPromise;
|
||||
|
||||
// Check the date was assigned to the input.
|
||||
|
|
|
@ -1316,6 +1316,10 @@ toolbarpaletteitem[type="spring"][place="toolbar"] {
|
|||
-moz-box-align: center;
|
||||
}
|
||||
|
||||
toolbarpaletteitem[type="spring"][place="toolbar"] {
|
||||
-moz-box-flex: 1000;
|
||||
}
|
||||
|
||||
toolbarpaletteitem[type="spacer"][place="toolbar"] toolbarspacer,
|
||||
toolbarpaletteitem[type="spring"][place="toolbar"] toolbarspring {
|
||||
height: 16px;
|
||||
|
|
|
@ -1051,7 +1051,7 @@ calendar-notifications-setting .remove-button {
|
|||
|
||||
.synced-account .config-list {
|
||||
gap: 0;
|
||||
margin-inline-start: 21px
|
||||
margin-inline-start: 21px;
|
||||
}
|
||||
|
||||
.config-item {
|
||||
|
@ -1061,7 +1061,7 @@ calendar-notifications-setting .remove-button {
|
|||
}
|
||||
|
||||
.config-item label::before {
|
||||
display: inline-block
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
margin-inline-end: 6px;
|
||||
|
|
Загрузка…
Ссылка в новой задаче