mercury_editor-2.0.x-dev/source/js/preview-screen.js
source/js/preview-screen.js
((Drupal, drupalSettings, $, once) => {
/**
* Adds the me_id GET parameter to a URL.
* @param {String} url The url to add the me_id GET parameter to.
* @return {String} The url with the me_id GET parameter.
*/
function addPreviewParam(url) {
const mercuryEditorId = drupalSettings.mercuryEditor?.id || null;
if (!mercuryEditorId) {
return url;
}
const urlObj = new URL(url, window.location.origin);
if (urlObj.origin !== window.location.origin) {
return url;
}
urlObj.searchParams.set('me_id', mercuryEditorId);
return urlObj.toString();
}
// Intercept all XMLHttpRequests to add me_id GET parameter.
const originalOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function open(
method,
url,
async,
user,
password,
) {
return originalOpen.call(
this,
method,
addPreviewParam(url),
async,
user,
password,
);
};
// Intercept all fetch requests to add me_id GET parameter.
const originalFetch = window.fetch;
window.fetch = (input, init) => {
let url = typeof input === 'string' ? input : input.url;
if (url) {
url = addPreviewParam(url);
}
return originalFetch(url || input, init);
};
/**
* Prevent a click.
*
* @param {Event} e The click event.
* @return {Boolean} False.
*/
function preventDefault(e) {
e.stopPropagation();
e.preventDefault();
return false;
}
/**
* Uses window.postMessage() to send UI clicks to the parent window.
*
* @param {Event} e The click event.
* @return {Boolean} False to prevent default action.
*/
function lpbClickHandler(e) {
// First, send the ajaxPageState to the parent window.
window.parent.postMessage({
type: 'ajaxPreviewPageState',
settings: drupalSettings.ajaxPageState,
});
// Then, send the click event.
const message = {
type: 'drupalAjax',
settings: {
dialogType: e.currentTarget.getAttribute('data-dialog-type'),
dialog: JSON.parse(e.currentTarget.getAttribute('data-dialog-options')),
dialogRenderer: JSON.parse(
e.currentTarget.getAttribute('data-dialog-renderer'),
),
url: addPreviewParam(e.currentTarget.getAttribute('href')),
},
};
window.parent.postMessage(message);
e.stopPropagation();
e.preventDefault();
return false;
}
/**
* Attaches the behavior to the edit screen.
*/
Drupal.behaviors.mercuryEditorPreviewScreen = {
attach(context) {
// Check for duplicate data attributes.
const duplicateContainers = Array.from(
document.querySelectorAll('[data-me-edit-screen-key]'),
)
.map((container) => container.getAttribute('data-me-edit-screen-key'))
.filter((value, index, self) => self.indexOf(value) !== index);
if (duplicateContainers.length > 0) {
console.error(
'Multiple HTML elements found using the same data attribute, "data-me-edit-screen-key", which should be unique. Make sure attributes are not passed to child elements in twig templates.',
duplicateContainers,
);
}
// Send the initial ajaxPageState to the parent window.
window.parent.postMessage({
type: 'ajaxPreviewPageState',
settings: drupalSettings.ajaxPageState,
});
window.parent.postMessage({
type: 'layoutParagraphsSettings',
settings: drupalSettings.lpBuilder || {}
});
// Attaches click handlers to links that use window.postMessage().
once(
'me-msg-broadcaster',
'.js-lpb-ui.use-ajax, .js-lpb-ui .use-ajax',
).forEach((el) => {
$(el).off(); // Since core attaches behavior with JQuery.
el.addEventListener('mousedown', preventDefault);
el.addEventListener('mouseup', preventDefault);
el.addEventListener('click', lpbClickHandler);
});
// Prevent links from working in the preview iframe.
if (window.parent !== window) {
once('me-stop-iframed-links', 'a', context).forEach((link) => {
if (link.closest('.lpb-controls') === null) {
link.setAttribute('target', '_parent');
link.addEventListener('click', (e) => {
e.stopPropagation();
e.preventDefault();
return false;
});
}
});
once(
'me-prevent-focus',
'a, button, input, textarea, select, details',
context,
).forEach((focussable) => {
if (
focussable.closest('.lpb-controls') === null &&
focussable.closest('.mercury-editor-ui') === null &&
!focussable.classList.contains('use-postmessage')
) {
focussable.setAttribute('tabindex', '-1');
}
});
}
},
};
document.addEventListener('lpb-component:blur', (event) => {
setTimeout(() => {
if (!document.querySelector('.js-lpb-component[data-active]')) {
window.parent.postMessage({
type: 'closeModal',
settings: {
componentUuid: event.target.getAttribute('data-uuid'),
componentType: event.target.getAttribute('data-type'),
},
});
}
}, 100);
});
document.addEventListener('lpb-component:focus', (event) => {
if (event.detail?.originalEvent?.type !== 'lpb-component:update') {
event.target.querySelector('.lpb-edit')?.click();
}
});
document.addEventListener(
'mouseup',
(event) => {
if (event.target.classList.contains('lpb-edit')) {
window.parent.postMessage({
type: 'openSidebar',
});
}
},
true,
);
// Sends all Layout Paragraphs events to the parent window.
[
'lpb-component:move',
'lpb-component:drop',
'lpb-component:insert',
'lpb-component:delete',
'lpb-component:update',
'lpb-component:blur',
'lpb-component:focus',
'lpb-component:drag',
].forEach((eventName) => {
document.addEventListener(eventName, (event) => {
window.parent.postMessage({
type: 'layoutParagraphsEvent',
eventName,
ref: event.target.getAttribute('data-uuid'),
value: event.target.outerHTML,
});
});
});
// Posts a message that the preview has been updated.
[
'lpb-component:move',
'lpb-component:drop',
'lpb-component:insert',
'lpb-component:delete',
'lpb-component:update',
].forEach((eventName) => {
document.addEventListener(eventName, (event) => {
window.parent.postMessage({
type: 'previewUpdated',
data: {
ref: event.target.getAttribute('data-uuid'),
value: event.target.outerHTML,
},
});
});
});
})(Drupal, drupalSettings, jQuery, once);
