component_library-1.0.x-dev/js/override_mode.js
js/override_mode.js
(function ($, Drupal, once) {
Drupal.behaviors.OverrideUiBehaviour = {
overrideMode: false,
overrideTrayOpen: false,
attach: function (context, settings) {
// Set a data attribute on each template element based on its paired HTML comment.
Drupal.behaviors.OverrideUiBehaviour.processHtmlComments(context);
// Close the off canvas dialog when clicking on the toolbar button and the
// dialog is already open.
once('OverrideUiToolbarBtn', '#enable-override-mode', context).forEach(function (toggleButton) {
toggleButton.addEventListener(
'click',
function (event) {
if (!Drupal.behaviors.OverrideUiBehaviour.overrideTrayOpen) {
const drupalAjaxDialog = Drupal.ajax({
url: settings.path.baseUrl + settings.path.pathPrefix + 'override-mode',
dialogType: 'dialog',
dialogRenderer: 'off_canvas',
dialog: { width: 300 },
});
drupalAjaxDialog.execute();
Drupal.behaviors.OverrideUiBehaviour.overrideTrayOpen = true;
} else {
$(toggleButton).off();
$closeButton = $('.ui-front').find('.ui-dialog-titlebar-close').click();
Drupal.behaviors.OverrideUiBehaviour.overrideTrayOpen = false;
}
event.preventDefault();
return false;
},
);
});
// Build the Override Mode tray list of buttons.
once('OverrideUiBehaviour', '#override-mode-tray', context).forEach(function (buttonContainer) {
// Flag that Override Mode is enabled.
Drupal.behaviors.OverrideUiBehaviour.overrideMode = true;
const overridableTemplates = document.querySelectorAll('[data-override-mode]:not(#override-mode-tray)');
const list = document.createElement('ul');
overridableTemplates.forEach(function (overridableTemplate) {
if (typeof drupalSettings.overrides[overridableTemplate.dataset.overrideMode] !== 'undefined') {
let settings = drupalSettings.overrides[overridableTemplate.dataset.overrideMode];
// Add a button to override this template.
let button = document.createElement('li');
button.classList.add('override-button');
button.setAttribute('data-override-mode', settings.uuid);
button.innerHTML = settings.theme_suggestion;
list.appendChild(button);
button.onmouseover = button.onmouseout = Drupal.behaviors.OverrideUiBehaviour.buttonHover;
button.onclick = overridableTemplate.onclick = Drupal.behaviors.OverrideUiBehaviour.openDialog;
}
});
buttonContainer.appendChild(list);
});
// CodeMirror tracks form submissions to update textareas, but this does
// not work on ajax requests. So we save data manually.
once('SaveCodeMirror', '.CodeMirror').forEach(function (editor) {
editor.CodeMirror.on('blur', function (codeMirror) {
codeMirror.save();
});
});
// Flag that Override Mode has been disabled.
$(window).on('dialog:afterclose', function (e, dialog, $element) {
if ($('#override-mode-tray', $element).length > 0) {
Drupal.behaviors.OverrideUiBehaviour.overrideMode = false;
Drupal.behaviors.OverrideUiBehaviour.overrideTrayOpen = false;
document.getElementsByTagName('html')[0].classList.remove('override-mode')
}
});
},
processHtmlComments: function (context) {
// Don't override the component override form. It's confusing.
if (
(context.classList && context.classList.length > 0 && context.classList.contains('component-override-form')) ||
(context.id === 'dynamic-elements-container') ||
(context.id === 'libraries-wrapper') ||
(context.id === 'override-preview-context')
) {
return;
}
const iterator = document.createNodeIterator(context, NodeFilter.SHOW_COMMENT, function () { return NodeFilter.FILTER_ACCEPT; });
// Loop through nodes to find Override Mode comments.
let overrideHtmlComment;
while (overrideHtmlComment = iterator.nextNode()) {
if (overrideHtmlComment.nodeValue.includes('OVERRIDE MODE BEGIN')) {
// Determine if we can simply add an attribute to the nextElementSibling.
let uuid = overrideHtmlComment.nodeValue.match(/uuid\[([^\]]*)\]/)[1]
let parent = overrideHtmlComment.parentElement;
let siblings = parent.childNodes;
let overrideBeginHtmlCommentIndex = Array.from(siblings).indexOf(overrideHtmlComment);
let overrideEndHtmlCommentIndex = null;
for (let i = overrideBeginHtmlCommentIndex; i < siblings.length; i++) {
if (
typeof siblings[i].data !== 'undefined' &&
siblings[i].data.includes('OVERRIDE MODE END') &&
siblings[i].data.includes(uuid)
) {
overrideEndHtmlCommentIndex = i;
}
}
let templateElement = null;
// The template contents could be a string or a set of nodes which we
// can't attach an attribute to. If we see the same element between
// the override HTML comments that means the template was wrapped in a
// tag and we can add an attribute to it.
if (siblings[overrideBeginHtmlCommentIndex].nextElementSibling === siblings[overrideEndHtmlCommentIndex].previousElementSibling) {
templateElement = overrideHtmlComment.nextElementSibling;
} else {
// Add a wrapper around the templated pieces.
let siblingsArray = Array.from(siblings);
let templatePieces = siblingsArray.slice(overrideBeginHtmlCommentIndex + 1, overrideEndHtmlCommentIndex);
templateElement = document.createElement('override_mode');
templatePieces.forEach(element => templateElement.appendChild(element));
overrideHtmlComment.after(templateElement);
}
if (templateElement && !templateElement.hasAttribute('data-override-mode')) {
// Set a binding attribute on the paired template element.
templateElement.setAttribute('data-override-mode', uuid);
templateElement.onmouseover = templateElement.onmouseout = Drupal.behaviors.OverrideUiBehaviour.domElementHover;
}
}
}
},
buttonHover: function (e) {
let pairedElement = document.querySelector('[data-override-mode="' + this.dataset.overrideMode + '"]:not(#override-mode-tray)')
if (e.type === 'mouseover') {
this.classList.add('override-mode-highlight');
pairedElement.classList.add('override-mode-highlight');
}
if (e.type === 'mouseout') {
pairedElement.classList.remove('override-mode-highlight');
this.classList.remove('override-mode-highlight');
}
},
domElementHover: function (e) {
e.stopPropagation();
if (!Drupal.behaviors.OverrideUiBehaviour.overrideMode) {
return;
}
let element = e.target;
let pairedButton = document.querySelector('[data-override-mode="' + this.dataset.overrideMode + '"].override-button');
if (e.type === 'mouseover') {
element.classList.add('override-mode-highlight');
pairedButton.classList.add('override-mode-highlight');
}
if (e.type === 'mouseout') {
element.classList.remove('override-mode-highlight');
pairedButton.classList.remove('override-mode-highlight');
}
},
openDialog: function (e) {
e.stopPropagation();
if (!Drupal.behaviors.OverrideUiBehaviour.overrideMode) {
return;
}
e.preventDefault();
let settings = drupalSettings.overrides[this.dataset.overrideMode];
let ajaxConfig = {
url: settings.url + '&destination=' + window.location.pathname,
dialogType: 'modal',
dialog: { width: '90%' },
type: 'POST',
dataType: 'text',
progress: {
type: 'fullscreen',
message: Drupal.t('Please wait...')
},
};
let variables = document.querySelector('script[data-uuid="' + settings.uuid + '"]');
if (variables !== null && typeof variables.textContent !== 'undefined') {
ajaxConfig.submit = {variables: variables.textContent};
}
let ajax = Drupal.ajax(ajaxConfig);
ajax.execute();
},
};
})(jQuery, Drupal, once);
