Files
binoc-central-mirror/mail/components/compose/content/cloudAttachmentLinkManager.js
T
2020-05-10 13:52:36 -04:00

484 lines
17 KiB
JavaScript

/* 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/. */
var gCloudAttachmentLinkManager = {
init: function() {
this.cloudAttachments = [];
let bucket = document.getElementById("attachmentBucket");
bucket.addEventListener("attachment-uploaded", this, false);
bucket.addEventListener("attachments-removed", this, false);
bucket.addEventListener("attachments-converted", this, false);
// If we're restoring a draft that has some attachments,
// check to see if any of them are marked to be sent via
// cloud, and if so, add them to our list.
for (let i = 0; i < bucket.getRowCount(); ++i) {
let attachment = bucket.getItemAtIndex(i).attachment;
if (attachment && attachment.sendViaCloud)
this.cloudAttachments.push(attachment);
}
gMsgCompose.RegisterStateListener(this);
},
NotifyComposeFieldsReady: function() {},
NotifyComposeBodyReady: function() {},
ComposeProcessDone: function() {},
SaveInFolderDone: function() {},
handleEvent: function(event) {
let mailDoc = document.getElementById("content-frame").contentDocument;
if (event.type == "attachment-uploaded") {
if (this.cloudAttachments.length == 0)
this._insertHeader(mailDoc);
let attachment = event.target.attachment;
let provider = event.target.cloudProvider;
this.cloudAttachments.push(attachment);
this._insertItem(mailDoc, attachment, provider);
}
else if (event.type == "attachments-removed" ||
event.type == "attachments-converted") {
let items = [];
let list = mailDoc.getElementById("cloudAttachmentList");
if (list)
items = list.getElementsByClassName("cloudAttachmentItem");
for (let attachment in fixIterator(
event.detail, Components.interfaces.nsIMsgAttachment)) {
// Remove the attachment from the message body.
if (list)
for (let i = 0; i < items.length; i++)
if (items[i].contentLocation == attachment.contentLocation)
items[i].remove();
// Now, remove the attachment from our internal list.
let index = this.cloudAttachments.indexOf(attachment);
if (index != -1)
this.cloudAttachments.splice(index, 1);
}
this._updateAttachmentCount(mailDoc);
if (items.length == 0) {
if (list)
list.remove();
this._removeRoot(mailDoc);
}
}
},
/**
* Removes the root node for an attachment list in an HTML email.
*
* @param aDocument the document to remove the root node from.
*/
_removeRoot: function(aDocument) {
let header = aDocument.getElementById("cloudAttachmentListRoot");
if (header)
header.remove();
},
/**
* Given some node, returns the textual HTML representation for the node
* and its children.
*
* @param aDocument the document that the node is embedded in
* @param aNode the node to get the textual representation from
*/
_getHTMLRepresentation: function(aDocument, aNode) {
let tmp = aDocument.createElement("p");
tmp.appendChild(aNode);
return tmp.innerHTML;
},
/**
* Generates an appropriately styled link.
*
* @param aDocument the document to append the link to - doesn't actually
* get appended, but is used to generate the anchor node.
* @param aContent the textual content of the link
* @param aHref the HREF attribute for the generated link
*/
_generateLink: function(aDocument, aContent, aHref) {
const LINK_COLOR = "#0F7EDB";
let link = aDocument.createElement("a");
link.href = aHref;
link.textContent = aContent;
link.style.cssText = "color: " + LINK_COLOR + " !important";
return link;
},
_findInsertionPoint: function(aDocument) {
let mailBody = aDocument.querySelector("body");
let editor = GetCurrentEditor();
let selection = editor.selection;
let childNodes = mailBody.childNodes;
let childToInsertAfter, childIndex;
// First, search for any text nodes that are immediate children of
// the body. If we find any, we'll insert after those.
for (childIndex = childNodes.length - 1; childIndex >= 0; childIndex--) {
if (childNodes[childIndex].nodeType == Node.TEXT_NODE) {
childToInsertAfter = childNodes[childIndex];
break;
}
}
if (childIndex != -1) {
selection.collapse(childToInsertAfter,
childToInsertAfter.nodeValue ?
childToInsertAfter.nodeValue.length : 0);
if (childToInsertAfter.nodeValue &&
childToInsertAfter.nodeValue.length > 0)
editor.insertLineBreak();
editor.insertLineBreak();
return;
}
// If there's a signature, let's get a hold of it now.
let signature = mailBody.querySelector(".moz-signature");
// Are we replying?
let replyCitation = mailBody.querySelector(".moz-cite-prefix");
if (replyCitation) {
if (gCurrentIdentity && gCurrentIdentity.replyOnTop == 0) {
// Replying below quote - we'll select the point right before
// the signature. If there's no signature, we'll just use the
// last node.
if (signature && signature.previousSibling)
selection.collapse(mailBody,
Array.indexOf(childNodes,
signature.previousSibling));
else {
selection.collapse(mailBody, childNodes.length - 1);
editor.insertLineBreak();
if (!gMsgCompose.composeHTML)
editor.insertLineBreak();
selection.collapse(mailBody, childNodes.length - 2);
}
} else {
// Replying above quote
if (replyCitation.previousSibling) {
let nodeIndex = Array.indexOf(childNodes, replyCitation.previousSibling);
if (nodeIndex <= 0) {
editor.insertLineBreak();
nodeIndex = 1;
}
selection.collapse(mailBody, nodeIndex);
} else {
editor.beginningOfDocument();
editor.insertLineBreak();
}
}
return;
}
// Are we forwarding?
let forwardBody = mailBody.querySelector(".moz-forward-container");
if (forwardBody) {
if (forwardBody.previousSibling) {
let nodeIndex = Array.indexOf(childNodes,
forwardBody.previousSibling);
if (nodeIndex <= 0) {
editor.insertLineBreak();
nodeIndex = 1;
}
// If we're forwarding, insert just before the forward body.
selection.collapse(mailBody, nodeIndex);
} else {
// Just insert after a linebreak at the top.
editor.beginningOfDocument();
editor.insertLineBreak();
selection.collapse(mailBody, 1);
}
return;
}
// If we haven't figured it out at this point, let's see if there's a
// signature, and just insert before it.
if (signature && signature.previousSibling) {
let nodeIndex = Array.indexOf(childNodes, signature.previousSibling);
if (nodeIndex <= 0) {
editor.insertLineBreak();
nodeIndex = 1;
}
selection.collapse(mailBody, nodeIndex);
return;
}
// If we haven't figured it out at this point, let's just put it
// at the bottom of the message body. If the "bottom" is also the top,
// then we'll insert a linebreak just above it.
let nodeIndex = childNodes.length - 1;
if (nodeIndex <= 0) {
editor.insertLineBreak();
nodeIndex = 1;
}
selection.collapse(mailBody, nodeIndex);
},
/**
* Attempts to find any elements with an id in aIDs, and sets those elements
* id attribute to the empty string, freeing up the ids for later use.
*
* @param aDocument the document to search for the elements.
* @param aIDs an array of id strings.
*/
_resetNodeIDs: function(aDocument, aIDs) {
for (let id of aIDs) {
let node = aDocument.getElementById(id);
if (node)
node.id = "";
}
},
/**
* Insert the header for the cloud attachment list, which we'll use to
* as an insertion point for the individual cloud attachments.
*
* @param aDocument the document to insert the header into.
*/
_insertHeader: function(aDocument) {
// If there already exists a cloudAttachmentListRoot,
// cloudAttachmentListHeader or cloudAttachmentList in the document,
// strip them of their IDs so that we don't conflict with them.
this._resetNodeIDs(aDocument, ["cloudAttachmentListRoot",
"cloudAttachmentListHeader",
"cloudAttachmentList"]);
let brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
let editor = GetCurrentEditor();
let selection = editor.selection;
let originalAnchor = selection.anchorNode;
let originalOffset = selection.anchorOffset;
// Save off the selection ranges so we can restore them later.
let ranges = [];
for (let i = 0; i < selection.rangeCount; i++)
ranges.push(selection.getRangeAt(i));
this._findInsertionPoint(aDocument);
if (gMsgCompose.composeHTML) {
// It's really quite strange, but if we don't set
// the innerHTML of each element to be non-empty, then
// the nodes fail to be added to the compose window.
let root = editor.createElementWithDefaults("div");
root.id = "cloudAttachmentListRoot";
root.style.padding = "15px";
root.style.backgroundColor = "#D9EDFF";
root.innerHTML = " ";
let header = editor.createElementWithDefaults("div");
header.id = "cloudAttachmentListHeader";
header.style.marginBottom = "15px";
header.innerHTML = " ";
root.appendChild(header);
let list = editor.createElementWithDefaults("div");
list.id = "cloudAttachmentList";
list.style.backgroundColor = "#FFFFFF";
list.style.padding = "15px";
list.display = "inline-block";
list.innerHTML = " ";
root.appendChild(list);
let footer = editor.createElementWithDefaults("div");
let appLinkUrl = Services.prefs
.getCharPref("mail.cloud_files.inserted_urls.footer.link");
let appname = this._generateLink(aDocument,
brandBundle.GetStringFromName("brandFullName"),
appLinkUrl);
let applink = this._getHTMLRepresentation(aDocument, appname);
let footerMessage = getComposeBundle().getFormattedString("cloudAttachmentListFooter", [applink], 1);
footer.innerHTML = footerMessage;
footer.style.color = "#444444";
footer.style.fontSize = "small";
footer.style.marginTop = "15px";
root.appendChild(footer);
editor.insertElementAtSelection(root, false);
}
else {
let root = editor.createElementWithDefaults("div");
root.id = "cloudAttachmentListRoot";
let header = editor.createElementWithDefaults("div");
header.id = "cloudAttachmentListHeader";
header.innerHTML = " ";
root.appendChild(header);
let list = editor.createElementWithDefaults("span");
list.id = "cloudAttachmentList";
root.appendChild(list);
editor.insertElementAtSelection(root, false);
}
selection.collapse(originalAnchor, originalOffset);
// Restore the selection ranges.
for (let range of ranges)
selection.addRange(range);
},
/**
* Updates the count of how many attachments have been added
* in HTML emails.
*
* @aDocument the document that contains the cloudAttachmentListHeader node.
*/
_updateAttachmentCount: function(aDocument) {
let header = aDocument.getElementById("cloudAttachmentListHeader");
if (!header)
return;
let count = PluralForm.get(this.cloudAttachments.length,
getComposeBundle().getString("cloudAttachmentCountHeader"));
header.textContent = count.replace("#1", this.cloudAttachments.length);
},
/**
* Insert the information for a cloud attachment.
*
* @param aDocument the document to insert the item into
* @param aAttachment the nsIMsgAttachment to insert
* @param aProviderType the cloud storage provider
*/
_insertItem: function(aDocument, aAttachment, aProvider) {
let list = aDocument.getElementById("cloudAttachmentList");
if (!list) {
this._insertHeader(aDocument);
list = aDocument.getElementById("cloudAttachmentList");
}
let node = aDocument.createElement("div");
node.className = "cloudAttachmentItem";
node.contentLocation = aAttachment.contentLocation;
if (gMsgCompose.composeHTML) {
node.style.border = "1px solid #CDCDCD";
node.style.borderRadius = "5px";
node.style.marginTop = "10px";
node.style.marginBottom = "10px";
node.style.padding = "15px";
let paperclip = aDocument.createElement("img");
paperclip.style.marginRight = "5px";
paperclip.style.cssFloat = "left";
paperclip.style.width = "24px";
paperclip.style.height = "24px";
paperclip.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAABVUlEQVR42mNgGChgbGzMqm9slqFnbHZLz8TsPwoGioHkQGrItgBsOLrBaFjfxCydbAvgLjc2zQNymZCkmPRMzfOhllwj3wKoK9EMB4PQ0FBmJHmgWtM1eqZmS8m1gEHXxGyLnon5WlzyyGyyLMBmwKgFoxYMPgv+gdjq1ta8YL6elRhU/i+1LDgAYuuamidC+Q1geVOzzVSxQN/EPAnKvwLM0cFA+hOYb2TmRIkFH0CaDExNDbS1HXgwim1o2QMKNvIsMDafCtW4DOwLMzM1YJl0ChxUxqaNQCFGsDqgRRB1ppdIssDQ3FwLqPE7ermvY2ysDK8zEEH3RdfYWIPkIlvX1DQaasAvfVPTGBQ5E3MvoPhXiAPMYympFxoQ4W7eA/IBKIhASRRiuOkUiutnoGuzYQYi4b/AOCmjWiMAGFz2QEO3gwwGunoXiE+T1oa5uTkfKeoBW+cLhPF1+Q8AAAAASUVORK5CYII=";
node.appendChild(paperclip);
let link = this._generateLink(aDocument, aAttachment.name,
aAttachment.contentLocation);
link.setAttribute("moz-do-not-send", "true");
node.appendChild(link);
let size = aDocument.createElement("span");
size.textContent = "(" + gMessenger.formatFileSize(aAttachment.size)
+ ")";
size.style.marginLeft = "5px";
size.style.fontSize = "small";
size.style.color = "grey";
node.appendChild(size);
let providerIdentity = aDocument.createElement("span");
providerIdentity.style.cssFloat = "right";
if (aProvider.iconClass) {
let providerIcon = aDocument.createElement("img");
providerIcon.style.marginRight = "5px";
providerIdentity.appendChild(providerIcon);
if (!/^chrome:\/\//i.test(aProvider.iconClass)) {
providerIcon.src = aProvider.iconClass;
} else {
try {
// Let's use the goodness from MsgComposeCommands.js since we're
// sitting right in a compose window.
providerIcon.src = window.loadBlockedImage(aProvider.iconClass, true);
} catch (e) {
// Couldn't load the referenced image.
Components.utils.reportError(e);
}
}
}
if (aProvider.serviceURL) {
let providerLink = this._generateLink(aDocument, aProvider.displayName,
aProvider.serviceURL);
providerIdentity.appendChild(providerLink);
} else {
let providerName = aDocument.createElement("span");
providerName.textContent = aProvider.displayName;
providerIdentity.appendChild(providerName);
}
node.appendChild(providerIdentity);
let downloadUrl = this._generateLink(aDocument,
aAttachment.contentLocation,
aAttachment.contentLocation);
downloadUrl.style.fontSize = "small";
downloadUrl.style.display = "block";
node.appendChild(downloadUrl);
}
else {
node.textContent = getComposeBundle().getFormattedString(
"cloudAttachmentListItem",
[aAttachment.name, gMessenger.formatFileSize(aAttachment.size),
aProvider.displayName,
aAttachment.contentLocation]);
}
this._updateAttachmentCount(aDocument);
list.appendChild(node);
},
/**
* Event handler for when mail is sent. For mail that is being sent
* (and not saved!), find any cloudAttachmentList* nodes that we've created,
* and strip their IDs out. That way, if the receiving user replies by
* sending some BigFiles, we don't run into ID conflicts.
*/
send: function(aEvent) {
const Ci = Components.interfaces;
let msgType = parseInt(aEvent.target.getAttribute("msgtype"));
if (msgType == Ci.nsIMsgCompDeliverMode.Now ||
msgType == Ci.nsIMsgCompDeliverMode.Later ||
msgType == Ci.nsIMsgCompDeliverMode.Background) {
const kIDs = ["cloudAttachmentList", "cloudAttachmentListRoot",
"cloudAttachmentListHeader"];
let mailDoc = document.getElementById("content-frame").contentDocument;
for (let id of kIDs) {
let element = mailDoc.getElementById(id);
if (element)
element.removeAttribute("id");
}
}
},
};
window.addEventListener("compose-window-init",
gCloudAttachmentLinkManager.init.bind(gCloudAttachmentLinkManager), true);
window.addEventListener("compose-send-message",
gCloudAttachmentLinkManager.send.bind(gCloudAttachmentLinkManager), true);