X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs-website;a=blobdiff_plain;f=web%2Fcore%2Fmodules%2Fviews_ui%2Fjs%2Fviews-admin.es6.js;fp=web%2Fcore%2Fmodules%2Fviews_ui%2Fjs%2Fviews-admin.es6.js;h=758d8d10023479269f06f0cc8396253403d8bdff;hp=a3b471f0cceb89764f29d3b37c013c83774310a2;hb=0bf8d09d2542548982e81a441b1f16e75873a04f;hpb=74df008bdbb3a11eeea356744f39b802369bda3c diff --git a/web/core/modules/views_ui/js/views-admin.es6.js b/web/core/modules/views_ui/js/views-admin.es6.js index a3b471f0c..758d8d100 100644 --- a/web/core/modules/views_ui/js/views-admin.es6.js +++ b/web/core/modules/views_ui/js/views-admin.es6.js @@ -3,7 +3,7 @@ * Some basic behaviors and utility functions for Views UI. */ -(function ($, Drupal, drupalSettings) { +(function($, Drupal, drupalSettings) { /** * @namespace */ @@ -21,9 +21,12 @@ attach() { // Only show the SQL rewrite warning when the user has chosen the // corresponding checkbox. - $('[data-drupal-selector="edit-query-options-disable-sql-rewrite"]').on('click', () => { - $('.sql-rewrite-warning').toggleClass('js-hide'); - }); + $('[data-drupal-selector="edit-query-options-disable-sql-rewrite"]').on( + 'click', + () => { + $('.sql-rewrite-warning').toggleClass('js-hide'); + }, + ); }, }; @@ -47,12 +50,13 @@ // The page title, block title, and menu link fields can all be // prepopulated with the view name - no regular expression needed. - const $fields = $context.find('[id^="edit-page-title"], [id^="edit-block-title"], [id^="edit-page-link-properties-title"]'); + const $fields = $context.find( + '[id^="edit-page-title"], [id^="edit-block-title"], [id^="edit-page-link-properties-title"]', + ); if ($fields.length) { if (!this.fieldsFiller) { this.fieldsFiller = new Drupal.viewsUi.FormFieldFiller($fields); - } - else { + } else { // After an AJAX response, this.fieldsFiller will still have event // handlers bound to the old version of the form fields (which don't // exist anymore). The event handlers need to be unbound and then @@ -67,22 +71,31 @@ const $pathField = $context.find('[id^="edit-page-path"]'); if ($pathField.length) { if (!this.pathFiller) { - this.pathFiller = new Drupal.viewsUi.FormFieldFiller($pathField, exclude, replace); - } - else { + this.pathFiller = new Drupal.viewsUi.FormFieldFiller( + $pathField, + exclude, + replace, + ); + } else { this.pathFiller.rebind($pathField); } } // Populate the RSS feed field with a URLified version of the view name, // and an .xml suffix (to make it unique). - const $feedField = $context.find('[id^="edit-page-feed-properties-path"]'); + const $feedField = $context.find( + '[id^="edit-page-feed-properties-path"]', + ); if ($feedField.length) { if (!this.feedFiller) { suffix = '.xml'; - this.feedFiller = new Drupal.viewsUi.FormFieldFiller($feedField, exclude, replace, suffix); - } - else { + this.feedFiller = new Drupal.viewsUi.FormFieldFiller( + $feedField, + exclude, + replace, + suffix, + ); + } else { this.feedFiller.rebind($feedField); } } @@ -106,7 +119,7 @@ * @param {string} [suffix=''] * A suffix to append at the end of the target field content. */ - Drupal.viewsUi.FormFieldFiller = function ($target, exclude, replace, suffix) { + Drupal.viewsUi.FormFieldFiller = function($target, exclude, replace, suffix) { /** * * @type {jQuery} @@ -150,7 +163,7 @@ * @return {*} * The result of the _populate call, which should be undefined. */ - this.populate = function () { + this.populate = function() { return self._populate.call(self); }; @@ -160,7 +173,7 @@ * @return {*} * The result of the _unbind call, which should be undefined. */ - this.unbind = function () { + this.unbind = function() { return self._unbind.call(self); }; @@ -168,65 +181,67 @@ // Object constructor; no return value. }; - $.extend(Drupal.viewsUi.FormFieldFiller.prototype, /** @lends Drupal.viewsUi.FormFieldFiller# */{ - - /** - * Bind the form-filling behavior. - */ - bind() { - this.unbind(); - // Populate the form field when the source changes. - this.source.on('keyup.viewsUi change.viewsUi', this.populate); - // Quit populating the field as soon as it gets focus. - this.target.on('focus.viewsUi', this.unbind); - }, + $.extend( + Drupal.viewsUi.FormFieldFiller.prototype, + /** @lends Drupal.viewsUi.FormFieldFiller# */ { + /** + * Bind the form-filling behavior. + */ + bind() { + this.unbind(); + // Populate the form field when the source changes. + this.source.on('keyup.viewsUi change.viewsUi', this.populate); + // Quit populating the field as soon as it gets focus. + this.target.on('focus.viewsUi', this.unbind); + }, - /** - * Get the source form field value as altered by the passed-in parameters. - * - * @return {string} - * The source form field value. - */ - getTransliterated() { - let from = this.source.val(); - if (this.exclude) { - from = from.toLowerCase().replace(this.exclude, this.replace); - } - return from; - }, + /** + * Get the source form field value as altered by the passed-in parameters. + * + * @return {string} + * The source form field value. + */ + getTransliterated() { + let from = this.source.val(); + if (this.exclude) { + from = from.toLowerCase().replace(this.exclude, this.replace); + } + return from; + }, - /** - * Populate the target form field with the altered source field value. - */ - _populate() { - const transliterated = this.getTransliterated(); - const suffix = this.suffix; - this.target.each(function (i) { - // Ensure that the maxlength is not exceeded by prepopulating the field. - const maxlength = $(this).attr('maxlength') - suffix.length; - $(this).val(transliterated.substr(0, maxlength) + suffix); - }); - }, + /** + * Populate the target form field with the altered source field value. + */ + _populate() { + const transliterated = this.getTransliterated(); + const suffix = this.suffix; + this.target.each(function(i) { + // Ensure that the maxlength is not exceeded by prepopulating the field. + const maxlength = $(this).attr('maxlength') - suffix.length; + $(this).val(transliterated.substr(0, maxlength) + suffix); + }); + }, - /** - * Stop prepopulating the form fields. - */ - _unbind() { - this.source.off('keyup.viewsUi change.viewsUi', this.populate); - this.target.off('focus.viewsUi', this.unbind); - }, + /** + * Stop prepopulating the form fields. + */ + _unbind() { + this.source.off('keyup.viewsUi change.viewsUi', this.populate); + this.target.off('focus.viewsUi', this.unbind); + }, - /** - * Bind event handlers to new form fields, after they're replaced via Ajax. - * - * @param {jQuery} $fields - * Fields to rebind functionality to. - */ - rebind($fields) { - this.target = $fields; - this.bind(); + /** + * Bind event handlers to new form fields, after they're replaced via Ajax. + * + * @param {jQuery} $fields + * Fields to rebind functionality to. + */ + rebind($fields) { + this.target = $fields; + this.bind(); + }, }, - }); + ); /** * Adds functionality for the add item form. @@ -261,13 +276,15 @@ * @param {jQuery} $form * The form element used. */ - Drupal.viewsUi.AddItemForm = function ($form) { + Drupal.viewsUi.AddItemForm = function($form) { /** * * @type {jQuery} */ this.$form = $form; - this.$form.find('.views-filterable-options :checkbox').on('click', $.proxy(this.handleCheck, this)); + this.$form + .find('.views-filterable-options :checkbox') + .on('click', $.proxy(this.handleCheck, this)); /** * Find the wrapper of the displayed text. @@ -288,15 +305,19 @@ * @param {jQuery.Event} event * The event triggered. */ - Drupal.viewsUi.AddItemForm.prototype.handleCheck = function (event) { + Drupal.viewsUi.AddItemForm.prototype.handleCheck = function(event) { const $target = $(event.target); - const label = $.trim($target.closest('td').next().html()); + const label = $.trim( + $target + .closest('td') + .next() + .html(), + ); // Add/remove the checked item to the list. if ($target.is(':checked')) { this.$selected_div.show().css('display', 'block'); this.checkedItems.push(label); - } - else { + } else { const position = $.inArray(label, this.checkedItems); // Delete the item from the list and make sure that the list doesn't have // undefined items left. @@ -318,9 +339,10 @@ /** * Refresh the display of the checked items. */ - Drupal.viewsUi.AddItemForm.prototype.refreshCheckedItems = function () { + Drupal.viewsUi.AddItemForm.prototype.refreshCheckedItems = function() { // Perhaps we should precache the text div, too. - this.$selected_div.find('.views-selected-options') + this.$selected_div + .find('.views-selected-options') .html(this.checkedItems.join(', ')) .trigger('dialogContentResize'); }; @@ -339,12 +361,18 @@ Drupal.behaviors.viewsUiRenderAddViewButton = { attach(context) { // Build the add display menu and pull the display input buttons into it. - const $menu = $(context).find('#views-display-menu-tabs').once('views-ui-render-add-view-button'); + const $menu = $(context) + .find('#views-display-menu-tabs') + .once('views-ui-render-add-view-button'); if (!$menu.length) { return; } - const $addDisplayDropdown = $(`
  • ${Drupal.t('Add')}
  • `); + const $addDisplayDropdown = $( + `
  • ${Drupal.t( + 'Add', + )}
  • `, + ); const $displayButtons = $menu.nextAll('input.add-display').detach(); $displayButtons .appendTo($addDisplayDropdown.find('.action-list')) @@ -358,7 +386,7 @@ // Remove the 'Add ' prefix from the button labels since they're being // placed in an 'Add' dropdown. @todo This assumes English, but so does // $addDisplayDropdown above. Add support for translation. - $displayButtons.each(function () { + $displayButtons.each(function() { const label = $(this).val(); if (label.substr(0, 4) === 'Add ') { $(this).val(label.substr(4)); @@ -367,7 +395,7 @@ $addDisplayDropdown.appendTo($menu); // Add the click handler for the add display button. - $menu.find('li.add > a').on('click', function (event) { + $menu.find('li.add > a').on('click', function(event) { event.preventDefault(); const $trigger = $(this); Drupal.behaviors.viewsUiRenderAddViewButton.toggleMenu($trigger); @@ -379,7 +407,7 @@ // We use the live binder because the open class on this item will be // toggled on and off and we want the handler to take effect in the cases // that the class is present, but not when it isn't. - $('li.add', $menu).on('mouseleave', function (event) { + $('li.add', $menu).on('mouseleave', function(event) { const $this = $(this); const $trigger = $this.children('a[href="#"]'); if ($this.children('.action-list').is(':visible')) { @@ -400,7 +428,7 @@ * function and not written specifically for this UI, but I'm not sure * where to put it. */ - Drupal.behaviors.viewsUiRenderAddViewButton.toggleMenu = function ($trigger) { + Drupal.behaviors.viewsUiRenderAddViewButton.toggleMenu = function($trigger) { $trigger.parent().toggleClass('open'); $trigger.next().slideToggle('fast'); }; @@ -441,7 +469,7 @@ * @param {jQuery} $form * The form element. */ - Drupal.viewsUi.OptionsSearch = function ($form) { + Drupal.viewsUi.OptionsSearch = function($form) { /** * * @type {jQuery} @@ -449,14 +477,23 @@ this.$form = $form; // Click on the title checks the box. - this.$form.on('click', 'td.title', (event) => { + this.$form.on('click', 'td.title', event => { const $target = $(event.currentTarget); - $target.closest('tr').find('input').trigger('click'); + $target + .closest('tr') + .find('input') + .trigger('click'); }); - const searchBoxSelector = '[data-drupal-selector="edit-override-controls-options-search"]'; - const controlGroupSelector = '[data-drupal-selector="edit-override-controls-group"]'; - this.$form.on('formUpdated', `${searchBoxSelector},${controlGroupSelector}`, $.proxy(this.handleFilter, this)); + const searchBoxSelector = + '[data-drupal-selector="edit-override-controls-options-search"]'; + const controlGroupSelector = + '[data-drupal-selector="edit-override-controls-group"]'; + this.$form.on( + 'formUpdated', + `${searchBoxSelector},${controlGroupSelector}`, + $.proxy(this.handleFilter, this), + ); this.$searchBox = this.$form.find(searchBoxSelector); this.$controlGroup = this.$form.find(controlGroupSelector); @@ -468,85 +505,89 @@ this.options = this.getOptions(this.$form.find('.filterable-option')); // Trap the ENTER key in the search box so that it doesn't submit the form. - this.$searchBox.on('keypress', (event) => { + this.$searchBox.on('keypress', event => { if (event.which === 13) { event.preventDefault(); } }); }; - $.extend(Drupal.viewsUi.OptionsSearch.prototype, /** @lends Drupal.viewsUi.OptionsSearch# */{ - - /** - * Assemble a list of all the filterable options on the form. - * - * @param {jQuery} $allOptions - * A jQuery object representing the rows of filterable options to be - * shown and hidden depending on the user's search terms. - * - * @return {Array} - * An array of all the filterable options. - */ - getOptions($allOptions) { - let $title; - let $description; - let $option; - const options = []; - const length = $allOptions.length; - for (let i = 0; i < length; i++) { - $option = $($allOptions[i]); - $title = $option.find('.title'); - $description = $option.find('.description'); - options[i] = { - // Search on the lowercase version of the title text + description. - searchText: `${$title.text().toLowerCase()} ${$description.text().toLowerCase()}`, - // Maintain a reference to the jQuery object for each row, so we don't - // have to create a new object inside the performance-sensitive keyup - // handler. - $div: $option, - }; - } - return options; - }, - - /** - * Filter handler for the search box and type select that hides or shows the relevant - * options. - * - * @param {jQuery.Event} event - * The formUpdated event. - */ - handleFilter(event) { - // Determine the user's search query. The search text has been converted - // to lowercase. - const search = this.$searchBox.val().toLowerCase(); - const words = search.split(' '); - // Get selected Group - const group = this.$controlGroup.val(); - - // Search through the search texts in the form for matching text. - this.options.forEach((option) => { - function hasWord(word) { - return option.searchText.indexOf(word) !== -1; + $.extend( + Drupal.viewsUi.OptionsSearch.prototype, + /** @lends Drupal.viewsUi.OptionsSearch# */ { + /** + * Assemble a list of all the filterable options on the form. + * + * @param {jQuery} $allOptions + * A jQuery object representing the rows of filterable options to be + * shown and hidden depending on the user's search terms. + * + * @return {Array} + * An array of all the filterable options. + */ + getOptions($allOptions) { + let $title; + let $description; + let $option; + const options = []; + const length = $allOptions.length; + for (let i = 0; i < length; i++) { + $option = $($allOptions[i]); + $title = $option.find('.title'); + $description = $option.find('.description'); + options[i] = { + // Search on the lowercase version of the title text + description. + searchText: `${$title + .text() + .toLowerCase()} ${$description.text().toLowerCase()}`, + // Maintain a reference to the jQuery object for each row, so we don't + // have to create a new object inside the performance-sensitive keyup + // handler. + $div: $option, + }; } + return options; + }, - let found = true; - // Each word in the search string has to match the item in order for the - // item to be shown. - if (search) { - found = words.every(hasWord); - } - if (found && group !== 'all') { - found = option.$div.hasClass(group); - } + /** + * Filter handler for the search box and type select that hides or shows the relevant + * options. + * + * @param {jQuery.Event} event + * The formUpdated event. + */ + handleFilter(event) { + // Determine the user's search query. The search text has been converted + // to lowercase. + const search = this.$searchBox.val().toLowerCase(); + const words = search.split(' '); + // Get selected Group + const group = this.$controlGroup.val(); + + // Search through the search texts in the form for matching text. + this.options.forEach(option => { + function hasWord(word) { + return option.searchText.indexOf(word) !== -1; + } - option.$div.toggle(found); - }); + let found = true; + // Each word in the search string has to match the item in order for the + // item to be shown. + if (search) { + found = words.every(hasWord); + } + if (found && group !== 'all') { + found = option.$div.hasClass(group); + } + + option.$div.toggle(found); + }); - // Adapt dialog to content size. - $(event.target).trigger('dialogContentResize'); + // Adapt dialog to content size. + $(event.target).trigger('dialogContentResize'); + }, }, - }); + ); /** * Preview functionality in the views edit form. @@ -559,7 +600,9 @@ Drupal.behaviors.viewsUiPreview = { attach(context) { // Only act on the edit view form. - const $contextualFiltersBucket = $(context).find('.views-display-column .views-ui-display-tab-bucket.argument'); + const $contextualFiltersBucket = $(context).find( + '.views-display-column .views-ui-display-tab-bucket.argument', + ); if ($contextualFiltersBucket.length === 0) { return; } @@ -567,17 +610,28 @@ // If the display has no contextual filters, hide the form where you // enter the contextual filters for the live preview. If it has contextual // filters, show the form. - const $contextualFilters = $contextualFiltersBucket.find('.views-display-setting a'); + const $contextualFilters = $contextualFiltersBucket.find( + '.views-display-setting a', + ); if ($contextualFilters.length) { - $('#preview-args').parent().show(); - } - else { - $('#preview-args').parent().hide(); + $('#preview-args') + .parent() + .show(); + } else { + $('#preview-args') + .parent() + .hide(); } // Executes an initial preview. - if ($('#edit-displays-live-preview').once('edit-displays-live-preview').is(':checked')) { - $('#preview-submit').once('edit-displays-live-preview').trigger('click'); + if ( + $('#edit-displays-live-preview') + .once('edit-displays-live-preview') + .is(':checked') + ) { + $('#preview-submit') + .once('edit-displays-live-preview') + .trigger('click'); } }, }; @@ -588,19 +642,26 @@ * @type {Drupal~behavior} * * @prop {Drupal~behaviorAttach} attach - * Attach handlers to make it possible to rearange the filters in the form + * Attach handlers to make it possible to rearrange the filters in the form * in question. * @see Drupal.viewsUi.RearrangeFilterHandler */ Drupal.behaviors.viewsUiRearrangeFilter = { attach(context) { // Only act on the rearrange filter form. - if (typeof Drupal.tableDrag === 'undefined' || typeof Drupal.tableDrag['views-rearrange-filters'] === 'undefined') { + if ( + typeof Drupal.tableDrag === 'undefined' || + typeof Drupal.tableDrag['views-rearrange-filters'] === 'undefined' + ) { return; } const $context = $(context); - const $table = $context.find('#views-rearrange-filters').once('views-rearrange-filters'); - const $operator = $context.find('.js-form-item-filter-groups-operator').once('views-rearrange-filters'); + const $table = $context + .find('#views-rearrange-filters') + .once('views-rearrange-filters'); + const $operator = $context + .find('.js-form-item-filter-groups-operator') + .once('views-rearrange-filters'); if ($table.length) { new Drupal.viewsUi.RearrangeFilterHandler($table, $operator); } @@ -617,7 +678,7 @@ * @param {jQuery} $operator * The filter groups operator element. */ - Drupal.viewsUi.RearrangeFilterHandler = function ($table, $operator) { + Drupal.viewsUi.RearrangeFilterHandler = function($table, $operator) { /** * Keep a reference to the `` being altered and to the div containing * the filter groups operator dropdown (if it exists). @@ -679,9 +740,13 @@ // next to the filters in each group, and bind a handler so that they change // based on the values of the operator dropdown within that group. this.redrawOperatorLabels(); - $table.find('.views-group-title select') + $table + .find('.views-group-title select') .once('views-rearrange-filter-handler') - .on('change.views-rearrange-filter-handler', $.proxy(this, 'redrawOperatorLabels')); + .on( + 'change.views-rearrange-filter-handler', + $.proxy(this, 'redrawOperatorLabels'), + ); // Bind handlers so that when a "Remove" link is clicked, we: // - Update the rowspans of cells containing an operator dropdown (since @@ -689,299 +754,362 @@ // - Redraw the operator labels next to the filters in the group (since the // filter that is currently displayed last in each group is not supposed // to have a label display next to it). - $table.find('a.views-groups-remove-link') + $table + .find('a.views-groups-remove-link') .once('views-rearrange-filter-handler') - .on('click.views-rearrange-filter-handler', $.proxy(this, 'updateRowspans')) - .on('click.views-rearrange-filter-handler', $.proxy(this, 'redrawOperatorLabels')); + .on( + 'click.views-rearrange-filter-handler', + $.proxy(this, 'updateRowspans'), + ) + .on( + 'click.views-rearrange-filter-handler', + $.proxy(this, 'redrawOperatorLabels'), + ); }; - $.extend(Drupal.viewsUi.RearrangeFilterHandler.prototype, /** @lends Drupal.viewsUi.RearrangeFilterHandler# */{ - - /** - * Insert links that allow filter groups to be added and removed. - */ - insertAddRemoveFilterGroupLinks() { - // Insert a link for adding a new group at the top of the page, and make - // it match the action link styling used in a typical page.html.twig. - // Since Drupal does not provide a theme function for this markup this is - // the best we can do. - $(``) - .prependTo(this.table.parent()) - // When the link is clicked, dynamically click the hidden form button - // for adding a new filter group. - .once('views-rearrange-filter-handler') - .find('#views-add-group-link') - .on('click.views-rearrange-filter-handler', $.proxy(this, 'clickAddGroupButton')); - - // Find each (visually hidden) button for removing a filter group and - // insert a link next to it. - const length = this.removeGroupButtons.length; - let i; - for (i = 0; i < length; i++) { - const $removeGroupButton = $(this.removeGroupButtons[i]); - const buttonId = $removeGroupButton.attr('id'); - $(`${Drupal.t('Remove group')}`) - .insertBefore($removeGroupButton) - // When the link is clicked, dynamically click the corresponding form - // button. + $.extend( + Drupal.viewsUi.RearrangeFilterHandler.prototype, + /** @lends Drupal.viewsUi.RearrangeFilterHandler# */ { + /** + * Insert links that allow filter groups to be added and removed. + */ + insertAddRemoveFilterGroupLinks() { + // Insert a link for adding a new group at the top of the page, and make + // it match the action link styling used in a typical page.html.twig. + // Since Drupal does not provide a theme function for this markup this is + // the best we can do. + $( + ``, + ) + .prependTo(this.table.parent()) + // When the link is clicked, dynamically click the hidden form button + // for adding a new filter group. .once('views-rearrange-filter-handler') - .on('click.views-rearrange-filter-handler', { buttonId }, $.proxy(this, 'clickRemoveGroupButton')); - } - }, + .find('#views-add-group-link') + .on( + 'click.views-rearrange-filter-handler', + $.proxy(this, 'clickAddGroupButton'), + ); + + // Find each (visually hidden) button for removing a filter group and + // insert a link next to it. + const length = this.removeGroupButtons.length; + let i; + for (i = 0; i < length; i++) { + const $removeGroupButton = $(this.removeGroupButtons[i]); + const buttonId = $removeGroupButton.attr('id'); + $( + `${Drupal.t( + 'Remove group', + )}`, + ) + .insertBefore($removeGroupButton) + // When the link is clicked, dynamically click the corresponding form + // button. + .once('views-rearrange-filter-handler') + .on( + 'click.views-rearrange-filter-handler', + { buttonId }, + $.proxy(this, 'clickRemoveGroupButton'), + ); + } + }, - /** - * Dynamically click the button that adds a new filter group. - * - * @param {jQuery.Event} event - * The event triggered. - */ - clickAddGroupButton(event) { - this.addGroupButton.trigger('mousedown'); - event.preventDefault(); - }, + /** + * Dynamically click the button that adds a new filter group. + * + * @param {jQuery.Event} event + * The event triggered. + */ + clickAddGroupButton(event) { + this.addGroupButton.trigger('mousedown'); + event.preventDefault(); + }, - /** - * Dynamically click a button for removing a filter group. - * - * @param {jQuery.Event} event - * Event being triggered, with event.data.buttonId set to the ID of the - * form button that should be clicked. - */ - clickRemoveGroupButton(event) { - this.table.find(`#${event.data.buttonId}`).trigger('mousedown'); - event.preventDefault(); - }, + /** + * Dynamically click a button for removing a filter group. + * + * @param {jQuery.Event} event + * Event being triggered, with event.data.buttonId set to the ID of the + * form button that should be clicked. + */ + clickRemoveGroupButton(event) { + this.table.find(`#${event.data.buttonId}`).trigger('mousedown'); + event.preventDefault(); + }, - /** - * Move the groups operator so that it's between the first two groups, and - * duplicate it between any subsequent groups. - * - * @return {jQuery} - * An operator element. - */ - duplicateGroupsOperator() { - let newRow; - let titleRow; + /** + * Move the groups operator so that it's between the first two groups, and + * duplicate it between any subsequent groups. + * + * @return {jQuery} + * An operator element. + */ + duplicateGroupsOperator() { + let newRow; + let titleRow; - const titleRows = $('tr.views-group-title').once('duplicateGroupsOperator'); + const titleRows = $('tr.views-group-title').once( + 'duplicateGroupsOperator', + ); - if (!titleRows.length) { - return this.operator; - } + if (!titleRows.length) { + return this.operator; + } - // Get rid of the explanatory text around the operator; its placement is - // explanatory enough. - this.operator.find('label').add('div.description').addClass('visually-hidden'); - this.operator.find('select').addClass('form-select'); - - // Keep a list of the operator dropdowns, so we can sync their behavior - // later. - const dropdowns = this.operator; - - // Move the operator to a new row just above the second group. - titleRow = $('tr#views-group-title-2'); - newRow = $(''); - newRow.find('td').append(this.operator); - newRow.insertBefore(titleRow); - const length = titleRows.length; - // Starting with the third group, copy the operator to a new row above the - // group title. - for (let i = 2; i < length; i++) { - titleRow = $(titleRows[i]); - // Make a copy of the operator dropdown and put it in a new table row. - const fakeOperator = this.operator.clone(); - fakeOperator.attr('id', ''); - newRow = $(''); - newRow.find('td').append(fakeOperator); + // Get rid of the explanatory text around the operator; its placement is + // explanatory enough. + this.operator + .find('label') + .add('div.description') + .addClass('visually-hidden'); + this.operator.find('select').addClass('form-select'); + + // Keep a list of the operator dropdowns, so we can sync their behavior + // later. + const dropdowns = this.operator; + + // Move the operator to a new row just above the second group. + titleRow = $('tr#views-group-title-2'); + newRow = $( + '', + ); + newRow.find('td').append(this.operator); newRow.insertBefore(titleRow); - dropdowns.add(fakeOperator); - } + const length = titleRows.length; + // Starting with the third group, copy the operator to a new row above the + // group title. + for (let i = 2; i < length; i++) { + titleRow = $(titleRows[i]); + // Make a copy of the operator dropdown and put it in a new table row. + const fakeOperator = this.operator.clone(); + fakeOperator.attr('id', ''); + newRow = $( + '', + ); + newRow.find('td').append(fakeOperator); + newRow.insertBefore(titleRow); + dropdowns.add(fakeOperator); + } - return dropdowns; - }, + return dropdowns; + }, - /** - * Make the duplicated groups operators change in sync with each other. - */ - syncGroupsOperators() { - if (this.dropdowns.length < 2) { - // We only have one dropdown (or none at all), so there's nothing to - // sync. - return; - } + /** + * Make the duplicated groups operators change in sync with each other. + */ + syncGroupsOperators() { + if (this.dropdowns.length < 2) { + // We only have one dropdown (or none at all), so there's nothing to + // sync. + return; + } - this.dropdowns.on('change', $.proxy(this, 'operatorChangeHandler')); - }, + this.dropdowns.on('change', $.proxy(this, 'operatorChangeHandler')); + }, - /** - * Click handler for the operators that appear between filter groups. - * - * Forces all operator dropdowns to have the same value. - * - * @param {jQuery.Event} event - * The event triggered. - */ - operatorChangeHandler(event) { - const $target = $(event.target); - const operators = this.dropdowns.find('select').not($target); + /** + * Click handler for the operators that appear between filter groups. + * + * Forces all operator dropdowns to have the same value. + * + * @param {jQuery.Event} event + * The event triggered. + */ + operatorChangeHandler(event) { + const $target = $(event.target); + const operators = this.dropdowns.find('select').not($target); - // Change the other operators to match this new value. - operators.val($target.val()); - }, - - /** - * @method - */ - modifyTableDrag() { - const tableDrag = Drupal.tableDrag['views-rearrange-filters']; - const filterHandler = this; + // Change the other operators to match this new value. + operators.val($target.val()); + }, /** - * Override the row.onSwap method from tabledrag.js. - * - * When a row is dragged to another place in the table, several things - * need to occur. - * - The row needs to be moved so that it's within one of the filter - * groups. - * - The operator cells that span multiple rows need their rowspan - * attributes updated to reflect the number of rows in each group. - * - The operator labels that are displayed next to each filter need to - * be redrawn, to account for the row's new location. + * @method */ - tableDrag.row.prototype.onSwap = function () { - if (filterHandler.hasGroupOperator) { - // Make sure the row that just got moved (this.group) is inside one - // of the filter groups (i.e. below an empty marker row or a - // draggable). If it isn't, move it down one. - const thisRow = $(this.group); - const previousRow = thisRow.prev('tr'); - if (previousRow.length && !previousRow.hasClass('group-message') && !previousRow.hasClass('draggable')) { - // Move the dragged row down one. - const next = thisRow.next(); - if (next.is('tr')) { - this.swap('after', next); + modifyTableDrag() { + const tableDrag = Drupal.tableDrag['views-rearrange-filters']; + const filterHandler = this; + + /** + * Override the row.onSwap method from tabledrag.js. + * + * When a row is dragged to another place in the table, several things + * need to occur. + * - The row needs to be moved so that it's within one of the filter + * groups. + * - The operator cells that span multiple rows need their rowspan + * attributes updated to reflect the number of rows in each group. + * - The operator labels that are displayed next to each filter need to + * be redrawn, to account for the row's new location. + */ + tableDrag.row.prototype.onSwap = function() { + if (filterHandler.hasGroupOperator) { + // Make sure the row that just got moved (this.group) is inside one + // of the filter groups (i.e. below an empty marker row or a + // draggable). If it isn't, move it down one. + const thisRow = $(this.group); + const previousRow = thisRow.prev('tr'); + if ( + previousRow.length && + !previousRow.hasClass('group-message') && + !previousRow.hasClass('draggable') + ) { + // Move the dragged row down one. + const next = thisRow.next(); + if (next.is('tr')) { + this.swap('after', next); + } } + filterHandler.updateRowspans(); } - filterHandler.updateRowspans(); - } - // Redraw the operator labels that are displayed next to each filter, to - // account for the row's new location. - filterHandler.redrawOperatorLabels(); - }; + // Redraw the operator labels that are displayed next to each filter, to + // account for the row's new location. + filterHandler.redrawOperatorLabels(); + }; - /** - * Override the onDrop method from tabledrag.js. - */ - tableDrag.onDrop = function () { - // If the tabledrag change marker (i.e., the "*") has been inserted - // inside a row after the operator label (i.e., "And" or "Or") - // rearrange the items so the operator label continues to appear last. - const changeMarker = $(this.oldRowElement).find('.tabledrag-changed'); - if (changeMarker.length) { - // Search for occurrences of the operator label before the change - // marker, and reverse them. - const operatorLabel = changeMarker.prevAll('.views-operator-label'); - if (operatorLabel.length) { - operatorLabel.insertAfter(changeMarker); + /** + * Override the onDrop method from tabledrag.js. + */ + tableDrag.onDrop = function() { + // If the tabledrag change marker (i.e., the "*") has been inserted + // inside a row after the operator label (i.e., "And" or "Or") + // rearrange the items so the operator label continues to appear last. + const changeMarker = $(this.oldRowElement).find('.tabledrag-changed'); + if (changeMarker.length) { + // Search for occurrences of the operator label before the change + // marker, and reverse them. + const operatorLabel = changeMarker.prevAll('.views-operator-label'); + if (operatorLabel.length) { + operatorLabel.insertAfter(changeMarker); + } } - } - // Make sure the "group" dropdown is properly updated when rows are - // dragged into an empty filter group. This is borrowed heavily from - // the block.js implementation of tableDrag.onDrop(). - const groupRow = $(this.rowObject.element).prevAll('tr.group-message').get(0); - const groupName = groupRow.className.replace(/([^ ]+[ ]+)*group-([^ ]+)-message([ ]+[^ ]+)*/, '$2'); - const groupField = $('select.views-group-select', this.rowObject.element); - if ($(this.rowObject.element).prev('tr').is('.group-message') && !groupField.is(`.views-group-select-${groupName}`)) { - const oldGroupName = groupField.attr('class').replace(/([^ ]+[ ]+)*views-group-select-([^ ]+)([ ]+[^ ]+)*/, '$2'); - groupField.removeClass(`views-group-select-${oldGroupName}`).addClass(`views-group-select-${groupName}`); - groupField.val(groupName); - } - }; - }, + // Make sure the "group" dropdown is properly updated when rows are + // dragged into an empty filter group. This is borrowed heavily from + // the block.js implementation of tableDrag.onDrop(). + const groupRow = $(this.rowObject.element) + .prevAll('tr.group-message') + .get(0); + const groupName = groupRow.className.replace( + /([^ ]+[ ]+)*group-([^ ]+)-message([ ]+[^ ]+)*/, + '$2', + ); + const groupField = $( + 'select.views-group-select', + this.rowObject.element, + ); + if ( + $(this.rowObject.element) + .prev('tr') + .is('.group-message') && + !groupField.is(`.views-group-select-${groupName}`) + ) { + const oldGroupName = groupField + .attr('class') + .replace( + /([^ ]+[ ]+)*views-group-select-([^ ]+)([ ]+[^ ]+)*/, + '$2', + ); + groupField + .removeClass(`views-group-select-${oldGroupName}`) + .addClass(`views-group-select-${groupName}`); + groupField.val(groupName); + } + }; + }, - /** - * Redraw the operator labels that are displayed next to each filter. - */ - redrawOperatorLabels() { - for (let i = 0; i < this.draggableRows.length; i++) { - // Within the row, the operator labels are displayed inside the first - // table cell (next to the filter name). - const $draggableRow = $(this.draggableRows[i]); - const $firstCell = $draggableRow.find('td').eq(0); - if ($firstCell.length) { - // The value of the operator label ("And" or "Or") is taken from the - // first operator dropdown we encounter, going backwards from the - // current row. This dropdown is the one associated with the current - // row's filter group. - const operatorValue = $draggableRow.prevAll('.views-group-title').find('option:selected').html(); - const operatorLabel = `${operatorValue}`; - // If the next visible row after this one is a draggable filter row, - // display the operator label next to the current row. (Checking for - // visibility is necessary here since the "Remove" links hide the - // removed row but don't actually remove it from the document). - const $nextRow = $draggableRow.nextAll(':visible').eq(0); - const $existingOperatorLabel = $firstCell.find('.views-operator-label'); - if ($nextRow.hasClass('draggable')) { - // If an operator label was already there, replace it with the new - // one. - if ($existingOperatorLabel.length) { - $existingOperatorLabel.replaceWith(operatorLabel); + /** + * Redraw the operator labels that are displayed next to each filter. + */ + redrawOperatorLabels() { + for (let i = 0; i < this.draggableRows.length; i++) { + // Within the row, the operator labels are displayed inside the first + // table cell (next to the filter name). + const $draggableRow = $(this.draggableRows[i]); + const $firstCell = $draggableRow.find('td').eq(0); + if ($firstCell.length) { + // The value of the operator label ("And" or "Or") is taken from the + // first operator dropdown we encounter, going backwards from the + // current row. This dropdown is the one associated with the current + // row's filter group. + const operatorValue = $draggableRow + .prevAll('.views-group-title') + .find('option:selected') + .html(); + const operatorLabel = `${operatorValue}`; + // If the next visible row after this one is a draggable filter row, + // display the operator label next to the current row. (Checking for + // visibility is necessary here since the "Remove" links hide the + // removed row but don't actually remove it from the document). + const $nextRow = $draggableRow.nextAll(':visible').eq(0); + const $existingOperatorLabel = $firstCell.find( + '.views-operator-label', + ); + if ($nextRow.hasClass('draggable')) { + // If an operator label was already there, replace it with the new + // one. + if ($existingOperatorLabel.length) { + $existingOperatorLabel.replaceWith(operatorLabel); + } + // Otherwise, append the operator label to the end of the table + // cell. + else { + $firstCell.append(operatorLabel); + } } - // Otherwise, append the operator label to the end of the table - // cell. + // If the next row doesn't contain a filter, then this is the last row + // in the group. We don't want to display the operator there (since + // operators should only display between two related filters, e.g. + // "filter1 AND filter2 AND filter3"). So we remove any existing label + // that this row has. else { - $firstCell.append(operatorLabel); + $existingOperatorLabel.remove(); } } - // If the next row doesn't contain a filter, then this is the last row - // in the group. We don't want to display the operator there (since - // operators should only display between two related filters, e.g. - // "filter1 AND filter2 AND filter3"). So we remove any existing label - // that this row has. - else { - $existingOperatorLabel.remove(); - } } - } - }, + }, - /** - * Update the rowspan attribute of each cell containing an operator - * dropdown. - */ - updateRowspans() { - let $row; - let $currentEmptyRow; - let draggableCount; - let $operatorCell; - const rows = $(this.table).find('tr'); - const length = rows.length; - for (let i = 0; i < length; i++) { - $row = $(rows[i]); - if ($row.hasClass('views-group-title')) { - // This row is a title row. - // Keep a reference to the cell containing the dropdown operator. - $operatorCell = $row.find('td.group-operator'); - // Assume this filter group is empty, until we find otherwise. - draggableCount = 0; - $currentEmptyRow = $row.next('tr'); - $currentEmptyRow.removeClass('group-populated').addClass('group-empty'); - // The cell with the dropdown operator should span the title row and - // the "this group is empty" row. - $operatorCell.attr('rowspan', 2); - } - else if ($row.hasClass('draggable') && $row.is(':visible')) { - // We've found a visible filter row, so we now know the group isn't - // empty. - draggableCount++; - $currentEmptyRow.removeClass('group-empty').addClass('group-populated'); - // The operator cell should span all draggable rows, plus the title. - $operatorCell.attr('rowspan', draggableCount + 1); + /** + * Update the rowspan attribute of each cell containing an operator + * dropdown. + */ + updateRowspans() { + let $row; + let $currentEmptyRow; + let draggableCount; + let $operatorCell; + const rows = $(this.table).find('tr'); + const length = rows.length; + for (let i = 0; i < length; i++) { + $row = $(rows[i]); + if ($row.hasClass('views-group-title')) { + // This row is a title row. + // Keep a reference to the cell containing the dropdown operator. + $operatorCell = $row.find('td.group-operator'); + // Assume this filter group is empty, until we find otherwise. + draggableCount = 0; + $currentEmptyRow = $row.next('tr'); + $currentEmptyRow + .removeClass('group-populated') + .addClass('group-empty'); + // The cell with the dropdown operator should span the title row and + // the "this group is empty" row. + $operatorCell.attr('rowspan', 2); + } else if ($row.hasClass('draggable') && $row.is(':visible')) { + // We've found a visible filter row, so we now know the group isn't + // empty. + draggableCount++; + $currentEmptyRow + .removeClass('group-empty') + .addClass('group-populated'); + // The operator cell should span all draggable rows, plus the title. + $operatorCell.attr('rowspan', draggableCount + 1); + } } - } + }, }, - }); + ); /** * Add a select all checkbox, which checks each checkbox at once. @@ -995,20 +1123,26 @@ attach(context) { const $context = $(context); - const $selectAll = $context.find('.js-form-item-options-value-all').once('filterConfigSelectAll'); + const $selectAll = $context + .find('.js-form-item-options-value-all') + .once('filterConfigSelectAll'); const $selectAllCheckbox = $selectAll.find('input[type=checkbox]'); - const $checkboxes = $selectAll.closest('.form-checkboxes').find('.js-form-type-checkbox:not(.js-form-item-options-value-all) input[type="checkbox"]'); + const $checkboxes = $selectAll + .closest('.form-checkboxes') + .find( + '.js-form-type-checkbox:not(.js-form-item-options-value-all) input[type="checkbox"]', + ); if ($selectAll.length) { - // Show the select all checkbox. + // Show the select all checkbox. $selectAll.show(); - $selectAllCheckbox.on('click', function () { + $selectAllCheckbox.on('click', function() { // Update all checkbox beside the select all checkbox. $checkboxes.prop('checked', $(this).is(':checked')); }); // Uncheck the select all checkbox if any of the others are unchecked. - $checkboxes.on('click', function () { + $checkboxes.on('click', function() { if ($(this).is('checked') === false) { $selectAllCheckbox.prop('checked', false); } @@ -1045,7 +1179,9 @@ */ Drupal.behaviors.viewsUiCheckboxify = { attach(context, settings) { - const $buttons = $('[data-drupal-selector="edit-options-expose-button-button"], [data-drupal-selector="edit-options-group-button-button"]').once('views-ui-checkboxify'); + const $buttons = $( + '[data-drupal-selector="edit-options-expose-button-button"], [data-drupal-selector="edit-options-group-button-button"]', + ).once('views-ui-checkboxify'); const length = $buttons.length; let i; for (i = 0; i < length; i++) { @@ -1069,19 +1205,37 @@ function changeDefaultWidget(event) { if ($(event.target).prop('checked')) { - $context.find('input.default-radios').parent().hide(); - $context.find('td.any-default-radios-row').parent().hide(); - $context.find('input.default-checkboxes').parent().show(); - } - else { - $context.find('input.default-checkboxes').parent().hide(); - $context.find('td.any-default-radios-row').parent().show(); - $context.find('input.default-radios').parent().show(); + $context + .find('input.default-radios') + .parent() + .hide(); + $context + .find('td.any-default-radios-row') + .parent() + .hide(); + $context + .find('input.default-checkboxes') + .parent() + .show(); + } else { + $context + .find('input.default-checkboxes') + .parent() + .hide(); + $context + .find('td.any-default-radios-row') + .parent() + .show(); + $context + .find('input.default-radios') + .parent() + .show(); } } // Update on widget change. - $context.find('input[name="options[group_info][multiple]"]') + $context + .find('input[name="options[group_info][multiple]"]') .on('change', changeDefaultWidget) // Update the first time the form is rendered. .trigger('change'); @@ -1096,7 +1250,7 @@ * @param {HTMLElement} button * The DOM object representing the button to be checkboxified. */ - Drupal.viewsUi.Checkboxifier = function (button) { + Drupal.viewsUi.Checkboxifier = function(button) { this.$button = $(button); this.$parent = this.$button.parent('div.views-expose, div.views-grouped'); this.$input = this.$parent.find('input:checkbox, input:radio'); @@ -1113,10 +1267,8 @@ * @param {jQuery.Event} e * The event triggered. */ - Drupal.viewsUi.Checkboxifier.prototype.clickHandler = function (e) { - this.$button - .trigger('click') - .trigger('submit'); + Drupal.viewsUi.Checkboxifier.prototype.clickHandler = function(e) { + this.$button.trigger('click').trigger('submit'); }; /** @@ -1130,34 +1282,37 @@ */ Drupal.behaviors.viewsUiOverrideSelect = { attach(context) { - $(context).find('[data-drupal-selector="edit-override-dropdown"]').once('views-ui-override-button-text').each(function () { - // Closures! :( - const $context = $(context); - const $submit = $context.find('[id^=edit-submit]'); - const oldValue = $submit.val(); - - $submit.once('views-ui-override-button-text') - .on('mouseup', function () { - $(this).val(oldValue); - return true; - }); - - $(this).on('change', function () { - const $this = $(this); - if ($this.val() === 'default') { - $submit.val(Drupal.t('Apply (all displays)')); - } - else if ($this.val() === 'default_revert') { - $submit.val(Drupal.t('Revert to default')); - } - else { - $submit.val(Drupal.t('Apply (this display)')); - } - const $dialog = $context.closest('.ui-dialog-content'); - $dialog.trigger('dialogButtonsChange'); - }) - .trigger('change'); - }); + $(context) + .find('[data-drupal-selector="edit-override-dropdown"]') + .once('views-ui-override-button-text') + .each(function() { + // Closures! :( + const $context = $(context); + const $submit = $context.find('[id^=edit-submit]'); + const oldValue = $submit.val(); + + $submit + .once('views-ui-override-button-text') + .on('mouseup', function() { + $(this).val(oldValue); + return true; + }); + + $(this) + .on('change', function() { + const $this = $(this); + if ($this.val() === 'default') { + $submit.val(Drupal.t('Apply (all displays)')); + } else if ($this.val() === 'default_revert') { + $submit.val(Drupal.t('Revert to default')); + } else { + $submit.val(Drupal.t('Apply (this display)')); + } + const $dialog = $context.closest('.ui-dialog-content'); + $dialog.trigger('dialogButtonsChange'); + }) + .trigger('change'); + }); }, }; @@ -1174,21 +1329,31 @@ const $context = $(context); // Handle handler deletion by looking for the hidden checkbox and hiding // the row. - $context.find('a.views-remove-link').once('views').on('click', function (event) { - const id = $(this).attr('id').replace('views-remove-link-', ''); - $context.find(`#views-row-${id}`).hide(); - $context.find(`#views-removed-${id}`).prop('checked', true); - event.preventDefault(); - }); + $context + .find('a.views-remove-link') + .once('views') + .on('click', function(event) { + const id = $(this) + .attr('id') + .replace('views-remove-link-', ''); + $context.find(`#views-row-${id}`).hide(); + $context.find(`#views-removed-${id}`).prop('checked', true); + event.preventDefault(); + }); // Handle display deletion by looking for the hidden checkbox and hiding // the row. - $context.find('a.display-remove-link').once('display').on('click', function (event) { - const id = $(this).attr('id').replace('display-remove-link-', ''); - $context.find(`#display-row-${id}`).hide(); - $context.find(`#display-removed-${id}`).prop('checked', true); - event.preventDefault(); - }); + $context + .find('a.display-remove-link') + .once('display') + .on('click', function(event) { + const id = $(this) + .attr('id') + .replace('display-remove-link-', ''); + $context.find(`#display-row-${id}`).hide(); + $context.find(`#display-removed-${id}`).prop('checked', true); + event.preventDefault(); + }); }, }; -}(jQuery, Drupal, drupalSettings)); +})(jQuery, Drupal, drupalSettings);