3 * Drupal's Settings Tray library.
6 (function ($, Drupal) {
10 var blockConfigureSelector = '[data-outside-in-edit]';
11 var toggleEditSelector = '[data-drupal-outsidein="toggle"]';
12 var itemsToToggleSelector = '[data-off-canvas-main-canvas], #toolbar-bar, [data-drupal-outsidein="editable"] a, [data-drupal-outsidein="editable"] button';
13 var contextualItemsSelector = '[data-contextual-id] a, [data-contextual-id] button';
14 var quickEditItemSelector = '[data-quickedit-entity-id]';
17 * Reacts to contextual links being added.
19 * @param {jQuery.Event} event
20 * The `drupalContextualLinkAdded` event.
21 * @param {object} data
22 * An object containing the data relevant to the event.
24 * @listens event:drupalContextualLinkAdded
26 $(document).on('drupalContextualLinkAdded', function (event, data) {
27 // Bind Ajax behaviors to all items showing the class.
28 // @todo Fix contextual links to work with use-ajax links in
29 // https://www.drupal.org/node/2764931.
30 Drupal.attachBehaviors(data.$el[0]);
32 // Bind a listener to all 'Quick edit' links for blocks
33 // Click "Edit" button in toolbar to force Contextual Edit which starts
34 // Settings Tray edit mode also.
35 data.$el.find(blockConfigureSelector)
36 .on('click.outsidein', function () {
37 if (!isInEditMode()) {
38 $(toggleEditSelector).trigger('click').trigger('click.outside_in');
40 // Always disable QuickEdit regardless of whether "EditMode" was just enabled.
45 $(document).on('keyup.outsidein', function (e) {
46 if (isInEditMode() && e.keyCode === 27) {
48 Drupal.t('Exited edit mode.')
55 * Gets all items that should be toggled with class during edit mode.
58 * Items that should be toggled.
60 function getItemsToToggle() {
61 return $(itemsToToggleSelector).not(contextualItemsSelector);
65 * Helper to check the state of the outside-in mode.
67 * @todo don't use a class for this.
70 * State of the outside-in edit mode.
72 function isInEditMode() {
73 return $('#toolbar-bar').hasClass('js-outside-in-edit-mode');
77 * Helper to toggle Edit mode.
79 function toggleEditMode() {
80 setEditModeState(!isInEditMode());
84 * Prevent default click events except contextual links.
86 * In edit mode the default action of click events is suppressed.
88 * @param {jQuery.Event} event
91 function preventClick(event) {
92 // Do not prevent contextual links.
93 if ($(event.target).closest('.contextual-links').length) {
96 event.preventDefault();
100 * Close any active toolbar tray before entering edit mode.
102 function closeToolbarTrays() {
103 $(Drupal.toolbar.models.toolbarModel.get('activeTab')).trigger('click');
107 * Disables the QuickEdit module editor if open.
109 function disableQuickEdit() {
110 $('.quickedit-toolbar button.action-cancel').trigger('click');
114 * Closes/removes off-canvas.
116 function closeOffCanvas() {
117 $('.ui-dialog-off-canvas .ui-dialog-titlebar-close').trigger('click');
121 * Helper to switch edit mode state.
123 * @param {boolean} editMode
124 * True enable edit mode, false disable edit mode.
126 function setEditModeState(editMode) {
127 if (!document.querySelector('[data-off-canvas-main-canvas]')) {
128 throw new Error('data-off-canvas-main-canvas is missing from outside-in-page-wrapper.html.twig');
130 editMode = !!editMode;
131 var $editButton = $(toggleEditSelector);
133 // Turn on edit mode.
135 $editButton.text(Drupal.t('Editing'));
138 $editables = $('[data-drupal-outsidein="editable"]').once('outsidein');
139 if ($editables.length) {
140 // Use event capture to prevent clicks on links.
141 document.querySelector('[data-off-canvas-main-canvas]').addEventListener('click', preventClick, true);
143 // When a click occurs try and find the outside-in edit link
146 .not(contextualItemsSelector)
147 .on('click.outsidein', function (e) {
148 // Contextual links are allowed to function in Edit mode.
149 if ($(e.target).closest('.contextual').length || !localStorage.getItem('Drupal.contextualToolbar.isViewing')) {
152 $(e.currentTarget).find(blockConfigureSelector).trigger('click');
155 $(quickEditItemSelector)
156 .not(contextualItemsSelector)
157 .on('click.outsidein', function (e) {
158 // For all non-contextual links or the contextual QuickEdit link close the off-canvas dialog.
159 if (!$(e.target).parent().hasClass('contextual') || $(e.target).parent().hasClass('quickedit')) {
162 // Do not trigger if target is quick edit link to avoid loop.
163 if ($(e.target).parent().hasClass('contextual') || $(e.target).parent().hasClass('quickedit')) {
166 $(e.currentTarget).find('li.quickedit a').trigger('click');
170 // Disable edit mode.
172 $editables = $('[data-drupal-outsidein="editable"]').removeOnce('outsidein');
173 if ($editables.length) {
174 document.querySelector('[data-off-canvas-main-canvas]').removeEventListener('click', preventClick, true);
175 $editables.off('.outsidein');
176 $(quickEditItemSelector).off('.outsidein');
179 $editButton.text(Drupal.t('Edit'));
183 getItemsToToggle().toggleClass('js-outside-in-edit-mode', editMode);
184 $('.edit-mode-inactive').toggleClass('visually-hidden', editMode);
188 * Attaches contextual's edit toolbar tab behavior.
190 * @type {Drupal~behavior}
192 * @prop {Drupal~behaviorAttach} attach
193 * Attaches contextual toolbar behavior on a contextualToolbar-init event.
195 Drupal.behaviors.outsideInEdit = {
196 attach: function () {
197 var editMode = localStorage.getItem('Drupal.contextualToolbar.isViewing') === 'false';
199 setEditModeState(true);
205 * Toggle the js-outside-edit-mode class on items that we want to disable while in edit mode.
207 * @type {Drupal~behavior}
209 * @prop {Drupal~behaviorAttach} attach
210 * Toggle the js-outside-edit-mode class.
212 Drupal.behaviors.toggleEditMode = {
213 attach: function () {
215 $(toggleEditSelector).once('outsidein').on('click.outsidein', toggleEditMode);
217 var search = Drupal.ajax.WRAPPER_FORMAT + '=drupal_dialog';
218 var replace = Drupal.ajax.WRAPPER_FORMAT + '=drupal_dialog_off_canvas';
219 // Loop through all Ajax links and change the format to dialog-off-canvas when
221 Drupal.ajax.instances
222 .filter(function (instance) {
223 var hasElement = instance && !!instance.element;
224 var rendererOffCanvas = false;
225 var wrapperOffCanvas = false;
227 rendererOffCanvas = $(instance.element).attr('data-dialog-renderer') === 'off_canvas';
228 wrapperOffCanvas = instance.options.url.indexOf('drupal_dialog_off_canvas') === -1;
230 return hasElement && rendererOffCanvas && wrapperOffCanvas;
232 .forEach(function (instance) {
233 // @todo Move logic for data-dialog-renderer attribute into ajax.js
234 // https://www.drupal.org/node/2784443
235 instance.options.url = instance.options.url.replace(search, replace);
236 // Check to make sure existing dialogOptions aren't overridden.
237 if (!('dialogOptions' in instance.options.data)) {
238 instance.options.data.dialogOptions = {};
240 instance.options.data.dialogOptions.outsideInActiveEditableId = $(instance.element).parents('.outside-in-editable').attr('id');
241 instance.progress = {type: 'fullscreen'};
246 // Manage Active editable class on opening and closing of the dialog.
248 'dialog:beforecreate': function (event, dialog, $element, settings) {
249 if ($element.is('#drupal-off-canvas')) {
250 $('body .outside-in-active-editable').removeClass('outside-in-active-editable');
251 var $activeElement = $('#' + settings.outsideInActiveEditableId);
252 if ($activeElement.length) {
253 $activeElement.addClass('outside-in-active-editable');
254 settings.dialogClass += ' ui-dialog-outside-in';
258 'dialog:beforeclose': function (event, dialog, $element) {
259 if ($element.is('#drupal-off-canvas')) {
260 $('body .outside-in-active-editable').removeClass('outside-in-active-editable');