Release v2.4.2
This commit is contained in:
@@ -19,7 +19,123 @@
|
||||
var message = form.getAttribute('data-confirm-message');
|
||||
if (message && !window.confirm(message)) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
var actionPath = '';
|
||||
try {
|
||||
actionPath = new URL(form.action, window.location.href).pathname;
|
||||
} catch (_error) {
|
||||
actionPath = String(form.action || '');
|
||||
}
|
||||
|
||||
if (!/\/delete(?:\/|$)/.test(actionPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (form.dataset && form.dataset.busy === 'true') {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
form.dataset.busy = 'true';
|
||||
|
||||
fetch(form.action, {
|
||||
method: (form.method || 'POST').toUpperCase(),
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'Accept': 'application/json, text/plain, */*'
|
||||
},
|
||||
credentials: 'same-origin'
|
||||
}).then(function (response) {
|
||||
if (!response.ok) {
|
||||
return response.text().then(function (text) {
|
||||
var error = new Error(text || 'Unable to delete item.');
|
||||
error.status = response.status;
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
if (isLoginRedirect(response)) {
|
||||
window.location.replace(response.url || '/login');
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.replace(response.url || window.location.href);
|
||||
}).catch(function (error) {
|
||||
if (typeof showToast === 'function') {
|
||||
var status = Number(error && error.status);
|
||||
var variant = status === 403 ? 'warning' : status >= 400 && status < 500 ? 'warning' : 'danger';
|
||||
showToast(error.message || 'Unable to delete item.', variant);
|
||||
return;
|
||||
}
|
||||
window.alert(error.message || 'Unable to delete item.');
|
||||
}).finally(function () {
|
||||
delete form.dataset.busy;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function initDeleteButtons() {
|
||||
document.addEventListener('click', function (event) {
|
||||
var deleteButton = event.target.closest('[data-delete-action-url]');
|
||||
if (!deleteButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (deleteButton.disabled || (deleteButton.dataset && deleteButton.dataset.busy === 'true')) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
var actionUrl = deleteButton.getAttribute('data-delete-action-url');
|
||||
if (!actionUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
var message = deleteButton.getAttribute('data-confirm-message');
|
||||
if (message && !window.confirm(message)) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
deleteButton.dataset.busy = 'true';
|
||||
|
||||
fetch(actionUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'Accept': 'application/json, text/plain, */*'
|
||||
},
|
||||
credentials: 'same-origin'
|
||||
}).then(function (response) {
|
||||
if (!response.ok) {
|
||||
return response.text().then(function (text) {
|
||||
var error = new Error(text || 'Unable to delete item.');
|
||||
error.status = response.status;
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
if (isLoginRedirect(response)) {
|
||||
window.location.replace(response.url || '/login');
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.replace(response.url || window.location.href);
|
||||
}).catch(function (error) {
|
||||
if (typeof showToast === 'function') {
|
||||
var status = Number(error && error.status);
|
||||
var variant = status === 403 ? 'warning' : status >= 400 && status < 500 ? 'warning' : 'danger';
|
||||
showToast(error.message || 'Unable to delete item.', variant);
|
||||
return;
|
||||
}
|
||||
window.alert(error.message || 'Unable to delete item.');
|
||||
}).finally(function () {
|
||||
delete deleteButton.dataset.busy;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -79,6 +195,191 @@
|
||||
}, true);
|
||||
}
|
||||
|
||||
function parseAsyncSaveResponseMessage(responseText) {
|
||||
var savedMessage = '';
|
||||
try {
|
||||
var responseDocument = new DOMParser().parseFromString(responseText || '', 'text/html');
|
||||
var toastBody = responseDocument.querySelector('.toast-body');
|
||||
if (toastBody && toastBody.textContent) {
|
||||
savedMessage = toastBody.textContent.trim();
|
||||
}
|
||||
} catch (_error) {
|
||||
savedMessage = '';
|
||||
}
|
||||
|
||||
return savedMessage;
|
||||
}
|
||||
|
||||
function getAsyncSaveActionField(form) {
|
||||
if (!form || !form.querySelector) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return form.querySelector('input[type="hidden"][name="save_action"]') || form.querySelector('input[type="hidden"][name="action"]');
|
||||
}
|
||||
|
||||
function isLoginRedirect(response) {
|
||||
if (!response || !response.redirected || !response.url) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return new URL(response.url, window.location.href).pathname === '/login';
|
||||
} catch (_error) {
|
||||
return /\/login(?:[?#]|$)/.test(String(response.url || ''));
|
||||
}
|
||||
}
|
||||
|
||||
async function submitAsyncSaveForm(form, event, options) {
|
||||
var settings = options || {};
|
||||
if (!form || !form.hasAttribute || !form.hasAttribute('data-async-save')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (form.dataset && form.dataset.busy === 'true') {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var message = form.getAttribute('data-confirm-message');
|
||||
if (message && !window.confirm(message)) {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
form.dataset.busy = 'true';
|
||||
|
||||
try {
|
||||
if (typeof settings.beforeSubmit === 'function') {
|
||||
var beforeResult = await settings.beforeSubmit(form, event);
|
||||
if (beforeResult === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var hiddenSaveAction = getAsyncSaveActionField(form);
|
||||
var formData = new FormData(form);
|
||||
var submitterValue = String((hiddenSaveAction && hiddenSaveAction.value) || form.dataset.submitterValue || '').trim().toLowerCase();
|
||||
if (event && event.submitter && event.submitter.name) {
|
||||
submitterValue = String(event.submitter.value || '').trim().toLowerCase();
|
||||
formData.set(event.submitter.name, event.submitter.value || '');
|
||||
}
|
||||
|
||||
var hasFileValue = false;
|
||||
formData.forEach(function (value) {
|
||||
if (value && typeof value === 'object' && typeof value.name === 'string') {
|
||||
hasFileValue = true;
|
||||
}
|
||||
});
|
||||
|
||||
var isMultipart = hasFileValue || String(form.enctype || '').toLowerCase() === 'multipart/form-data';
|
||||
var body = isMultipart ? formData : new URLSearchParams();
|
||||
|
||||
if (!isMultipart) {
|
||||
formData.forEach(function (value, key) {
|
||||
body.append(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
var response = await fetch(form.action, {
|
||||
method: (form.method || 'POST').toUpperCase(),
|
||||
headers: Object.assign({
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'Accept': 'text/html, application/json, text/plain, */*'
|
||||
}, isMultipart ? {} : {
|
||||
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
|
||||
}),
|
||||
body: isMultipart ? body : body.toString(),
|
||||
credentials: 'same-origin'
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(await response.text() || (settings.errorMessage || 'Unable to save changes.'));
|
||||
}
|
||||
|
||||
if (isLoginRedirect(response)) {
|
||||
window.location.replace(response.url || '/login');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (submitterValue === 'close' || submitterValue === 'new') {
|
||||
var redirectUrl = submitterValue === 'close'
|
||||
? String(response.url || form.dataset.asyncSaveCloseUrl || window.location.href)
|
||||
: String(response.url || form.dataset.asyncSaveNewUrl || window.location.href);
|
||||
window.location.replace(redirectUrl);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (form.dataset && form.dataset.asyncSaveNewRedirect === 'response-url') {
|
||||
window.location.replace(String(response.url || form.dataset.asyncSaveNewUrl || window.location.href));
|
||||
return true;
|
||||
}
|
||||
|
||||
var responseText = await response.text();
|
||||
var responseDocument = null;
|
||||
try {
|
||||
responseDocument = new DOMParser().parseFromString(responseText || '', 'text/html');
|
||||
} catch (_error) {
|
||||
responseDocument = null;
|
||||
}
|
||||
|
||||
if (typeof settings.afterSuccess === 'function') {
|
||||
await settings.afterSuccess({
|
||||
form: form,
|
||||
response: response,
|
||||
responseText: responseText,
|
||||
responseDocument: responseDocument,
|
||||
submitterValue: submitterValue
|
||||
});
|
||||
}
|
||||
|
||||
clearFormDirty(form);
|
||||
|
||||
var successMessage = typeof settings.getSuccessMessage === 'function'
|
||||
? settings.getSuccessMessage({
|
||||
form: form,
|
||||
response: response,
|
||||
responseText: responseText,
|
||||
responseDocument: responseDocument,
|
||||
submitterValue: submitterValue
|
||||
})
|
||||
: parseAsyncSaveResponseMessage(responseText);
|
||||
|
||||
showToast(successMessage || String(settings.fallbackSuccessMessage || 'Saved.'), 'success');
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (typeof showToast === 'function') {
|
||||
var variant = Number(error && error.status) >= 400 && Number(error && error.status) < 500 ? 'warning' : 'danger';
|
||||
showToast(error && error.message ? error.message : (settings.errorMessage || 'Unable to save changes.'), variant);
|
||||
} else {
|
||||
window.alert(error && error.message ? error.message : (settings.errorMessage || 'Unable to save changes.'));
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
delete form.dataset.busy;
|
||||
delete form.dataset.submitterValue;
|
||||
if (typeof settings.afterFinally === 'function') {
|
||||
settings.afterFinally(form);
|
||||
}
|
||||
var hiddenInput = getAsyncSaveActionField(form);
|
||||
if (hiddenInput) {
|
||||
hiddenInput.value = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.webAsyncSaveForm = window.webAsyncSaveForm || {};
|
||||
window.webAsyncSaveForm.submit = submitAsyncSaveForm;
|
||||
window.webAsyncSaveForm.parseResponseMessage = parseAsyncSaveResponseMessage;
|
||||
|
||||
function initAsyncCommandForms() {
|
||||
function getAnnouncementActionState(actionPath) {
|
||||
var isPlay = /\/play$/.test(actionPath);
|
||||
@@ -174,7 +475,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
var hiddenInput = form.querySelector('input[type="hidden"][name="save_action"]');
|
||||
var hiddenInput = getAsyncSaveActionField(form);
|
||||
if (!hiddenInput) {
|
||||
hiddenInput = document.createElement('input');
|
||||
hiddenInput.type = 'hidden';
|
||||
@@ -208,6 +509,9 @@
|
||||
if (typeof window.initTableSearches === 'function') {
|
||||
window.initTableSearches(targetElement);
|
||||
}
|
||||
if (targetElement.id === 'playlist-items-body' && typeof window.rebindPlaylistTableDrag === 'function') {
|
||||
window.rebindPlaylistTableDrag(targetElement);
|
||||
}
|
||||
}
|
||||
|
||||
function replaceRefreshTargetFromPage(refreshTargetSelector, sequenceId) {
|
||||
@@ -311,7 +615,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
var button = target.closest('button[name="save_action"]');
|
||||
var button = target.closest('button[name="save_action"], button[name="action"]');
|
||||
if (!button) {
|
||||
return;
|
||||
}
|
||||
@@ -330,129 +634,24 @@
|
||||
if (!form || !form.hasAttribute || !form.hasAttribute('data-async-save')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (form.dataset && form.dataset.busy === 'true') {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
var message = form.getAttribute('data-confirm-message');
|
||||
if (message && !window.confirm(message)) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
form.dataset.busy = 'true';
|
||||
|
||||
var hiddenSaveAction = form.querySelector('input[type="hidden"][name="save_action"]');
|
||||
var formData = new FormData(form);
|
||||
var submitterValue = String((hiddenSaveAction && hiddenSaveAction.value) || form.dataset.submitterValue || '').trim().toLowerCase();
|
||||
if (event.submitter && event.submitter.name) {
|
||||
submitterValue = String(event.submitter.value || '').trim().toLowerCase();
|
||||
formData.set(event.submitter.name, event.submitter.value || '');
|
||||
}
|
||||
var hasFileValue = false;
|
||||
formData.forEach(function (value) {
|
||||
if (value && typeof value === 'object' && typeof value.name === 'string') {
|
||||
hasFileValue = true;
|
||||
}
|
||||
});
|
||||
|
||||
var isMultipart = hasFileValue || String(form.enctype || '').toLowerCase() === 'multipart/form-data';
|
||||
var body = isMultipart ? formData : new URLSearchParams();
|
||||
|
||||
if (!isMultipart) {
|
||||
formData.forEach(function (value, key) {
|
||||
body.append(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
fetch(form.action, {
|
||||
method: (form.method || 'POST').toUpperCase(),
|
||||
headers: Object.assign({
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'Accept': 'text/html, application/json, text/plain, */*'
|
||||
}, isMultipart ? {} : {
|
||||
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
|
||||
}),
|
||||
body: isMultipart ? body : body.toString(),
|
||||
credentials: 'same-origin'
|
||||
}).then(function (response) {
|
||||
if (!response.ok) {
|
||||
return response.text().then(function (text) {
|
||||
var error = new Error(text || 'Unable to save changes.');
|
||||
error.status = response.status;
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
var actionUrl = '';
|
||||
try {
|
||||
actionUrl = new URL(form.action, window.location.href).pathname;
|
||||
} catch (_error) {
|
||||
actionUrl = String(form.action || '');
|
||||
}
|
||||
var refreshTargetSelector = String(form.getAttribute('data-async-save-refresh-target') || '').trim();
|
||||
var refreshStateUrl = String(form.getAttribute('data-async-save-refresh-state-url') || '').trim();
|
||||
var refreshTaskId = getResponseQueryValue(response.url || '', 'refresh_task_id');
|
||||
var shouldFollowRedirect = Boolean(form.hasAttribute('data-async-save-new-url')) && !/\/\d+(?:\/|$)/.test(actionUrl);
|
||||
if (submitterValue === 'close' || submitterValue === 'new') {
|
||||
var redirectUrl = submitterValue === 'close'
|
||||
? String(form.dataset.asyncSaveCloseUrl || response.url || window.location.href)
|
||||
: String(form.dataset.asyncSaveNewUrl || response.url || window.location.href);
|
||||
window.location.replace(redirectUrl);
|
||||
return;
|
||||
}
|
||||
if (shouldFollowRedirect && response.url) {
|
||||
clearFormDirty(form);
|
||||
window.location.replace(response.url);
|
||||
return;
|
||||
}
|
||||
if (form.hasAttribute('data-async-save-reload-on-success')) {
|
||||
clearFormDirty(form);
|
||||
window.location.replace(response.url || window.location.href);
|
||||
return;
|
||||
}
|
||||
clearFormDirty(form);
|
||||
return response.text().then(function (text) {
|
||||
var savedMessage = '';
|
||||
var responseDocument = null;
|
||||
try {
|
||||
responseDocument = new DOMParser().parseFromString(text || '', 'text/html');
|
||||
var toastBody = responseDocument.querySelector('.toast-body');
|
||||
if (toastBody && toastBody.textContent) {
|
||||
savedMessage = toastBody.textContent.trim();
|
||||
}
|
||||
} catch (_error) {
|
||||
savedMessage = '';
|
||||
}
|
||||
submitAsyncSaveForm(form, event, {
|
||||
fallbackSuccessMessage: 'Saved.',
|
||||
afterSuccess: function (context) {
|
||||
var refreshTargetSelector = String(form.getAttribute('data-async-save-refresh-target') || '').trim();
|
||||
var refreshStateUrl = String(form.getAttribute('data-async-save-refresh-state-url') || '').trim();
|
||||
var refreshTaskId = getResponseQueryValue(context.response.url || '', 'refresh_task_id');
|
||||
|
||||
if (refreshTargetSelector && refreshStateUrl && refreshTaskId) {
|
||||
refreshSequence += 1;
|
||||
watchRefreshTask(refreshStateUrl, refreshTaskId, refreshTargetSelector, refreshSequence);
|
||||
} else if (refreshTargetSelector && responseDocument) {
|
||||
} else if (refreshTargetSelector && context.responseDocument) {
|
||||
var currentTarget = document.querySelector(refreshTargetSelector);
|
||||
var nextTarget = responseDocument.querySelector(refreshTargetSelector);
|
||||
var nextTarget = context.responseDocument.querySelector(refreshTargetSelector);
|
||||
if (currentTarget && nextTarget) {
|
||||
currentTarget.outerHTML = nextTarget.outerHTML;
|
||||
rebindRefreshTarget(document.querySelector(refreshTargetSelector));
|
||||
}
|
||||
}
|
||||
|
||||
showToast(savedMessage || 'Saved.', 'success');
|
||||
});
|
||||
}).catch(function (error) {
|
||||
if (typeof showToast === 'function') {
|
||||
var variant = Number(error && error.status) >= 400 && Number(error && error.status) < 500 ? 'warning' : 'danger';
|
||||
showToast(error.message || 'Unable to save changes.', variant);
|
||||
return;
|
||||
}
|
||||
window.alert(error.message || 'Unable to save changes.');
|
||||
}).finally(function () {
|
||||
delete form.dataset.busy;
|
||||
delete form.dataset.submitterValue;
|
||||
if (hiddenSaveAction) {
|
||||
hiddenSaveAction.value = '';
|
||||
}
|
||||
});
|
||||
}, true);
|
||||
@@ -651,6 +850,7 @@
|
||||
}
|
||||
|
||||
initConfirmForms();
|
||||
initDeleteButtons();
|
||||
initDirtyTracking();
|
||||
initCancelConfirm();
|
||||
initAsyncCommandForms();
|
||||
|
||||
Reference in New Issue
Block a user