108 lines
3.4 KiB
JavaScript
108 lines
3.4 KiB
JavaScript
(function(root, factory) {
|
|
const api = factory();
|
|
if (typeof module === 'object' && module.exports) module.exports = api;
|
|
else root.issueAttachment = api;
|
|
})(typeof self !== 'undefined' ? self : this, function() {
|
|
'use strict';
|
|
|
|
const MAX_BYTES = 2 * 1024 * 1024;
|
|
const IMAGE_TYPES = new Set(['image/png', 'image/jpeg', 'image/webp']);
|
|
|
|
function create(options) {
|
|
const readDataUrl = options.readDataUrl;
|
|
const upload = options.upload;
|
|
let selected = null;
|
|
let confirmed = null;
|
|
|
|
function select(file) {
|
|
if (!file || !IMAGE_TYPES.has(file.type)) {
|
|
throw new Error('Choose a PNG, JPEG, or WebP screenshot.');
|
|
}
|
|
if (!Number.isFinite(file.size) || file.size <= 0 || file.size > MAX_BYTES) {
|
|
throw new Error('Choose a screenshot that is 2 MB or smaller.');
|
|
}
|
|
selected = file;
|
|
confirmed = null;
|
|
return state();
|
|
}
|
|
|
|
function clear() {
|
|
selected = null;
|
|
confirmed = null;
|
|
}
|
|
|
|
function state() {
|
|
return selected ? {
|
|
name: selected.name,
|
|
size: selected.size,
|
|
uploaded: Boolean(confirmed),
|
|
} : null;
|
|
}
|
|
|
|
async function prepareComment(item, body) {
|
|
const text = String(body || '').trim();
|
|
if (!selected) return text;
|
|
if (!confirmed) {
|
|
const dataUrl = await readDataUrl(selected);
|
|
const marker = ';base64,';
|
|
const markerAt = String(dataUrl).indexOf(marker);
|
|
if (markerAt < 0) throw new Error('The screenshot could not be read. Choose it again.');
|
|
confirmed = await upload({
|
|
repository: item.repository,
|
|
number: item.number,
|
|
filename: selected.name,
|
|
content_type: selected.type,
|
|
data: String(dataUrl).slice(markerAt + marker.length),
|
|
});
|
|
if (!confirmed || typeof confirmed.markdown !== 'string' || !confirmed.markdown) {
|
|
confirmed = null;
|
|
throw new Error('The server did not confirm the screenshot upload.');
|
|
}
|
|
}
|
|
return text ? text + '\n\n' + confirmed.markdown : confirmed.markdown;
|
|
}
|
|
|
|
return { select, clear, state, prepareComment };
|
|
}
|
|
|
|
function mount(options) {
|
|
const controller = create(options);
|
|
const clearSelection = controller.clear;
|
|
let previewUrl = '';
|
|
|
|
function clearPreview() {
|
|
if (previewUrl) options.revokeObjectURL(previewUrl);
|
|
previewUrl = '';
|
|
options.image.src = '';
|
|
options.preview.hidden = true;
|
|
options.input.value = '';
|
|
clearSelection();
|
|
}
|
|
|
|
options.input.addEventListener('change', event => {
|
|
const file = event.target.files && event.target.files[0];
|
|
try {
|
|
controller.select(file);
|
|
} catch (error) {
|
|
options.status.textContent = error.message;
|
|
options.input.value = '';
|
|
return;
|
|
}
|
|
if (previewUrl) options.revokeObjectURL(previewUrl);
|
|
previewUrl = options.createObjectURL(file);
|
|
options.image.src = previewUrl;
|
|
options.meta.textContent = file.name + ' · ' + Math.ceil(file.size / 1024) + ' KB';
|
|
options.preview.hidden = false;
|
|
options.status.textContent = 'Screenshot ready to upload with this comment.';
|
|
});
|
|
options.remove.addEventListener('click', () => {
|
|
clearPreview();
|
|
options.status.textContent = 'Screenshot removed. Your comment is unchanged.';
|
|
});
|
|
|
|
return Object.assign(controller, { clear: clearPreview });
|
|
}
|
|
|
|
return { create, mount, MAX_BYTES };
|
|
});
|