645 lines
13 KiB
JavaScript
645 lines
13 KiB
JavaScript
'use strict';
|
|
|
|
var iframeObservers = new WeakMap();
|
|
|
|
document.addEventListener(
|
|
'DOMContentLoaded',
|
|
function () {
|
|
initializeEmailList();
|
|
}
|
|
);
|
|
|
|
function initializeEmailList() {
|
|
var emailItems =
|
|
document.querySelectorAll('.email-item');
|
|
|
|
emailItems.forEach(function (item) {
|
|
var summary =
|
|
item.querySelector('.email-summary');
|
|
|
|
var downloadButton =
|
|
item.querySelector('.download-icon');
|
|
|
|
if (summary) {
|
|
summary.addEventListener(
|
|
'click',
|
|
function (event) {
|
|
if (
|
|
event.target.closest(
|
|
'.download-icon'
|
|
)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
toggleEmail(item);
|
|
}
|
|
);
|
|
|
|
summary.addEventListener(
|
|
'keydown',
|
|
function (event) {
|
|
if (
|
|
event.key !== 'Enter' &&
|
|
event.key !== ' '
|
|
) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
event.target.closest(
|
|
'.download-icon'
|
|
)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
event.preventDefault();
|
|
|
|
toggleEmail(item);
|
|
}
|
|
);
|
|
}
|
|
|
|
if (downloadButton) {
|
|
downloadButton.addEventListener(
|
|
'click',
|
|
function (event) {
|
|
event.stopPropagation();
|
|
|
|
var id = item.dataset.id;
|
|
|
|
if (!id) {
|
|
console.error(
|
|
'Email ID is missing'
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
downloadEmail(
|
|
event,
|
|
id
|
|
);
|
|
}
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
function toggleEmail(item) {
|
|
var id = item.dataset.id;
|
|
|
|
if (!id) {
|
|
console.error(
|
|
'Email ID is missing'
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
var summary =
|
|
item.querySelector('.email-summary');
|
|
|
|
var detail =
|
|
item.querySelector('.email-detail');
|
|
|
|
var toggleIcon =
|
|
item.querySelector('.toggle');
|
|
|
|
var iframe =
|
|
item.querySelector(
|
|
'.email-content-frame'
|
|
);
|
|
|
|
if (!detail || !iframe) {
|
|
console.error(
|
|
'Email UI elements are missing for:',
|
|
id
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
var isHidden =
|
|
detail.classList.contains('hidden');
|
|
|
|
if (isHidden) {
|
|
detail.classList.remove('hidden');
|
|
|
|
if (toggleIcon) {
|
|
toggleIcon.classList.add(
|
|
'rotated'
|
|
);
|
|
}
|
|
|
|
if (summary) {
|
|
summary.setAttribute(
|
|
'aria-expanded',
|
|
'true'
|
|
);
|
|
}
|
|
|
|
loadEmailContent(
|
|
id,
|
|
iframe
|
|
);
|
|
} else {
|
|
detail.classList.add('hidden');
|
|
|
|
if (toggleIcon) {
|
|
toggleIcon.classList.remove(
|
|
'rotated'
|
|
);
|
|
}
|
|
|
|
if (summary) {
|
|
summary.setAttribute(
|
|
'aria-expanded',
|
|
'false'
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
function loadEmailContent(id, iframe) {
|
|
if (
|
|
iframe.dataset.loaded === 'true'
|
|
) {
|
|
resizeIframe(iframe);
|
|
return;
|
|
}
|
|
|
|
if (
|
|
iframe.dataset.loading === 'true'
|
|
) {
|
|
return;
|
|
}
|
|
|
|
iframe.dataset.loading = 'true';
|
|
|
|
fetch(
|
|
'/api/view?id=' +
|
|
encodeURIComponent(id),
|
|
{
|
|
method: 'GET',
|
|
credentials: 'same-origin'
|
|
}
|
|
)
|
|
.then(function (response) {
|
|
handleUnauthorized(response);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(
|
|
'HTTP error ' +
|
|
response.status
|
|
);
|
|
}
|
|
|
|
return response.text();
|
|
})
|
|
.then(function (data) {
|
|
resetIframeObserver(iframe);
|
|
|
|
writeEmailToIframe(
|
|
iframe,
|
|
data
|
|
);
|
|
|
|
iframe.dataset.loaded =
|
|
'true';
|
|
|
|
delete iframe.dataset.loading;
|
|
|
|
resizeIframe(iframe);
|
|
})
|
|
.catch(function (error) {
|
|
delete iframe.dataset.loading;
|
|
|
|
// При 401 уже выполняется redirect.
|
|
if (
|
|
error &&
|
|
error.message ===
|
|
'Session expired'
|
|
) {
|
|
return;
|
|
}
|
|
|
|
resetIframeObserver(iframe);
|
|
|
|
writeErrorToIframe(
|
|
iframe,
|
|
error.message
|
|
);
|
|
|
|
console.error(
|
|
'Failed to load email:',
|
|
id,
|
|
error
|
|
);
|
|
});
|
|
}
|
|
|
|
function handleUnauthorized(response) {
|
|
if (response.status !== 401) {
|
|
return;
|
|
}
|
|
|
|
window.location.href = '/login';
|
|
|
|
throw new Error(
|
|
'Session expired'
|
|
);
|
|
}
|
|
|
|
function writeEmailToIframe(
|
|
iframe,
|
|
emailBody
|
|
) {
|
|
var doc =
|
|
iframe.contentWindow.document;
|
|
|
|
doc.open();
|
|
|
|
doc.write(
|
|
'<!DOCTYPE html>' +
|
|
|
|
'<html>' +
|
|
|
|
'<head>' +
|
|
|
|
'<meta charset="UTF-8">' +
|
|
|
|
'<meta ' +
|
|
'name="viewport" ' +
|
|
'content="width=device-width, initial-scale=1.0"' +
|
|
'>' +
|
|
|
|
'<base target="_blank">' +
|
|
|
|
'<style>' +
|
|
|
|
'html {' +
|
|
'margin:0 !important;' +
|
|
'padding:0 !important;' +
|
|
'background:white !important;' +
|
|
'}' +
|
|
|
|
'body {' +
|
|
'margin:0 !important;' +
|
|
'padding:15px !important;' +
|
|
'font-family:monospace !important;' +
|
|
'font-size:12px !important;' +
|
|
'line-height:1.4 !important;' +
|
|
'background:white !important;' +
|
|
'color:#333 !important;' +
|
|
'overflow-wrap:anywhere !important;' +
|
|
'}' +
|
|
|
|
'* {' +
|
|
'box-sizing:border-box !important;' +
|
|
'max-width:100% !important;' +
|
|
'}' +
|
|
|
|
'img {' +
|
|
'max-width:100% !important;' +
|
|
'height:auto !important;' +
|
|
'}' +
|
|
|
|
'table {' +
|
|
'max-width:100% !important;' +
|
|
'}' +
|
|
|
|
'pre {' +
|
|
'margin:0 !important;' +
|
|
'font-family:inherit !important;' +
|
|
'white-space:pre-wrap !important;' +
|
|
'overflow-wrap:anywhere !important;' +
|
|
'}' +
|
|
|
|
'a {' +
|
|
'color:#2563eb !important;' +
|
|
'text-decoration:underline !important;' +
|
|
'}' +
|
|
|
|
'a:visited {' +
|
|
'color:#7c3aed !important;' +
|
|
'}' +
|
|
|
|
'a:hover {' +
|
|
'color:#1d4ed8 !important;' +
|
|
'}' +
|
|
|
|
'</style>' +
|
|
|
|
'</head>' +
|
|
|
|
'<body>' +
|
|
emailBody +
|
|
'</body>' +
|
|
|
|
'</html>'
|
|
);
|
|
|
|
doc.close();
|
|
}
|
|
|
|
function writeErrorToIframe(
|
|
iframe,
|
|
message
|
|
) {
|
|
var doc =
|
|
iframe.contentWindow.document;
|
|
|
|
doc.open();
|
|
|
|
doc.write(
|
|
'<!DOCTYPE html>' +
|
|
|
|
'<html>' +
|
|
|
|
'<head>' +
|
|
'<meta charset="UTF-8">' +
|
|
'</head>' +
|
|
|
|
'<body style="' +
|
|
'padding:15px;' +
|
|
'font-family:monospace;' +
|
|
'font-size:12px;' +
|
|
'color:#b91c1c;' +
|
|
'">' +
|
|
|
|
'Error loading email: ' +
|
|
escapeHTML(message) +
|
|
|
|
'</body>' +
|
|
|
|
'</html>'
|
|
);
|
|
|
|
doc.close();
|
|
|
|
resizeIframe(iframe);
|
|
}
|
|
|
|
function resetIframeObserver(iframe) {
|
|
var observer =
|
|
iframeObservers.get(iframe);
|
|
|
|
if (observer) {
|
|
observer.disconnect();
|
|
|
|
iframeObservers.delete(
|
|
iframe
|
|
);
|
|
}
|
|
}
|
|
|
|
function resizeIframe(iframe) {
|
|
if (
|
|
!iframe ||
|
|
!iframe.contentWindow
|
|
) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
var doc =
|
|
iframe.contentWindow.document;
|
|
|
|
if (!doc || !doc.body) {
|
|
return;
|
|
}
|
|
|
|
updateIframeHeight(
|
|
iframe,
|
|
doc
|
|
);
|
|
|
|
if (
|
|
typeof ResizeObserver ===
|
|
'undefined'
|
|
) {
|
|
return;
|
|
}
|
|
|
|
var existing =
|
|
iframeObservers.get(iframe);
|
|
|
|
if (existing) {
|
|
return;
|
|
}
|
|
|
|
var observer =
|
|
new ResizeObserver(
|
|
function () {
|
|
try {
|
|
var currentDoc =
|
|
iframe
|
|
.contentWindow
|
|
.document;
|
|
|
|
updateIframeHeight(
|
|
iframe,
|
|
currentDoc
|
|
);
|
|
} catch (error) {
|
|
// Iframe may have been replaced.
|
|
}
|
|
}
|
|
);
|
|
|
|
observer.observe(
|
|
doc.body
|
|
);
|
|
|
|
iframeObservers.set(
|
|
iframe,
|
|
observer
|
|
);
|
|
} catch (error) {
|
|
console.warn(
|
|
'Cannot resize email iframe:',
|
|
error
|
|
);
|
|
}
|
|
}
|
|
|
|
function updateIframeHeight(
|
|
iframe,
|
|
doc
|
|
) {
|
|
if (!doc || !doc.body) {
|
|
return;
|
|
}
|
|
|
|
var bodyHeight =
|
|
doc.body.scrollHeight;
|
|
|
|
var htmlHeight =
|
|
doc.documentElement
|
|
? doc.documentElement.scrollHeight
|
|
: 0;
|
|
|
|
var height = Math.max(
|
|
bodyHeight,
|
|
htmlHeight,
|
|
180
|
|
);
|
|
|
|
iframe.style.height =
|
|
height + 20 + 'px';
|
|
}
|
|
|
|
function downloadEmail(
|
|
event,
|
|
id
|
|
) {
|
|
var button =
|
|
event.currentTarget;
|
|
|
|
if (!id) {
|
|
return;
|
|
}
|
|
|
|
var originalTransform =
|
|
button.style.transform;
|
|
|
|
var originalOpacity =
|
|
button.style.opacity;
|
|
|
|
button.disabled = true;
|
|
|
|
button.style.transform =
|
|
'scale(0.8)';
|
|
|
|
button.style.opacity =
|
|
'0.5';
|
|
|
|
fetch(
|
|
'/api/download?id=' +
|
|
encodeURIComponent(id),
|
|
{
|
|
method: 'GET',
|
|
credentials: 'same-origin'
|
|
}
|
|
)
|
|
.then(function (response) {
|
|
handleUnauthorized(response);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(
|
|
'HTTP error ' +
|
|
response.status
|
|
);
|
|
}
|
|
|
|
return Promise.all([
|
|
response.blob(),
|
|
Promise.resolve(
|
|
getDownloadFilename(
|
|
response,
|
|
id
|
|
)
|
|
)
|
|
]);
|
|
})
|
|
.then(function (result) {
|
|
var blob = result[0];
|
|
var filename = result[1];
|
|
|
|
var objectURL =
|
|
URL.createObjectURL(blob);
|
|
|
|
var link =
|
|
document.createElement('a');
|
|
|
|
link.href = objectURL;
|
|
link.download = filename;
|
|
|
|
document.body.appendChild(
|
|
link
|
|
);
|
|
|
|
link.click();
|
|
link.remove();
|
|
|
|
window.setTimeout(
|
|
function () {
|
|
URL.revokeObjectURL(
|
|
objectURL
|
|
);
|
|
},
|
|
1000
|
|
);
|
|
})
|
|
.catch(function (error) {
|
|
if (
|
|
error &&
|
|
error.message ===
|
|
'Session expired'
|
|
) {
|
|
return;
|
|
}
|
|
|
|
console.error(
|
|
'Failed to download email:',
|
|
id,
|
|
error
|
|
);
|
|
|
|
window.alert(
|
|
'Failed to download email'
|
|
);
|
|
})
|
|
.finally(function () {
|
|
button.disabled = false;
|
|
|
|
button.style.transform =
|
|
originalTransform;
|
|
|
|
button.style.opacity =
|
|
originalOpacity;
|
|
});
|
|
}
|
|
|
|
function getDownloadFilename(
|
|
response,
|
|
id
|
|
) {
|
|
var contentDisposition =
|
|
response.headers.get(
|
|
'Content-Disposition'
|
|
);
|
|
|
|
if (contentDisposition) {
|
|
var match =
|
|
contentDisposition.match(
|
|
/filename="([^"]+)"/i
|
|
);
|
|
|
|
if (match && match[1]) {
|
|
return match[1];
|
|
}
|
|
}
|
|
|
|
var parts = id.split('/');
|
|
|
|
if (parts.length > 0) {
|
|
return parts[
|
|
parts.length - 1
|
|
];
|
|
}
|
|
|
|
return 'email.eml';
|
|
}
|
|
|
|
function escapeHTML(value) {
|
|
return String(value)
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, ''');
|
|
}
|