Release v2.2.0
This commit is contained in:
@@ -0,0 +1,772 @@
|
||||
// Shared browser utilities for region helper modules.
|
||||
|
||||
(function () {
|
||||
function escapeHtml(value) {
|
||||
if (window.webUiHelpers && typeof window.webUiHelpers.escapeHtml === 'function') {
|
||||
return window.webUiHelpers.escapeHtml(value);
|
||||
}
|
||||
|
||||
return String(value === undefined || value === null ? '' : value)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function sanitizePreviewHtml(html) {
|
||||
var output = String(html || '');
|
||||
output = output.replace(/<script[\s\S]*?<\/script>/gi, '');
|
||||
output = output.replace(/<style[\s\S]*?<\/style>/gi, '');
|
||||
return output.replace(/<[^>]+>/g, function (tag) {
|
||||
var match = tag.match(/^<\s*(\/?)\s*([a-z0-9]+)([\s\S]*?)(\/?)>$/i);
|
||||
if (!match) {
|
||||
return '';
|
||||
}
|
||||
|
||||
var closing = Boolean(match[1]);
|
||||
var name = String(match[2] || '').toLowerCase();
|
||||
var attrText = String(match[3] || '');
|
||||
var selfClosing = Boolean(match[4]) || name === 'br' || name === 'hr';
|
||||
var allowed = ['a', 'b', 'blockquote', 'br', 'code', 'div', 'em', 'figure', 'figcaption', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'li', 'ol', 'p', 'pre', 'span', 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'th', 'thead', 'tr', 'u', 'ul'];
|
||||
if (allowed.indexOf(name) === -1) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (closing) {
|
||||
return '</' + name + '>';
|
||||
}
|
||||
|
||||
return '<' + name + sanitizeTagAttributes(name, attrText) + '>';
|
||||
});
|
||||
}
|
||||
|
||||
function sanitizeRichText(html) {
|
||||
return sanitizePreviewHtml(html);
|
||||
}
|
||||
|
||||
function sanitizeTagAttributes(tagName, attrText) {
|
||||
var allowedAttributes = {
|
||||
a: ['href', 'title', 'target', 'rel', 'class', 'style'],
|
||||
blockquote: ['class', 'style'],
|
||||
div: ['class', 'style'],
|
||||
figure: ['class', 'style'],
|
||||
figcaption: ['class', 'style'],
|
||||
h1: ['class', 'style'],
|
||||
h2: ['class', 'style'],
|
||||
h3: ['class', 'style'],
|
||||
h4: ['class', 'style'],
|
||||
h5: ['class', 'style'],
|
||||
h6: ['class', 'style'],
|
||||
li: ['class', 'style'],
|
||||
ol: ['class', 'style', 'start'],
|
||||
p: ['class', 'style'],
|
||||
pre: ['class', 'style'],
|
||||
span: ['class', 'style'],
|
||||
table: ['class', 'style'],
|
||||
td: ['class', 'style', 'colspan', 'rowspan'],
|
||||
th: ['class', 'style', 'colspan', 'rowspan', 'scope'],
|
||||
tr: ['class', 'style'],
|
||||
ul: ['class', 'style']
|
||||
};
|
||||
var allowed = allowedAttributes[tagName] || [];
|
||||
if (!allowed.length) {
|
||||
return '';
|
||||
}
|
||||
|
||||
var attrs = [];
|
||||
attrText.replace(/([a-zA-Z0-9:-]+)(?:\s*=\s*("([^"]*)"|'([^']*)'|([^\s"'>/=`]+)))?/g, function (_full, key, _valuePart, doubleQuoted, singleQuoted, bareValue) {
|
||||
var lowerKey = String(key || '').toLowerCase();
|
||||
if (allowed.indexOf(lowerKey) === -1) {
|
||||
return '';
|
||||
}
|
||||
|
||||
var value = doubleQuoted !== undefined ? doubleQuoted : singleQuoted !== undefined ? singleQuoted : bareValue !== undefined ? bareValue : '';
|
||||
if (lowerKey === 'href' && /^(?:\s*javascript:|\s*data:)/i.test(String(value || ''))) {
|
||||
return '';
|
||||
}
|
||||
if (lowerKey === 'style' && /(?:expression\s*\(|javascript:|url\s*\()/i.test(String(value || ''))) {
|
||||
return '';
|
||||
}
|
||||
if (lowerKey === 'target') {
|
||||
var targetValue = String(value || '').trim();
|
||||
if (targetValue === '_blank') {
|
||||
attrs.push(' target="_blank"');
|
||||
if (attrs.indexOf(' rel="noreferrer noopener"') === -1) {
|
||||
attrs.push(' rel="noreferrer noopener"');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
attrs.push(' ' + lowerKey + '="' + escapeHtml(value) + '"');
|
||||
return '';
|
||||
});
|
||||
|
||||
return attrs.join('');
|
||||
}
|
||||
|
||||
function sanitizeFontFamily(value) {
|
||||
return String(value || '').trim();
|
||||
}
|
||||
|
||||
function sanitizeTextColor(value) {
|
||||
return String(value || '').trim() || '#000000';
|
||||
}
|
||||
|
||||
function normalizeAcceptList(value) {
|
||||
return Array.isArray(value) ? value : String(value || '').split(',').map(function (item) {
|
||||
return String(item || '').trim();
|
||||
}).filter(Boolean);
|
||||
}
|
||||
|
||||
function fileMatchesAccept(file, acceptValue) {
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var acceptList = normalizeAcceptList(acceptValue).map(function (item) {
|
||||
return item.toLowerCase();
|
||||
});
|
||||
|
||||
if (!acceptList.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var fileName = String(file.name || '').toLowerCase();
|
||||
var fileType = String(file.type || '').toLowerCase();
|
||||
|
||||
return acceptList.some(function (rule) {
|
||||
if (rule === '*/*') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (rule.charAt(0) === '.') {
|
||||
return fileName.endsWith(rule);
|
||||
}
|
||||
|
||||
if (rule.endsWith('/*')) {
|
||||
return fileType.indexOf(rule.slice(0, -1)) === 0;
|
||||
}
|
||||
|
||||
return fileType === rule;
|
||||
});
|
||||
}
|
||||
|
||||
function bindRegionMediaRemoveControls(templateFields, options) {
|
||||
var callbacks = options || {};
|
||||
|
||||
if (!templateFields || templateFields.dataset.regionMediaRemoveBound === '1') {
|
||||
return;
|
||||
}
|
||||
|
||||
templateFields.dataset.regionMediaRemoveBound = '1';
|
||||
|
||||
function getCard(regionId) {
|
||||
if (typeof callbacks.getCardByRegionId === 'function') {
|
||||
return callbacks.getCardByRegionId(regionId);
|
||||
}
|
||||
|
||||
return regionId ? templateFields.querySelector('[data-region-id="' + regionId + '"]') : null;
|
||||
}
|
||||
|
||||
function getMediaType(button, card) {
|
||||
if (typeof callbacks.getMediaType === 'function') {
|
||||
return callbacks.getMediaType(card, button);
|
||||
}
|
||||
|
||||
if (button && button.hasAttribute('data-remove-region-video')) {
|
||||
return 'video';
|
||||
}
|
||||
|
||||
if (button && button.hasAttribute('data-remove-region-image')) {
|
||||
return 'image';
|
||||
}
|
||||
|
||||
if (card && card.dataset && String(card.dataset.regionMediaType || '').trim()) {
|
||||
return String(card.dataset.regionMediaType || '').trim() === 'video' ? 'video' : 'image';
|
||||
}
|
||||
|
||||
return 'image';
|
||||
}
|
||||
|
||||
function getExistingPrefix(mediaType) {
|
||||
if (typeof callbacks.getExistingMediaPrefix === 'function') {
|
||||
return callbacks.getExistingMediaPrefix(mediaType);
|
||||
}
|
||||
|
||||
return String(mediaType || '').trim() === 'video' ? 'existing_region_video_' : 'existing_region_image_';
|
||||
}
|
||||
|
||||
function getMediaPrefix(mediaType) {
|
||||
if (typeof callbacks.getMediaPrefix === 'function') {
|
||||
return callbacks.getMediaPrefix(mediaType);
|
||||
}
|
||||
|
||||
return String(mediaType || '').trim() === 'video' ? 'region_video_' : 'region_image_';
|
||||
}
|
||||
|
||||
function getVideoDurationHiddenInput(regionId) {
|
||||
if (typeof callbacks.getVideoDurationHiddenInput === 'function') {
|
||||
return callbacks.getVideoDurationHiddenInput(regionId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function handleRemove(button) {
|
||||
var regionId = button.getAttribute('data-remove-region-image') || button.getAttribute('data-remove-region-video');
|
||||
var card = getCard(regionId);
|
||||
var mediaType = getMediaType(button, card);
|
||||
var hidden = card && card.querySelector('input[type="hidden"][name="' + getExistingPrefix(mediaType) + regionId + '"]');
|
||||
var input = card && card.querySelector('input[type="file"][name="' + getMediaPrefix(mediaType) + regionId + '"]');
|
||||
var uploadPath = input ? String(input.dataset.uploadedPath || '').trim() : '';
|
||||
|
||||
if (input && input.dataset.previewUrl) {
|
||||
URL.revokeObjectURL(input.dataset.previewUrl);
|
||||
delete input.dataset.previewUrl;
|
||||
}
|
||||
|
||||
if (input && input.dataset.uploadedNeedsCleanup === '1' && uploadPath && typeof callbacks.queueUploadCleanup === 'function') {
|
||||
callbacks.queueUploadCleanup([uploadPath]);
|
||||
}
|
||||
|
||||
if (input) {
|
||||
input.value = '';
|
||||
delete input.dataset.uploadedPath;
|
||||
delete input.dataset.uploadedNeedsCleanup;
|
||||
}
|
||||
|
||||
if (hidden) {
|
||||
hidden.value = '';
|
||||
}
|
||||
|
||||
var durationHidden = getVideoDurationHiddenInput(regionId);
|
||||
if (durationHidden) {
|
||||
durationHidden.value = '';
|
||||
}
|
||||
|
||||
if (typeof callbacks.renderPreview === 'function') {
|
||||
callbacks.renderPreview(card, '');
|
||||
}
|
||||
|
||||
if (typeof callbacks.markEdited === 'function') {
|
||||
callbacks.markEdited();
|
||||
}
|
||||
|
||||
if (typeof callbacks.requestPreviewRender === 'function') {
|
||||
callbacks.requestPreviewRender();
|
||||
}
|
||||
}
|
||||
|
||||
templateFields.addEventListener('click', function (event) {
|
||||
var button = event.target && event.target.closest ? event.target.closest('[data-remove-region-image], [data-remove-region-video]') : null;
|
||||
|
||||
if (!button || !templateFields.contains(button)) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleRemove(button);
|
||||
});
|
||||
|
||||
templateFields.addEventListener('keydown', function (event) {
|
||||
if (event.key !== 'Enter' && event.key !== ' ') {
|
||||
return;
|
||||
}
|
||||
|
||||
var button = event.target && event.target.closest ? event.target.closest('[data-remove-region-image], [data-remove-region-video]') : null;
|
||||
|
||||
if (!button || !templateFields.contains(button)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
handleRemove(button);
|
||||
});
|
||||
}
|
||||
|
||||
function createRegionMediaUploadController(templateFields, options) {
|
||||
var callbacks = options || {};
|
||||
|
||||
if (!templateFields) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function getCard(regionId) {
|
||||
if (typeof callbacks.getCardByRegionId === 'function') {
|
||||
return callbacks.getCardByRegionId(regionId);
|
||||
}
|
||||
|
||||
return regionId ? templateFields.querySelector('[data-region-id="' + regionId + '"]') : null;
|
||||
}
|
||||
|
||||
function getMediaTypeFromInput(input) {
|
||||
if (typeof callbacks.getMediaTypeFromInput === 'function') {
|
||||
return callbacks.getMediaTypeFromInput(input);
|
||||
}
|
||||
|
||||
return input && input.name && input.name.indexOf('region_video_') === 0 ? 'video' : 'image';
|
||||
}
|
||||
|
||||
function getMediaPrefix(mediaType) {
|
||||
if (typeof callbacks.getMediaPrefix === 'function') {
|
||||
return callbacks.getMediaPrefix(mediaType);
|
||||
}
|
||||
|
||||
return String(mediaType || '').trim() === 'video' ? 'region_video_' : 'region_image_';
|
||||
}
|
||||
|
||||
function getExistingMediaPrefix(mediaType) {
|
||||
if (typeof callbacks.getExistingMediaPrefix === 'function') {
|
||||
return callbacks.getExistingMediaPrefix(mediaType);
|
||||
}
|
||||
|
||||
return String(mediaType || '').trim() === 'video' ? 'existing_region_video_' : 'existing_region_image_';
|
||||
}
|
||||
|
||||
function getUploadZone(input) {
|
||||
if (typeof callbacks.getUploadZone === 'function') {
|
||||
return callbacks.getUploadZone(input);
|
||||
}
|
||||
|
||||
return input ? input.closest('[data-region-upload-zone]') : null;
|
||||
}
|
||||
|
||||
function getUploadProgressNode(zone) {
|
||||
return zone ? zone.querySelector('[data-region-upload-progress]') : null;
|
||||
}
|
||||
|
||||
function getUploadProgressBar(zone) {
|
||||
return zone ? zone.querySelector('[data-region-upload-progress-bar]') : null;
|
||||
}
|
||||
|
||||
function getUploadProgressText(zone) {
|
||||
return zone ? zone.querySelector('[data-region-upload-progress-text]') : null;
|
||||
}
|
||||
|
||||
function setUploadProgress(zone, percent, text, isIndeterminate) {
|
||||
var progress = getUploadProgressNode(zone);
|
||||
var bar = getUploadProgressBar(zone);
|
||||
var progressText = getUploadProgressText(zone);
|
||||
var normalizedPercent = Math.max(0, Math.min(100, Math.round(Number(percent || 0))));
|
||||
|
||||
if (!progress || !bar) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (progressText) {
|
||||
progressText.textContent = text || 'Uploading...';
|
||||
}
|
||||
|
||||
progress.hidden = false;
|
||||
bar.style.width = (isIndeterminate ? 100 : normalizedPercent) + '%';
|
||||
bar.textContent = isIndeterminate ? 'Uploading...' : normalizedPercent + '%';
|
||||
bar.setAttribute('aria-valuenow', String(isIndeterminate ? 100 : normalizedPercent));
|
||||
bar.classList.toggle('progress-bar-striped', Boolean(isIndeterminate));
|
||||
bar.classList.toggle('progress-bar-animated', Boolean(isIndeterminate));
|
||||
}
|
||||
|
||||
function setUploadState(input, isUploading, percent, text, isIndeterminate) {
|
||||
var zone = getUploadZone(input);
|
||||
var progress = getUploadProgressNode(zone);
|
||||
|
||||
if (!zone || !progress) {
|
||||
return;
|
||||
}
|
||||
|
||||
zone.classList.toggle('is-uploading', Boolean(isUploading));
|
||||
zone.setAttribute('aria-busy', isUploading ? 'true' : 'false');
|
||||
input.disabled = Boolean(isUploading);
|
||||
|
||||
if (isUploading) {
|
||||
setUploadProgress(zone, percent, text, isIndeterminate);
|
||||
return;
|
||||
}
|
||||
|
||||
progress.hidden = true;
|
||||
if (input) {
|
||||
input.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function setInputFile(input, file) {
|
||||
if (!input || !file) {
|
||||
return;
|
||||
}
|
||||
|
||||
var dataTransfer = new DataTransfer();
|
||||
dataTransfer.items.add(file);
|
||||
input.files = dataTransfer.files;
|
||||
input.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
|
||||
function queueUploadCleanup(uploadPaths) {
|
||||
var normalizedPaths = Array.from(new Set((uploadPaths || []).map(function (value) {
|
||||
return String(value || '').trim();
|
||||
}).filter(Boolean)));
|
||||
|
||||
if (!normalizedPaths.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof callbacks.queueUploadCleanup === 'function') {
|
||||
callbacks.queueUploadCleanup(normalizedPaths);
|
||||
return;
|
||||
}
|
||||
|
||||
var payload = JSON.stringify({ uploadPaths: normalizedPaths });
|
||||
var url = String(callbacks.cleanupUrl || '/slides/uploads/cleanup');
|
||||
|
||||
if (navigator.sendBeacon) {
|
||||
try {
|
||||
navigator.sendBeacon(url, new Blob([payload], { type: 'application/json' }));
|
||||
return;
|
||||
} catch (_error) {
|
||||
// Fall back to fetch below.
|
||||
}
|
||||
}
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: payload,
|
||||
credentials: 'same-origin',
|
||||
keepalive: true
|
||||
}).catch(function () {
|
||||
// Best-effort cleanup only.
|
||||
});
|
||||
}
|
||||
|
||||
function getPendingUploadCleanupPaths() {
|
||||
return Array.prototype.slice.call(templateFields.querySelectorAll('input[type="file"][name^="region_image_"], input[type="file"][name^="region_video_"]')).map(function (input) {
|
||||
if (!input || String(input.dataset.uploadedNeedsCleanup || '') !== '1') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return String(input.dataset.uploadedPath || '').trim();
|
||||
}).filter(Boolean);
|
||||
}
|
||||
|
||||
function clearPendingUploadCleanupPaths() {
|
||||
Array.prototype.slice.call(templateFields.querySelectorAll('input[type="file"][name^="region_image_"], input[type="file"][name^="region_video_"]')).forEach(function (input) {
|
||||
if (!input) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete input.dataset.uploadedNeedsCleanup;
|
||||
});
|
||||
}
|
||||
|
||||
function uploadFile(input, file) {
|
||||
if (!input || !file) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
var mediaType = getMediaTypeFromInput(input);
|
||||
var regionId = input.name.replace(/^region_(?:image|video)_/, '');
|
||||
var card = getCard(regionId);
|
||||
var hidden = card && card.querySelector('input[type="hidden"][name="' + getExistingMediaPrefix(mediaType) + regionId + '"]');
|
||||
var zone = getUploadZone(input);
|
||||
var uploadToken = String(Date.now()) + ':' + Math.random().toString(16).slice(2);
|
||||
var previousUploadedPath = String(input.dataset.uploadedPath || '').trim();
|
||||
var previousNeedsCleanup = String(input.dataset.uploadedNeedsCleanup || '') === '1';
|
||||
var uploadUrl = String(callbacks.uploadUrl || '/slides/uploads');
|
||||
var uploadTimeoutMs = Math.max(1, Number(callbacks.uploadTimeoutMs || 30000));
|
||||
|
||||
input.dataset.uploadToken = uploadToken;
|
||||
setUploadState(input, true, 0, 'Uploading...', true);
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
var formData = new FormData();
|
||||
formData.append('file', file, file.name || 'upload');
|
||||
|
||||
xhr.open('POST', uploadUrl, true);
|
||||
xhr.responseType = 'text';
|
||||
xhr.withCredentials = true;
|
||||
xhr.timeout = uploadTimeoutMs;
|
||||
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
xhr.setRequestHeader('Accept', 'application/json, text/plain, */*');
|
||||
|
||||
xhr.upload.onprogress = function (event) {
|
||||
if (input.dataset.uploadToken !== uploadToken) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!event || !event.lengthComputable || !event.total) {
|
||||
setUploadProgress(zone, 100, 'Uploading...', true);
|
||||
return;
|
||||
}
|
||||
|
||||
var percent = Math.round((event.loaded / event.total) * 100);
|
||||
setUploadProgress(zone, percent, 'Uploading ' + percent + '%', false);
|
||||
};
|
||||
|
||||
xhr.onload = function () {
|
||||
if (input.dataset.uploadToken !== uploadToken) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
var response = {
|
||||
ok: xhr.status >= 200 && xhr.status < 300,
|
||||
status: xhr.status,
|
||||
statusText: xhr.statusText,
|
||||
responseText: xhr.responseText || ''
|
||||
};
|
||||
|
||||
if (!response.ok) {
|
||||
var error = new Error(response.responseText || 'Unable to upload media.');
|
||||
error.status = response.status;
|
||||
error.responseText = response.responseText;
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (String(response.responseText || '').trim().toLowerCase().indexOf('<!doctype html') === 0 || String(response.responseText || '').indexOf('<html') !== -1) {
|
||||
reject(new Error('Upload redirected to an HTML page. Please sign in again and retry.'));
|
||||
return;
|
||||
}
|
||||
|
||||
var payload = {};
|
||||
try {
|
||||
payload = JSON.parse(response.responseText || '{}') || {};
|
||||
} catch (_error) {
|
||||
reject(new Error('Unable to parse the upload response.'));
|
||||
return;
|
||||
}
|
||||
|
||||
var uploadedPath = String(payload.path || '').trim();
|
||||
if (!uploadedPath) {
|
||||
reject(new Error('Unable to upload media.'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (input.dataset.previewUrl) {
|
||||
URL.revokeObjectURL(input.dataset.previewUrl);
|
||||
delete input.dataset.previewUrl;
|
||||
}
|
||||
|
||||
if (hidden) {
|
||||
hidden.value = uploadedPath;
|
||||
}
|
||||
input.dataset.uploadedPath = uploadedPath;
|
||||
input.dataset.uploadedNeedsCleanup = '1';
|
||||
input.value = '';
|
||||
input.setCustomValidity('');
|
||||
|
||||
if (typeof callbacks.renderPreview === 'function') {
|
||||
callbacks.renderPreview(card, uploadedPath);
|
||||
}
|
||||
|
||||
if (typeof callbacks.syncDuration === 'function') {
|
||||
callbacks.syncDuration(card, uploadedPath);
|
||||
}
|
||||
|
||||
if (typeof callbacks.markEdited === 'function') {
|
||||
callbacks.markEdited();
|
||||
}
|
||||
|
||||
if (typeof callbacks.requestPreviewRender === 'function') {
|
||||
callbacks.requestPreviewRender();
|
||||
}
|
||||
|
||||
if (previousNeedsCleanup && previousUploadedPath && previousUploadedPath !== uploadedPath) {
|
||||
queueUploadCleanup([previousUploadedPath]);
|
||||
}
|
||||
|
||||
setUploadState(input, false);
|
||||
resolve(payload);
|
||||
};
|
||||
|
||||
xhr.onerror = function () {
|
||||
if (input.dataset.uploadToken !== uploadToken) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
reject(new Error('Unable to upload media.'));
|
||||
};
|
||||
|
||||
xhr.onabort = function () {
|
||||
if (input.dataset.uploadToken !== uploadToken) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
reject(new Error('Unable to upload media.'));
|
||||
};
|
||||
|
||||
xhr.ontimeout = function () {
|
||||
if (input.dataset.uploadToken !== uploadToken) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
reject(new Error('Upload timed out. Please try again.'));
|
||||
};
|
||||
|
||||
xhr.onloadend = function () {
|
||||
if (input.dataset.uploadToken === uploadToken) {
|
||||
delete input.dataset.uploadToken;
|
||||
setUploadState(input, false);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.send(formData);
|
||||
}).catch(function (error) {
|
||||
if (input.dataset.uploadToken === uploadToken) {
|
||||
delete input.dataset.uploadToken;
|
||||
setUploadState(input, false);
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
function handleSelection(input, file) {
|
||||
if (typeof callbacks.handleMediaSelection !== 'function') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return callbacks.handleMediaSelection(input, file, {
|
||||
setInputFile: setInputFile,
|
||||
uploadFile: uploadFile,
|
||||
getMediaTypeFromInput: getMediaTypeFromInput,
|
||||
getExistingMediaPrefix: getExistingMediaPrefix,
|
||||
getMediaPrefix: getMediaPrefix,
|
||||
getUploadZone: getUploadZone,
|
||||
queueUploadCleanup: queueUploadCleanup,
|
||||
getPendingUploadCleanupPaths: getPendingUploadCleanupPaths,
|
||||
clearPendingUploadCleanupPaths: clearPendingUploadCleanupPaths
|
||||
});
|
||||
}
|
||||
|
||||
if (!templateFields.dataset.regionMediaUploadBound) {
|
||||
templateFields.dataset.regionMediaUploadBound = '1';
|
||||
|
||||
templateFields.addEventListener('change', function (event) {
|
||||
var input = event.target && event.target.matches && event.target.matches('input[type="file"][name^="region_image_"], input[type="file"][name^="region_video_"]') ? event.target : null;
|
||||
|
||||
if (!input || !templateFields.contains(input)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var file = input.files && input.files.length ? input.files[0] : null;
|
||||
var hookResult = handleSelection(input, file);
|
||||
|
||||
if (hookResult === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hookResult && typeof hookResult.then === 'function') {
|
||||
hookResult.catch(function (error) {
|
||||
if (input.dataset.previewUrl) {
|
||||
URL.revokeObjectURL(input.dataset.previewUrl);
|
||||
delete input.dataset.previewUrl;
|
||||
}
|
||||
input.value = '';
|
||||
if (typeof callbacks.onUploadError === 'function') {
|
||||
callbacks.onUploadError(error, input);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (file) {
|
||||
uploadFile(input, file).catch(function (error) {
|
||||
if (input.dataset.previewUrl) {
|
||||
URL.revokeObjectURL(input.dataset.previewUrl);
|
||||
delete input.dataset.previewUrl;
|
||||
}
|
||||
input.value = '';
|
||||
if (typeof callbacks.onUploadError === 'function') {
|
||||
callbacks.onUploadError(error, input);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
templateFields.addEventListener('dragover', function (event) {
|
||||
var zone = event.target && event.target.closest ? event.target.closest('[data-region-upload-zone]') : null;
|
||||
if (!zone || !templateFields.contains(zone)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
zone.classList.add('is-dragover');
|
||||
});
|
||||
|
||||
templateFields.addEventListener('dragleave', function (event) {
|
||||
var zone = event.target && event.target.closest ? event.target.closest('[data-region-upload-zone]') : null;
|
||||
if (!zone || !templateFields.contains(zone)) {
|
||||
return;
|
||||
}
|
||||
|
||||
zone.classList.remove('is-dragover');
|
||||
});
|
||||
|
||||
templateFields.addEventListener('drop', function (event) {
|
||||
var zone = event.target && event.target.closest ? event.target.closest('[data-region-upload-zone]') : null;
|
||||
|
||||
if (!zone || !templateFields.contains(zone)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
zone.classList.remove('is-dragover');
|
||||
|
||||
var input = zone.querySelector('input[type="file"]');
|
||||
var files = event.dataTransfer && event.dataTransfer.files ? event.dataTransfer.files : null;
|
||||
var file = files && files.length ? files[0] : null;
|
||||
|
||||
if (!input || !file) {
|
||||
return;
|
||||
}
|
||||
|
||||
var hookResult = handleSelection(input, file);
|
||||
|
||||
if (hookResult === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hookResult && typeof hookResult.then === 'function') {
|
||||
hookResult.catch(function (error) {
|
||||
if (input.dataset.previewUrl) {
|
||||
URL.revokeObjectURL(input.dataset.previewUrl);
|
||||
delete input.dataset.previewUrl;
|
||||
}
|
||||
input.value = '';
|
||||
if (typeof callbacks.onUploadError === 'function') {
|
||||
callbacks.onUploadError(error, input);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setInputFile(input, file);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
uploadFile: uploadFile,
|
||||
queueUploadCleanup: queueUploadCleanup,
|
||||
getPendingUploadCleanupPaths: getPendingUploadCleanupPaths,
|
||||
clearPendingUploadCleanupPaths: clearPendingUploadCleanupPaths,
|
||||
setInputFile: setInputFile
|
||||
};
|
||||
}
|
||||
|
||||
window.pulseRegionUtils = {
|
||||
escapeHtml: escapeHtml,
|
||||
sanitizePreviewHtml: sanitizePreviewHtml,
|
||||
sanitizeRichText: sanitizeRichText,
|
||||
sanitizeFontFamily: sanitizeFontFamily,
|
||||
sanitizeTextColor: sanitizeTextColor,
|
||||
normalizeAcceptList: normalizeAcceptList,
|
||||
fileMatchesAccept: fileMatchesAccept,
|
||||
bindRegionMediaRemoveControls: bindRegionMediaRemoveControls,
|
||||
createRegionMediaUploadController: createRegionMediaUploadController
|
||||
};
|
||||
}());
|
||||
Reference in New Issue
Block a user