From 2854b60dcc391b89e0ca84eb31c1b1cd883b9f3a Mon Sep 17 00:00:00 2001 From: Brian Canini <canini.16@osu.edu> Date: Thu, 9 Jul 2020 09:45:14 -0400 Subject: [PATCH] - Installing drupal/jquery_ui (1.4.0): Loading from cache - Installing drupal/jquery_ui_touch_punch (1.0.0): Loading from cache - Installing drupal/jquery_ui_slider (1.1.0): Loading from cache - Installing drupal/jquery_ui_datepicker (1.0.0): Loading from cache - Installing drupal/better_exposed_filters (5.0.0-beta1): Loading from cache --- composer.json | 7 +- composer.lock | 286 ++- vendor/composer/installed.json | 294 ++- .../.circleci/config.yml | 162 ++ web/modules/better_exposed_filters/.gitignore | 11 + .../better_exposed_filters/PATCHES.txt | 7 - .../better_exposed_filters.api.php | 29 +- .../better_exposed_filters.info.yml | 15 +- .../better_exposed_filters.install | 138 ++ .../better_exposed_filters.libraries.yml | 19 +- .../better_exposed_filters.module | 266 +- .../better_exposed_filters.services.yml | 15 + .../better_exposed_filters.theme | 603 ----- .../better_exposed_filters/composer.json | 33 + ...er_exposed_filters.exposed_form.schema.yml | 167 +- .../better_exposed_filters.filter.schema.yml | 90 + .../better_exposed_filters.pager.schema.yml | 32 + .../better_exposed_filters.sort.schema.yml | 50 + .../css/better_exposed_filters.css | 27 +- .../better_exposed_filters/docker-compose.yml | 32 + .../includes/better_exposed_filters.theme.inc | 321 +++ .../better_exposed_filters/js/auto_submit.js | 95 +- .../js/bef_datepickers.js | 11 +- .../js/bef_select_all_none.js | 53 +- .../better_exposed_filters/js/bef_sliders.js | 73 +- .../js/better_exposed_filters.js | 21 +- web/modules/better_exposed_filters/phpcs.xml | 12 + .../phpunit.core.xml.dist | 75 + .../BetterExposedFiltersFilterWidget.php | 34 + .../BetterExposedFiltersPagerWidget.php | 34 + .../BetterExposedFiltersSortWidget.php | 34 + .../src/BetterExposedFiltersHelper.php | 203 ++ .../Plugin/BetterExposedFiltersWidgetBase.php | 198 ++ .../BetterExposedFiltersWidgetInterface.php | 56 + .../BetterExposedFiltersWidgetManager.php | 45 + .../filter/DatePickers.php | 189 ++ .../filter/DefaultWidget.php | 24 + .../filter/FilterWidgetBase.php | 331 +++ .../better_exposed_filters/filter/Hidden.php | 40 + .../better_exposed_filters/filter/Links.php | 78 + .../filter/RadioButtons.php | 114 + .../better_exposed_filters/filter/Single.php | 75 + .../better_exposed_filters/filter/Sliders.php | 187 ++ .../pager/DefaultWidget.php | 15 + .../better_exposed_filters/pager/Links.php | 35 + .../pager/PagerWidgetBase.php | 87 + .../pager/RadioButtons.php | 30 + .../sort/DefaultWidget.php | 30 + .../better_exposed_filters/sort/Links.php | 36 + .../sort/RadioButtons.php | 31 + .../sort/SortWidgetBase.php | 278 +++ .../exposed_form/BetterExposedFilters.php | 2136 ++++++----------- .../templates/bef-checkboxes.html.twig | 20 +- .../templates/bef-hidden.html.twig | 4 +- .../templates/bef-links.html.twig | 18 +- .../templates/bef-nested-elements.html.twig | 9 +- .../templates/bef-radios.html.twig | 8 +- .../tests/bef_test/bef_test.info.yml | 17 - .../tests/better_exposed_filters.test | 476 ---- .../tests/modules/bef_test/bef_test.info.yml | 16 + .../{ => modules}/bef_test/bef_test.install | 58 +- .../{ => modules}/bef_test/bef_test.module | 0 ...ity_form_display.node.bef_test.default.yml | 2 - ...ity_view_display.node.bef_test.default.yml | 0 ...tity_view_display.node.bef_test.teaser.yml | 0 .../field.field.node.bef_test.body.yml | 0 ....field.node.bef_test.field_bef_boolean.yml | 0 ...ld.field.node.bef_test.field_bef_email.yml | 0 ....field.node.bef_test.field_bef_integer.yml | 0 ....field.node.bef_test.field_bef_letters.yml | 0 ...field.node.bef_test.field_bef_location.yml | 0 ...ld.field.node.bef_test.field_bef_price.yml | 0 .../field.storage.node.field_bef_boolean.yml | 0 .../field.storage.node.field_bef_email.yml | 0 .../field.storage.node.field_bef_integer.yml | 0 .../field.storage.node.field_bef_letters.yml | 0 .../field.storage.node.field_bef_location.yml | 0 .../field.storage.node.field_bef_price.yml | 0 .../config/install/node.type.bef_test.yml | 7 - .../taxonomy.vocabulary.bef_test_location.yml | 0 .../config/install/views.view.bef_test.yml | 273 +-- .../BetterExposedFiltersTest.php | 244 ++ .../Kernel/BetterExposedFiltersKernelTest.php | 123 + .../BetterExposedFiltersKernelTestBase.php | 77 + .../Plugin/filter/FilterWidgetKernelTest.php | 145 ++ .../filter/HiddenFilterWidgetKernelTest.php | 78 + .../filter/LinksFilterWidgetKernelTest.php | 68 + .../RadioButtonsFilterWidgetKernelTest.php | 120 + .../filter/SingleFilterWidgetKernelTest.php | 49 + .../pager/LinksPagerWidgetKernelTest.php | 50 + .../RadioButtonsPagerWidgetKernelTest.php | 50 + .../Plugin/sort/LinksSortWidgetKernelTest.php | 50 + .../sort/RadioButtonsSortWidgetKernelTest.php | 50 + .../Plugin/sort/SortWidgetKernelTest.php | 136 ++ .../src/Traits/BetterExposedFiltersTrait.php | 44 + .../BetterExposedFiltersHelperUnitTest.php | 356 +++ .../src/Unit/BetterExposedFiltersTest.php | 222 -- web/modules/jquery_ui/CHANGELOG.txt | 32 + web/modules/jquery_ui/LICENSE.txt | 339 +++ web/modules/jquery_ui/README.txt | 44 + .../jquery_ui/jquery.ui/themes/base/core.css | 97 + .../images/ui-bg_flat_0_aaaaaa_40x100.png | 3 + .../base/images/ui-icons_444444_256x240.png | 11 + .../base/images/ui-icons_555555_256x240.png | 11 + .../base/images/ui-icons_777620_256x240.png | 11 + .../base/images/ui-icons_777777_256x240.png | 11 + .../base/images/ui-icons_cc0000_256x240.png | 11 + .../base/images/ui-icons_ffffff_256x240.png | 11 + .../jquery_ui/jquery.ui/themes/base/theme.css | 443 ++++ .../jquery_ui/jquery.ui/ui/data-min.js | 4 + .../jquery.ui/ui/disable-selection-min.js | 4 + .../jquery.ui/ui/escape-selector-min.js | 4 + .../jquery_ui/jquery.ui/ui/focusable-min.js | 4 + .../jquery_ui/jquery.ui/ui/form-min.js | 4 + .../jquery.ui/ui/form-reset-mixin-min.js | 4 + web/modules/jquery_ui/jquery.ui/ui/ie-min.js | 4 + .../jquery_ui/jquery.ui/ui/jquery-1-7-min.js | 4 + .../jquery_ui/jquery.ui/ui/keycode-min.js | 4 + .../jquery_ui/jquery.ui/ui/labels-min.js | 4 + .../jquery_ui/jquery.ui/ui/plugin-min.js | 4 + .../jquery_ui/jquery.ui/ui/position-min.js | 4 + .../jquery.ui/ui/safe-active-element-min.js | 4 + .../jquery_ui/jquery.ui/ui/safe-blur-min.js | 4 + .../jquery.ui/ui/scroll-parent-min.js | 4 + .../jquery_ui/jquery.ui/ui/tabbable-min.js | 4 + .../jquery_ui/jquery.ui/ui/unique-id-min.js | 4 + .../jquery_ui/jquery.ui/ui/version-min.js | 4 + .../jquery_ui/jquery.ui/ui/widget-min.js | 4 + .../jquery.ui/ui/widgets/mouse-min.js | 4 + web/modules/jquery_ui/jquery_ui.info.yml | 11 + web/modules/jquery_ui/jquery_ui.libraries.yml | 32 + web/modules/jquery_ui/jquery_ui.module | 23 + web/modules/jquery_ui_datepicker/LICENSE.txt | 339 +++ web/modules/jquery_ui_datepicker/README.md | 40 + .../jquery.ui/themes/base/datepicker.css | 185 ++ .../jquery.ui/ui/widgets/datepicker-min.js | 5 + .../jquery_ui_datepicker.info.yml | 12 + .../jquery_ui_datepicker.libraries.yml | 13 + .../jquery_ui_datepicker.module | 23 + web/modules/jquery_ui_slider/LICENSE.txt | 339 +++ web/modules/jquery_ui_slider/README.md | 39 + .../jquery.ui/themes/base/slider.css | 75 + .../jquery.ui/ui/widgets/slider-min.js | 4 + .../jquery_ui_slider.info.yml | 12 + .../jquery_ui_slider.libraries.yml | 14 + .../jquery_ui_slider/jquery_ui_slider.module | 23 + web/modules/jquery_ui_touch_punch/LICENSE.txt | 339 +++ web/modules/jquery_ui_touch_punch/README.md | 43 + .../jquery_ui_touch_punch/composer.json | 28 + .../jquery_ui_touch_punch.info.yml | 12 + .../jquery_ui_touch_punch.install | 52 + .../jquery_ui_touch_punch.libraries.yml | 8 + .../jquery_ui_touch_punch.module | 23 + 153 files changed, 9280 insertions(+), 3552 deletions(-) create mode 100644 web/modules/better_exposed_filters/.circleci/config.yml create mode 100755 web/modules/better_exposed_filters/.gitignore delete mode 100644 web/modules/better_exposed_filters/PATCHES.txt create mode 100644 web/modules/better_exposed_filters/better_exposed_filters.install create mode 100644 web/modules/better_exposed_filters/better_exposed_filters.services.yml delete mode 100644 web/modules/better_exposed_filters/better_exposed_filters.theme create mode 100644 web/modules/better_exposed_filters/composer.json create mode 100644 web/modules/better_exposed_filters/config/schema/better_exposed_filters.filter.schema.yml create mode 100644 web/modules/better_exposed_filters/config/schema/better_exposed_filters.pager.schema.yml create mode 100644 web/modules/better_exposed_filters/config/schema/better_exposed_filters.sort.schema.yml create mode 100644 web/modules/better_exposed_filters/docker-compose.yml create mode 100644 web/modules/better_exposed_filters/includes/better_exposed_filters.theme.inc create mode 100644 web/modules/better_exposed_filters/phpcs.xml create mode 100644 web/modules/better_exposed_filters/phpunit.core.xml.dist create mode 100644 web/modules/better_exposed_filters/src/Annotation/BetterExposedFiltersFilterWidget.php create mode 100644 web/modules/better_exposed_filters/src/Annotation/BetterExposedFiltersPagerWidget.php create mode 100644 web/modules/better_exposed_filters/src/Annotation/BetterExposedFiltersSortWidget.php create mode 100644 web/modules/better_exposed_filters/src/BetterExposedFiltersHelper.php create mode 100644 web/modules/better_exposed_filters/src/Plugin/BetterExposedFiltersWidgetBase.php create mode 100644 web/modules/better_exposed_filters/src/Plugin/BetterExposedFiltersWidgetInterface.php create mode 100644 web/modules/better_exposed_filters/src/Plugin/BetterExposedFiltersWidgetManager.php create mode 100644 web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/DatePickers.php create mode 100644 web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/DefaultWidget.php create mode 100644 web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/FilterWidgetBase.php create mode 100644 web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/Hidden.php create mode 100644 web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/Links.php create mode 100644 web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/RadioButtons.php create mode 100644 web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/Single.php create mode 100644 web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/Sliders.php create mode 100644 web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/pager/DefaultWidget.php create mode 100644 web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/pager/Links.php create mode 100644 web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/pager/PagerWidgetBase.php create mode 100644 web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/pager/RadioButtons.php create mode 100644 web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/sort/DefaultWidget.php create mode 100644 web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/sort/Links.php create mode 100644 web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/sort/RadioButtons.php create mode 100644 web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/sort/SortWidgetBase.php delete mode 100644 web/modules/better_exposed_filters/tests/bef_test/bef_test.info.yml delete mode 100644 web/modules/better_exposed_filters/tests/better_exposed_filters.test create mode 100644 web/modules/better_exposed_filters/tests/modules/bef_test/bef_test.info.yml rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/bef_test.install (55%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/bef_test.module (100%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/config/install/core.entity_form_display.node.bef_test.default.yml (96%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/config/install/core.entity_view_display.node.bef_test.default.yml (100%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/config/install/core.entity_view_display.node.bef_test.teaser.yml (100%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/config/install/field.field.node.bef_test.body.yml (100%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/config/install/field.field.node.bef_test.field_bef_boolean.yml (100%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/config/install/field.field.node.bef_test.field_bef_email.yml (100%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/config/install/field.field.node.bef_test.field_bef_integer.yml (100%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/config/install/field.field.node.bef_test.field_bef_letters.yml (100%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/config/install/field.field.node.bef_test.field_bef_location.yml (100%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/config/install/field.field.node.bef_test.field_bef_price.yml (100%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/config/install/field.storage.node.field_bef_boolean.yml (100%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/config/install/field.storage.node.field_bef_email.yml (100%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/config/install/field.storage.node.field_bef_integer.yml (100%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/config/install/field.storage.node.field_bef_letters.yml (100%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/config/install/field.storage.node.field_bef_location.yml (100%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/config/install/field.storage.node.field_bef_price.yml (100%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/config/install/node.type.bef_test.yml (69%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/config/install/taxonomy.vocabulary.bef_test_location.yml (100%) rename web/modules/better_exposed_filters/tests/{ => modules}/bef_test/config/install/views.view.bef_test.yml (87%) create mode 100644 web/modules/better_exposed_filters/tests/src/FunctionalJavascript/BetterExposedFiltersTest.php create mode 100644 web/modules/better_exposed_filters/tests/src/Kernel/BetterExposedFiltersKernelTest.php create mode 100644 web/modules/better_exposed_filters/tests/src/Kernel/BetterExposedFiltersKernelTestBase.php create mode 100644 web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/FilterWidgetKernelTest.php create mode 100644 web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/HiddenFilterWidgetKernelTest.php create mode 100644 web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/LinksFilterWidgetKernelTest.php create mode 100644 web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/RadioButtonsFilterWidgetKernelTest.php create mode 100644 web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/SingleFilterWidgetKernelTest.php create mode 100644 web/modules/better_exposed_filters/tests/src/Kernel/Plugin/pager/LinksPagerWidgetKernelTest.php create mode 100644 web/modules/better_exposed_filters/tests/src/Kernel/Plugin/pager/RadioButtonsPagerWidgetKernelTest.php create mode 100644 web/modules/better_exposed_filters/tests/src/Kernel/Plugin/sort/LinksSortWidgetKernelTest.php create mode 100644 web/modules/better_exposed_filters/tests/src/Kernel/Plugin/sort/RadioButtonsSortWidgetKernelTest.php create mode 100644 web/modules/better_exposed_filters/tests/src/Kernel/Plugin/sort/SortWidgetKernelTest.php create mode 100644 web/modules/better_exposed_filters/tests/src/Traits/BetterExposedFiltersTrait.php create mode 100644 web/modules/better_exposed_filters/tests/src/Unit/BetterExposedFiltersHelperUnitTest.php delete mode 100644 web/modules/better_exposed_filters/tests/src/Unit/BetterExposedFiltersTest.php create mode 100644 web/modules/jquery_ui/CHANGELOG.txt create mode 100644 web/modules/jquery_ui/LICENSE.txt create mode 100644 web/modules/jquery_ui/README.txt create mode 100644 web/modules/jquery_ui/jquery.ui/themes/base/core.css create mode 100644 web/modules/jquery_ui/jquery.ui/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png create mode 100644 web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_444444_256x240.png create mode 100644 web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_555555_256x240.png create mode 100644 web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_777620_256x240.png create mode 100644 web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_777777_256x240.png create mode 100644 web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_cc0000_256x240.png create mode 100644 web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_ffffff_256x240.png create mode 100644 web/modules/jquery_ui/jquery.ui/themes/base/theme.css create mode 100644 web/modules/jquery_ui/jquery.ui/ui/data-min.js create mode 100644 web/modules/jquery_ui/jquery.ui/ui/disable-selection-min.js create mode 100644 web/modules/jquery_ui/jquery.ui/ui/escape-selector-min.js create mode 100644 web/modules/jquery_ui/jquery.ui/ui/focusable-min.js create mode 100644 web/modules/jquery_ui/jquery.ui/ui/form-min.js create mode 100644 web/modules/jquery_ui/jquery.ui/ui/form-reset-mixin-min.js create mode 100644 web/modules/jquery_ui/jquery.ui/ui/ie-min.js create mode 100644 web/modules/jquery_ui/jquery.ui/ui/jquery-1-7-min.js create mode 100644 web/modules/jquery_ui/jquery.ui/ui/keycode-min.js create mode 100644 web/modules/jquery_ui/jquery.ui/ui/labels-min.js create mode 100644 web/modules/jquery_ui/jquery.ui/ui/plugin-min.js create mode 100644 web/modules/jquery_ui/jquery.ui/ui/position-min.js create mode 100644 web/modules/jquery_ui/jquery.ui/ui/safe-active-element-min.js create mode 100644 web/modules/jquery_ui/jquery.ui/ui/safe-blur-min.js create mode 100644 web/modules/jquery_ui/jquery.ui/ui/scroll-parent-min.js create mode 100644 web/modules/jquery_ui/jquery.ui/ui/tabbable-min.js create mode 100644 web/modules/jquery_ui/jquery.ui/ui/unique-id-min.js create mode 100644 web/modules/jquery_ui/jquery.ui/ui/version-min.js create mode 100644 web/modules/jquery_ui/jquery.ui/ui/widget-min.js create mode 100644 web/modules/jquery_ui/jquery.ui/ui/widgets/mouse-min.js create mode 100644 web/modules/jquery_ui/jquery_ui.info.yml create mode 100644 web/modules/jquery_ui/jquery_ui.libraries.yml create mode 100644 web/modules/jquery_ui/jquery_ui.module create mode 100644 web/modules/jquery_ui_datepicker/LICENSE.txt create mode 100644 web/modules/jquery_ui_datepicker/README.md create mode 100644 web/modules/jquery_ui_datepicker/jquery.ui/themes/base/datepicker.css create mode 100644 web/modules/jquery_ui_datepicker/jquery.ui/ui/widgets/datepicker-min.js create mode 100644 web/modules/jquery_ui_datepicker/jquery_ui_datepicker.info.yml create mode 100644 web/modules/jquery_ui_datepicker/jquery_ui_datepicker.libraries.yml create mode 100644 web/modules/jquery_ui_datepicker/jquery_ui_datepicker.module create mode 100644 web/modules/jquery_ui_slider/LICENSE.txt create mode 100644 web/modules/jquery_ui_slider/README.md create mode 100644 web/modules/jquery_ui_slider/jquery.ui/themes/base/slider.css create mode 100644 web/modules/jquery_ui_slider/jquery.ui/ui/widgets/slider-min.js create mode 100644 web/modules/jquery_ui_slider/jquery_ui_slider.info.yml create mode 100644 web/modules/jquery_ui_slider/jquery_ui_slider.libraries.yml create mode 100644 web/modules/jquery_ui_slider/jquery_ui_slider.module create mode 100644 web/modules/jquery_ui_touch_punch/LICENSE.txt create mode 100644 web/modules/jquery_ui_touch_punch/README.md create mode 100644 web/modules/jquery_ui_touch_punch/composer.json create mode 100644 web/modules/jquery_ui_touch_punch/jquery_ui_touch_punch.info.yml create mode 100644 web/modules/jquery_ui_touch_punch/jquery_ui_touch_punch.install create mode 100644 web/modules/jquery_ui_touch_punch/jquery_ui_touch_punch.libraries.yml create mode 100644 web/modules/jquery_ui_touch_punch/jquery_ui_touch_punch.module diff --git a/composer.json b/composer.json index 1dd54db9eb..9b93ff5073 100644 --- a/composer.json +++ b/composer.json @@ -92,7 +92,7 @@ "drupal/administerusersbyrole": "3.0", "drupal/allowed_formats": "1.3", "drupal/anchor_link": "1.7", - "drupal/better_exposed_filters": "3.0-alpha6", + "drupal/better_exposed_filters": "5.0-beta1", "drupal/bigmenu": "1.0.0-alpha1", "drupal/block_field": "1.0.0-rc1", "drupal/block_permissions": "1.1", @@ -276,9 +276,6 @@ "drupal/addtocalendar": { "UTC Time Adjustment": "patches/utc-time-adjustment.patch" }, - "drupal/better_exposed_filters": { - "2961022": "https://www.drupal.org/files/issues/2018-09-27/better_exposed_filters-autosubmit-fix-2961022-4.patch" - }, "drupal/entity_clone": { "3060223": "https://www.drupal.org/files/issues/2019-10-17/%20entity_clone-corrupted-paragraph-cloning-3060223-5.patch" }, @@ -308,4 +305,4 @@ "php": "7.3" } } -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index 9aff2845f4..88b6a0bed8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4061d56ed0fe7f80e7e5e4a47a2ae8c5", + "content-hash": "13505e055380c19ee4acdea061f5ea71", "packages": [ { "name": "alchemy/zippy", @@ -2339,46 +2339,48 @@ }, { "name": "drupal/better_exposed_filters", - "version": "3.0.0-alpha6", + "version": "5.0.0-beta1", "source": { "type": "git", "url": "https://git.drupalcode.org/project/better_exposed_filters.git", - "reference": "8.x-3.0-alpha6" + "reference": "8.x-5.0-beta1" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/better_exposed_filters-8.x-3.0-alpha6.zip", - "reference": "8.x-3.0-alpha6", - "shasum": "0cd9849aa28eea822e18555107e4539d755d95ef" + "url": "https://ftp.drupal.org/files/projects/better_exposed_filters-8.x-5.0-beta1.zip", + "reference": "8.x-5.0-beta1", + "shasum": "bc5ce873fa699ee584fdeb2fd28ad6c5cb456b03" }, "require": { - "drupal/core": "*" + "drupal/core": "^8.8 || ^9", + "drupal/jquery_ui": "^1.4", + "drupal/jquery_ui_datepicker": "^1.0", + "drupal/jquery_ui_slider": "^1.1", + "drupal/jquery_ui_touch_punch": "^1.0" }, "type": "drupal-module", "extra": { - "branch-alias": { - "dev-3.x": "3.x-dev" - }, "drupal": { - "version": "8.x-3.0-alpha6", - "datestamp": "1550449381", + "version": "8.x-5.0-beta1", + "datestamp": "1594141892", "security-coverage": { "status": "not-covered", - "message": "Alpha releases are not covered by Drupal security advisories." + "message": "Beta releases are not covered by Drupal security advisories." } - }, - "patches_applied": { - "2961022": "https://www.drupal.org/files/issues/2018-09-27/better_exposed_filters-autosubmit-fix-2961022-4.patch" } }, "notification-url": "https://packages.drupal.org/8/downloads", "license": [ - "GPL-2.0-or-later" + "GPL-2.0+" ], "authors": [ { - "name": "chr.fritsch", - "homepage": "https://www.drupal.org/user/2103716" + "name": "Mike Keran", + "homepage": "https://www.drupal.org/u/mikeker" + }, + { + "name": "Martin Keereman", + "homepage": "https://www.drupal.org/u/etroid" }, { "name": "jkopel", @@ -2388,19 +2390,16 @@ "name": "mikeker", "homepage": "https://www.drupal.org/user/192273" }, - { - "name": "mortona2k", - "homepage": "https://www.drupal.org/user/1029484" - }, { "name": "rlhawk", "homepage": "https://www.drupal.org/user/352283" } ], - "description": "Provides advanced options (such as links, checkboxes, or jQueryUI widgets) for exposed Views elements.", + "description": "Replaces the Views default single- or multi-select boxes with more advanced options.", "homepage": "https://www.drupal.org/project/better_exposed_filters", "support": { - "source": "http://cgit.drupalcode.org/better_exposed_filters" + "source": "https://git.drupalcode.org/project/better_exposed_filters", + "issues": "https://www.drupal.org/project/issues/better_exposed_filters" } }, { @@ -5402,6 +5401,243 @@ "source": "https://git.drupalcode.org/project/inline_entity_form" } }, + { + "name": "drupal/jquery_ui", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/jquery_ui.git", + "reference": "8.x-1.4" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/jquery_ui-8.x-1.4.zip", + "reference": "8.x-1.4", + "shasum": "64c19ecc8902e2b4b1ab0cc5f5fe28dbc83bfebe" + }, + "require": { + "drupal/core": "^8 || ^9" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "8.x-1.4", + "datestamp": "1582149957", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "RobLoach", + "homepage": "https://www.drupal.org/user/61114" + }, + { + "name": "jjeff", + "homepage": "https://www.drupal.org/user/17190" + }, + { + "name": "lauriii", + "homepage": "https://www.drupal.org/user/1078742" + }, + { + "name": "litwol", + "homepage": "https://www.drupal.org/user/78134" + }, + { + "name": "mfb", + "homepage": "https://www.drupal.org/user/12302" + }, + { + "name": "mfer", + "homepage": "https://www.drupal.org/user/25701" + }, + { + "name": "mikelutz", + "homepage": "https://www.drupal.org/user/2972409" + }, + { + "name": "sun", + "homepage": "https://www.drupal.org/user/54136" + }, + { + "name": "webchick", + "homepage": "https://www.drupal.org/user/24967" + }, + { + "name": "zrpnr", + "homepage": "https://www.drupal.org/user/1448368" + } + ], + "description": "Provides jQuery UI library.", + "homepage": "https://www.drupal.org/project/jquery_ui", + "support": { + "source": "https://git.drupalcode.org/project/jquery_ui" + } + }, + { + "name": "drupal/jquery_ui_datepicker", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/jquery_ui_datepicker.git", + "reference": "8.x-1.0" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/jquery_ui_datepicker-8.x-1.0.zip", + "reference": "8.x-1.0", + "shasum": "954f12f93e82b6c93e6797fe2e6e66604ad1d2b2" + }, + "require": { + "drupal/core": "^8 || ^9", + "drupal/jquery_ui": "*" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "8.x-1.0", + "datestamp": "1589684301", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "jrockowitz", + "homepage": "https://www.drupal.org/user/371407" + }, + { + "name": "zrpnr", + "homepage": "https://www.drupal.org/user/1448368" + } + ], + "description": "Provides jQuery UI Datepicker library.", + "homepage": "https://www.drupal.org/project/jquery_ui_datepicker", + "support": { + "source": "https://git.drupalcode.org/project/jquery_ui_datepicker" + } + }, + { + "name": "drupal/jquery_ui_slider", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/jquery_ui_slider.git", + "reference": "8.x-1.1" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/jquery_ui_slider-8.x-1.1.zip", + "reference": "8.x-1.1", + "shasum": "79b90cf60d45fc33ffdaa84bb2d6563f78a7d3d1" + }, + "require": { + "drupal/core": "^8 || ^9", + "drupal/jquery_ui": "*" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "8.x-1.1", + "datestamp": "1584107817", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "bnjmnm", + "homepage": "https://www.drupal.org/user/2369194" + }, + { + "name": "lauriii", + "homepage": "https://www.drupal.org/user/1078742" + }, + { + "name": "zrpnr", + "homepage": "https://www.drupal.org/user/1448368" + } + ], + "description": "Provides jQuery UI Slider library.", + "homepage": "https://www.drupal.org/project/jquery_ui_slider", + "support": { + "source": "https://git.drupalcode.org/project/jquery_ui_slider" + } + }, + { + "name": "drupal/jquery_ui_touch_punch", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/jquery_ui_touch_punch.git", + "reference": "1.0.0" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/jquery_ui_touch_punch-1.0.0.zip", + "reference": "1.0.0", + "shasum": "8444a0ed897ba3d8e8876a9602ec8b3dca678cd1" + }, + "require": { + "drupal/core": "^8 || ^9", + "drupal/jquery_ui": "^1.0" + }, + "suggest": { + "furf/jquery-ui-touch-punch": "Required to use drupal/jquery_ui_touch_punch module." + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "1.0.0", + "datestamp": "1591893292", + "security-coverage": { + "status": "not-covered", + "message": "Project has not opted into security advisory coverage!" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0+" + ], + "authors": [ + { + "name": "Naveen Valecha", + "homepage": "https://drupal.org/u/naveenvalecha", + "role": "Maintainer" + } + ], + "description": "Provides jQuery UI Touch Punch library.", + "homepage": "https://www.drupal.org/project/jquery_ui_touch_punch", + "keywords": [ + "Drupal", + "jquery_ui_touch_punch" + ], + "support": { + "source": "https://www.drupal.org/project/jquery_ui_touch_punch", + "issues": "https://www.drupal.org/project/issues/jquery_ui_touch_punch", + "irc": "irc://irc.freenode.org/drupal-contribute" + } + }, { "name": "drupal/libraries", "version": "3.0.0-alpha1", diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 34351e0277..7268602fde 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -2412,48 +2412,50 @@ }, { "name": "drupal/better_exposed_filters", - "version": "3.0.0-alpha6", - "version_normalized": "3.0.0.0-alpha6", + "version": "5.0.0-beta1", + "version_normalized": "5.0.0.0-beta1", "source": { "type": "git", "url": "https://git.drupalcode.org/project/better_exposed_filters.git", - "reference": "8.x-3.0-alpha6" + "reference": "8.x-5.0-beta1" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/better_exposed_filters-8.x-3.0-alpha6.zip", - "reference": "8.x-3.0-alpha6", - "shasum": "0cd9849aa28eea822e18555107e4539d755d95ef" + "url": "https://ftp.drupal.org/files/projects/better_exposed_filters-8.x-5.0-beta1.zip", + "reference": "8.x-5.0-beta1", + "shasum": "bc5ce873fa699ee584fdeb2fd28ad6c5cb456b03" }, "require": { - "drupal/core": "*" + "drupal/core": "^8.8 || ^9", + "drupal/jquery_ui": "^1.4", + "drupal/jquery_ui_datepicker": "^1.0", + "drupal/jquery_ui_slider": "^1.1", + "drupal/jquery_ui_touch_punch": "^1.0" }, "type": "drupal-module", "extra": { - "branch-alias": { - "dev-3.x": "3.x-dev" - }, "drupal": { - "version": "8.x-3.0-alpha6", - "datestamp": "1550449381", + "version": "8.x-5.0-beta1", + "datestamp": "1594141892", "security-coverage": { "status": "not-covered", - "message": "Alpha releases are not covered by Drupal security advisories." + "message": "Beta releases are not covered by Drupal security advisories." } - }, - "patches_applied": { - "2961022": "https://www.drupal.org/files/issues/2018-09-27/better_exposed_filters-autosubmit-fix-2961022-4.patch" } }, "installation-source": "dist", "notification-url": "https://packages.drupal.org/8/downloads", "license": [ - "GPL-2.0-or-later" + "GPL-2.0+" ], "authors": [ { - "name": "chr.fritsch", - "homepage": "https://www.drupal.org/user/2103716" + "name": "Mike Keran", + "homepage": "https://www.drupal.org/u/mikeker" + }, + { + "name": "Martin Keereman", + "homepage": "https://www.drupal.org/u/etroid" }, { "name": "jkopel", @@ -2463,19 +2465,16 @@ "name": "mikeker", "homepage": "https://www.drupal.org/user/192273" }, - { - "name": "mortona2k", - "homepage": "https://www.drupal.org/user/1029484" - }, { "name": "rlhawk", "homepage": "https://www.drupal.org/user/352283" } ], - "description": "Provides advanced options (such as links, checkboxes, or jQueryUI widgets) for exposed Views elements.", + "description": "Replaces the Views default single- or multi-select boxes with more advanced options.", "homepage": "https://www.drupal.org/project/better_exposed_filters", "support": { - "source": "http://cgit.drupalcode.org/better_exposed_filters" + "source": "https://git.drupalcode.org/project/better_exposed_filters", + "issues": "https://www.drupal.org/project/issues/better_exposed_filters" } }, { @@ -5564,6 +5563,251 @@ "source": "https://git.drupalcode.org/project/inline_entity_form" } }, + { + "name": "drupal/jquery_ui", + "version": "1.4.0", + "version_normalized": "1.4.0.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/jquery_ui.git", + "reference": "8.x-1.4" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/jquery_ui-8.x-1.4.zip", + "reference": "8.x-1.4", + "shasum": "64c19ecc8902e2b4b1ab0cc5f5fe28dbc83bfebe" + }, + "require": { + "drupal/core": "^8 || ^9" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "8.x-1.4", + "datestamp": "1582149957", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + } + }, + "installation-source": "dist", + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "RobLoach", + "homepage": "https://www.drupal.org/user/61114" + }, + { + "name": "jjeff", + "homepage": "https://www.drupal.org/user/17190" + }, + { + "name": "lauriii", + "homepage": "https://www.drupal.org/user/1078742" + }, + { + "name": "litwol", + "homepage": "https://www.drupal.org/user/78134" + }, + { + "name": "mfb", + "homepage": "https://www.drupal.org/user/12302" + }, + { + "name": "mfer", + "homepage": "https://www.drupal.org/user/25701" + }, + { + "name": "mikelutz", + "homepage": "https://www.drupal.org/user/2972409" + }, + { + "name": "sun", + "homepage": "https://www.drupal.org/user/54136" + }, + { + "name": "webchick", + "homepage": "https://www.drupal.org/user/24967" + }, + { + "name": "zrpnr", + "homepage": "https://www.drupal.org/user/1448368" + } + ], + "description": "Provides jQuery UI library.", + "homepage": "https://www.drupal.org/project/jquery_ui", + "support": { + "source": "https://git.drupalcode.org/project/jquery_ui" + } + }, + { + "name": "drupal/jquery_ui_datepicker", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/jquery_ui_datepicker.git", + "reference": "8.x-1.0" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/jquery_ui_datepicker-8.x-1.0.zip", + "reference": "8.x-1.0", + "shasum": "954f12f93e82b6c93e6797fe2e6e66604ad1d2b2" + }, + "require": { + "drupal/core": "^8 || ^9", + "drupal/jquery_ui": "*" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "8.x-1.0", + "datestamp": "1589684301", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + } + }, + "installation-source": "dist", + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "jrockowitz", + "homepage": "https://www.drupal.org/user/371407" + }, + { + "name": "zrpnr", + "homepage": "https://www.drupal.org/user/1448368" + } + ], + "description": "Provides jQuery UI Datepicker library.", + "homepage": "https://www.drupal.org/project/jquery_ui_datepicker", + "support": { + "source": "https://git.drupalcode.org/project/jquery_ui_datepicker" + } + }, + { + "name": "drupal/jquery_ui_slider", + "version": "1.1.0", + "version_normalized": "1.1.0.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/jquery_ui_slider.git", + "reference": "8.x-1.1" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/jquery_ui_slider-8.x-1.1.zip", + "reference": "8.x-1.1", + "shasum": "79b90cf60d45fc33ffdaa84bb2d6563f78a7d3d1" + }, + "require": { + "drupal/core": "^8 || ^9", + "drupal/jquery_ui": "*" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "8.x-1.1", + "datestamp": "1584107817", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + } + }, + "installation-source": "dist", + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "bnjmnm", + "homepage": "https://www.drupal.org/user/2369194" + }, + { + "name": "lauriii", + "homepage": "https://www.drupal.org/user/1078742" + }, + { + "name": "zrpnr", + "homepage": "https://www.drupal.org/user/1448368" + } + ], + "description": "Provides jQuery UI Slider library.", + "homepage": "https://www.drupal.org/project/jquery_ui_slider", + "support": { + "source": "https://git.drupalcode.org/project/jquery_ui_slider" + } + }, + { + "name": "drupal/jquery_ui_touch_punch", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/jquery_ui_touch_punch.git", + "reference": "1.0.0" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/jquery_ui_touch_punch-1.0.0.zip", + "reference": "1.0.0", + "shasum": "8444a0ed897ba3d8e8876a9602ec8b3dca678cd1" + }, + "require": { + "drupal/core": "^8 || ^9", + "drupal/jquery_ui": "^1.0" + }, + "suggest": { + "furf/jquery-ui-touch-punch": "Required to use drupal/jquery_ui_touch_punch module." + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "1.0.0", + "datestamp": "1591893292", + "security-coverage": { + "status": "not-covered", + "message": "Project has not opted into security advisory coverage!" + } + } + }, + "installation-source": "dist", + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0+" + ], + "authors": [ + { + "name": "Naveen Valecha", + "homepage": "https://drupal.org/u/naveenvalecha", + "role": "Maintainer" + } + ], + "description": "Provides jQuery UI Touch Punch library.", + "homepage": "https://www.drupal.org/project/jquery_ui_touch_punch", + "keywords": [ + "Drupal", + "jquery_ui_touch_punch" + ], + "support": { + "source": "https://www.drupal.org/project/jquery_ui_touch_punch", + "issues": "https://www.drupal.org/project/issues/jquery_ui_touch_punch", + "irc": "irc://irc.freenode.org/drupal-contribute" + } + }, { "name": "drupal/libraries", "version": "3.0.0-alpha1", diff --git a/web/modules/better_exposed_filters/.circleci/config.yml b/web/modules/better_exposed_filters/.circleci/config.yml new file mode 100644 index 0000000000..e973d2e474 --- /dev/null +++ b/web/modules/better_exposed_filters/.circleci/config.yml @@ -0,0 +1,162 @@ +defaults: &defaults + docker: + # specify the version you desire here (avoid latest except for testing) + - image: mkeereman/drupal8_tests:8.7 + + - image: selenium/standalone-chrome-debug:3.14.0-beryllium + + - image: mariadb:10.3 + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: 1 + working_directory: /var/www/html/modules/better_exposed_filters + +# YAML does not support merging of lists. That means we can't have a default +# 'steps' configuration, though we can have defaults for individual step +# properties. + +# We use the composer.json as a way to determine if we can cache our build. +restore_cache: &restore_cache + keys: + - v4-dependencies-{{ checksum "composer.json" }}-{{ checksum "../../composer.json" }} + +# If composer.json hasn't changed, restore the Composer cache directory. We +# don't restore the lock file so we ensure we get updated dependencies. +save_cache: &save_cache + paths: + - /root/.composer/cache/files + key: v4-dependencies-{{ checksum "composer.json" }}-{{ checksum "../../composer.json" }} + +# Install composer dependencies into the workspace to share with all jobs. +update_dependencies: &update_dependencies + <<: *defaults + steps: + - checkout + + - restore_cache: *restore_cache + + - run: + working_directory: /var/www/html + command: | + ./update-dependencies.sh $CIRCLE_PROJECT_REPONAME + + - save_cache: *save_cache + + - persist_to_workspace: + root: /var/www/html + paths: + - . + +# Run Drupal unit and kernel tests as one job. This command invokes the test.sh +# hook. +unit_kernel_tests: &unit_kernel_tests + <<: *defaults + steps: + - attach_workspace: + at: /var/www/html + + - checkout + + - run: + working_directory: /var/www/html + command: | + ./test.sh $CIRCLE_PROJECT_REPONAME + + - store_test_results: + path: /var/www/html/artifacts/phpunit + - store_artifacts: + path: /var/www/html/artifacts + +# Run Drupal functional and functional JS tests as one job. This command invokes +# the test-functional.sh and test-functional-js hooks. +functional_tests: &functional_tests + <<: *defaults + steps: + - attach_workspace: + at: /var/www/html + + - checkout + + - run: + working_directory: /var/www/html + command: | + ./test-functional.sh $CIRCLE_PROJECT_REPONAME + ./test-functional-js.sh $CIRCLE_PROJECT_REPONAME + + - store_test_results: + path: /var/www/html/artifacts/phpunit + - store_artifacts: + path: /var/www/html/artifacts + +# Run code quality tests. This invokes code-sniffer.sh. +code_sniffer: &code_sniffer + <<: *defaults + steps: + - attach_workspace: + at: /var/www/html + + - checkout + + # Don't exit builds in CI when warning/error occurs. + # @todo fix errors so this becomes reliable + - run: + working_directory: /var/www/html + command: | + vendor/bin/phpcs --config-set ignore_warnings_on_exit 1 + vendor/bin/phpcs --config-set ignore_errors_on_exit 1 + ./code-sniffer.sh $CIRCLE_PROJECT_REPONAME + + - store_test_results: + path: /var/www/html/artifacts/phpcs + - store_artifacts: + path: /var/www/html/artifacts + +# Run code coverage tests. This invokes code-coverage-stats.sh. +code_coverage: &code_coverage + <<: *defaults + steps: + - attach_workspace: + at: /var/www/html + + - checkout + + - run: + working_directory: /var/www/html + command: | + ./code-coverage-stats.sh $CIRCLE_PROJECT_REPONAME + - store_artifacts: + path: /var/www/html/artifacts + +# Declare all of the jobs we should run. +version: 2 +jobs: + update-dependencies: + <<: *update_dependencies + run-unit-kernel-tests: + <<: *unit_kernel_tests + run-functional-tests: + <<: *functional_tests + run-code-sniffer: + <<: *code_sniffer + run-code-coverage: + <<: *code_coverage + +workflows: + version: 2 + + # Declare a workflow that runs all of our jobs in parallel. + test_and_lint: + jobs: + - update-dependencies + - run-unit-kernel-tests: + requires: + - update-dependencies + - run-functional-tests: + requires: + - update-dependencies + - run-code-sniffer: + requires: + - update-dependencies + - run-code-coverage: + requires: + - update-dependencies + - run-unit-kernel-tests diff --git a/web/modules/better_exposed_filters/.gitignore b/web/modules/better_exposed_filters/.gitignore new file mode 100755 index 0000000000..b870036857 --- /dev/null +++ b/web/modules/better_exposed_filters/.gitignore @@ -0,0 +1,11 @@ +# OS generated files # +###################### +.DS_Store* +ehthumbs.db +Icon + +Thumbs.db +._* +vendor +composer.lock +.idea diff --git a/web/modules/better_exposed_filters/PATCHES.txt b/web/modules/better_exposed_filters/PATCHES.txt deleted file mode 100644 index 21e24f9cfe..0000000000 --- a/web/modules/better_exposed_filters/PATCHES.txt +++ /dev/null @@ -1,7 +0,0 @@ -This file was automatically generated by Composer Patches (https://github.com/cweagans/composer-patches) -Patches applied to this directory: - -2961022 -Source: https://www.drupal.org/files/issues/2018-09-27/better_exposed_filters-autosubmit-fix-2961022-4.patch - - diff --git a/web/modules/better_exposed_filters/better_exposed_filters.api.php b/web/modules/better_exposed_filters/better_exposed_filters.api.php index b84a1b20c7..361e898538 100644 --- a/web/modules/better_exposed_filters/better_exposed_filters.api.php +++ b/web/modules/better_exposed_filters/better_exposed_filters.api.php @@ -1,37 +1,36 @@ <?php + /** * @file - * Hooks provided by the Better Expoosed Filters module. + * Hooks provided by the Better Exposed Filters module. */ /** - * Alters Better Exposed Filters settings before the exposed form widgets are - * built. + * Alters BEF options before the exposed form widgets are built. * - * @param $settings - * The settings array. - * @param $view + * @param array $options + * The BEF options array. + * @param \Drupal\views\ViewExecutable $view * The view to which the settings apply. - * @param $displayHandler + * @param \Drupal\views\Plugin\views\display\DisplayPluginBase $displayHandler * The display handler to which the settings apply. */ -function hook_better_exposed_filters_settings_alter(&$settings, $view, $displayHandler) { +function hook_better_exposed_filters_options_alter(array &$options, ViewExecutable $view, DisplayPluginBase $displayHandler) { // Set the min/max value of a slider. $settings['field_price_value']['slider_options']['bef_slider_min'] = 500; $settings['field_price_value']['slider_options']['bef_slider_max'] = 5000; } - /** * Modify the array of BEF display options for an exposed filter. * - * @param array $display_options - * The set of display options available to this filter. - * @param object $filter - * The exposed filter. + * @param array $widgets + * The set of BEF widgets available to this filter. + * @param \Drupal\views\Plugin\views\HandlerBase $filter + * The exposed views filter plugin. */ -function hook_better_exposed_filters_display_options_alter(&$display_options, $filter) { +function hook_better_exposed_filters_filter_widgets_alter(array &$widgets, HandlerBase $filter) { if ($filter instanceof CustomViewsFilterFoo) { - $display_options['bef_links'] = t('Links'); + $widgets['bef_links'] = t('Links'); } } diff --git a/web/modules/better_exposed_filters/better_exposed_filters.info.yml b/web/modules/better_exposed_filters/better_exposed_filters.info.yml index 352b3a2858..f3cb90cb9a 100644 --- a/web/modules/better_exposed_filters/better_exposed_filters.info.yml +++ b/web/modules/better_exposed_filters/better_exposed_filters.info.yml @@ -1,13 +1,16 @@ name: Better Exposed Filters -description: Provides advanced options (such as links, checkboxes, or jQueryUI widgets) for exposed Views elements. -# core: 8.x +description: Provides advanced options (e.g. links, checkboxes, or other widgets) to exposed Views elements. +core_version_requirement: ^8.8 || ^9 type: module package: Views dependencies: - drupal:views + - jquery_ui:jquery_ui + - jquery_ui_slider:jquery_ui_slider + - jquery_ui_touch_punch:jquery_ui_touch_punch + - jquery_ui_datepicker:jquery_ui_datepicker -# Information added by Drupal.org packaging script on 2019-02-18 -version: '8.x-3.0-alpha6' -core: '8.x' +# Information added by Drupal.org packaging script on 2020-07-07 +version: '8.x-5.0-beta1' project: 'better_exposed_filters' -datestamp: 1550449395 +datestamp: 1594141894 diff --git a/web/modules/better_exposed_filters/better_exposed_filters.install b/web/modules/better_exposed_filters/better_exposed_filters.install new file mode 100644 index 0000000000..b3fa54ad90 --- /dev/null +++ b/web/modules/better_exposed_filters/better_exposed_filters.install @@ -0,0 +1,138 @@ +<?php + +/** + * @file + * Contains better_exposed_filters.install. + */ + +/** + * Provide upgrade path from 8.x-3.x to 8.x-4.x. + */ +function better_exposed_filters_update_8001() { + $config_factory = \Drupal::configFactory(); + foreach ($config_factory->listAll('views.view.') as $config_name) { + $config = $config_factory->getEditable($config_name); + + // Go through each display on each view. + $displays = $config->get('display'); + foreach ($displays as $display_name => $display) { + if (!empty($display['display_options']['exposed_form'])) { + $exposed_form = $display['display_options']['exposed_form']; + // Find BEF exposed forms. + if (isset($exposed_form['type']) && $exposed_form['type'] === 'bef') { + $bef_settings = []; + foreach ($exposed_form['options']['bef'] as $type => $option) { + // General settings. + if ($type === 'general') { + $bef_settings['general'] = [ + 'autosubmit' => $option['autosubmit'] ?? FALSE, + 'autosubmit_exclude_textfield' => $option['autosubmit_exclude_textfield'] ?? FALSE, + 'autosubmit_hide' => $option['autosubmit_hide'] ?? FALSE, + 'input_required' => $exposed_form['options']['input_required'] ?? FALSE, + ]; + + if (isset($exposed_form['options']['text_input_required'])) { + $bef_settings['general'] += [ + 'text_input_required' => $exposed_form['options']['text_input_required'], + 'text_input_required_format' => 'basic_html', + ]; + } + + $bef_settings['general'] += [ + 'allow_secondary' => $option['allow_secondary'] ?? FALSE, + 'secondary_label' => $option['secondary_label'] ?? 'Advanced options', + ]; + } + // Sort settings. + elseif ($type === 'sort') { + $bef_settings['sort'] = [ + 'plugin_id' => $option['bef_format'] ?? 'default', + 'advanced' => [ + 'combine' => $option['advanced']['combine'] ?? FALSE, + 'combine_rewrite' => $option['advanced']['combine_rewrite'] ?? '', + 'reset' => $option['advanced']['reset'] ?? FALSE, + 'reset_label' => $option['advanced']['reset_label'] ?? '', + 'collapsible' => $option['advanced']['collapsible'] ?? FALSE, + 'collapsible_label' => $option['advanced']['collapsible_label'] ?? '', + 'is_secondary' => $option['advanced']['is_secondary'] ?? FALSE, + ], + ]; + } + // Pager settings. + elseif ($type === 'pager') { + $bef_settings['pager'] = [ + 'plugin_id' => $option['bef_format'] ?? 'default', + 'advanced' => [ + 'is_secondary' => $option['is_secondary'] ?? FALSE, + ], + ]; + } + // Filter settings. + else { + // This would indicate a newer version of the config already. + if ($type === 'filter') { + continue; + } + + $field_name = $type; + $bef_settings['filter'][$field_name] = [ + 'plugin_id' => $option['bef_format'] ?? 'default', + ]; + + // Checkboxes/Radio buttons. + if ($option['bef_format'] === 'bef') { + $bef_settings['filter'][$field_name]['select_all_none'] = $option['more_options']['select_all_none'] ?? FALSE; + $bef_settings['filter'][$field_name]['select_all_none_nested'] = $option['more_options']['select_all_none_nested'] ?? FALSE; + } + + // Links. + if ($option['bef_format'] === 'bef_links') { + $bef_settings['filter'][$field_name]['select_all_none'] = $option['more_options']['select_all_none'] ?? FALSE; + } + + // Sliders. + if ($option['bef_format'] === 'bef_sliders') { + // Animate option is now split into two separate options. + $animate = $option['slider_options']['bef_slider_animate']; + $animate_ms = 0; + if (empty($animate)) { + $animate = 'none'; + } + elseif (is_int($animate)) { + $animate = 'custom'; + $animate_ms = $animate; + } + + $bef_settings['filter'][$field_name] = array_merge($bef_settings['filter'][$field_name], [ + 'min' => $option['slider_options']['bef_slider_min'] ?? 0, + 'max' => $option['slider_options']['bef_slider_max'] ?? 99999, + 'step' => $option['slider_options']['bef_slider_step'] ?? 1, + 'animate' => $animate, + 'animate_ms' => $animate_ms, + 'orientation' => $option['slider_options']['bef_slider_orientation'] ?? 'horizontal', + ]); + } + + // Shared advanced settings. + $bef_settings['filter'][$field_name]['advanced'] = [ + 'collapsible' => $option['more_options']['bef_collapsible'] ?? FALSE, + 'is_secondary' => $option['more_options']['is_secondary'] ?? FALSE, + 'placeholder_text' => $option['more_options']['placeholder_text'] ?? '', + 'rewrite' => [ + 'filter_rewrite_values' => $option['more_options']['rewrite']['filter_rewrite_values'] ?? '', + ], + // New option. + 'sort_options' => FALSE, + ]; + } + } + + // Update BEF settings. + $config->set("display.$display_name.display_options.exposed_form.options.bef", $bef_settings); + } + } + } + + $config->save(TRUE); + } +} diff --git a/web/modules/better_exposed_filters/better_exposed_filters.libraries.yml b/web/modules/better_exposed_filters/better_exposed_filters.libraries.yml index 4db82b9868..2a7a85d8dc 100644 --- a/web/modules/better_exposed_filters/better_exposed_filters.libraries.yml +++ b/web/modules/better_exposed_filters/better_exposed_filters.libraries.yml @@ -1,5 +1,5 @@ general: - version: 3.x + version: 4.x css: theme: css/better_exposed_filters.css: {} @@ -9,7 +9,7 @@ general: - core/drupal - core/jquery auto_submit: - version: 3.x + version: 4.x js: js/auto_submit.js: {} dependencies: @@ -17,24 +17,31 @@ auto_submit: - core/jquery.once - core/drupal.debounce select_all_none: - version: 3.x + version: 4.x js: js/bef_select_all_none.js: {} dependencies: - core/drupal - core/jquery + - core/jquery.once sliders: - version: 3.x + version: 4.x js: js/bef_sliders.js: {} dependencies: - core/drupal - core/jquery + - core/jquery.once + - jquery_ui/mouse + - jquery_ui_touch_punch/touch-punch + - jquery_ui_slider/slider datepickers: - version: 3.x + version: 4.x js: js/bef_datepickers.js: {} dependencies: - core/drupal - core/jquery - - core/jquery.ui.slider + - jquery_ui/mouse + - jquery_ui_touch_punch/touch-punch + - jquery_ui_datepicker/datepicker diff --git a/web/modules/better_exposed_filters/better_exposed_filters.module b/web/modules/better_exposed_filters/better_exposed_filters.module index bf29b7869f..7980f0fefe 100644 --- a/web/modules/better_exposed_filters/better_exposed_filters.module +++ b/web/modules/better_exposed_filters/better_exposed_filters.module @@ -1,14 +1,17 @@ <?php + /** * @file - * Allows the use of checkboxes, radio buttons or hidden fields for exposed - * Views filters. + * General functions and hook implementations. + * + * @see https://www.drupal.org/project/better_exposed_filters */ -use Drupal\Core\Url; -use Drupal\Core\Routing\RouteMatchInterface; -use Drupal\Core\Render\Element; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\Core\Url; + +require_once __DIR__ . '/includes/better_exposed_filters.theme.inc'; /** * Implements hook_help(). @@ -26,36 +29,9 @@ function better_exposed_filters_help($route_name, RouteMatchInterface $route_mat $output .= '<dt>' . t('Styling Better Exposed Filters') . '</dt>'; $output .= '<dd>' . t('BEF provides some additional HTML structure to help you style your exposed filters. For some common examples see the <a href=":doco">online documentation</a>.', [':doco' => 'https://www.drupal.org/node/766974']) . '</dd>'; return $output; - break; } } -/** - * Implements hook_theme(). - */ -function better_exposed_filters_theme($existing, $type, $theme, $path) { - return array( - 'bef_checkboxes' => [ - 'render element' => 'element', - ], - 'bef_radios' => [ - 'render element' => 'element', - ], - 'bef_links' => [ - 'render element' => 'element', - ], - 'bef_hidden' => [ - 'render element' => 'element', - ], - 'bef_tree' => [ - 'variables' => [], - ], - 'bef_secondary_exposed_elements' => [ - 'variables' => [], - ], - ); -} - /** * Implements hook_form_FORM_ID_alter(). */ @@ -89,229 +65,3 @@ function better_exposed_filters_form_views_ui_config_item_form_alter(&$form, For '#token_types' => [], ]; } - -/****************************************************************************** - * Preprocess functions for BEF themed elements. - ******************************************************************************/ - -function template_preprocess_bef_checkboxes(&$variables) { - $variables['children'] = Element::children($variables['element']); - $variables['hasSelectAllNone'] = !empty($variables['element']['#bef_select_all_none']) ? TRUE : FALSE; - $variables['hasSelectAllNoneNested'] = !empty($variables['element']['#bef_select_all_none_nested']) ? TRUE : FALSE; - _bef_preprocess_nested_elements($variables); -} - -function template_preprocess_bef_radios(&$variables) { - $variables['children'] = Element::children($variables['element']); - _bef_preprocess_nested_elements($variables); -} - -/** - * Implements hook_template_preprocess_default_variables_alter(). - */ -function template_preprocess_bef_hidden(&$variables) { - $element = $variables['element']; - - // bef_hidden is only used for multi-select elements being converted to - // hidden. - $variables['isMultiple'] = TRUE; - $variables['selected'] = empty($element['#value']) ? $element['#default_value'] : $element['#value']; - $variables['hiddenElements'] = []; - foreach ($element['#options'] as $value => $label) { - $variables['hiddenElements'][$value] = [ - '#type' => 'hidden', - '#value' => $value, - '#name' => $element['#name'] . '[]', - ]; - } - - // @todo: - // Check for optgroups. Put subelements in the $element_set array and add a - // group heading. Otherwise, just add the element to the set. - //$element_set = array(); - //if (is_array($elem)) { - // $element_set = $elem; - //} - //else { - // $element_set[$option] = $elem; - //} -} - -function template_preprocess_bef_links(&$variables) { - // Collect some variables before we start tweaking the element. - $element = &$variables['element']; - $options = $element['#options']; - $name = $element['#name']; - - // Get the query string arguments from the current request. - $existingQuery = \Drupal::service('request_stack')->getCurrentRequest()->query->all(); - - // Remove page parameter from query. - unset($existingQuery['page']); - - // Pass currently selected values for this filter to the template as an array. - $variables['selected'] = $element['#value']; - if (!is_array($variables['selected'])) { - $variables['selected'] = [$variables['selected']]; - } - - $variables['links'] = []; - foreach ($options as $optionValue => $optionLabel) { - // Build a new Url object for each link since the query string changes with - // each option. - /** @var Drupal\Core\Url $url */ - $url = clone($element['#bef_path']); - - // Allow visitors to toggle a filter setting on and off. This is not as - // simple as setOptions('foo', '') as that still leaves an entry which is - // rendered rather than removing the entry from the query string altogether. - // Calling $url->setOption() still leaves a value behind. Instead we work - // with the entire options array and remove items from it as needed. - $urlOptions = $url->getOptions(); - - if ($element['#multiple']) { - $selectedValues = $element['#value']; - - $newQuery = isset($existingQuery[$name]) ? $existingQuery[$name] : []; - if (in_array($optionValue, $selectedValues)) { - // Allow users to toggle an option using the same link. - $newQuery = array_filter($newQuery, function ($value) use ($selectedValues) { - return !in_array($value, $selectedValues); - }); - } - else { - $newQuery[] = $optionValue; - } - if (empty($newQuery)) { - unset($urlOptions['query'][$name]); - } - else { - $urlOptions['query'][$name] = $newQuery; - } - } - else { - if ($optionValue == $element['#value']) { - // Allow toggle link functionality -- click the same link to turn an - // option on or off. - $newQuery = $existingQuery; - unset($newQuery[$name]); - if (empty($newQuery)) { - // Remove the query string completely. - unset($urlOptions['query']); - } - else { - $urlOptions['query'] = $newQuery; - } - } - else { - $urlOptions['query'] = $existingQuery; - $urlOptions['query'][$name] = $optionValue; - } - } - - // Add our updated options to the Url object. - $url->setOptions($urlOptions); - - // Provide the Twig template with an array of links. - $variables['links'][$optionValue] = [ - '#type' => 'link', - '#title' => $optionLabel, - '#theme_wrappers' => ['container'], - '#url' => $url, - ]; - } - - // Handle nested links. But first add the links as children to the element - // for consistent processing between checkboxes/radio buttons and links. - $variables['element'] = array_replace($variables['element'], $variables['links']); - $variables['children'] = Element::children($variables['element']); - _bef_preprocess_nested_elements($variables); -} - -/** - * Prepares variables for views exposed form templates. - * - * Default template: views-exposed-form.html.twig. - * - * @param array $variables - * An associative array containing: - * - form: A render element representing the form. - */ -function better_exposed_filters_preprocess_views_exposed_form(&$variables) { - // Checks if Token module is enabled. - if (!\Drupal::moduleHandler()->moduleExists('token')) { - return; - } - // Replaces tokens in description field of the exposed filter. - foreach ($variables['form']['#info'] as $name => &$info) { - if (isset($info['description']) && isset($variables['form'][explode('filter-', $name)[1]]['#description'])) { - $info['description'] = \Drupal::service('token')->replace($info['description']); - $variables['form'][explode('filter-', $name)[1]]['#description'] = $info['description']; - } - } -} - -/****************************************************************************** - * Utility functions for BEF themed elements. - ******************************************************************************/ - -/** - * Internal function to provide information the templates for rendering nested - * elements. Adds 'is_nested' and 'depth' to $variables. Requires 'children' to - * be set in $variables before being called. - * - * @param array $variables - */ -function _bef_preprocess_nested_elements(array &$variables) { - if (empty($variables['element']['#bef_nested'])) { - return; - } - - // Provide a hierarchical info on the element children for the template to - // render as a nested <ul>. Views prepends '-' characters for each level of - // depth in the vocabulary. Store that information, but remove the hyphens as - // we don't want to display them. - $variables['isNested'] = TRUE; - $variables['depth'] = []; - foreach ($variables['children'] as $child) { - if ($child == 'All') { - // For non-required filters, put the any/all option at the root. - $variables['depth'][$child] = 0; - // And don't change the text as it defaults to "- Any -" and we do not - // want to remove the leading hyphens. - continue; - } - - $original = $variables['element'][$child]['#title']; - $variables['element'][$child]['#title'] = ltrim($original, '-'); - $variables['depth'][$child] = strlen($original) - strlen($variables['element'][$child]['#title']); - } -} - -/** - * Unpacks sort_by and sort_order from the sort_bef_combine element. - * - * @param $form - * @param \FormStateInterface $form_state - */ -function bef_sort_combine_submit($form, FormStateInterface $form_state) { - $sortBy = $sortOrder = ''; - $combined = $form_state->getValue('sort_bef_combine'); - if (!empty($combined)) { - list($sortBy, $sortOrder) = explode(' ', $combined); - } - $form_state->setValue('sort_by', $sortBy); - $form_state->setValue('sort_order', $sortOrder); - - // And pass this along to Views. - //views_exposed_form_submit($form, $form_state); -} - -/** - * Return whether or not the slider has been selected for the given filter. - */ -function _better_exposed_filters_slider_selected($element, &$form_state) { - return (isset($element['#bef_filter_id']) && - isset($form_state['values']['exposed_form_options']['bef'][$element['#bef_filter_id']]['bef_format']) && - $form_state['values']['exposed_form_options']['bef'][$element['#bef_filter_id']]['bef_format'] == 'bef_slider'); -} diff --git a/web/modules/better_exposed_filters/better_exposed_filters.services.yml b/web/modules/better_exposed_filters/better_exposed_filters.services.yml new file mode 100644 index 0000000000..018fb4da25 --- /dev/null +++ b/web/modules/better_exposed_filters/better_exposed_filters.services.yml @@ -0,0 +1,15 @@ +services: + # Helpers + better_exposed_filters.bef_helper: + class: Drupal\better_exposed_filters\BetterExposedFiltersHelper + + # Plugins + plugin.manager.better_exposed_filters_filter_widget: + class: Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetManager + arguments: [filter, '@container.namespaces', '@cache.discovery', '@module_handler'] + plugin.manager.better_exposed_filters_pager_widget: + class: Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetManager + arguments: [pager, '@container.namespaces', '@cache.discovery', '@module_handler'] + plugin.manager.better_exposed_filters_sort_widget: + class: Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetManager + arguments: [sort, '@container.namespaces', '@cache.discovery', '@module_handler'] diff --git a/web/modules/better_exposed_filters/better_exposed_filters.theme b/web/modules/better_exposed_filters/better_exposed_filters.theme deleted file mode 100644 index d51a79d5b9..0000000000 --- a/web/modules/better_exposed_filters/better_exposed_filters.theme +++ /dev/null @@ -1,603 +0,0 @@ -<?php -/** - * @file - * Provides theming functions to display exposed forms using different - * interfaces. - */ - - -/** - * Themes a select element as checkboxes enclosed in a details element. - * - * @param array $vars - * An array of arrays, the 'element' item holds the properties of the element - * - * @return string - * HTML representing the form element. - */ -function theme_select_as_checkboxes_details($vars) { - // Merge incoming element with some default values. Prevents a lot of this. - // $foo = isset($bar) ? $bar : $bar_default; - $element = array_merge( - array( - '#bef_title' => '', - '#bef_description' => '', - '#bef_operator' => array(), - ), - $vars['element'] - ); - - $details = array( - '#title' => $element['#bef_title'], - '#description' => $element['#bef_description'], - '#attributes' => array( - 'class' => array( - 'bef-select-as-checkboxes-details', - ), - ), - ); - if (empty($element['#value'])) { - // Using the FAPI #collapsible and #collapsed attribute doesn't work here - // TODO: not sure why... - $details['#attributes']['class'][] = 'collapsed'; - } - - // We rendered the description as part of the details element, don't render - // it again along with the checkboxes. - unset($element['#bef_description']); - - $children = ''; - if (!empty($element['#bef_operator'])) { - // Put an exposed operator inside the details element. - $children = drupal_render($element['#bef_operator']); - } - - // Render the checkboxes. - // @TODO: Render in a template - $children .= theme('select_as_checkboxes', array('element' => $element)); - - $details['#children'] = $children; - - // @TODO: Render in a template - return theme('fieldset', array('element' => $details)); -} - -/** - * Themes a select element as a series of hidden fields. - * - * @see http://api.drupal.org/api/function/theme_select/7 - * - * @param array $vars - * An array of arrays, the 'element' item holds the properties of the element. - * - * @return string - * HTML representing the form element. - */ -function theme_select_as_hidden($vars) { - $element = $vars['element']; - $output = ''; - $selected_options = empty($element['#value']) ? $element['#default_value'] : $element['#value']; - $properties = array( - 'title' => isset($element['#title']) ? $element['#title'] : '', - 'description' => isset($element['#bef_description']) ? $element['#bef_description'] : '', - 'required' => FALSE, - ); - - foreach ($element['#options'] as $option => $elem) { - // Check for Taxonomy-based filters. - if (is_object($elem)) { - $slice = array_slice($elem->option, 0, 1, TRUE); - $option = key($slice); - $elem = current($slice); - } - - // Check for optgroups. Put subelements in the $element_set array and add a - // group heading. Otherwise, just add the element to the set. - $element_set = array(); - if (is_array($elem)) { - $element_set = $elem; - } - else { - $element_set[$option] = $elem; - } - - foreach ($element_set as $key => $value) { - // Only render fields for selected values -- no selected values renders - // zero fields. - if (array_search($key, $selected_options) !== FALSE) { - // Custom ID for each hidden field based on the <select>'s original ID. - $id = drupal_html_id($element['#id'] . '-' . $key); - - // Very similar to theme_hidden() - // (http://api.drupal.org/api/function/theme_hidden/7). - // @TODO: Render in a template - $hidden = '<input type="hidden" ' - // Brackets are key -- just like select. - . 'name="' . filter_xss($element['#name']) . '[]" ' - . 'id="' . $id . '" ' - . 'value="' . check_plain($key) . '" ' - . drupal_attributes($element['#attributes']) . ' />'; - // @TODO: Render in a template - $output .= theme('form_element', array( - 'element' => array_merge($properties, array( - '#id' => $id, - '#children' => $hidden, - )) - )); - } - } - } - return $output; -} - -/** - * Themes a select element as radio buttons enclosed in a details element. - * - * @param array $vars - * An array of arrays, the 'element' item holds the properties of the element. - * - * @return string - * HTML representing the form element. - */ -function theme_select_as_radios_details($vars) { - // Merge incoming element with some default values. Prevents a lot of this. - // $foo = isset($bar) ? $bar : $bar_default; - $element = array_merge( - array( - '#bef_title' => '', - '#bef_description' => '', - '#bef_operator' => array(), - ), - $vars['element'] - ); - - // The "all" option is the first in the list. If the selected radio button is - // the all option, then leave the details element collapsed. Otherwise, - // render it opened. - $keys = array_keys($element['#options']); - $all = array_shift($keys); - - $details = array( - '#title' => $element['#bef_title'], - '#description' => $element['#bef_description'], - '#attributes' => array( - 'class' => array( - 'bef-select-as-checkboxes-details', - ), - ), - ); - - // We rendered the description as part of the details element, don't render - // it again along with the checkboxes. - unset($element['#bef_description']); - - $children = ''; - if (!empty($element['#bef_operator'])) { - // Put an exposed operator inside the fieldset. - $children = drupal_render($element['#bef_operator']); - } - - // Render the radio buttons. - // @TODO: render in a template - $children .= theme('select_as_radios', $element); - - $details['#children'] = $children; - // @TODO: render in a template - return theme('fieldset', array('element' => $details)); -} - -/** - * Themes a taxonomy-based exposed filter as a nested unordered list. - * - * Note: this routine depends on the '-' char prefixed on the term names by - * Views to determine depth. - * - * @param array $vars - * An array of arrays, the 'element' item holds the properties of the element. - * - * @return string - * Nested, unordered list of filter options - */ -function theme_select_as_tree($vars) { - $element = $vars['element']; - - // The selected keys from #options. - $selected_options = empty($element['#value']) ? $element['#default_value'] : $element['#value']; - - // Build a bunch of nested unordered lists to represent the hierarchy based - // on the '-' prefix added by Views or optgroup structure. - $output = '<ul class="bef-tree">'; - $curr_depth = 0; - foreach ($element['#options'] as $option_value => $option_label) { - - // Check for Taxonomy-based filters. - if (is_object($option_label)) { - $slice = array_slice($option_label->option, 0, 1, TRUE); - $option_value = key($slice); - $option_label = current($slice); - } - - // Check for optgroups -- which is basically a two-level deep tree. - if (is_array($option_label)) { - // TODO: - } - else { - // Build hierarchy based on prefixed '-' on the element label. - if (t('- Any -') == $option_label) { - $depth = 0; - } - else { - preg_match('/^(-*).*$/', $option_label, $matches); - $depth = strlen($matches[1]); - $option_label = ltrim($option_label, '-'); - } - - // Build either checkboxes or radio buttons, depending on Views' settings. - $html = ''; - if (!empty($element['#multiple'])) { - $html = bef_checkbox( - $element, - $option_value, - $option_label, - (array_search($option_value, $selected_options) !== FALSE) - ); - } - else { - $element[$option_value]['#title'] = $option_label; - // @TODO: Render in a template - $element[$option_value]['#children'] = theme('radio', array('element' => $element[$option_value])); - // @TODO: Render in a template - $html .= theme('form_element', array('element' => $element[$option_value])); - } - - if ($depth > $curr_depth) { - // We've moved down a level: create a new nested <ul>. - // TODO: Is there is a way to jump more than one level deeper at a time? - // I don't think so... - $output .= "<ul class='bef-tree-child bef-tree-depth-$depth'><li>$html"; - $curr_depth = $depth; - } - elseif ($depth < $curr_depth) { - // We've moved up a level: finish previous <ul> and <li> tags, once for - // each level, since we can jump multiple levels up at a time. - while ($depth < $curr_depth) { - $output .= '</li></ul>'; - $curr_depth--; - } - $output .= "</li><li>$html"; - } - else { - // Remain at same level as previous entry. No </li> needed if we're at - // the top level. - if (0 == $curr_depth) { - $output .= "<li>$html"; - } - else { - $output .= "</li><li>$html"; - } - } - } - } // foreach ($element['#options'] as $option_value => $option_label) - - if (!$curr_depth) { - // Close last <li> tag. - $output .= '</li>'; - } - else { - // Finish closing <ul> and <li> tags. - while ($curr_depth) { - $curr_depth--; - $output .= '</li></ul></li>'; - } - } - - // Close the opening <ul class="bef-tree"> tag. - $output .= '</ul>'; - - // Add exposed filter description. - $description = ''; - if (!empty($element['#bef_description'])) { - $description = '<div class="description">' . $element['#bef_description'] . '</div>'; - } - - // Add the select all/none option, if needed. - if (!empty($element['#bef_select_all_none'])) { - if (empty($element['#attributes']['class'])) { - $element['#attributes']['class'] = array(); - } - $element['#attributes']['class'][] = 'bef-select-all-none'; - } - // Add the select all/none nested option, if needed. - if (!empty($element['#bef_select_all_none_nested'])) { - if (empty($element['#attributes']['class'])) { - $element['#attributes']['class'] = array(); - } - $element['#attributes']['class'][] = 'bef-select-all-none-nested'; - } - - // Name and multiple attributes are not valid for <div>'s. - if (isset($element['#attributes']['name'])) { - unset($element['#attributes']['name']); - } - if (isset($element['#attributes']['multiple'])) { - unset($element['#attributes']['multiple']); - } - - return '<div' . drupal_attributes($element['#attributes']) . ">$description$output</div>"; -} - -/** - * Themes a select drop-down as a collection of links. - * - * @see http://api.drupal.org/api/function/theme_select/7 - * - * @param array $vars - * An array of arrays, the 'element' item holds the properties of the element. - * - * @return string - * HTML representing the form element. - */ -function theme_select_as_links($vars) { - $element = $vars['element']; - - $output = ''; - $name = $element['#name']; - - // Collect selected values so we can properly style the links later. - $selected_options = array(); - if (empty($element['#value'])) { - if (!empty($element['#default_values'])) { - $selected_options[] = $element['#default_values']; - } - } - else { - $selected_options[] = $element['#value']; - } - - // Add to the selected options specified by Views whatever options are in the - // URL query string, but only for this filter. - $urllist = parse_url(request_uri()); - if (isset($urllist['query'])) { - $query = array(); - parse_str(urldecode($urllist['query']), $query); - foreach ($query as $key => $value) { - if ($key != $name) { - continue; - } - if (is_array($value)) { - // This filter allows multiple selections, so put each one on the - // selected_options array. - foreach ($value as $option) { - $selected_options[] = $option; - } - } - else { - $selected_options[] = $value; - } - } - } - - // Clean incoming values to prevent XSS attacks. - if (is_array($element['#value'])) { - foreach ($element['#value'] as $index => $item) { - unset($element['#value'][$index]); - $element['#value'][filter_xss($index)] = filter_xss($item); - } - } - elseif (is_string($element['#value'])) { - $element['#value'] = filter_xss($element['#value']); - } - - // Go through each filter option and build the appropriate link or plain text. - foreach ($element['#options'] as $option => $elem) { - // Check for Taxonomy-based filters. - if (is_object($elem)) { - $slice = array_slice($elem->option, 0, 1, TRUE); - $option = key($slice); - $elem = current($slice); - } - - // Check for optgroups. Put subelements in the $element_set array and add - // a group heading. Otherwise, just add the element to the set. - $element_set = array(); - if (is_array($elem)) { - $element_set = $elem; - } - else { - $element_set[$option] = $elem; - } - - $links = array(); - $multiple = !empty($element['#multiple']); - - // If we're in an exposed block, we'll get passed a path to use for the - // Views results page. - $path = ''; - if (!empty($element['#bef_path'])) { - $path = $element['#bef_path']; - } - - foreach ($element_set as $key => $value) { - // Custom ID for each link based on the <select>'s original ID. - $id = drupal_html_id($element['#id'] . '-' . $key); - $elem = array( - '#id' => $id, - '#markup' => '', - '#type' => 'bef-link', - '#name' => $id, - ); - if (array_search($key, $selected_options) === FALSE) { - $elem['#children'] = l($value, bef_replace_query_string_arg($name, $key, $multiple, FALSE, $path)); - // @TODO: Render in a template - $output .= theme('form_element', array('element' => $elem)); - } - else { - $elem['#children'] = l($value, bef_replace_query_string_arg($name, $key, $multiple, TRUE, $path)); - _form_set_class($elem, array('bef-select-as-links-selected')); - // @TODO: Render in a template - $output .= str_replace('form-item', 'form-item selected', theme('form_element', array('element' => $elem))); - } - } - } - - $properties = array( - '#description' => isset($element['#bef_description']) ? $element['#bef_description'] : '', - '#children' => $output, - ); - - // @TODO: Render in a template - $output = '<div class="bef-select-as-links">'; - $output .= theme('form_element', array('element' => $properties)); - if (!empty($element['#value'])) { - if (is_array($element['#value'])) { - foreach ($element['#value'] as $value) { - $output .= '<input type="hidden" name="' . $name . '[]" value="' . $value . '" />'; - } - } - else { - $output .= '<input type="hidden" name="' . $name . '" value="' . $element['#value'] . '" />'; - } - } - $output .= '</div>'; - - return $output; -} - -/** - * Themes some exposed form elements in a details element. - * - * @param array $vars - * An array of arrays, the 'element' item holds the properties of the element. - * - * @return string - * HTML to render the form element. - */ -function theme_secondary_exposed_elements($vars) { - $element = $vars['element']; - - // TODO: put HTML in a template - $output = '<div class="bef-secondary-options">'; - foreach (element_children($element) as $id) { - $output .= drupal_render($element[$id]); - } - $output .= '</div>'; - - return $output; -} - -/* - * - * Helper functions - * - */ - -/** - * Build a BEF checkbox. - * - * @see http://api.drupal.org/api/function/theme_checkbox/7 - * - * @param array $element - * Original <select> element generated by Views. - * @param string $value - * Return value of this checkbox option. - * @param string $label - * Label of this checkbox option. - * @param bool $selected - * Checked or not. - * - * @return [type] - * HTML to render a checkbox. - */ -function bef_checkbox($element, $value, $label, $selected) { - $value = check_plain($value); - $label = filter_xss_admin($label); - $id = drupal_html_id($element['#id'] . '-' . $value); - // Custom ID for each checkbox based on the <select>'s original ID. - $properties = array( - '#required' => FALSE, - '#id' => $id, - '#type' => 'bef-checkbox', - '#name' => $id, - ); - - // Prevent the select-all-none class from cascading to all checkboxes. - if (!empty($element['#attributes']['class']) - && FALSE !== ($key = array_search('bef-select-all-none', $element['#attributes']['class']))) { - unset($element['#attributes']['class'][$key]); - } - - // Unset the name attribute as we are setting it manually. - unset($element['#attributes']['name']); - - // Unset the multiple attribute as it doesn't apply for checkboxes. - unset ($element['#attributes']['multiple']); - - // @TODO: Render in a template - $checkbox = '<input type="checkbox" ' - // Brackets are key -- just like select. - . 'name="' . $element['#name'] . '[]" ' - . 'id="' . $id . '" ' - . 'value="' . $value . '" ' - . ($selected ? 'checked="checked" ' : '') - . drupal_attributes($element['#attributes']) . ' />'; - $properties['#children'] = "$checkbox <label class='option' for='$id'>$label</label>"; - $output = theme('form_element', array('element' => $properties)); - return $output; -} - -/** - * Replaces/adds a given query string argument to the current URL. - * - * @param string $key - * Query string key (argument). - * @param string $value - * Query string value. - * @param bool $multiple - * (optional) TRUE if this key/value pair allows multiple values. - * @param bool $remove - * (optional) TRUE if this key/value should be a link to remove/unset the - * filter. - * @param string $path - * (optional) Use this specify the View results page when the exposed form - * is displayed as a block and may be a different URL from the results. - * Defaults to the current path if unspecified. - * - * @return string - * URL. - */ -function bef_replace_query_string_arg($key, $value, $multiple = FALSE, $remove = FALSE, $path = '') { - if (!$path) { - $path = implode('/', arg()); - } - - // Prevents us from having to check for each index from parse_url that we may - // use. - $urllist = array('path' => '', 'fragment' => '', 'query' => ''); - $urllist = array_merge($urllist, parse_url(request_uri())); - $fragment = urldecode($urllist['fragment']); - $query = array(); - parse_str(urldecode($urllist['query']), $query); - if (isset($query[$key]) && is_array($query[$key])) { - // Multiple values allowed for this existing key. - if ($remove && ($key_remove = array_search($value, $query[$key])) !== FALSE) { - unset($query[$key][$key_remove]); - } - else { - $query[$key][] = $value; - } - } - else { - // Create a new key. - if ($multiple && !$remove) { - $query[$key] = array($value); - } - elseif (!$remove) { - $query[$key] = $value; - } - } - return url($path, array( - 'query' => $query, - 'fragment' => $fragment, - 'absolute' => TRUE, - )); -} diff --git a/web/modules/better_exposed_filters/composer.json b/web/modules/better_exposed_filters/composer.json new file mode 100644 index 0000000000..e568c7e3ef --- /dev/null +++ b/web/modules/better_exposed_filters/composer.json @@ -0,0 +1,33 @@ +{ + "name": "drupal/better_exposed_filters", + "type": "drupal-module", + "description": "Replaces the Views default single- or multi-select boxes with more advanced options.", + "homepage": "https://www.drupal.org/project/better_exposed_filters", + "authors": [ + { + "name": "Mike Keran", + "homepage": "https://www.drupal.org/u/mikeker" + }, + { + "name": "Martin Keereman", + "homepage": "https://www.drupal.org/u/etroid" + } + ], + "support": { + "issues": "https://www.drupal.org/project/issues/better_exposed_filters", + "source": "https://git.drupalcode.org/project/better_exposed_filters" + }, + "license": "GPL-2.0+", + "repositories": [ + { + "type": "composer", + "url": "https://packages.drupal.org/8" + } + ], + "require": { + "drupal/jquery_ui": "^1.4", + "drupal/jquery_ui_slider": "^1.1", + "drupal/jquery_ui_touch_punch": "^1.0", + "drupal/jquery_ui_datepicker": "^1.0" + } +} diff --git a/web/modules/better_exposed_filters/config/schema/better_exposed_filters.exposed_form.schema.yml b/web/modules/better_exposed_filters/config/schema/better_exposed_filters.exposed_form.schema.yml index 2bc504662f..2b24fdabb0 100644 --- a/web/modules/better_exposed_filters/config/schema/better_exposed_filters.exposed_form.schema.yml +++ b/web/modules/better_exposed_filters/config/schema/better_exposed_filters.exposed_form.schema.yml @@ -1,117 +1,8 @@ -# BEF general settings -better_exposed_filters.general: - label: 'General BEF settings' - type: mapping - mapping: - allow_secondary: - type: boolean - label: 'Enable secondary exposed form options' - secondary_label: - type: label - label: 'Secondary options label' - autosubmit: - type: boolean - label: 'Autosubmit' - autosubmit_exclude_textfield: - type: boolean - label: 'Exclude Textfield' - autosubmit_hide: - type: boolean - label: 'Hide submit button' - -# BEF exposed sort settings -better_exposed_filters.sort: - type: mapping - label: 'Exposed sort BEF settings' - mapping: - bef_format: - type: string - label: 'Display exposed sort options as' - advanced: - type: mapping - mapping: - collapsible: - type: boolean - label: 'Make sort options collapsible' - collapsible_label: - type: label - label: 'Collapsible details element title' - combine: - type: boolean - label: 'Combine sort order with sort by' - combine_rewrite: - type: text - label: 'Rewrite combined sort options' - reset: - type: boolean - label: 'Include reset sort option' - reset_label: - type: label - label: 'Reset sort label' - is_secondary: - type: boolean - label: 'Sort is a secondary option' - -# BEF exposed filter settings -better_exposed_filters.*: - label: 'BEF filter settings' - type: mapping - mapping: - bef_format: - type: string - label: 'Display exposed filter as' - more_options: - label: 'More options' - type: mapping - mapping: - placeholder_text: - type: text - label: 'Placeholder text for text fields' - bef_select_all_none: - type: boolean - label: 'Add select all/none links' - bef_select_all_none_nested: - type: boolean - label: 'Add select all/none links for heirarchical filters' - bef_collapsible: - type: boolean - label: 'Place filter in a collapsible HTML element' - is_secondary: - type: boolean - label: 'This filter is a secondary option' - rewrite: - type: mapping - label: 'Rewrite filter options' - mapping: - filter_rewrite_values: - type: text - label: 'Rewrite filter option' - slider_options: - label: 'Slider options' - type: mapping - mapping: - bef_slider_min: - type: float - label: 'Silder min option' - translatable: true - bef_slider_max: - type: float - label: 'Silder max option' - translatable: true - bef_slider_step: - type: float - label: 'Silder step option' - translatable: true - bef_slider_animate: - type: string - label: 'Silder animate option' - translatable: true - bef_slider_orientation: - type: string - label: 'Silder orientation' - translatable: true - +# # Schema for the Better Exposed Filters configuration files. +# + +# Views exposed form. views.exposed_form.bef: type: views_exposed_form label: 'Better Exposed Filters' @@ -126,7 +17,49 @@ views.exposed_form.bef: type: text label: 'Text format for the text_input_required field' bef: - type: sequence - label: 'BEF Settings' - sequence: - type: better_exposed_filters.[%key] + type: mapping + label: 'BEF options' + mapping: + general: + type: better_exposed_filters.general + sort: + type: better_exposed_filters.sort.[plugin_id] + pager: + type: better_exposed_filters.pager.[plugin_id] + filter: + type: sequence + label: 'Filters' + sequence: + type: better_exposed_filters.filter.[plugin_id] + +# +# BEF general settings. +# +better_exposed_filters.general: + label: 'General BEF settings' + type: mapping + mapping: + autosubmit: + type: boolean + label: 'Auto-submit' + autosubmit_exclude_textfield: + type: boolean + label: 'Exclude Textfield' + autosubmit_textfield_delay: + type: integer + label: 'Delay for textfield autosubmit' + autosubmit_hide: + type: boolean + label: 'Hide submit button' + input_required: + type: boolean + label: 'Only display results after the user has selected a filter option' + allow_secondary: + type: boolean + label: 'Enable secondary exposed form options' + secondary_label: + type: label + label: 'Secondary options label' + secondary_open: + type: boolean + label: 'Secondary options is open' diff --git a/web/modules/better_exposed_filters/config/schema/better_exposed_filters.filter.schema.yml b/web/modules/better_exposed_filters/config/schema/better_exposed_filters.filter.schema.yml new file mode 100644 index 0000000000..dca60c0254 --- /dev/null +++ b/web/modules/better_exposed_filters/config/schema/better_exposed_filters.filter.schema.yml @@ -0,0 +1,90 @@ +# +# Schema for the Better Exposed Filters filter widgets. +# +better_exposed_filters_filter_widget: + type: mapping + mapping: + plugin_id: + type: string + label: 'Plugin id' + advanced: + type: mapping + mapping: + sort_options: + type: boolean + label: 'Sort filter options alphabetically' + placeholder_text: + type: string + label: 'Placeholder text for the filter' + rewrite: + type: mapping + label: 'Rewrite filter options' + mapping: + filter_rewrite_values: + type: text + label: 'Rewrite filter option' + collapsible: + type: boolean + label: 'Make sort options collapsible' + is_secondary: + type: boolean + label: 'This filter is a secondary option' + +# BEF exposed filter widgets +better_exposed_filters.filter.*: + label: 'Exposed filter BEF settings' + type: better_exposed_filters_filter_widget + +better_exposed_filters.filter.default: + label: 'Default' + type: better_exposed_filters_filter_widget + +better_exposed_filters.filter.bef: + label: 'Checkboxes/Radio Buttons' + type: better_exposed_filters_filter_widget + mapping: + select_all_none: + type: boolean + label: 'Add select all/none links' + select_all_none_nested: + type: boolean + label: 'Add select all/none links to hierarchical lists' + +better_exposed_filters.filter.bef_links: + label: 'Links' + type: better_exposed_filters_filter_widget + mapping: + select_all_none: + type: boolean + label: 'Add select all/none links' + +better_exposed_filters.filter.bef_hidden: + label: 'Hidden' + type: better_exposed_filters_filter_widget + +better_exposed_filters.filter.bef_sliders: + label: 'jQuery UI Slider' + type: better_exposed_filters_filter_widget + mapping: + min: + type: string + label: 'Range minimum' + max: + type: string + label: 'Range maximum' + step: + type: string + label: 'Step' + animate: + type: string + label: 'Animation speed' + animate_ms: + type: string + label: 'Animation speed in milliseconds' + orientation: + type: string + label: 'Orientation' + +better_exposed_filters.filter.bef_datepickers: + label: 'jQuery UI Date Picker' + type: better_exposed_filters_filter_widget diff --git a/web/modules/better_exposed_filters/config/schema/better_exposed_filters.pager.schema.yml b/web/modules/better_exposed_filters/config/schema/better_exposed_filters.pager.schema.yml new file mode 100644 index 0000000000..9ae547cf93 --- /dev/null +++ b/web/modules/better_exposed_filters/config/schema/better_exposed_filters.pager.schema.yml @@ -0,0 +1,32 @@ +# +# Schema for the Better Exposed Filters pager widgets. +# +better_exposed_filters_pager_widget: + type: mapping + mapping: + plugin_id: + type: string + label: 'Plugin id' + advanced: + type: mapping + mapping: + is_secondary: + type: boolean + label: 'Pager is a secondary option' + +# BEF exposed pager widgets +better_exposed_filters.pager.*: + type: better_exposed_filters_pager_widget + label: 'Exposed pager BEF settings' + +better_exposed_filters.pager.default: + type: better_exposed_filters_pager_widget + label: 'Default' + +better_exposed_filters.pager.bef: + type: better_exposed_filters_pager_widget + label: 'Checkboxes/Radio Buttons' + +better_exposed_filters.pager.bef_links: + type: better_exposed_filters_pager_widget + label: 'Links' diff --git a/web/modules/better_exposed_filters/config/schema/better_exposed_filters.sort.schema.yml b/web/modules/better_exposed_filters/config/schema/better_exposed_filters.sort.schema.yml new file mode 100644 index 0000000000..16dcbed9b6 --- /dev/null +++ b/web/modules/better_exposed_filters/config/schema/better_exposed_filters.sort.schema.yml @@ -0,0 +1,50 @@ +# +# Schema for the Better Exposed Filters sort widgets. +# +better_exposed_filters_sort_widget: + type: mapping + mapping: + plugin_id: + type: string + label: 'Plugin id' + advanced: + type: mapping + mapping: + combine: + type: boolean + label: 'Combine sort order with sort by' + combine_rewrite: + type: text + label: 'Rewrite combined sort options' + reset: + type: boolean + label: 'Include reset sort option' + reset_label: + type: label + label: 'Reset sort label' + collapsible: + type: boolean + label: 'Make sort options collapsible' + collapsible_label: + type: label + label: 'Collapsible details element title' + is_secondary: + type: boolean + label: 'Sort is a secondary option' + +# BEF exposed sort widgets +better_exposed_filters.sort.*: + type: better_exposed_filters_sort_widget + label: 'Exposed sort BEF settings' + +better_exposed_filters.sort.default: + type: better_exposed_filters_sort_widget + label: 'Default' + +better_exposed_filters.sort.bef: + type: better_exposed_filters_sort_widget + label: 'Checkboxes/Radio Buttons' + +better_exposed_filters.sort.bef_links: + type: better_exposed_filters_sort_widget + label: 'Links' diff --git a/web/modules/better_exposed_filters/css/better_exposed_filters.css b/web/modules/better_exposed_filters/css/better_exposed_filters.css index 4c72b47f1f..30cf32ebb5 100644 --- a/web/modules/better_exposed_filters/css/better_exposed_filters.css +++ b/web/modules/better_exposed_filters/css/better_exposed_filters.css @@ -1,7 +1,9 @@ /** * @file * better_exposed_filters.css - * + */ + +/* * Basic styling for features added by Better Exposed Filters */ .bef-exposed-form .form--inline .form-item { @@ -10,25 +12,6 @@ .bef-exposed-form .form--inline > .form-item { float: left; /* LRT */ } - - -/* Commenting out CSS additions until they are proven to be needed in D8. */ - -/* - * Prevents collapsed filters from disappearing completely -fieldset.bef-select-as-radios-fieldset.collapsed legend, -fieldset.bef-select-as-checkboxes-fieldset.collapsed legend -{ - position: relative; -} - */ - -/* - * Styles for the jQuery UI slider widgets. -.bef-slider.ui-slider-horizontal { - margin-top: 6px; -} -.bef-slider.ui-slider-vertical { - margin-top: 12px; +.bef-exposed-form .form--inline .bef--secondary { + clear: left; } -*/ diff --git a/web/modules/better_exposed_filters/docker-compose.yml b/web/modules/better_exposed_filters/docker-compose.yml new file mode 100644 index 0000000000..b0ed54a16a --- /dev/null +++ b/web/modules/better_exposed_filters/docker-compose.yml @@ -0,0 +1,32 @@ +version: "3" + +services: + drupal8: + image: mkeereman/drupal8_tests:8.7 + ports: + - "8081:80" + volumes: + - ./:/app + networks: + bef: + + mariadb-host: + image: mariadb:10.3 + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: 1 + ports: + - "3309:3306" + networks: + bef: + + chrome: + image: selenium/standalone-chrome-debug:3.14.0-beryllium + ports: + - "4444:4444" + - "32771:5900" + networks: + bef: + +networks: + bef: + driver: bridge diff --git a/web/modules/better_exposed_filters/includes/better_exposed_filters.theme.inc b/web/modules/better_exposed_filters/includes/better_exposed_filters.theme.inc new file mode 100644 index 0000000000..aaa1e096be --- /dev/null +++ b/web/modules/better_exposed_filters/includes/better_exposed_filters.theme.inc @@ -0,0 +1,321 @@ +<?php + +/** + * @file + * Theme hooks, preprocessor, and suggestions. + */ + +use Drupal\Component\Utility\Html; +use Drupal\Core\Render\Element; +use Drupal\Core\Template\Attribute; + +/** + * Implements hook_theme(). + */ +function better_exposed_filters_theme($existing, $type, $theme, $path) { + return [ + 'bef_checkboxes' => [ + 'render element' => 'element', + ], + 'bef_radios' => [ + 'render element' => 'element', + ], + 'bef_links' => [ + 'render element' => 'element', + ], + 'bef_hidden' => [ + 'render element' => 'element', + ], + ]; +} + +/** + * Implements hook_theme_suggestions_alter(). + */ +function better_exposed_filters_theme_suggestions_alter(array &$suggestions, array $variables, $hook) { + // Target bef elements. + if ($hook === 'form_element') { + $plugin_type = $variables['element']['#context']['plugin_type'] ?? FALSE; + if ($plugin_type && $plugin_type === 'bef') { + $view_id = $variables['element']['#context']['#view_id']; + $display_id = $variables['element']['#context']['#display_id']; + + if ($view_id) { + $suggestions[] = $hook . '__' . $view_id; + if ($display_id) { + $suggestions[] = $hook . '__' . $view_id . '__' . $display_id; + } + } + } + } +} + +/** + * Prepares variables for views exposed form templates. + * + * Default template: views-exposed-form.html.twig. + * + * @param array $variables + * An associative array containing: + * - form: A render element representing the form. + */ +function better_exposed_filters_preprocess_views_exposed_form(array &$variables) { + // Checks if Token module is enabled. + if (!\Drupal::moduleHandler()->moduleExists('token')) { + return; + } + // Replaces tokens in description field of the exposed filter. + foreach ($variables['form']['#info'] as $name => &$info) { + if (isset($info['description']) && isset($variables['form'][explode('filter-', $name)[1]]['#description'])) { + $info['description'] = \Drupal::service('token')->replace($info['description']); + $variables['form'][explode('filter-', $name)[1]]['#description'] = $info['description']; + } + } +} + +/****************************************************************************** + * Preprocess functions for BEF themed elements. + ******************************************************************************/ + +/** + * Prepares variables for bef-checkboxes template. + * + * Default template: bef-checkboxes.html.twig. + * + * @param array $variables + * An associative array containing: + * - element: An associative array containing the exposed form element. + */ +function template_preprocess_bef_checkboxes(array &$variables) { + $element = &$variables['element']; + + // Create new wrapper attributes since the element attributes will be used + // on the fieldset (@see template_preprocess_fieldset). + $variables['wrapper_attributes'] = new Attribute(); + + $variables['children'] = Element::children($element); + $variables['show_select_all_none'] = $element['#bef_select_all_none'] ?? FALSE; + $variables['show_select_all_none_nested'] = $element['#bef_select_all_none_nested'] ?? FALSE; + $variables['display_inline'] = $element['#bef_display_inline'] ?? FALSE; + + // Set element name. + $variables['attributes']['name'] = $element['#name']; + + // Handle nested checkboxes. + if (!empty($variables['element']['#bef_nested'])) { + _bef_preprocess_nested_elements($variables); + } +} + +/** + * Prepares variables for bef-radios template. + * + * Default template: bef-radios.html.twig. + * + * @param array $variables + * An associative array containing: + * - element: An associative array containing the exposed form element. + */ +function template_preprocess_bef_radios(array &$variables) { + $element = &$variables['element']; + + // Create new wrapper attributes since the element attributes will be used + // on the fieldset (@see template_preprocess_fieldset). + $variables['wrapper_attributes'] = new Attribute(); + + $variables['children'] = Element::children($element); + $variables['display_inline'] = $element['#bef_display_inline'] ?? FALSE; + + // Set element name. + $variables['attributes']['name'] = $element['#name']; + + // Handle nested radio buttons. + if (!empty($variables['element']['#bef_nested'])) { + _bef_preprocess_nested_elements($variables); + } +} + +/** + * Prepares variables for bef-hidden template. + * + * Default template: bef-hidden.html.twig. + * + * @param array $variables + * An associative array containing: + * - element: An associative array containing the exposed form element. + */ +function template_preprocess_bef_hidden(array &$variables) { + $element = $variables['element']; + + // This theme function is only used for multi-select elements. + $variables['is_multiple'] = TRUE; + $variables['selected'] = empty($element['#value']) ? $element['#default_value'] : $element['#value']; + $variables['hidden_elements'] = []; + foreach ($element['#options'] as $value => $label) { + $variables['hidden_elements'][$value] = [ + '#type' => 'hidden', + '#value' => $value, + '#name' => $element['#name'] . '[]', + ]; + } + + // @todo: + // Check for optgroups. Put subelements in the $element_set array and add a + // group heading. Otherwise, just add the element to the set. + // $element_set = array(); + // if (is_array($elem)) { + // $element_set = $elem; + // } + // else { + // $element_set[$option] = $elem; + // } +} + +/** + * Prepares variables for bef-links template. + * + * Default template: bef-links.html.twig. + * + * @param array $variables + * An associative array containing: + * - element: An associative array containing the exposed form element. + */ +function template_preprocess_bef_links(array &$variables) { + // Collect some variables before we start tweaking the element. + $element = &$variables['element']; + $options = $element['#options']; + $name = $element['#name']; + + // Set element name. + $variables['attributes']['name'] = $name; + + // Get the query string arguments from the current request. + $existing_query = \Drupal::service('request_stack')->getCurrentRequest()->query->all(); + + // Remove page parameter from query. + unset($existing_query['page']); + + // Store selected values. + $selectedValues = $element['#value']; + if (!is_array($selectedValues)) { + $selectedValues = [$selectedValues]; + } + + $variables['links'] = []; + foreach ($options as $optionValue => $optionLabel) { + // Build a new Url object for each link since the query string changes with + // each option. + /** @var Drupal\Core\Url $url */ + $url = clone($element['#bef_path']); + + // Allow visitors to toggle a filter setting on and off. This is not as + // simple as setOptions('foo', '') as that still leaves an entry which is + // rendered rather than removing the entry from the query string altogether. + // Calling $url->setOption() still leaves a value behind. Instead we work + // with the entire options array and remove items from it as needed. + $urlOptions = $url->getOptions(); + + if ($element['#multiple']) { + $newQuery = isset($existing_query[$name]) ? $existing_query[$name] : []; + if (in_array($optionValue, $selectedValues)) { + // Allow users to toggle an option using the same link. + $newQuery = array_filter($newQuery, function ($value) use ($selectedValues) { + return !in_array($value, $selectedValues); + }); + } + else { + $newQuery[] = $optionValue; + } + if (empty($newQuery)) { + unset($urlOptions['query'][$name]); + } + else { + $urlOptions['query'][$name] = $newQuery; + } + } + else { + if ($optionValue == $element['#value']) { + // Allow toggle link functionality -- click the same link to turn an + // option on or off. + $newQuery = $existing_query; + unset($newQuery[$name]); + if (empty($newQuery)) { + // Remove the query string completely. + unset($urlOptions['query']); + } + else { + $urlOptions['query'] = $newQuery; + } + } + else { + $urlOptions['query'] = $existing_query; + $urlOptions['query'][$name] = $optionValue; + } + } + + // Add our updated options to the Url object. + $url->setOptions($urlOptions); + + // Provide the Twig template with an array of links. + $variables['links'][$optionValue] = [ + '#attributes' => [ + 'id' => Html::getUniqueId('edit-' . implode('-', [$name, $optionValue])), + 'name' => $name . '[' . $optionValue . ']', + 'class' => [ + 'bef-link', + ], + ], + '#type' => 'link', + '#title' => $optionLabel, + '#url' => $url, + ]; + + if (in_array($optionValue, $selectedValues)) { + $variables['links'][$optionValue]['#attributes']['class'][] = 'bef-link--selected'; + } + } + + // Handle nested links. But first add the links as children to the element + // for consistent processing between checkboxes/radio buttons and links. + $variables['element'] = array_replace($variables['element'], $variables['links']); + $variables['children'] = Element::children($variables['element']); + if (!empty($variables['element']['#bef_nested'])) { + _bef_preprocess_nested_elements($variables); + } +} + +/****************************************************************************** + * Utility functions for BEF themed elements. + ******************************************************************************/ + +/** + * Internal function to handled nested form elements. + * + * Adds 'is_nested' and 'depth' $variables. Requires 'children' to be set in + * variables array before being called. + * + * @param array $variables + * An associative array containing: + * - element: An associative array containing the exposed form element. + */ +function _bef_preprocess_nested_elements(array &$variables) { + // Provide a hierarchical info on the element children for the template to + // render as a nested <ul>. Views prepends '-' characters for each level of + // depth in the vocabulary. Store that information, but remove the hyphens as + // we don't want to display them. + $variables['is_nested'] = TRUE; + $variables['depth'] = []; + foreach ($variables['children'] as $child) { + if ($child === 'All') { + // For non-required filters, put the any/all option at the root. + $variables['depth'][$child] = 0; + // And don't change the text as it defaults to "- Any -" and we do not + // want to remove the leading hyphens. + continue; + } + + $original = $variables['element'][$child]['#title']; + $variables['element'][$child]['#title'] = ltrim($original, '-'); + $variables['depth'][$child] = strlen($original) - strlen($variables['element'][$child]['#title']); + } +} diff --git a/web/modules/better_exposed_filters/js/auto_submit.js b/web/modules/better_exposed_filters/js/auto_submit.js index 64fa2cc3c4..f4cf09f3ca 100644 --- a/web/modules/better_exposed_filters/js/auto_submit.js +++ b/web/modules/better_exposed_filters/js/auto_submit.js @@ -1,5 +1,6 @@ /** - * @file auto_submit.js + * @file + * auto_submit.js * * Provides a "form auto-submit" feature for the Better Exposed Filters module. */ @@ -7,7 +8,7 @@ (function ($, Drupal) { /** - * To make a form auto submit, all you have to do is 3 things: + * To make a form auto submit, all you have to do is 3 things:. * * Use the "better_exposed_filters/auto_submit" js library. * @@ -40,54 +41,82 @@ * supported. We probably could use additional support for HTML5 input types. */ Drupal.behaviors.betterExposedFiltersAutoSubmit = { - attach: function(context) { - // e.keyCode: key - var ignoredKeyCodes = [ - 16, // shift - 17, // ctrl - 18, // alt - 20, // caps lock - 33, // page up - 34, // page down - 35, // end - 36, // home - 37, // left arrow - 38, // up arrow - 39, // right arrow - 40, // down arrow - 9, // tab - 13, // enter - 27 // esc - ]; - + attach: function (context) { // When exposed as a block, the form #attributes are moved from the form // to the block element, thus the second selector. // @see \Drupal\block\BlockViewBuilder::preRender var selectors = 'form[data-bef-auto-submit-full-form], [data-bef-auto-submit-full-form] form, [data-bef-auto-submit]'; - function triggerSubmit ($target) { - $target.closest('form').find('[data-bef-auto-submit-click]').click(); - } - // The change event bubbles so we only need to bind it to the outer form // in case of a full form, or a single element when specified explicitly. - $(selectors).addBack(selectors).once('bef-auto-submit').on('change keyup keypress', function (e) { + $(selectors, context).addBack(selectors).each(function (i, e) { + // Store the current form. + var $form = $(e); + + // Retrieve the autosubmit delay for this particular form. + var autoSubmitDelay = $form.data('bef-auto-submit-delay') || 500; + + // Attach event listeners. + $form.once('bef-auto-submit') + // On change, trigger the submit immediately. + .on('change', triggerSubmit) + // On keyup, wait for a specified number of milliseconds before + // triggering autosubmit. Each new keyup event resets the timer. + .on('keyup', Drupal.debounce(triggerSubmit, autoSubmitDelay)); + }); + + /** + * Triggers form autosubmit when conditions are right. + * + * - Checks first that the element that was the target of the triggering + * event is `:text` or `textarea`, but is not `.hasDatePicker`. + * - Checks that the keycode of the keyup was not in the list of ignored + * keys (navigation keys etc). + * + * @param {object} e - The triggering event. + */ + function triggerSubmit(e) { + // e.keyCode: key. + var ignoredKeyCodes = [ + 16, // Shift. + 17, // Ctrl. + 18, // Alt. + 20, // Caps lock. + 33, // Page up. + 34, // Page down. + 35, // End. + 36, // Home. + 37, // Left arrow. + 38, // Up arrow. + 39, // Right arrow. + 40, // Down arrow. + 9, // Tab. + 13, // Enter. + 27 // Esc. + ]; + + // Triggering element. var $target = $(e.target); + var $submit = $target.closest('form').find('[data-bef-auto-submit-click]'); // Don't submit on changes to excluded elements or a submit element. if ($target.is('[data-bef-auto-submit-exclude], :submit')) { return true; } - // Use debounce to prevent excessive submits on text field changes. - // Navigation key presses are ignored. - else if ($target.is(':text:not(.hasDatepicker), textarea') && $.inArray(e.keyCode, ignoredKeyCodes) === -1) { - Drupal.debounce(triggerSubmit, 500)($target); + + // Submit only if this is a non-datepicker textfield and if the + // incoming keycode is not one of the excluded values. + if ( + $target.is(':text:not(.hasDatepicker), textarea') + && $.inArray(e.keyCode, ignoredKeyCodes) === -1 + ) { + $submit.click(); } // Only trigger submit if a change was the trigger (no keyup). else if (e.type === 'change') { - triggerSubmit($target); + $submit.click(); } - }); + } } } diff --git a/web/modules/better_exposed_filters/js/bef_datepickers.js b/web/modules/better_exposed_filters/js/bef_datepickers.js index 36fa448703..658d2ca73a 100644 --- a/web/modules/better_exposed_filters/js/bef_datepickers.js +++ b/web/modules/better_exposed_filters/js/bef_datepickers.js @@ -1,5 +1,6 @@ /** - * @file bef_datepickers.js + * @file + * bef_datepickers.js * * Provides jQueryUI Datepicker integration with Better Exposed Filters. */ @@ -10,13 +11,13 @@ */ Drupal.behaviors.betterExposedFiltersDatePickers = { - attach: function(context, settings) { + attach: function (context, settings) { - // Check for and initialize datepickers + // Check for and initialize datepickers. var befSettings = drupalSettings.better_exposed_filters; if (befSettings && befSettings.datepicker && befSettings.datepicker_options && $.fn.datepicker) { var opt = []; - $.each(befSettings.datepicker_options, function(key, val) { + $.each(befSettings.datepicker_options, function (key, val) { if (key && val) { opt[key] = JSON.parse(val); } @@ -26,4 +27,4 @@ } }; -}) (jQuery, Drupal, drupalSettings); +})(jQuery, Drupal, drupalSettings); diff --git a/web/modules/better_exposed_filters/js/bef_select_all_none.js b/web/modules/better_exposed_filters/js/bef_select_all_none.js index 30d6313c20..ed90cd488a 100644 --- a/web/modules/better_exposed_filters/js/bef_select_all_none.js +++ b/web/modules/better_exposed_filters/js/bef_select_all_none.js @@ -1,8 +1,10 @@ /** - * @file bef_select_all_none.js + * @file + * bef_select_all_none.js * * Adds select all/none toggle functionality to an exposed filter. */ + (function ($) { Drupal.behaviors.betterExposedFiltersSelectAllNone = { attach: function (context) { @@ -14,7 +16,7 @@ var selAll = Drupal.t('Select All'); var selNone = Drupal.t('Select None'); - // Set up a prototype link and event handlers + // Set up a prototype link and event handlers. var link = $('<a class="bef-toggle" href="#">' + selAll + '</a>') link.click(function (event) { // Don't actually follow the link... @@ -22,38 +24,36 @@ event.stopPropagation(); if (selAll == $(this).text()) { - // Select all the checkboxes + // Select all the checkboxes. $(this) .html(selNone) .siblings('.bef-select-all-none, .bef-tree') .find('input:checkbox').each(function () { $(this).prop('checked', true); // @TODO: - //_bef_highlight(this, context); + // _bef_highlight(this, context); }) .end() // attr() doesn't trigger a change event, so we do it ourselves. But just on - // one checkbox otherwise we have many spinning cursors - .find('input[type=checkbox]:first').change() - ; + // one checkbox otherwise we have many spinning cursors. + .find('input[type=checkbox]:first').change(); } else { - // Unselect all the checkboxes + // Unselect all the checkboxes. $(this) .html(selAll) .siblings('.bef-select-all-none, .bef-tree') .find('input:checkbox').each(function () { $(this).prop('checked', false); // @TODO: - //_bef_highlight(this, context); + // _bef_highlight(this, context); }) .end() // attr() doesn't trigger a change event, so we do it ourselves. But just on - // one checkbox otherwise we have many spinning cursors - .find('input[type=checkbox]:first').change() - ; + // one checkbox otherwise we have many spinning cursors. + .find('input[type=checkbox]:first').change(); } }); @@ -61,32 +61,30 @@ selected .addClass('bef-processed') .each(function (index) { - // Clone the link prototype and insert into the DOM + // Clone the link prototype and insert into the DOM. var newLink = link.clone(true); newLink.insertBefore($(this)); - // If all checkboxes are already checked by default then switch to Select None + // If all checkboxes are already checked by default then switch to Select None. if ($('input:checkbox:checked', this).length == $('input:checkbox', this).length) { newLink.text(selNone); } - }) - ; + }); } // @TODO: // Add highlight class to checked checkboxes for better theming - //$('.bef-tree input[type="checkbox"], .bef-checkboxes input[type="checkbox"]') - //// Highlight newly selected checkboxes + // $('.bef-tree input[type="checkbox"], .bef-checkboxes input[type="checkbox"]') + // Highlight newly selected checkboxes // .change(function () { // _bef_highlight(this, context); // }) // .filter(':checked').closest('.form-item', context).addClass('highlight') - //; - + // ; // @TODO: Put this somewhere else... // Check for and initialize datepickers - //if (Drupal.settings.better_exposed_filters.datepicker) { + // if (Drupal.settings.better_exposed_filters.datepicker) { // // Note: JavaScript does not treat "" as null // if (Drupal.settings.better_exposed_filters.datepicker_options.dateformat) { // $('.bef-datepicker').datepicker({ @@ -96,26 +94,25 @@ // else { // $('.bef-datepicker').datepicker(); // } - //} - + // } } // attach: function() { - }; // Drupal.behaviors.better_exposed_filters = { + }; // Drupal.behaviors.better_exposed_filters = {. Drupal.behaviors.betterExposedFiltersAllNoneNested = { attach:function (context, settings) { $('.bef-select-all-none-nested li').has('ul').once('bef-all-none-nested').each(function () { var $this = $(this); // Check/uncheck child terms along with their parent. - $this.find('input:checkbox:first').change(function() { + $this.find('input:checkbox:first').change(function () { $(this).closest('li').find('ul li input:checkbox').prop('checked', this.checked); }); // When a child term is checked or unchecked, set the parent term's // status as needed. - $this.find('ul input:checkbox').change(function() { + $this.find('ul input:checkbox').change(function () { // Determine the number of unchecked sibling checkboxes. var $this = $(this); - var uncheckedSiblings = $this.closest('li').siblings('li').find('> div > input:checkbox:not(:checked)').size(); + var uncheckedSiblings = $this.closest('li').siblings('li').find('> div > input:checkbox:not(:checked)').length; // If this term or any siblings are unchecked, uncheck the parent and // all ancestors. @@ -134,4 +131,4 @@ } } -}) (jQuery); +})(jQuery); diff --git a/web/modules/better_exposed_filters/js/bef_sliders.js b/web/modules/better_exposed_filters/js/bef_sliders.js index 4a1a30dd8a..33dfdec706 100644 --- a/web/modules/better_exposed_filters/js/bef_sliders.js +++ b/web/modules/better_exposed_filters/js/bef_sliders.js @@ -1,36 +1,39 @@ /** - * @file bef_sliders.js + * @file + * bef_sliders.js * - * Adds jQuery Slider functionality to an exposed filter. + * Adds jQuery UI Slider functionality to an exposed filter. */ + (function ($, Drupal, drupalSettings) { Drupal.behaviors.better_exposed_filters_slider = { - attach: function(context, settings) { + attach: function (context, settings) { if (drupalSettings.better_exposed_filters.slider) { $.each(drupalSettings.better_exposed_filters.slider_options, function (i, sliderOptions) { - var id = "input#edit-" + sliderOptions.id; + var data_selector = 'edit-' + sliderOptions.dataSelector; // Collect all possible input fields for this filter. - var $inputs = $(id + ", " + id + "-max, " + id + "-min", context).once('slider-filter'); + var $inputs = $("input[data-drupal-selector=" + data_selector + "], input[data-drupal-selector=" + data_selector + "-max], input[data-drupal-selector=" + data_selector + "-min]", context).once('slider-filter'); - if ($inputs.length == 1) { + // This is a single-value filter. + if ($inputs.length === 1) { // This is a single-value filter. var $input = $($inputs[0]); // Get the default value. We use slider min if there is no default. - var default_value = parseFloat(($input.val() == '') ? sliderOptions.min : $input.val(), 10); + var defaultValue = parseFloat(($input.val() === '') ? sliderOptions.min : $input.val()); // Set the element value in case we are using the slider min. - $input.val(default_value); + $input.val(defaultValue); // Build the HTML and settings for the slider. var slider = $('<div class="bef-slider"></div>').slider({ - min: parseFloat(sliderOptions.min, 10), - max: parseFloat(sliderOptions.max, 10), - step: parseFloat(sliderOptions.step, 10), + min: parseFloat(sliderOptions.min), + max: parseFloat(sliderOptions.max), + step: parseFloat(sliderOptions.step), animate: sliderOptions.animate ? sliderOptions.animate : false, orientation: sliderOptions.orientation, - value: default_value, + value: defaultValue, slide: function (event, ui) { $input.val(ui.value); }, @@ -45,9 +48,9 @@ // Attach stop listeners. stop: function (event, ui) { // Click the auto submit button. - $(this).parents('form').find('.ctools-auto-submit-click').click(); + $(this).parents('form').find('[data-bef-auto-submit-click]').click(); } - }) + }); $input.after(slider); @@ -56,32 +59,29 @@ befUpdateSlider($(this), null, sliderOptions); }); } - else if ($inputs.length == 2) { + else if ($inputs.length === 2) { // This is an in-between or not-in-between filter. Use a range // filter and tie the min and max into the two input elements. var $min = $($inputs[0]), $max = $($inputs[1]), - default_min, - default_max; - - // Get the default values. We use slider min & max if there are - // no defaults. - default_min = parseFloat(($min.val() == '') ? sliderOptions.min : $min.val(), 10); - default_max = parseFloat(($max.val() == '') ? sliderOptions.max : $max.val(), 10); + // Get the default values. We use slider min & max if there are + // no defaults. + defaultMin = parseFloat(($min.val() == '') ? sliderOptions.min : $min.val()), + defaultMax = parseFloat(($max.val() == '') ? sliderOptions.max : $max.val()); // Set the element value in case we are using the slider min & max. - $min.val(default_min); - $max.val(default_max); + $min.val(defaultMin); + $max.val(defaultMax); var slider = $('<div class="bef-slider"></div>').slider({ range: true, - min: parseFloat(sliderOptions.min, 10), - max: parseFloat(sliderOptions.max, 10), - step: parseFloat(sliderOptions.step, 10), + min: parseFloat(sliderOptions.min), + max: parseFloat(sliderOptions.max), + step: parseFloat(sliderOptions.step), animate: sliderOptions.animate ? sliderOptions.animate : false, orientation: sliderOptions.orientation, - values: [default_min, default_max], - // Update the textfields as the sliders are moved + values: [defaultMin, defaultMax], + // Update the textfields as the sliders are moved. slide: function (event, ui) { $min.val(ui.values[0]); $max.val(ui.values[1]); @@ -97,7 +97,7 @@ $max.val(ui.values[1]); }, // Attach stop listeners. - stop: function(event, ui) { + stop: function (event, ui) { // Click the auto submit button. $(this).parents('form').find('.ctools-auto-submit-click').click(); } @@ -106,10 +106,10 @@ $min.after(slider); // Update the slider when the fields are updated. - $min.blur(function() { + $min.blur(function () { befUpdateSlider($(this), 0, sliderOptions); }); - $max.blur(function() { + $max.blur(function () { befUpdateSlider($(this), 1, sliderOptions); }); } @@ -139,17 +139,18 @@ * The options for the current slider. */ function befUpdateSlider($el, valIndex, sliderOptions) { - var val = parseFloat($el.val(), 10), + var val = parseFloat($el.val()), currentMin = $el.parents('div.views-widget').next('.bef-slider').slider('values', 0), currentMax = $el.parents('div.views-widget').next('.bef-slider').slider('values', 1); + // If we have a range slider. if (valIndex != null) { // Make sure the min is not more than the current max value. - if (valIndex == 0 && val > currentMax) { + if (valIndex === 0 && val > currentMax) { val = currentMax; } // Make sure the max is not more than the current max value. - if (valIndex == 1 && val < currentMin) { + if (valIndex === 1 && val < currentMin) { val = currentMin; } // If the number is invalid, go back to the last value. @@ -176,4 +177,4 @@ } } -}) (jQuery, Drupal, drupalSettings); +})(jQuery, Drupal, drupalSettings); diff --git a/web/modules/better_exposed_filters/js/better_exposed_filters.js b/web/modules/better_exposed_filters/js/better_exposed_filters.js index 4722a68e28..a3c39405cf 100644 --- a/web/modules/better_exposed_filters/js/better_exposed_filters.js +++ b/web/modules/better_exposed_filters/js/better_exposed_filters.js @@ -1,19 +1,20 @@ /** - * @file better_exposed_filters.js + * @file + * better_exposed_filters.js * - * Provides some client-side functionality for the Better Exposed Filters module + * Provides some client-side functionality for the Better Exposed Filters module. */ + (function ($, Drupal, drupalSettings) { Drupal.behaviors.betterExposedFilters = { - attach: function(context, settings) { - // Add highlight class to checked checkboxes for better theming + attach: function (context, settings) { + // Add highlight class to checked checkboxes for better theming. $('.bef-tree input[type=checkbox], .bef-checkboxes input[type=checkbox]') - // Highlight newly selected checkboxes - .change(function() { + // Highlight newly selected checkboxes. + .change(function () { _bef_highlight(this, context); }) - .filter(':checked').closest('.form-item', context).addClass('highlight') - ; + .filter(':checked').closest('.form-item', context).addClass('highlight'); } }; @@ -22,7 +23,7 @@ */ /** - * Adds/Removes the highlight class from the form-item div as appropriate + * Adds/Removes the highlight class from the form-item div as appropriate. */ function _bef_highlight(elem, context) { $elem = $(elem, context); @@ -31,4 +32,4 @@ : $elem.closest('.form-item', context).removeClass('highlight'); } -}) (jQuery, Drupal, drupalSettings); +})(jQuery, Drupal, drupalSettings); diff --git a/web/modules/better_exposed_filters/phpcs.xml b/web/modules/better_exposed_filters/phpcs.xml new file mode 100644 index 0000000000..41aa994eea --- /dev/null +++ b/web/modules/better_exposed_filters/phpcs.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<ruleset name="Drupal 8 coding standards"> + <description>Drupal 8 coding standards</description> + + <exclude-pattern>*/.git/*</exclude-pattern> + <exclude-pattern>*/config/*</exclude-pattern> + <exclude-pattern>*/icons/*</exclude-pattern> + <exclude-pattern>*/vendor/*</exclude-pattern> + + <rule ref="Drupal"/> + +</ruleset> diff --git a/web/modules/better_exposed_filters/phpunit.core.xml.dist b/web/modules/better_exposed_filters/phpunit.core.xml.dist new file mode 100644 index 0000000000..a9b007de70 --- /dev/null +++ b/web/modules/better_exposed_filters/phpunit.core.xml.dist @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- This based on Drupal's core phpunit.xml.dist. --> +<!-- TODO set checkForUnintentionallyCoveredCode="true" once https://www.drupal.org/node/2626832 is resolved. --> +<!-- PHPUnit expects functional tests to be run with either a privileged user + or your current system user. See core/tests/README.md and + https://www.drupal.org/node/2116263 for details. +--> +<phpunit bootstrap="tests/bootstrap.php" colors="true" + beStrictAboutTestsThatDoNotTestAnything="true" + beStrictAboutOutputDuringTests="true" + beStrictAboutChangesToGlobalState="true" + convertDeprecationsToExceptions="false"> +<!-- TODO set printerClass="\Drupal\Tests\Listeners\HtmlOutputPrinter" once + https://youtrack.jetbrains.com/issue/WI-24808 is resolved. Drupal provides a + result printer that links to the html output results for functional tests. + Unfortunately, this breaks the output of PHPStorm's PHPUnit runner. However, if + using the command line you can add + - -printer="\Drupal\Tests\Listeners\HtmlOutputPrinter" to use it (note there + should be no spaces between the hyphens). +--> + <php> + <!-- Set error reporting to E_ALL. --> + <ini name="error_reporting" value="32767"/> + <!-- Do not limit the amount of memory tests take to run. --> + <ini name="memory_limit" value="-1"/> + <!-- Example SIMPLETEST_BASE_URL value: http://localhost --> + <env name="SIMPLETEST_BASE_URL" value=""/> + <!-- Example SIMPLETEST_DB value: mysql://username:password@localhost/databasename#table_prefix --> + <env name="SIMPLETEST_DB" value=""/> + <!-- Example BROWSERTEST_OUTPUT_DIRECTORY value: /path/to/webroot/sites/simpletest/browser_output --> + <env name="BROWSERTEST_OUTPUT_DIRECTORY" value=""/> + <!-- To disable deprecation testing completely uncomment the next line. --> + <env name="SYMFONY_DEPRECATIONS_HELPER" value="disabled"/> + <!-- Example for changing the driver class for mink tests MINK_DRIVER_CLASS value: 'Drupal\FunctionalJavascriptTests\DrupalSelenium2Driver' --> + <!-- Example for changing the driver args to mink tests MINK_DRIVER_ARGS value: '["http://127.0.0.1:8510"]' --> + <!-- Example for changing the driver args to phantomjs tests MINK_DRIVER_ARGS_PHANTOMJS value: '["http://127.0.0.1:8510"]' --> + <!-- Example for changing the driver args to webdriver tests MINK_DRIVER_ARGS_WEBDRIVER value: '["firefox", null, "http://localhost:4444/wd/hub"]' --> + </php> + <testsuites> + <testsuite name="unit"> + <file>./tests/TestSuites/UnitTestSuite.php</file> + </testsuite> + <testsuite name="kernel"> + <file>./tests/TestSuites/KernelTestSuite.php</file> + </testsuite> + <testsuite name="functional"> + <file>./tests/TestSuites/FunctionalTestSuite.php</file> + </testsuite> + <testsuite name="nonfunctional"> + <file>./tests/TestSuites/UnitTestSuite.php</file> + <file>./tests/TestSuites/KernelTestSuite.php</file> + </testsuite> + <testsuite name="functional-javascript"> + <file>./tests/TestSuites/FunctionalJavascriptTestSuite.php</file> + </testsuite> + </testsuites> + <listeners> + <listener class="\Drupal\Tests\Listeners\DrupalListener"> + </listener> + <!-- The Symfony deprecation listener has to come after the Drupal listener --> + <listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener"> + </listener> + </listeners> + <!-- Filter for coverage reports. --> + <filter> + <whitelist processUncoveredFilesFromWhitelist="true"> + <directory>../modules/better_exposed_filters</directory> + <!-- By definition test classes have no tests. --> + <exclude> + <directory>../modules/better_exposed_filters/tests</directory> + </exclude> + </whitelist> + </filter> +</phpunit> diff --git a/web/modules/better_exposed_filters/src/Annotation/BetterExposedFiltersFilterWidget.php b/web/modules/better_exposed_filters/src/Annotation/BetterExposedFiltersFilterWidget.php new file mode 100644 index 0000000000..7c56ca16fe --- /dev/null +++ b/web/modules/better_exposed_filters/src/Annotation/BetterExposedFiltersFilterWidget.php @@ -0,0 +1,34 @@ +<?php + +namespace Drupal\better_exposed_filters\Annotation; + +use Drupal\Component\Annotation\Plugin; + +/** + * Defines a Better exposed filters widget item annotation object. + * + * @see \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersFilterWidgetManager + * @see plugin_api + * + * @Annotation + */ +class BetterExposedFiltersFilterWidget extends Plugin { + + + /** + * The plugin ID. + * + * @var string + */ + public $id; + + /** + * The label of the plugin. + * + * @var \Drupal\Core\Annotation\Translation + * + * @ingroup plugin_translatable + */ + public $label; + +} diff --git a/web/modules/better_exposed_filters/src/Annotation/BetterExposedFiltersPagerWidget.php b/web/modules/better_exposed_filters/src/Annotation/BetterExposedFiltersPagerWidget.php new file mode 100644 index 0000000000..d5ce55b991 --- /dev/null +++ b/web/modules/better_exposed_filters/src/Annotation/BetterExposedFiltersPagerWidget.php @@ -0,0 +1,34 @@ +<?php + +namespace Drupal\better_exposed_filters\Annotation; + +use Drupal\Component\Annotation\Plugin; + +/** + * Defines a Better exposed filters widget item annotation object. + * + * @see \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetManager + * @see plugin_api + * + * @Annotation + */ +class BetterExposedFiltersPagerWidget extends Plugin { + + + /** + * The plugin ID. + * + * @var string + */ + public $id; + + /** + * The label of the plugin. + * + * @var \Drupal\Core\Annotation\Translation + * + * @ingroup plugin_translatable + */ + public $label; + +} diff --git a/web/modules/better_exposed_filters/src/Annotation/BetterExposedFiltersSortWidget.php b/web/modules/better_exposed_filters/src/Annotation/BetterExposedFiltersSortWidget.php new file mode 100644 index 0000000000..24e3940d62 --- /dev/null +++ b/web/modules/better_exposed_filters/src/Annotation/BetterExposedFiltersSortWidget.php @@ -0,0 +1,34 @@ +<?php + +namespace Drupal\better_exposed_filters\Annotation; + +use Drupal\Component\Annotation\Plugin; + +/** + * Defines a Better exposed filters sort widget item annotation object. + * + * @see \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersSortWidgetManager + * @see plugin_api + * + * @Annotation + */ +class BetterExposedFiltersSortWidget extends Plugin { + + + /** + * The plugin ID. + * + * @var string + */ + public $id; + + /** + * The label of the plugin. + * + * @var \Drupal\Core\Annotation\Translation + * + * @ingroup plugin_translatable + */ + public $label; + +} diff --git a/web/modules/better_exposed_filters/src/BetterExposedFiltersHelper.php b/web/modules/better_exposed_filters/src/BetterExposedFiltersHelper.php new file mode 100644 index 0000000000..048a5c6560 --- /dev/null +++ b/web/modules/better_exposed_filters/src/BetterExposedFiltersHelper.php @@ -0,0 +1,203 @@ +<?php + +namespace Drupal\better_exposed_filters; + +/** + * Defines a helper class for better exposed filters. + */ +class BetterExposedFiltersHelper { + + /** + * Rewrites a set of options given a string from the config form. + * + * Rewrites should be specified, one per line, using the format + * old_string|new_string. If new_string is empty, the option will be removed. + * + * @param array $options + * An array of key => value pairs that may be rewritten. + * @param string $rewrite_settings + * String representing the entry in the settings form. + * @param bool $reorder + * Reorder $options based on the rewrite settings. + * + * @return array + * Rewritten $options. + */ + public static function rewriteOptions(array $options, $rewrite_settings, $reorder = FALSE) { + // Break out early if we don't have anything to rewrite. + if (empty($rewrite_settings) || !is_string($rewrite_settings)) { + return $options; + } + + $rewrites = []; + $order = []; + $return = []; + + // Get a copy of the option, flattened with their keys preserved. + $flat_options = self::flattenOptions($options, TRUE); + + // Preserve order. + if (!$reorder) { + $order = array_keys($options); + } + + $lines = explode("\n", trim($rewrite_settings)); + foreach ($lines as $line) { + list($search, $replace) = array_map('trim', explode('|', $line)); + if (!empty($search)) { + $rewrites[$search] = $replace; + + // Find the key of the option we need to reorder. + if ($reorder) { + $key = array_search($search, $flat_options); + if ($key !== FALSE) { + $order[] = $key; + } + } + } + } + + // Reorder options in the order they are specified in rewrites. + // Collect the keys to use later. + if ($reorder && !empty($order)) { + // Start with the items that were listed in the rewrite settings. + foreach ($order as $key) { + $return[$key] = $options[$key]; + unset($options[$key]); + } + + // Tack remaining options on the end. + $return += $options; + } + else { + $return = $options; + } + + // Rewrite the option value. + foreach ($return as $index => &$choice) { + if (is_object($choice) && isset($choice->option)) { + $key = key($choice->option); + $value = &$choice->option[$key]; + } + else { + $choice = (string) $choice; + $value = &$choice; + } + + if (!is_scalar($value)) { + // We give up... + continue; + } + + if (isset($rewrites[$value])) { + if ('' === $rewrites[$value]) { + unset($return[$index]); + } + else { + $value = $rewrites[$value]; + } + } + } + return $return; + } + + /** + * Flattens list of mixed options into a simple array of scalar value. + * + * @param array $options + * List of mixed options - scalar, translatable markup or taxonomy term + * options. + * @param bool $preserve_keys + * TRUE if the original keys should be preserved, FALSE otherwise. + * + * @return array + * Flattened list of scalar options. + */ + public static function flattenOptions(array $options, $preserve_keys = FALSE) { + $flat_options = []; + + foreach ($options as $key => $choice) { + if (is_array($choice)) { + $flat_options = array_merge($flat_options, self::flattenOptions($choice)); + } + elseif (is_object($choice) && isset($choice->option)) { + $key = $preserve_keys ? $key : key($choice->option); + $flat_options[$key] = current($choice->option); + } + else { + $flat_options[$key] = (string) $choice; + } + } + return $flat_options; + } + + /** + * Sort options alphabetically. + * + * @param array $options + * Array of unsorted options - scalar, translatable markup or taxonomy term + * options. + * + * @return array + * Alphabetically sorted array of original values. + */ + public static function sortOptions(array $options) { + // Flatten array of mixed values to a simple array of scalar values. + $flat_options = self::flattenOptions($options, TRUE); + + // Alphabetically sort our list of concatenated values. + asort($flat_options); + // Now use its keys to sort the original array. + return array_replace(array_flip(array_keys($flat_options)), $options); + } + + /** + * Sort nested options alphabetically. + * + * @param array $options + * Array of nested unsorted options - scalar, translatable markup or + * taxonomy term options. + * @param string $delimiter + * The delimiter used to indicate nested level. (e.g. -Seattle) + * + * @return array + * Alphabetically sorted array of original values. + */ + public static function sortNestedOptions(array $options, $delimiter = '-') { + // Flatten array of mixed values to a simple array of scalar values. + $flat_options = self::flattenOptions($options, TRUE); + $prev_key = NULL; + $level = 0; + $parent = [$level => '']; + + // Iterate over each option. + foreach ($flat_options as $key => &$choice) { + // For each option, determine the nested level based on the delimiter. + // Example: + // - 'United States' will have level 0. + // - '-Seattle' will have level 1. + $cur_level = strlen($choice) - strlen(ltrim($choice, $delimiter)); + + // If we are going down a level, keep track of its parent value. + if ($cur_level > $level) { + $parent[$cur_level] = $flat_options[$prev_key]; + } + + // Prepend each option value with its parent for easier sorting. + // Example: + // '-Seattle' is below 'United States', its concatenated value will become + // 'United States-Seattle' etc... + $choice = $parent[$cur_level] . $choice; + + // Update level and prev_key. + $level = $cur_level; + $prev_key = $key; + } + + // Alphabetically sort our list of concatenated values. + asort($flat_options); + // Now use its keys to sort the original array. + return array_replace(array_flip(array_keys($flat_options)), $options); + } + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/BetterExposedFiltersWidgetBase.php b/web/modules/better_exposed_filters/src/Plugin/BetterExposedFiltersWidgetBase.php new file mode 100644 index 0000000000..cc2246f7dc --- /dev/null +++ b/web/modules/better_exposed_filters/src/Plugin/BetterExposedFiltersWidgetBase.php @@ -0,0 +1,198 @@ +<?php + +namespace Drupal\better_exposed_filters\Plugin; + +use Drupal\Component\Plugin\PluginBase; +use Drupal\Component\Utility\NestedArray; +use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\Core\Url; +use Drupal\views\Plugin\views\ViewsHandlerInterface; +use Drupal\views\ViewExecutable; + +/** + * Base class for Better exposed filters widget plugins. + */ +abstract class BetterExposedFiltersWidgetBase extends PluginBase implements BetterExposedFiltersWidgetInterface { + + use StringTranslationTrait; + + /** + * The views executable object. + * + * @var \Drupal\views\ViewExecutable + */ + protected $view; + + /** + * The views plugin this configuration will affect when exposed. + * + * @var \Drupal\views\Plugin\views\ViewsHandlerInterface + */ + protected $handler; + + /** + * {@inheritdoc} + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + + $this->configuration = NestedArray::mergeDeep($this->defaultConfiguration(), $configuration); + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return [ + 'plugin_id' => $this->pluginId, + ]; + } + + /** + * {@inheritdoc} + */ + public function getConfiguration() { + return $this->configuration; + } + + /** + * {@inheritdoc} + */ + public function setConfiguration(array $configuration) { + $this->configuration = $configuration; + } + + /** + * {@inheritdoc} + */ + public function setView(ViewExecutable $view) { + $this->view = $view; + } + + /** + * {@inheritdoc} + */ + public function setViewsHandler(ViewsHandlerInterface $handler) { + $this->handler = $handler; + } + + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { + // Validation is optional. + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + // Apply submitted form state to configuration. + $values = $form_state->getValues(); + foreach ($values as $key => $value) { + if (array_key_exists($key, $this->configuration)) { + $this->configuration[$key] = $value; + } + else { + // Remove from form state. + unset($values[$key]); + } + } + } + + /* + * Helper functions. + */ + + /** + * Sets metadata on the form elements for easier processing. + * + * @param array $element + * The form element to apply the metadata to. + * + * @see ://www.drupal.org/project/drupal/issues/2511548 + */ + protected function addContext(array &$element) { + $element['#context'] = [ + '#plugin_type' => 'bef', + '#plugin_id' => $this->pluginId, + '#view_id' => $this->view->id(), + '#display_id' => $this->view->current_display, + ]; + } + + /** + * Moves an exposed form element into a field group. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * Exposed views form state. + * @param string $element + * The key of the form element. + * @param string $group + * The name of the group element. + * + * @throws \Drupal\Component\Plugin\Exception\PluginException + * If the instance cannot be created, such as if the ID is invalid. + */ + protected function addElementToGroup(array &$form, FormStateInterface $form_state, $element, $group) { + // Ensure group is enabled. + $form[$group]['#access'] = TRUE; + + // Add element to group. + $form[$element]['#group'] = $group; + + // Persist state of collapsible field-sets with active elements. + if (empty($form[$group]['#open'])) { + // Use raw user input to determine if field-set should be open or closed. + $user_input = $form_state->getUserInput()[$element] ?? [0]; + // Take multiple values into account. + if (!is_array($user_input)) { + $user_input = [$user_input]; + } + + // Check if one or more values are set for our current element. + $options = $form[$element]['#options'] ?? []; + $default_value = $form[$element]['#default_value'] ?? key($options); + $has_values = array_reduce($user_input, function ($carry, $value) use ($form, $element, $default_value) { + return $carry || ($value === $default_value ? '' : ($value || $default_value === 0)); + }, FALSE); + + if ($has_values) { + $form[$group]['#open'] = TRUE; + } + } + } + + /** + * Returns exposed form action URL object. + * + * @param \Drupal\Core\Form\FormStateInterface $form_state + * Exposed views form state. + * + * @return \Drupal\Core\Url + * Url object. + */ + protected function getExposedFormActionUrl(FormStateInterface $form_state) { + /** @var \Drupal\views\ViewExecutable $view */ + $view = $form_state->get('view'); + $display = $form_state->get('display'); + + if (isset($display['display_options']['path'])) { + return Url::fromRoute(implode('.', [ + 'view', + $view->id(), + $display['id'], + ])); + } + + $request = \Drupal::request(); + $url = Url::createFromRequest(clone $request); + $url->setAbsolute(); + + return $url; + } + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/BetterExposedFiltersWidgetInterface.php b/web/modules/better_exposed_filters/src/Plugin/BetterExposedFiltersWidgetInterface.php new file mode 100644 index 0000000000..61ec777a24 --- /dev/null +++ b/web/modules/better_exposed_filters/src/Plugin/BetterExposedFiltersWidgetInterface.php @@ -0,0 +1,56 @@ +<?php + +namespace Drupal\better_exposed_filters\Plugin; + +use Drupal\Component\Plugin\ConfigurableInterface; +use Drupal\Component\Plugin\PluginInspectionInterface; +use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Plugin\PluginFormInterface; +use Drupal\views\Plugin\views\ViewsHandlerInterface; +use Drupal\views\ViewExecutable; + +/** + * Defines an interface for Better exposed filters filter widget plugins. + */ +interface BetterExposedFiltersWidgetInterface extends PluginFormInterface, PluginInspectionInterface, ConfigurableInterface { + + /** + * Sets the view object. + * + * @param \Drupal\views\ViewExecutable $view + * The views executable object. + */ + public function setView(ViewExecutable $view); + + /** + * Sets the exposed view handler plugin. + * + * @param \Drupal\views\Plugin\views\ViewsHandlerInterface $handler + * The views handler plugin this configuration will affect when exposed. + */ + public function setViewsHandler(ViewsHandlerInterface $handler); + + /** + * Verify this plugin can be used on the form element. + * + * @param mixed $handler + * The handler type we are altering (e.g. filter, pager, sort). + * @param array $options + * The options for this handler. + * + * @return bool + * If this plugin can be used. + */ + public static function isApplicable($handler = NULL, array $options = []); + + /** + * Manipulate views exposed from element. + * + * @param array $form + * The views configuration form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * Form state. + */ + public function exposedFormAlter(array &$form, FormStateInterface $form_state); + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/BetterExposedFiltersWidgetManager.php b/web/modules/better_exposed_filters/src/Plugin/BetterExposedFiltersWidgetManager.php new file mode 100644 index 0000000000..540d30e162 --- /dev/null +++ b/web/modules/better_exposed_filters/src/Plugin/BetterExposedFiltersWidgetManager.php @@ -0,0 +1,45 @@ +<?php + +namespace Drupal\better_exposed_filters\Plugin; + +use Drupal\Core\Plugin\DefaultPluginManager; +use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\Extension\ModuleHandlerInterface; +use Symfony\Component\DependencyInjection\Container; + +/** + * Provides the Better exposed filters widget plugin manager. + */ +class BetterExposedFiltersWidgetManager extends DefaultPluginManager { + + /** + * The widget type. + * + * @var string + */ + protected $type; + + /** + * Constructs a new BetterExposedFiltersFilterWidgetManager object. + * + * @param string $type + * The plugin type, for example filter, pager or sort. + * @param \Traversable $namespaces + * An object that implements \Traversable which contains the root paths + * keyed by the corresponding namespace to look for plugin implementations. + * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend + * Cache backend instance to use. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler to invoke the alter hook with. + */ + public function __construct($type, \Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) { + $plugin_interface = 'Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface'; + $plugin_definition_annotation_name = 'Drupal\better_exposed_filters\Annotation\BetterExposedFilters' . Container::camelize($type) . 'Widget'; + parent::__construct("Plugin/better_exposed_filters/$type", $namespaces, $module_handler, $plugin_interface, $plugin_definition_annotation_name); + + $this->type = $type; + $this->alterInfo('better_exposed_filters_better_exposed_filters_' . $type . '_widget_info'); + $this->setCacheBackend($cache_backend, 'better_exposed_filters:' . $type . '_widget'); + } + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/DatePickers.php b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/DatePickers.php new file mode 100644 index 0000000000..7633a73f26 --- /dev/null +++ b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/DatePickers.php @@ -0,0 +1,189 @@ +<?php + +namespace Drupal\better_exposed_filters\Plugin\better_exposed_filters\filter; + +use Drupal\Core\Form\FormStateInterface; + +/** + * JQuery UI date picker widget implementation. + * + * @BetterExposedFiltersFilterWidget( + * id = "bef_datepicker", + * label = @Translation("jQuery UI Date Picker"), + * ) + */ +class DatePickers extends FilterWidgetBase { + + /** + * {@inheritdoc} + */ + public static function isApplicable($filter = NULL, array $filter_options = []) { + /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase $filter */ + $is_applicable = FALSE; + + if ((is_a($filter, 'Drupal\views\Plugin\views\filter\Date') || !empty($filter->date_handler)) && !$filter->isAGroup()) { + $is_applicable = TRUE; + } + + return $is_applicable; + } + + /** + * {@inheritdoc} + */ + public function exposedFormAlter(array &$form, FormStateInterface $form_state) { + $field_id = $this->getExposedFilterFieldId(); + + parent::exposedFormAlter($form, $form_state); + + // Attach the JS (@see /js/datepickers.js) + $form['#attached']['library'][] = 'better_exposed_filters/datepickers'; + + // Date picker settings. + $form[$field_id]['#attached']['drupalSettings']['better_exposed_filters']['datepicker'] = TRUE; + $form[$field_id]['#attached']['drupalSettings']['better_exposed_filters']['datepicker_options'] = []; + $drupal_settings = &$form[$field_id]['#attached']['drupalSettings']['better_exposed_filters']['datepicker_options']; + + // Single Date API-based input element. + $is_single_date = isset($form[$field_id]['value']['#type']) + && 'date_text' == $form[$field_id]['value']['#type']; + // Double Date-API-based input elements such as "in-between". + $is_double_date = isset($form[$field_id]['min']) && isset($form[$field_id]['max']) + && 'date_text' == $form[$field_id]['min']['#type'] + && 'date_text' == $form[$field_id]['max']['#type']; + + if ($is_single_date || $is_double_date) { + if (isset($form[$field_id]['value'])) { + $format = $form[$field_id]['value']['#date_format']; + $form[$field_id]['value']['#attributes']['class'][] = 'bef-datepicker'; + } + else { + // Both min and max share the same format. + $format = $form[$field_id]['min']['#date_format']; + $form[$field_id]['min']['#attributes']['class'][] = 'bef-datepicker'; + $form[$field_id]['max']['#attributes']['class'][] = 'bef-datepicker'; + } + + // Convert Date API format to jQuery UI date format. + $mapping = $this->getjQueryUiDateFormatting(); + $drupal_settings['dateformat'] = str_replace(array_keys($mapping), array_values($mapping), $format); + } + else { + /* + * Standard Drupal date field. Depending on the settings, the field + * can be at $form[$field_id] (single field) or + * $form[$field_id][subfield] for two-value date fields or filters + * with exposed operators. + */ + $fields = ['min', 'max', 'value']; + if (count(array_intersect($fields, array_keys($form[$field_id])))) { + foreach ($fields as $field) { + if (isset($form[$field_id][$field])) { + $form[$field_id][$field]['#attributes']['class'][] = 'bef-datepicker'; + } + } + } + else { + $form[$field_id]['#attributes']['class'][] = 'bef-datepicker'; + } + } + } + + /** + * Convert Date API formatting to jQuery formatDate formatting. + * + * @TODO: To be honest, I'm not sure this is needed. Can you set a + * Date API field to accept anything other than Y-m-d? Well, better + * safe than sorry... + * + * @see http://us3.php.net/manual/en/function.date.php + * @see http://docs.jquery.com/UI/Datepicker/formatDate + * + * @return array + * PHP date format => jQuery formatDate format + * (comments are for the PHP format, lines that are commented out do + * not have a jQuery formatDate equivalent, but maybe someday they + * will...) + */ + private function getjQueryUiDateFormatting() { + return [ + /* Day */ + // Day of the month, 2 digits with leading zeros 01 to 31. + 'd' => 'dd', + // A textual representation of a day, three letters Mon through + // Sun. + 'D' => 'D', + // Day of the month without leading zeros 1 to 31. + 'j' => 'd', + // (lowercase 'L') A full textual representation of the day of the + // week Sunday through Saturday. + 'l' => 'DD', + // ISO-8601 numeric representation of the day of the week (added + // in PHP 5.1.0) 1 (for Monday) through 7 (for Sunday). + // 'N' => ' ', + // English ordinal suffix for the day of the month, 2 characters + // st, nd, rd or th. Works well with j. + // 'S' => ' ', + // Numeric representation of the day of the week 0 (for Sunday) + // through 6 (for Saturday). + // 'w' => ' ', + // The day of the year (starting from 0) 0 through 365. + 'z' => 'o', + /* Week */ + // ISO-8601 week number of year, weeks starting on Monday (added + // in PHP 4.1.0) Example: 42 (the 42nd week in the year). + // 'W' => ' ',. + /* Month */ + // A full textual representation of a month, such as January or + // March January through December. + 'F' => 'MM', + // Numeric representation of a month, with leading zeros 01 + // through 12. + 'm' => 'mm', + // A short textual representation of a month, three letters Jan + // through Dec. + 'M' => 'M', + // Numeric representation of a month, without leading zeros 1 + // through 12. + 'n' => 'm', + // Number of days in the given month 28 through 31. + // 't' => ' ',. + /* Year */ + // Whether it's a leap year 1 if it is a leap year, 0 otherwise. + // 'L' => ' ', + // ISO-8601 year number. This has the same value as Y, except that + // if the ISO week number (W) belongs to the previous or next + // year, that year is used instead. (added in PHP 5.1.0). + // Examples: 1999 or 2003. + // 'o' => ' ', + // A full numeric representation of a year, 4 digits Examples: + // 1999 or 2003. + 'Y' => 'yy', + // A two digit representation of a year Examples: 99 or 03. + 'y' => 'y', + + /* Time */ + // Lowercase Ante meridiem and Post meridiem am or pm. + // 'a' => ' ', + // Uppercase Ante meridiem and Post meridiem AM or PM. + // 'A' => ' ', + // Swatch Internet time 000 through 999. + // 'B' => ' ', + // 12-hour format of an hour without leading zeros 1 through 12. + // 'g' => ' ', + // 24-hour format of an hour without leading zeros 0 through 23. + // 'G' => ' ', + // 12-hour format of an hour with leading zeros 01 through 12. + // 'h' => ' ', + // 24-hour format of an hour with leading zeros 00 through 23. + // 'H' => ' ', + // Minutes with leading zeros 00 to 59. + // 'i' => ' ', + // Seconds, with leading zeros 00 through 59. + // 's' => ' ', + // Microseconds (added in PHP 5.2.2) Example: 654321. + // 'u' => ' ',. + ]; + } + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/DefaultWidget.php b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/DefaultWidget.php new file mode 100644 index 0000000000..2a5ec2a7b3 --- /dev/null +++ b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/DefaultWidget.php @@ -0,0 +1,24 @@ +<?php + +namespace Drupal\better_exposed_filters\Plugin\better_exposed_filters\filter; + +use Drupal\Core\Form\FormStateInterface; + +/** + * Default widget implementation. + * + * @BetterExposedFiltersFilterWidget( + * id = "default", + * label = @Translation("Default"), + * ) + */ +class DefaultWidget extends FilterWidgetBase { + + /** + * {@inheritdoc} + */ + public static function isApplicable($filter = NULL, array $filter_options = []) { + return TRUE; + } + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/FilterWidgetBase.php b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/FilterWidgetBase.php new file mode 100644 index 0000000000..460532e5e8 --- /dev/null +++ b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/FilterWidgetBase.php @@ -0,0 +1,331 @@ +<?php + +namespace Drupal\better_exposed_filters\Plugin\better_exposed_filters\filter; + +use Drupal\better_exposed_filters\BetterExposedFiltersHelper; +use Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetBase; +use Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface; +use Drupal\Core\Form\FormState; +use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\views\Plugin\views\filter\NumericFilter; +use Drupal\views\Plugin\views\filter\StringFilter; + +/** + * Base class for Better exposed filters widget plugins. + */ +abstract class FilterWidgetBase extends BetterExposedFiltersWidgetBase implements BetterExposedFiltersWidgetInterface { + + use StringTranslationTrait; + + /** + * {@inheritdoc} + */ + public static function isApplicable($filter = NULL, array $filter_options = []) { + /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase $filter */ + $is_applicable = FALSE; + + // Sanity check to ensure we have a filter to work with. + if (!isset($filter)) { + return $is_applicable; + } + + // Check various filter types and determine what options are available. + if (is_a($filter, 'Drupal\views\Plugin\views\filter\String') || is_a($filter, 'Drupal\views\Plugin\views\filter\InOperator')) { + if (in_array($filter->operator, ['in', 'or', 'and', 'not'])) { + $is_applicable = TRUE; + } + if (in_array($filter->operator, ['empty', 'not empty'])) { + $is_applicable = TRUE; + } + } + + if (is_a($filter, 'Drupal\views\Plugin\views\filter\BooleanOperator')) { + $is_applicable = TRUE; + } + + if (is_a($filter, 'Drupal\taxonomy\Plugin\views\filter\TaxonomyIndexTid')) { + // Autocomplete and dropdown taxonomy filter are both instances of + // TaxonomyIndexTid, but we can't show BEF options for the autocomplete + // widget. + if ($filter_options['type'] == 'select') { + $is_applicable = TRUE; + } + } + + if ($filter->isAGroup()) { + $is_applicable = TRUE; + } + + return $is_applicable; + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return parent::defaultConfiguration() + [ + 'advanced' => [ + 'collapsible' => FALSE, + 'is_secondary' => FALSE, + 'placeholder_text' => '', + 'rewrite' => [ + 'filter_rewrite_values' => '', + ], + 'sort_options' => FALSE, + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase $filter */ + $filter = $this->handler; + $filter_widget_type = $this->getExposedFilterWidgetType(); + + $form['advanced'] = [ + '#type' => 'details', + '#title' => $this->t('Advanced filter options'), + '#weight' => 10, + ]; + + // Allow users to sort options. + $supported_types = ['select']; + if (in_array($filter_widget_type, $supported_types)) { + $form['advanced']['sort_options'] = [ + '#type' => 'checkbox', + '#title' => 'Sort filter options', + '#default_value' => !empty($this->configuration['advanced']['sort_options']), + '#description' => $this->t('The options will be sorted alphabetically.'), + ]; + } + + // Allow users to specify placeholder text. + $supported_types = ['entity_autocomplete', 'textfield']; + if (in_array($filter_widget_type, $supported_types)) { + $form['advanced']['placeholder_text'] = [ + '#type' => 'textfield', + '#title' => $this->t('Placeholder text'), + '#description' => $this->t('Text to be shown in the text field until it is edited. Leave blank for no placeholder to be set.'), + '#default_value' => $this->configuration['advanced']['placeholder_text'], + ]; + } + + // Allow rewriting of filter options for any filter. String and numeric + // filters allow unlimited filter options via textfields, so we can't + // offer rewriting for those. + // @TODO: check other core filter types + if ((!$filter instanceof StringFilter && !$filter instanceof NumericFilter) || $filter->isAGroup()) { + $form['advanced']['rewrite']['filter_rewrite_values'] = [ + '#type' => 'textarea', + '#title' => $this->t('Rewrite the text displayed'), + '#default_value' => $this->configuration['advanced']['rewrite']['filter_rewrite_values'], + '#description' => $this->t('Use this field to rewrite the filter options displayed. Use the format of current_text|replacement_text, one replacement per line. For example: <pre> + Current|Replacement + On|Yes + Off|No + </pre> Leave the replacement text blank to remove an option altogether. If using hierarchical taxonomy filters, do not including leading hyphens in the current text. + '), + ]; + } + + // Allow any filter to be collapsible. + $form['advanced']['collapsible'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Make filter options collapsible'), + '#default_value' => !empty($this->configuration['advanced']['collapsible']), + '#description' => $this->t( + 'Puts the filter options in a collapsible details element.' + ), + ]; + + // Allow any filter to be moved into the secondary options element. + $form['advanced']['is_secondary'] = [ + '#type' => 'checkbox', + '#title' => $this->t('This is a secondary option'), + '#default_value' => !empty($this->configuration['advanced']['is_secondary']), + '#states' => [ + 'visible' => [ + ':input[name="exposed_form_options[bef][general][allow_secondary]"]' => ['checked' => TRUE], + ], + ], + '#description' => $this->t('Places this element in the secondary options portion of the exposed form.'), + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function exposedFormAlter(array &$form, FormStateInterface $form_state) { + /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase $filter */ + $filter = $this->handler; + $filter_id = $filter->options['expose']['identifier']; + $field_id = $this->getExposedFilterFieldId(); + $is_collapsible = $this->configuration['advanced']['collapsible']; + $is_secondary = !empty($form['secondary']) && $this->configuration['advanced']['is_secondary']; + + // Sort options alphabetically. + if ($this->configuration['advanced']['sort_options']) { + $form[$field_id]['#nested'] = $filter->options['hierarchy'] ?? FALSE; + $form[$field_id]['#nested_delimiter'] = '-'; + $form[$field_id]['#pre_process'][] = [$this, 'processSortedOptions']; + } + + // Check for placeholder text. + if (!empty($this->configuration['advanced']['placeholder_text'])) { + // @todo: Add token replacement for placeholder text. + $form[$field_id]['#placeholder'] = $this->configuration['advanced']['placeholder_text']; + } + + // Handle filter value rewrites. + if ($this->configuration['advanced']['rewrite']['filter_rewrite_values']) { + $form[$field_id]['#options'] = BetterExposedFiltersHelper::rewriteOptions($form[$field_id]['#options'], $this->configuration['advanced']['rewrite']['filter_rewrite_values']); + // @todo what is $selected? + // if (isset($selected) && !isset($form[$field_id]['#options'][$selected])) { + // Avoid "Illegal choice" errors. + // $form[$field_id]['#default_value'] = NULL; + // } + } + + // Identify all exposed filter elements. + $identifier = $filter_id; + $exposed_label = $filter->options['expose']['label']; + $exposed_description = $filter->options['expose']['description']; + + if ($filter->isAGroup()) { + $identifier = $filter->options['group_info']['identifier']; + $exposed_label = $filter->options['group_info']['label']; + $exposed_description = $filter->options['group_info']['description']; + } + + // If selected, collect our collapsible filter form element and put it in + // a details element. + if ($is_collapsible) { + $form[$field_id . '_collapsible'] = [ + '#type' => 'details', + '#title' => $exposed_label, + ]; + + if ($is_secondary) { + // Move secondary elements. + $form[$field_id . '_collapsible']['#group'] = 'secondary'; + } + } + + $filter_elements = [ + $identifier, + $filter->options['expose']['operator_id'], + ]; + + // Iterate over all exposed filter elements. + foreach ($filter_elements as $element) { + // Sanity check to make sure the element exists. + if (empty($form[$element])) { + continue; + } + + // Move collapsible elements. + if ($is_collapsible) { + $this->addElementToGroup($form, $form_state, $element, $field_id . '_collapsible'); + } + else { + $form[$element]['#title'] = $exposed_label; + $form[$element]['#description'] = $exposed_description; + + // Move secondary elements. + if ($is_secondary) { + $this->addElementToGroup($form, $form_state, $element, 'secondary'); + } + } + + // Finally, add some metadata to the form element. + $this->addContext($form[$element]); + } + } + + /** + * Sorts the options for a given form element alphabetically. + * + * @param array $element + * The form element. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * Form state. + * + * @return array + * The altered element. + */ + public function processSortedOptions(array $element, FormStateInterface $form_state) { + $options = &$element['#options']; + + // Ensure "- Any -" value does not get sorted. + $any_option = FALSE; + if (empty($element['#required'])) { + // We use array_slice to preserve they keys needed to determine the value + // when using a filter (e.g. taxonomy terms). + $any_option = array_slice($options, 0, 1, TRUE); + // Array_slice does not modify the existing array, we need to remove the + // option manually. + unset($options[key($any_option)]); + } + + // Not all option arrays will have simple data types. We perform a custom + // sort in case users want to sort more complex fields (e.g taxonomy terms). + if (!empty($element['#nested'])) { + $delimiter = $element['#nested_delimiter'] ?? '-'; + $options = BetterExposedFiltersHelper::sortNestedOptions($options, $delimiter); + } + else { + $options = BetterExposedFiltersHelper::sortOptions($options); + } + + // Restore the "- Any -" value at the first position. + if ($any_option) { + $options = $any_option + $options; + } + + return $element; + } + + /** + * Helper function to get the unique identifier for the exposed filter. + * + * Takes into account grouped filters with custom identifiers. + */ + protected function getExposedFilterFieldId() { + /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase $filter */ + $filter = $this->handler; + $field_id = $filter->options['expose']['identifier']; + $is_grouped_filter = $filter->options['is_grouped'] ?: FALSE; + + // Grouped filters store their identifier elsewhere. + if ($is_grouped_filter) { + $field_id = $filter->options['group_info']['identifier']; + } + + return $field_id; + } + + /** + * Helper function to get the widget type of the exposed filter. + * + * @return string + * The type of the form render element use for the exposed filter. + */ + protected function getExposedFilterWidgetType() { + // We need to dig into the exposed form configuration to retrieve the + // form type of the filter. + $form = []; + $form_state = new FormState(); + $form_state->set('exposed', TRUE); + /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase $filter */ + $filter = $this->handler; + $filter_id = $filter->options['expose']['identifier']; + + return $form[$filter_id]['#type'] ?? $form[$filter_id]['value']['#type'] ?? ''; + } + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/Hidden.php b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/Hidden.php new file mode 100644 index 0000000000..892c933fc3 --- /dev/null +++ b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/Hidden.php @@ -0,0 +1,40 @@ +<?php + +namespace Drupal\better_exposed_filters\Plugin\better_exposed_filters\filter; + +use Drupal\Core\Form\FormStateInterface; + +/** + * Default widget implementation. + * + * @BetterExposedFiltersFilterWidget( + * id = "bef_hidden", + * label = @Translation("Hidden"), + * ) + */ +class Hidden extends FilterWidgetBase { + + /** + * {@inheritdoc} + */ + public function exposedFormAlter(array &$form, FormStateInterface $form_state) { + $field_id = $this->getExposedFilterFieldId(); + + parent::exposedFormAlter($form, $form_state); + + if (empty($form[$field_id]['#multiple'])) { + // Single entry filters can simply be changed to a different element + // type. + $form[$field_id]['#type'] = 'hidden'; + } + else { + // Hide the label. + $form['#info']["filter-$field_id"]['label'] = ''; + $form[$field_id]['#title'] = ''; + + // Use BEF's preprocess and template to output the hidden elements. + $form[$field_id]['#theme'] = 'bef_hidden'; + } + } + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/Links.php b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/Links.php new file mode 100644 index 0000000000..358503109e --- /dev/null +++ b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/Links.php @@ -0,0 +1,78 @@ +<?php + +namespace Drupal\better_exposed_filters\Plugin\better_exposed_filters\filter; + +use Drupal\better_exposed_filters\BetterExposedFiltersHelper; +use Drupal\Core\Form\FormStateInterface; + +/** + * Default widget implementation. + * + * @BetterExposedFiltersFilterWidget( + * id = "bef_links", + * label = @Translation("Links"), + * ) + */ +class Links extends FilterWidgetBase { + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return parent::defaultConfiguration() + [ + 'select_all_none' => FALSE, + ]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase $filter */ + $filter = $this->handler; + + $form = parent::buildConfigurationForm($form, $form_state); + + $form['select_all_none'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Add select all/none links'), + '#default_value' => !empty($this->configuration['select_all_none']), + '#disabled' => !$filter->options['expose']['multiple'], + '#description' => $this->t('Add a "Select All/None" link when rendering the exposed filter using checkboxes. If this option is disabled, edit the filter and check the "Allow multiple selections".' + ), + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function exposedFormAlter(array &$form, FormStateInterface $form_state) { + /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase $filter */ + $filter = $this->handler; + $field_id = $this->getExposedFilterFieldId(); + + parent::exposedFormAlter($form, $form_state); + + if (!empty($form[$field_id])) { + // Clean up filters that pass objects as options instead of strings. + if (!empty($form[$field_id]['#options'])) { + $form[$field_id]['#options'] = BetterExposedFiltersHelper::flattenOptions($form[$field_id]['#options']); + } + + // Support rendering hierarchical links (e.g. taxonomy terms). + if (!empty($filter->options['hierarchy'])) { + $form[$field_id]['#bef_nested'] = TRUE; + } + + $form[$field_id]['#theme'] = 'bef_links'; + // Exposed form displayed as blocks can appear on pages other than + // the view results appear on. This can cause problems with + // select_as_links options as they will use the wrong path. We + // provide a hint for theme functions to correct this. + $form[$field_id]['#bef_path'] = $this->getExposedFormActionUrl($form_state); + } + } + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/RadioButtons.php b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/RadioButtons.php new file mode 100644 index 0000000000..4c4a7420ac --- /dev/null +++ b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/RadioButtons.php @@ -0,0 +1,114 @@ +<?php + +namespace Drupal\better_exposed_filters\Plugin\better_exposed_filters\filter; + +use Drupal\better_exposed_filters\BetterExposedFiltersHelper; +use Drupal\Core\Form\FormStateInterface; + +/** + * Default widget implementation. + * + * @BetterExposedFiltersFilterWidget( + * id = "bef", + * label = @Translation("Checkboxes/Radio Buttons"), + * ) + */ +class RadioButtons extends FilterWidgetBase { + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return parent::defaultConfiguration() + [ + 'select_all_none' => FALSE, + 'select_all_none_nested' => FALSE, + 'display_inline' => FALSE, + ]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase $filter */ + $filter = $this->handler; + + $form = parent::buildConfigurationForm($form, $form_state); + + $form['select_all_none'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Add select all/none links'), + '#default_value' => !empty($this->configuration['select_all_none']), + '#disabled' => !$filter->options['expose']['multiple'], + '#description' => $this->t('Add a "Select All/None" link when rendering the exposed filter using checkboxes. If this option is disabled, edit the filter and check the "Allow multiple selections".' + ), + ]; + + $form['select_all_none_nested'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Add nested all/none selection'), + '#default_value' => !empty($this->configuration['select_all_none_nested']), + '#disabled' => (!$filter->options['expose']['multiple']) || (isset($filter->options['hierarchy']) && !$filter->options['hierarchy']), + '#description' => $this->t('When a parent checkbox is checked, check all its children. If this option is disabled, edit the filter and check "Allow multiple selections" and edit the filter settings and check "Show hierarchy in dropdown".' + ), + ]; + + $form['display_inline'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Display inline'), + '#default_value' => !empty($this->configuration['display_inline']), + '#description' => $this->t('Display checkbox/radio options inline.' + ), + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function exposedFormAlter(array &$form, FormStateInterface $form_state) { + /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase $filter */ + $filter = $this->handler; + // Form element is designated by the element ID which is user- + // configurable. + $field_id = $filter->options['is_grouped'] ? $filter->options['group_info']['identifier'] : $filter->options['expose']['identifier']; + + parent::exposedFormAlter($form, $form_state); + + if (!empty($form[$field_id])) { + // Clean up filters that pass objects as options instead of strings. + if (!empty($form[$field_id]['#options'])) { + $form[$field_id]['#options'] = BetterExposedFiltersHelper::flattenOptions($form[$field_id]['#options']); + } + + // Support rendering hierarchical checkboxes/radio buttons (e.g. taxonomy + // terms). + if (!empty($filter->options['hierarchy'])) { + $form[$field_id]['#bef_nested'] = TRUE; + } + + // Display inline. + $form[$field_id]['#bef_display_inline'] = $this->configuration['display_inline']; + + // Render as checkboxes if filter allows multiple selections. + if (!empty($form[$field_id]['#multiple'])) { + $form[$field_id]['#theme'] = 'bef_checkboxes'; + $form[$field_id]['#type'] = 'checkboxes'; + + // Show all/none option. + $form[$field_id]['#bef_select_all_none'] = $this->configuration['select_all_none']; + $form[$field_id]['#bef_select_all_none_nested'] = $this->configuration['select_all_none_nested']; + + // Attach the JS (@see /js/bef_select_all_none.js) + $form['#attached']['library'][] = 'better_exposed_filters/select_all_none'; + } + // Else render as radio buttons. + else { + $form[$field_id]['#theme'] = 'bef_radios'; + $form[$field_id]['#type'] = 'radios'; + } + } + } + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/Single.php b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/Single.php new file mode 100644 index 0000000000..f2bc094fbd --- /dev/null +++ b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/Single.php @@ -0,0 +1,75 @@ +<?php + +namespace Drupal\better_exposed_filters\Plugin\better_exposed_filters\filter; + +use Drupal\Core\Form\FormStateInterface; + +/** + * Single on/off widget implementation. + * + * @BetterExposedFiltersFilterWidget( + * id = "bef_single", + * label = @Translation("Single On/Off Checkbox"), + * ) + */ +class Single extends FilterWidgetBase { + + /** + * {@inheritdoc} + */ + public static function isApplicable($filter = NULL, array $filter_options = []) { + /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase $filter */ + $is_applicable = FALSE; + + // Sanity check to ensure we have a filter to work with. + if (!isset($filter)) { + return $is_applicable; + } + + if (is_a($filter, 'Drupal\views\Plugin\views\filter\BooleanOperator') || ($filter->isAGroup() && count($filter->options['group_info']['group_items']) == 1)) { + $is_applicable = TRUE; + } + + return $is_applicable; + } + + /** + * {@inheritdoc} + */ + public function exposedFormAlter(array &$form, FormStateInterface $form_state) { + /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase $filter */ + $filter = $this->handler; + // Form element is designated by the element ID which is user- + // configurable, and stored differently for grouped filters. + $exposed_id = $filter->options['expose']['identifier']; + $field_id = $this->getExposedFilterFieldId(); + + parent::exposedFormAlter($form, $form_state); + + if (!empty($form[$field_id])) { + // Views populates missing values in $form_state['input'] with the + // defaults and a checkbox does not appear in $_GET (or $_POST) so it + // will appear to be missing when a user submits a form. Because of + // this, instead of unchecking the checkbox value will revert to the + // default. More, the default value for select values (i.e. 'Any') is + // reused which results in the checkbox always checked. + $input = $form_state->getUserInput(); + // The input value ID is not always consistent. + // Prioritize the field ID, but default to exposed ID. + // @todo Remove $exposed_id once + // https://www.drupal.org/project/drupal/issues/288429 is fixed. + $input_value = isset($input[$field_id]) ? $input[$field_id] : (isset($input[$exposed_id]) ? $input[$exposed_id] : NULL); + $checked = FALSE; + // We need to be super careful when working with raw input values. Let's + // make sure the value exists in our list of possible options. + if (in_array($input_value, array_keys($form[$field_id]['#options'])) && $input_value !== 'All') { + $checked = (bool) $input_value; + } + $form[$field_id]['#type'] = 'checkbox'; + $form[$field_id]['#default_value'] = 0; + $form[$field_id]['#return_value'] = 1; + $form[$field_id]['#value'] = $checked ? 1 : 0; + } + } + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/Sliders.php b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/Sliders.php new file mode 100644 index 0000000000..fbf0455c9b --- /dev/null +++ b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/filter/Sliders.php @@ -0,0 +1,187 @@ +<?php + +namespace Drupal\better_exposed_filters\Plugin\better_exposed_filters\filter; + +use Drupal\Component\Utility\Html; +use Drupal\Core\Form\FormStateInterface; + +/** + * JQuery UI slider widget implementation. + * + * @BetterExposedFiltersFilterWidget( + * id = "bef_sliders", + * label = @Translation("jQuery UI Slider"), + * ) + */ +class Sliders extends FilterWidgetBase { + + // Slider animation options. + const ANIMATE_NONE = 'none'; + const ANIMATE_SLOW = 'slow'; + const ANIMATE_NORMAL = 'normal'; + const ANIMATE_FAST = 'fast'; + const ANIMATE_CUSTOM = 'custom'; + + // Slider orientation options. + const ORIENTATION_HORIZONTAL = 'horizontal'; + const ORIENTATION_VERTICAL = 'vertical'; + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return parent::defaultConfiguration() + [ + 'min' => 0, + 'max' => 99999, + 'step' => 1, + 'animate' => self::ANIMATE_NONE, + 'animate_ms' => 0, + 'orientation' => self::ORIENTATION_HORIZONTAL, + ]; + } + + /** + * {@inheritdoc} + */ + public static function isApplicable($filter = NULL, array $filter_options = []) { + /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase $filter */ + $is_applicable = FALSE; + + // The date filter handler extends the numeric filter handler so we have + // to exclude it specifically. + $is_numeric_filter = is_a($filter, 'Drupal\views\Plugin\views\filter\NumericFilter'); + $is_range_filter = is_a($filter, 'Drupal\range\Plugin\views\filter\Range'); + $is_date_filter = is_a($filter, 'Drupal\views\Plugin\views\filter\Date'); + if (($is_numeric_filter || $is_range_filter) && !$is_date_filter && !$filter->isAGroup()) { + $is_applicable = TRUE; + } + + return $is_applicable; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase $filter */ + $filter = $this->handler; + + $form = parent::buildConfigurationForm($form, $form_state); + + $form['min'] = [ + '#type' => 'number', + '#title' => $this->t('Range minimum'), + '#default_value' => $this->configuration['min'], + '#description' => $this->t('The minimum allowed value for the jQuery range slider. It can be positive, negative, or zero and have up to 11 decimal places.'), + ]; + + $form['max'] = [ + '#type' => 'number', + '#title' => $this->t('Range maximum'), + '#default_value' => $this->configuration['max'], + '#description' => $this->t('The maximum allowed value for the jQuery range slider. It can be positive, negative, or zero and have up to 11 decimal places.'), + ]; + + $form['step'] = [ + '#type' => 'number', + '#title' => $this->t('Step'), + '#default_value' => $this->configuration['step'], + '#description' => $this->t('Determines the size or amount of each interval or step the slider takes between the min and max.') . '<br />' . $this->t('The full specified value range of the slider (Range maximum - Range minimum) must be evenly divisible by the step.') . '<br />' . $this->t('The step must be a positive number of up to 5 decimal places.'), + '#min' => 0, + ]; + + $form['animate'] = [ + '#type' => 'select', + '#title' => $this->t('Animation speed'), + '#options' => [ + self::ANIMATE_NONE => $this->t('None'), + self::ANIMATE_SLOW => $this->t('Slow'), + self::ANIMATE_NORMAL => $this->t('Normal'), + self::ANIMATE_FAST => $this->t('Fast'), + self::ANIMATE_CUSTOM => $this->t('Custom'), + ], + '#default_value' => $this->configuration['animate'], + '#description' => $this->t('Whether to slide handle smoothly when user click outside handle on the bar.'), + ]; + + $form['animate_ms'] = [ + '#type' => 'number', + '#title' => $this->t('Animation speed in milliseconds'), + '#default_value' => $this->configuration['animate_ms'], + '#description' => $this->t('The number of milliseconds to run the animation (e.g. 1000).'), + '#states' => [ + 'visible' => [ + ':input[name="exposed_form_options[bef][filter][' . $filter->field . '][configuration][animate]"]' => ['value' => self::ANIMATE_CUSTOM], + ], + ], + ]; + + $form['orientation'] = [ + '#type' => 'select', + '#title' => $this->t('Orientation'), + '#options' => [ + self::ORIENTATION_HORIZONTAL => $this->t('Horizontal'), + self::ORIENTATION_VERTICAL => $this->t('Vertical'), + ], + '#default_value' => $this->configuration['orientation'], + '#description' => $this->t('The orientation of the jQuery range slider.'), + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { + parent::validateConfigurationForm($form, $form_state); + + // Max must be > min. + $min = $form_state->getValue('min'); + $max = $form_state->getValue('max'); + if ($max <= $min) { + $form_state->setError($form['max'], $this->t('The slider max value must be greater than the slider min value.')); + } + + // Step must have: + // - No more than 5 decimal places. + // - Slider range must be evenly divisible by step. + $step = $form_state->getValue('step'); + if (strlen(substr(strrchr((string) $step, '.'), 1)) > 5) { + $form_state->setError($form['step'], $this->t('The slider step option for %name cannot have more than 5 decimal places.')); + } + + // Very small step and a vary large range can go beyond the max value of + // an int in PHP. Thus we look for a decimal point when casting the result + // to a string. + if (strpos((string) ($max - $min) / $step, '.')) { + $form_state->setError($form['step'], $this->t('The slider range must be evenly divisible by the step option.')); + } + } + + /** + * {@inheritdoc} + */ + public function exposedFormAlter(array &$form, FormStateInterface $form_state) { + $field_id = $this->getExposedFilterFieldId(); + + parent::exposedFormAlter($form, $form_state); + + // Attach the JS (@see /js/sliders.js) + $form[$field_id]['#attached']['library'][] = 'better_exposed_filters/sliders'; + + // Set the slider settings. + $form[$field_id]['#attached']['drupalSettings']['better_exposed_filters']['slider'] = TRUE; + $form[$field_id]['#attached']['drupalSettings']['better_exposed_filters']['slider_options'][$field_id] = [ + 'min' => $this->configuration['min'], + 'max' => $this->configuration['max'], + 'step' => $this->configuration['step'], + 'animate' => ($this->configuration['animate'] === self::ANIMATE_CUSTOM) ? $this->configuration['animate_ms'] : $this->configuration['animate'], + 'orientation' => $this->configuration['orientation'], + 'id' => Html::getUniqueId($field_id), + 'dataSelector' => Html::getId($field_id), + 'viewId' => $form['#id'], + ]; + } + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/pager/DefaultWidget.php b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/pager/DefaultWidget.php new file mode 100644 index 0000000000..841711d1ef --- /dev/null +++ b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/pager/DefaultWidget.php @@ -0,0 +1,15 @@ +<?php + +namespace Drupal\better_exposed_filters\Plugin\better_exposed_filters\pager; + +/** + * Default widget implementation. + * + * @BetterExposedFiltersPagerWidget( + * id = "default", + * label = @Translation("Default"), + * ) + */ +class DefaultWidget extends PagerWidgetBase { + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/pager/Links.php b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/pager/Links.php new file mode 100644 index 0000000000..9ec7ba4bb1 --- /dev/null +++ b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/pager/Links.php @@ -0,0 +1,35 @@ +<?php + +namespace Drupal\better_exposed_filters\Plugin\better_exposed_filters\pager; + +use Drupal\Core\Form\FormStateInterface; + +/** + * Radio Buttons pager widget implementation. + * + * @BetterExposedFiltersPagerWidget( + * id = "bef_links", + * label = @Translation("Links"), + * ) + */ +class Links extends PagerWidgetBase { + + /** + * {@inheritdoc} + */ + public function exposedFormAlter(array &$form, FormStateInterface $form_state) { + parent::exposedFormAlter($form, $form_state); + + if (!empty($form['items_per_page'] && count($form['items_per_page']['#options']) > 1)) { + $form['items_per_page']['#theme'] = 'bef_links'; + $form['items_per_page']['#items_per_page'] = max($form['items_per_page']['#default_value'], key($form['items_per_page']['#options'])); + + // Exposed form displayed as blocks can appear on pages other than + // the view results appear on. This can cause problems with + // select_as_links options as they will use the wrong path. We + // provide a hint for theme functions to correct this. + $form['items_per_page']['#bef_path'] = $this->getExposedFormActionUrl($form_state); + } + } + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/pager/PagerWidgetBase.php b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/pager/PagerWidgetBase.php new file mode 100644 index 0000000000..18923fa7b4 --- /dev/null +++ b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/pager/PagerWidgetBase.php @@ -0,0 +1,87 @@ +<?php + +namespace Drupal\better_exposed_filters\Plugin\better_exposed_filters\pager; + +use Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetBase; +use Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface; +use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\StringTranslation\StringTranslationTrait; + +/** + * Base class for Better exposed pager widget plugins. + */ +abstract class PagerWidgetBase extends BetterExposedFiltersWidgetBase implements BetterExposedFiltersWidgetInterface { + + use StringTranslationTrait; + + /** + * List of available exposed sort form element keys. + * + * @var array + */ + protected $pagerElements = [ + 'items_per_page', + 'offset', + ]; + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return parent::defaultConfiguration() + [ + 'advanced' => [ + 'is_secondary' => FALSE, + ], + ]; + } + + /** + * {@inheritdoc} + */ + public static function isApplicable($handler = NULL, array $options = []) { + return TRUE; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form = []; + + $form['advanced']['is_secondary'] = [ + '#type' => 'checkbox', + '#title' => $this->t('This is a secondary option'), + '#default_value' => !empty($this->configuration['advanced']['is_secondary']), + '#states' => [ + 'visible' => [ + ':input[name="exposed_form_options[bef][general][allow_secondary]"]' => ['checked' => TRUE], + ], + ], + '#description' => $this->t('Places this element in the secondary options portion of the exposed form.'), + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function exposedFormAlter(array &$form, FormStateInterface $form_state) { + $is_secondary = !empty($form['secondary']) && $this->configuration['advanced']['is_secondary']; + + foreach ($this->pagerElements as $element) { + // Sanity check to make sure the element exists. + if (empty($form[$element])) { + continue; + } + + if ($is_secondary) { + $this->addElementToGroup($form, $form_state, $element, 'secondary'); + } + + // Finally, add some metadata to the form element. + $this->addContext($form[$element]); + } + } + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/pager/RadioButtons.php b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/pager/RadioButtons.php new file mode 100644 index 0000000000..fa3b2030d7 --- /dev/null +++ b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/pager/RadioButtons.php @@ -0,0 +1,30 @@ +<?php + +namespace Drupal\better_exposed_filters\Plugin\better_exposed_filters\pager; + +use Drupal\Core\Form\FormStateInterface; + +/** + * Radio Buttons pager widget implementation. + * + * @BetterExposedFiltersPagerWidget( + * id = "bef", + * label = @Translation("Radio Buttons"), + * ) + */ +class RadioButtons extends PagerWidgetBase { + + /** + * {@inheritdoc} + */ + public function exposedFormAlter(array &$form, FormStateInterface $form_state) { + parent::exposedFormAlter($form, $form_state); + + if (!empty($form['items_per_page'])) { + $form['items_per_page']['#type'] = 'radios'; + $form['items_per_page']['#prefix'] = '<div class="bef-sortby bef-select-as-radios">'; + $form['items_per_page']['#suffix'] = '</div>'; + } + } + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/sort/DefaultWidget.php b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/sort/DefaultWidget.php new file mode 100644 index 0000000000..9b917d5172 --- /dev/null +++ b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/sort/DefaultWidget.php @@ -0,0 +1,30 @@ +<?php + +namespace Drupal\better_exposed_filters\Plugin\better_exposed_filters\sort; + +use Drupal\Core\Form\FormStateInterface; + +/** + * Default widget implementation. + * + * @BetterExposedFiltersSortWidget( + * id = "default", + * label = @Translation("Default"), + * ) + */ +class DefaultWidget extends SortWidgetBase { + + /** + * {@inheritdoc} + */ + public function exposedFormAlter(array &$form, FormStateInterface $form_state) { + parent::exposedFormAlter($form, $form_state); + + foreach ($this->sortElements as $element) { + if (!empty($form[$element])) { + $form[$element]['#type'] = 'select'; + } + } + } + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/sort/Links.php b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/sort/Links.php new file mode 100644 index 0000000000..85530bc6b7 --- /dev/null +++ b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/sort/Links.php @@ -0,0 +1,36 @@ +<?php + +namespace Drupal\better_exposed_filters\Plugin\better_exposed_filters\sort; + +use Drupal\Core\Form\FormStateInterface; + +/** + * Radio Buttons sort widget implementation. + * + * @BetterExposedFiltersSortWidget( + * id = "bef_links", + * label = @Translation("Links"), + * ) + */ +class Links extends SortWidgetBase { + + /** + * {@inheritdoc} + */ + public function exposedFormAlter(array &$form, FormStateInterface $form_state) { + parent::exposedFormAlter($form, $form_state); + + foreach ($this->sortElements as $element) { + if (!empty($form[$element])) { + $form[$element]['#theme'] = 'bef_links'; + + // Exposed form displayed as blocks can appear on pages other than + // the view results appear on. This can cause problems with + // select_as_links options as they will use the wrong path. We + // provide a hint for theme functions to correct this. + $form[$element]['#bef_path'] = $this->getExposedFormActionUrl($form_state); + } + } + } + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/sort/RadioButtons.php b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/sort/RadioButtons.php new file mode 100644 index 0000000000..5e39c5c349 --- /dev/null +++ b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/sort/RadioButtons.php @@ -0,0 +1,31 @@ +<?php + +namespace Drupal\better_exposed_filters\Plugin\better_exposed_filters\sort; + +use Drupal\Core\Form\FormStateInterface; + +/** + * Radio Buttons sort widget implementation. + * + * @BetterExposedFiltersSortWidget( + * id = "bef", + * label = @Translation("Radio Buttons"), + * ) + */ +class RadioButtons extends SortWidgetBase { + + /** + * {@inheritdoc} + */ + public function exposedFormAlter(array &$form, FormStateInterface $form_state) { + parent::exposedFormAlter($form, $form_state); + + foreach ($this->sortElements as $element) { + if (!empty($form[$element])) { + $form[$element]['#theme'] = 'bef_radios'; + $form[$element]['#type'] = 'radios'; + } + } + } + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/sort/SortWidgetBase.php b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/sort/SortWidgetBase.php new file mode 100644 index 0000000000..5c3b309442 --- /dev/null +++ b/web/modules/better_exposed_filters/src/Plugin/better_exposed_filters/sort/SortWidgetBase.php @@ -0,0 +1,278 @@ +<?php + +namespace Drupal\better_exposed_filters\Plugin\better_exposed_filters\sort; + +use Drupal\better_exposed_filters\BetterExposedFiltersHelper; +use Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetBase; +use Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface; +use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\StringTranslation\StringTranslationTrait; + +/** + * Base class for Better exposed pager widget plugins. + */ +abstract class SortWidgetBase extends BetterExposedFiltersWidgetBase implements BetterExposedFiltersWidgetInterface { + + use StringTranslationTrait; + + /** + * List of available exposed sort form element keys. + * + * @var array + */ + protected $sortElements = [ + 'sort_bef_combine', + 'sort_by', + 'sort_order', + ]; + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return parent::defaultConfiguration() + [ + 'advanced' => [ + 'collapsible' => FALSE, + 'collapsible_label' => $this->t('Sort options'), + 'combine' => FALSE, + 'combine_rewrite' => '', + 'is_secondary' => FALSE, + 'reset' => FALSE, + 'reset_label' => '', + ], + ]; + } + + /** + * {@inheritdoc} + */ + public static function isApplicable($handler = NULL, array $options = []) { + return TRUE; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form = []; + + $form['advanced'] = [ + '#type' => 'details', + '#title' => $this->t('Advanced sort options'), + ]; + + // We can only combine sort order and sort by if both options are exposed. + $form['advanced']['combine'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Combine sort order with sort by'), + '#default_value' => !empty($this->configuration['advanced']['combine']), + '#description' => $this->t('Combines the sort by options and order (ascending or decending) into a single list. Use this to display "Option1 (ascending)", "Option1 (descending)", "Option2 (ascending)", "Option2 (descending)" in a single form element. Sort order should first be exposed by selecting <em>Allow people to choose the sort order</em>.'), + '#states' => [ + 'enabled' => [ + ':input[name="exposed_form_options[expose_sort_order]"]' => ['checked' => TRUE], + ], + ], + ]; + + $form['advanced']['combine_rewrite'] = [ + '#type' => 'textarea', + '#title' => $this->t('Rewrite the text displayed'), + '#default_value' => $this->configuration['advanced']['combine_rewrite'], + '#description' => $this->t('Use this field to rewrite the text displayed for combined sort options and sort order. Use the format of current_text|replacement_text, one replacement per line. For example: <pre> +Post date Asc|Oldest first +Post date Desc|Newest first +Title Asc|A -> Z +Title Desc|Z -> A</pre> Leave the replacement text blank to remove an option altogether. The order the options appear will be changed to match the order of options in this field.'), + '#states' => [ + 'visible' => [ + ':input[name="exposed_form_options[bef][sort][configuration][advanced][combine]"]' => ['checked' => TRUE], + ], + ], + ]; + + $form['advanced']['reset'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Include a "Reset sort" option'), + '#default_value' => !empty($this->configuration['advanced']['reset']), + '#description' => $this->t('Adds a "Reset sort" link; Views will use the default sort order.'), + ]; + + $form['advanced']['reset_label'] = [ + '#type' => 'textfield', + '#title' => $this->t('"Reset sort" label'), + '#default_value' => $this->configuration['advanced']['reset_label'], + '#description' => $this->t('This cannot be left blank if the above option is checked'), + '#states' => [ + 'visible' => [ + ':input[name="exposed_form_options[bef][sort][configuration][advanced][reset]"]' => ['checked' => TRUE], + ], + 'required' => [ + ':input[name="exposed_form_options[bef][sort][configuration][advanced][reset]"]' => ['checked' => TRUE], + ], + ], + ]; + + $form['advanced']['collapsible'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Make sort options collapsible'), + '#default_value' => !empty($this->configuration['advanced']['collapsible']), + '#description' => $this->t( + 'Puts the sort options in a collapsible details element.' + ), + ]; + + $form['advanced']['collapsible_label'] = [ + '#type' => 'textfield', + '#title' => $this->t('Collapsible details element title'), + '#default_value' => $this->configuration['advanced']['collapsible_label'], + '#description' => $this->t('This cannot be left blank or there will be no way to show/hide sort options.'), + '#states' => [ + 'visible' => [ + ':input[name="exposed_form_options[bef][sort][configuration][advanced][collapsible]"]' => ['checked' => TRUE], + ], + 'required' => [ + ':input[name="exposed_form_options[bef][sort][configuration][advanced][collapsible]"]' => ['checked' => TRUE], + ], + ], + ]; + + $form['advanced']['is_secondary'] = [ + '#type' => 'checkbox', + '#title' => $this->t('This is a secondary option'), + '#default_value' => !empty($this->configuration['advanced']['is_secondary']), + '#states' => [ + 'visible' => [ + ':input[name="exposed_form_options[bef][general][allow_secondary]"]' => ['checked' => TRUE], + ], + ], + '#description' => $this->t('Places this element in the secondary options portion of the exposed form.'), + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function exposedFormAlter(array &$form, FormStateInterface $form_state) { + $is_collapsible = $this->configuration['advanced']['collapsible'] + && !empty($this->configuration['advanced']['collapsible_label']); + $is_secondary = !empty($form['secondary']) && $this->configuration['advanced']['is_secondary']; + + // Check for combined sort_by and sort_order. + if ($this->configuration['advanced']['combine'] && !empty($form['sort_order'])) { + $options = []; + $selected = ''; + + foreach ($form['sort_by']['#options'] as $by_key => $by_val) { + foreach ($form['sort_order']['#options'] as $order_key => $order_val) { + // Use a space to separate the two keys, we'll unpack them in our + // submit handler. + $options[$by_key . '_' . $order_key] = "$by_val $order_val"; + + if ($form['sort_order']['#default_value'] === $order_key && empty($selected)) { + // Respect default sort order set in Views. The default sort field + // will be the first one if there are multiple sort criteria. + $selected = $by_key . '_' . $order_key; + } + } + } + + // Rewrite the option values if any were specified. + if (!empty($this->configuration['advanced']['combine_rewrite'])) { + $options = BetterExposedFiltersHelper::rewriteOptions($options, $this->configuration['advanced']['combine_rewrite'], TRUE); + if (!isset($options[$selected])) { + // Avoid "illegal choice" errors if the selected option is + // eliminated by the rewrite. + $selected = NULL; + } + } + + // Add reset sort option at the top of the list. + if ($this->configuration['advanced']['reset']) { + $options = [' ' => $this->configuration['advanced']['reset_label']] + $options; + } + + $form['sort_bef_combine'] = [ + '#type' => 'select', + '#options' => $options, + '#default_value' => $selected, + // Already sanitized by Views. + '#title' => $form['sort_by']['#title'], + ]; + + // Add our submit routine to process. + $form['#submit'][] = [$this, 'sortCombineSubmitForm']; + + // Pretend we're another exposed form widget. + $form['#info']['sort-sort_bef_combine'] = [ + 'value' => 'sort_bef_combine', + ]; + + // Remove the existing sort_by and sort_order elements. + unset($form['sort_by']); + unset($form['sort_order']); + } + else { + // Add reset sort option if selected. + if ($this->configuration['advanced']['reset']) { + array_unshift($form['sort_by']['#options'], $this->configuration['advanced']['reset_label']); + } + } + + // If selected, collect all sort-related form elements and put them in a + // details element. + if ($is_collapsible) { + $form['bef_sort_options'] = [ + '#type' => 'details', + '#title' => $this->configuration['advanced']['collapsible_label'], + ]; + + if ($is_secondary) { + // Move secondary elements. + $form['bef_sort_options']['#group'] = 'secondary'; + } + } + + // Iterate over all exposed sort elements. + foreach ($this->sortElements as $element) { + // Sanity check to make sure the element exists. + if (empty($form[$element])) { + continue; + } + + // Move collapsible elements. + if ($is_collapsible) { + $this->addElementToGroup($form, $form_state, $element, 'bef_sort_options'); + } + // Move secondary elements. + elseif ($is_secondary) { + $this->addElementToGroup($form, $form_state, $element, 'secondary'); + } + + // Finally, add some metadata to the form element. + $this->addContext($form[$element]); + } + } + + /** + * Unpacks sort_by and sort_order from the sort_bef_combine element. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + public function sortCombineSubmitForm(array $form, FormStateInterface $form_state) { + $sort_by = $sort_order = ''; + $combined = $form_state->getValue('sort_bef_combine'); + if (!empty($combined)) { + $parts = explode('_', $combined); + $sort_order = trim(array_pop($parts)); + $sort_by = trim(implode('_', $parts)); + } + $form_state->setValue('sort_by', $sort_by); + $form_state->setValue('sort_order', $sort_order); + } + +} diff --git a/web/modules/better_exposed_filters/src/Plugin/views/exposed_form/BetterExposedFilters.php b/web/modules/better_exposed_filters/src/Plugin/views/exposed_form/BetterExposedFilters.php index f4ce19d18c..710e43a8f9 100644 --- a/web/modules/better_exposed_filters/src/Plugin/views/exposed_form/BetterExposedFilters.php +++ b/web/modules/better_exposed_filters/src/Plugin/views/exposed_form/BetterExposedFilters.php @@ -2,14 +2,15 @@ namespace Drupal\better_exposed_filters\Plugin\views\exposed_form; -use Drupal\Component\Utility\Html; -use Drupal\Core\Form\FormState; +use Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetManager; +use Drupal\Component\Utility\NestedArray; +use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Form\SubformState; use Drupal\Core\Render\Element; use Drupal\Core\Url; use Drupal\views\Plugin\views\exposed_form\InputRequired; -use Drupal\views\Plugin\views\filter\NumericFilter; -use Drupal\views\Plugin\views\filter\StringFilter; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Exposed form plugin that provides a basic exposed form. @@ -25,614 +26,684 @@ class BetterExposedFilters extends InputRequired { /** - * @inheritdoc + * BEF filters widget plugin manager. + * + * @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetManager + */ + public $filterWidgetManager; + + /** + * BEF pager widget plugin manager. + * + * @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetManager + */ + public $pagerWidgetManager; + + /** + * BEF sort widget plugin manager. + * + * @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetManager + */ + public $sortWidgetManager; + + /** + * The module handler. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + + /** + * BetterExposedFilters constructor. + * + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin_id for the plugin instance. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetManager $filter_widget_manager + * The better exposed filter widget manager for filter widgets. + * @param \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetManager $pager_widget_manager + * The better exposed filter widget manager for pager widgets. + * @param \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetManager $sort_widget_manager + * The better exposed filter widget manager for sort widgets. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * Manage drupal modules. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, BetterExposedFiltersWidgetManager $filter_widget_manager, BetterExposedFiltersWidgetManager $pager_widget_manager, BetterExposedFiltersWidgetManager $sort_widget_manager, ModuleHandlerInterface $module_handler) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->filterWidgetManager = $filter_widget_manager; + $this->pagerWidgetManager = $pager_widget_manager; + $this->sortWidgetManager = $sort_widget_manager; + $this->moduleHandler = $module_handler; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('plugin.manager.better_exposed_filters_filter_widget'), + $container->get('plugin.manager.better_exposed_filters_pager_widget'), + $container->get('plugin.manager.better_exposed_filters_sort_widget'), + $container->get('module_handler') + ); + } + + /** + * {@inheritdoc} + */ + protected function defineOptions() { + $options = parent::defineOptions(); + + // General, sort, pagers, and filter. + $bef_options = [ + 'general' => [ + 'autosubmit' => FALSE, + 'autosubmit_exclude_textfield' => FALSE, + 'autosubmit_textfield_delay' => 500, + 'autosubmit_hide' => FALSE, + 'input_required' => FALSE, + 'allow_secondary' => FALSE, + 'secondary_label' => $this->t('Advanced options'), + 'secondary_open' => FALSE, + ], + 'sort' => [ + 'plugin_id' => 'default', + ], + ]; + + // Initialize options if any sort is exposed. + // Iterate over each sort and determine if any sorts are exposed. + $is_sort_exposed = FALSE; + /* @var \Drupal\views\Plugin\views\HandlerBase $sort */ + foreach ($this->view->display_handler->getHandlers('sort') as $sort) { + if ($sort->isExposed()) { + $is_sort_exposed = TRUE; + break; + } + } + if ($is_sort_exposed) { + $bef_options['sort']['plugin_id'] = 'default'; + } + + // Initialize options if the pager is exposed. + $pager = $this->view->getPager(); + if ($pager && $pager->usesExposed()) { + $bef_options['pager']['plugin_id'] = 'default'; + } + + // Go through each exposed filter and set default format. + /* @var \Drupal\views\Plugin\views\HandlerBase $filter */ + foreach ($this->view->display_handler->getHandlers('filter') as $filter_id => $filter) { + if (!$filter->isExposed()) { + continue; + } + + $bef_options['filter'][$filter_id]['plugin_id'] = 'default'; + } + + // Iterate over bef options and convert them to be compatible with views + // default options. + $options += $this->createOptionDefaults(['bef' => $bef_options]); + + return $options; + } + + /** + * Creates a list of view handler default options. + * + * Views handlers expect default options in a specific format. + * + * @param array $options + * An array of plugin defaults. + * + * @return array + * An array of plugin options. + * + * @see \Drupal\views\Plugin\views\PluginBase::setOptionDefaults + */ + protected function createOptionDefaults(array $options) { + $result = []; + foreach ($options as $key => $option) { + if (is_array($option)) { + $result[$key]['contains'] = $this->createOptionDefaults($option); + } + else { + $result[$key]['default'] = $option; + } + } + + return $result; + } + + /** + * Build the views options form and adds custom options for BEF. + * + * @inheritDoc */ public function buildOptionsForm(&$form, FormStateInterface $form_state) { - parent::buildOptionsForm($form, $form_state); + // Ensure that the form values are stored in their original location, and + // not dependent on their position in the form tree. We are moving around + // a few elements to make the UI more user friendly. + $original_form = []; + parent::buildOptionsForm($original_form, $form_state); + foreach (Element::children($original_form) as $element) { + $original_form[$element]['#parents'] = ['exposed_form_options', $element]; + } + + // Save shorthand for BEF options. + $bef_options = $this->options['bef']; + + // User raw user input for AJAX callbacks. + $user_input = $form_state->getUserInput(); + $bef_input = $user_input['exposed_form_options']['bef'] ?? NULL; + + /* + * General BEF settings + */ + // Reorder some existing form elements. + $form['bef']['general']['submit_button'] = $original_form['submit_button']; + $form['bef']['general']['reset_button'] = $original_form['reset_button']; + $form['bef']['general']['reset_button_label'] = $original_form['reset_button_label']; + + // Add the 'auto-submit' functionality. + $form['bef']['general']['autosubmit'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Enable auto-submit'), + '#description' => $this->t('Automatically submits the form when an element has changed.'), + '#default_value' => $bef_options['general']['autosubmit'], + ]; - $bef_options = array(); + $form['bef']['general']['autosubmit_exclude_textfield'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Exclude Textfield'), + '#description' => $this->t('Exclude textfields from auto-submit. User will have to press enter key, or click submit button.'), + '#default_value' => $bef_options['general']['autosubmit_exclude_textfield'], + '#states' => [ + 'visible' => [ + ':input[name="exposed_form_options[bef][general][autosubmit]"]' => ['checked' => TRUE], + ], + ], + ]; - // Get current settings and default values for new filters/ - $existing = $this->getSettings(); + $form['bef']['general']['autosubmit_textfield_delay'] = [ + '#type' => 'number', + '#title' => $this->t('Delay for textfield autosubmit'), + '#description' => $this->t('Configure a delay in ms before triggering autosubmit on textfields.'), + '#default_value' => $bef_options['general']['autosubmit_textfield_delay'], + '#min' => 0, + '#states' => [ + 'visible' => [ + ':input[name="exposed_form_options[bef][general][autosubmit]"]' => ['checked' => TRUE], + ':input[name="exposed_form_options[bef][general][autosubmit_exclude_textfield]"]' => ['checked' => FALSE], + ], + ], + ]; + + $form['bef']['general']['autosubmit_hide'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Hide submit button'), + '#description' => $this->t('Hides submit button if auto-submit and javascript are enabled.'), + '#default_value' => $bef_options['general']['autosubmit_hide'], + '#states' => [ + 'visible' => [ + ':input[name="exposed_form_options[bef][general][autosubmit]"]' => ['checked' => TRUE], + ], + ], + ]; // Insert a checkbox to make the input required optional just before the // input required text field. Only show the text field if the input required // option is selected. - $textInputRequired = $form['text_input_required']; - unset($form['text_input_required']); - $form['input_required'] = [ + $form['bef']['general']['input_required'] = [ '#type' => 'checkbox', '#title' => $this->t('Input required'), '#description' => $this->t('Only display results after the user has selected a filter option.'), - '#default_value' => !empty($this->options['input_required']), + '#default_value' => $bef_options['general']['input_required'], ]; - $form['text_input_required'] = $textInputRequired += [ + $original_form['text_input_required'] += [ '#states' => [ 'visible' => [ - 'input[name="exposed_form_options[input_required]"]' => ['checked' => TRUE], + 'input[name="exposed_form_options[bef][general][input_required]"]' => ['checked' => TRUE], ], ], ]; + $form['bef']['general']['text_input_required'] = $original_form['text_input_required']; /* - * Add general options for exposed form items. + * Allow exposed form items to be displayed as secondary options. */ - $bef_options['general']['allow_secondary'] = array( + $form['bef']['general']['allow_secondary'] = [ '#type' => 'checkbox', '#title' => $this->t('Enable secondary exposed form options'), - '#default_value' => $existing['general']['allow_secondary'], + '#default_value' => $bef_options['general']['allow_secondary'], '#description' => $this->t('Allows you to specify some exposed form elements as being secondary options and places those elements in a collapsible "details" element. Use this option to place some exposed filters in an "Advanced Search" area of the form, for example.'), - ); - $bef_options['general']['secondary_label'] = array( + ]; + $form['bef']['general']['secondary_label'] = [ '#type' => 'textfield', - '#default_value' => $existing['general']['secondary_label'], + '#default_value' => $bef_options['general']['secondary_label'], '#title' => $this->t('Secondary options label'), '#description' => $this->t( 'The name of the details element to hold secondary options. This cannot be left blank or there will be no way to show/hide these options.' ), - '#states' => array( - 'required' => array( - ':input[name="exposed_form_options[bef][general][allow_secondary]"]' => array('checked' => TRUE), - ), - 'visible' => array( - ':input[name="exposed_form_options[bef][general][allow_secondary]"]' => array('checked' => TRUE), - ), - ), - ); - - // Add the 'autosbumit' functionality from Views 7.x. - $bef_options['general']['autosubmit'] = array( - '#type' => 'checkbox', - '#title' => $this->t('Autosubmit'), - '#description' => $this->t('Automatically submit the form once an element is changed.'), - '#default_value' => $existing['general']['autosubmit'], - ); - - $bef_options['general']['autosubmit_exclude_textfield'] = array( - '#type' => 'checkbox', - '#title' => $this->t('Exclude Textfield'), - '#description' => $this->t('Exclude Textfield from autosubmit. User will have to press enter key or click submit.'), - '#default_value' => $existing['general']['autosubmit_exclude_textfield'], - '#states' => array( - 'visible' => array( - ':input[name="exposed_form_options[bef][general][autosubmit]"]' => array('checked' => TRUE), - ), - ), - ); - - $bef_options['general']['autosubmit_hide'] = array( + '#states' => [ + 'required' => [ + ':input[name="exposed_form_options[bef][general][allow_secondary]"]' => ['checked' => TRUE], + ], + 'visible' => [ + ':input[name="exposed_form_options[bef][general][allow_secondary]"]' => ['checked' => TRUE], + ], + ], + ]; + $form['bef']['general']['secondary_open'] = [ '#type' => 'checkbox', - '#title' => $this->t('Hide submit button'), - '#description' => $this->t('Hide submit button if javascript is enabled.'), - '#default_value' => $existing['general']['autosubmit_hide'], - '#states' => array( - 'visible' => array( - ':input[name="exposed_form_options[bef][general][autosubmit]"]' => array('checked' => TRUE), - ), - ), - ); + '#default_value' => $bef_options['general']['secondary_open'], + '#title' => $this->t('Secondary option open by default'), + '#description' => $this->t('Indicates whether the details element should be open by default.'), + '#states' => [ + 'visible' => [ + ':input[name="exposed_form_options[bef][general][allow_secondary]"]' => ['checked' => TRUE], + ], + ], + ]; /* * Add options for exposed sorts. */ - $exposed = FALSE; + // Add intro explaining BEF sorts. + $documentation_uri = Url::fromUri('http://drupal.org/node/1701012')->toString(); + $form['bef']['sort']['bef_intro'] = [ + '#markup' => '<h3>' . $this->t('Exposed Sort Settings') . '</h3><p>' . $this->t('This section lets you select additional options for exposed sorts. Some options are only available in certain situations. If you do not see the options you expect, please see the <a href=":link">BEF settings documentation page</a> for more details.', [':link' => $documentation_uri]) . '</p>', + ]; + + // Iterate over each sort and determine if any sorts are exposed. + $is_sort_exposed = FALSE; /* @var \Drupal\views\Plugin\views\HandlerBase $sort */ - foreach ($this->view->display_handler->getHandlers('sort') as $label => $sort) { + foreach ($this->view->display_handler->getHandlers('sort') as $sort) { if ($sort->isExposed()) { - $exposed = TRUE; + $is_sort_exposed = TRUE; break; } } - if ($exposed) { - $bef_options['sort']['bef_format'] = array( + + $form['bef']['sort']['empty'] = [ + '#type' => 'item', + '#description' => $this->t('No sort elements have been exposed yet.'), + '#access' => !$is_sort_exposed, + ]; + + if ($is_sort_exposed) { + $options = []; + foreach ($this->sortWidgetManager->getDefinitions() as $plugin_id => $definition) { + if ($definition['class']::isApplicable()) { + $options[$plugin_id] = $definition['label']; + } + } + + $form['bef']['sort']['configuration'] = [ + '#prefix' => "<div id='bef-sort-configuration'>", + '#suffix' => "</div>", + '#type' => 'container', + ]; + + // Get selected plugin_id on AJAX callback directly from the form state. + $selected_plugin_id = $bef_input['sort']['configuration']['plugin_id'] ?? + $bef_options['sort']['plugin_id']; + + $form['bef']['sort']['configuration']['plugin_id'] = [ '#type' => 'select', '#title' => $this->t('Display exposed sort options as'), - '#default_value' => $existing['sort']['bef_format'], - '#options' => array( - 'default' => $this->t('Default Views element'), - 'bef' => $this->t('Radio Buttons'), - 'bef_links' => $this->t('Links'), - ), + '#default_value' => $selected_plugin_id, + '#options' => $options, '#description' => $this->t('Select a format for the exposed sort options.'), - ); - $bef_options['sort']['advanced'] = array( - '#type' => 'details', - '#title' => $this->t('Advanced sort options'), - ); - $bef_options['sort']['advanced']['collapsible'] = array( - '#type' => 'checkbox', - '#title' => $this->t('Make sort options collapsible'), - '#default_value' => $existing['sort']['advanced']['collapsible'], - '#description' => $this->t( - 'Puts the sort options in a collapsible details element.' - ), - ); - $bef_options['sort']['advanced']['collapsible_label'] = array( - '#type' => 'textfield', - '#title' => $this->t('Collapsible details element title'), - '#default_value' => empty($existing['sort']['advanced']['collapsible_label']) ? $this->t('Sort options') : $existing['sort']['advanced']['collapsible_label'], - '#description' => $this->t('This cannot be left blank or there will be no way to show/hide sort options.'), - '#states' => array( - 'visible' => array( - ':input[name="exposed_form_options[bef][sort][advanced][collapsible]"]' => array('checked' => TRUE), - ), - 'required' => array( - ':input[name="exposed_form_options[bef][sort][advanced][collapsible]"]' => array('checked' => TRUE), - ), - ), - ); - - // We can only combine sort order and sort by if both options are exposed. - $bef_options['sort']['advanced']['combine'] = array( - '#type' => 'checkbox', - '#title' => $this->t('Combine sort order with sort by'), - '#default_value' => $existing['sort']['advanced']['combine'], - '#description' => $this->t('Combines the sort by options and order (ascending or decending) into a single list. Use this to display "Option1 (ascending)", "Option1 (descending)", "Option2 (ascending)", "Option2 (descending)" in a single form element. Sort order should first be exposed by selecting <em>Allow people to choose the sort order</em>.'), - '#states' => [ - 'enabled' => [ - ':input[name="exposed_form_options[expose_sort_order]"]' => ['checked' => TRUE], - ], + '#ajax' => [ + 'event' => 'change', + 'effect' => 'fade', + 'progress' => 'throbber', + // Since views options forms are complex, they're built by + // Drupal in a different way. To bypass this problem we need to + // provide the full path to the Ajax callback. + 'callback' => __CLASS__ . '::ajaxCallback', + 'wrapper' => 'bef-sort-configuration', ], - ); - $bef_options['sort']['advanced']['combine_rewrite'] = array( - '#type' => 'textarea', - '#title' => $this->t('Rewrite the text displayed'), - '#default_value' => $existing['sort']['advanced']['combine_rewrite'], - '#description' => $this->t('Use this field to rewrite the text displayed for combined sort options and sort order. Use the format of current_text|replacement_text, one replacement per line. For example: <pre> -Post date Asc|Oldest first -Post date Desc|Newest first -Title Asc|A -> Z -Title Desc|Z -> A</pre> Leave the replacement text blank to remove an option altogether. The order the options appear will be changed to match the order of options in this field.'), - '#states' => array( - 'visible' => array( - ':input[name="exposed_form_options[bef][sort][advanced][combine]"]' => array('checked' => TRUE), - ), - ), - ); - $bef_options['sort']['advanced']['reset'] = array( - '#type' => 'checkbox', - '#title' => $this->t('Include a "Reset sort" option'), - '#default_value' => $existing['sort']['advanced']['reset'], - '#description' => $this->t('Adds a "Reset sort" link; Views will use the default sort order.'), - ); - $bef_options['sort']['advanced']['reset_label'] = array( - '#type' => 'textfield', - '#title' => $this->t('"Reset sort" label'), - '#default_value' => $existing['sort']['advanced']['reset_label'], - '#description' => $this->t('This cannot be left blank if the above option is checked'), - '#states' => array( - 'visible' => array( - ':input[name="exposed_form_options[bef][sort][advanced][reset]"]' => array('checked' => TRUE), - ), - 'required' => array( - ':input[name="exposed_form_options[bef][sort][advanced][reset]"]' => array('checked' => TRUE), - ), - ), - ); - $bef_options['sort']['advanced']['is_secondary'] = array( - '#type' => 'checkbox', - '#title' => $this->t('This is a secondary option'), - '#default_value' => $existing['sort']['advanced']['is_secondary'], - '#states' => array( - 'visible' => array( - ':input[name="exposed_form_options[bef][general][allow_secondary]"]' => array('checked' => TRUE), - ), - ), - '#description' => $this->t('Places this element in the secondary options portion of the exposed form.'), - ); + ]; + + // Move some existing form elements. + $form['bef']['sort']['configuration']['exposed_sorts_label'] = $original_form['exposed_sorts_label']; + $form['bef']['sort']['configuration']['expose_sort_order'] = $original_form['expose_sort_order']; + $form['bef']['sort']['configuration']['sort_asc_label'] = $original_form['sort_asc_label']; + $form['bef']['sort']['configuration']['sort_desc_label'] = $original_form['sort_desc_label']; + + if ($selected_plugin_id) { + $plugin_configuration = $bef_options['sort'] ?? []; + /** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */ + $plugin = $this->sortWidgetManager->createInstance($selected_plugin_id, $plugin_configuration); + $plugin->setView($this->view); + + $subform = &$form['bef']['sort']['configuration']; + $subform_state = SubformState::createForSubform($subform, $form, $form_state); + $subform += $plugin->buildConfigurationForm($subform, $subform_state); + } } /* * Add options for exposed pager. */ - if (isset($this->display->display_options['pager']) && $this->display->display_options['pager']['options']['expose']['items_per_page']) { - $bef_options['pager']['bef_format'] = array( - '#type' => 'select', - '#title' => $this->t('Display exposed pager options as'), - '#default_value' => $existing['pager']['bef_format'], - '#options' => array( - 'default' => $this->t('Default (Views render element)'), - 'bef' => $this->t('Radio Buttons'), - 'bef_links' => $this->t('Links'), - ), - '#description' => $this->t('Select a format for the exposed pager options.'), - ); - $bef_options['pager']['is_secondary'] = array( - '#type' => 'checkbox', - '#title' => $this->t('This is a secondary option'), - '#default_value' => $existing['pager']['is_secondary'], - '#states' => array( - 'visible' => array( - ':input[name="exposed_form_options[bef][general][allow_secondary]"]' => array('checked' => TRUE), - ), - ), - '#description' => $this->t('Places this element in the secondary options portion of the exposed form.'), - ); - } + $documentation_uri = Url::fromUri('http://drupal.org/node/1701012')->toString(); + $form['bef']['pager']['bef_intro'] = [ + '#markup' => '<h3>' . $this->t('Exposed Pager Settings') . '</h3><p>' . $this->t('This section lets you select additional options for exposed pagers. Some options are only available in certain situations. If you do not see the options you expect, please see the <a href=":link">BEF settings documentation page</a> for more details.', [':link' => $documentation_uri]) . '</p>', + ]; + $pager = $this->view->getPager(); + $is_pager_exposed = $pager && $pager->usesExposed(); - // Only add the description text once -- it was getting a little long to be - // added to each filter. - $bef_filter_intro = FALSE; + $form['bef']['pager']['empty'] = [ + '#type' => 'item', + '#description' => $this->t('No pager elements have been exposed yet.'), + '#access' => !$is_pager_exposed, + ]; - // Go through each filter and add BEF options. - /* @var \Drupal\views\Plugin\views\HandlerBase $filter */ - foreach ($this->view->display_handler->getHandlers('filter') as $label => $filter) { - if (!$filter->isExposed()) { - continue; - } + if ($is_pager_exposed) { + $options = []; - // If we're adding BEF filter options, add an intro to explain what's - // going on. - if (!$bef_filter_intro) { - $bef_options['bef_intro'] = array( - '#markup' => '<h3>' - . $this->t('Exposed Filter Settings') - . '</h3><p>' - . $this->t('This section lets you select additional options for exposed filters. Some options are only available in certain situations. If you do not see the options you expect, please see the <a href=":link">BEF settings documentation page</a> for more details.', - array( - ':link' => Url::fromUri('http://drupal.org/node/1701012') - ->toString() - )) - . '</p>', - ); - $bef_filter_intro = TRUE; + foreach ($this->pagerWidgetManager->getDefinitions() as $plugin_id => $definition) { + if ($definition['class']::isApplicable()) { + $options[$plugin_id] = $definition['label']; + } } - // These filter operators get our standard options: select, radio or - // checkboxes, links, etc. - $bef_standard = FALSE; - - // These filters get a single on/off checkbox option for boolean - // operators. - $bef_single = FALSE; + $form['bef']['pager']['configuration'] = [ + '#prefix' => "<div id='bef-pager-configuration'>", + '#suffix' => "</div>", + '#type' => 'container', + ]; - // Used for taxonomy filters with hierarchy. - $bef_nested = FALSE; + // Get selected plugin_id on AJAX callback directly from the form state. + $selected_plugin_id = $bef_input['pager']['configuration']['plugin_id'] ?? + $bef_options['pager']['plugin_id']; - // Used for taxonomy filters with hierarchy rendered as links. - $bef_nested_links = FALSE; - - // Used for date-based filters. - $bef_datepicker = FALSE; + $form['bef']['pager']['configuration']['plugin_id'] = [ + '#type' => 'select', + '#title' => $this->t('Display exposed pager options as'), + '#default_value' => $selected_plugin_id, + '#options' => $options, + '#description' => $this->t('Select a format for the exposed pager options.'), + '#ajax' => [ + 'event' => 'change', + 'effect' => 'fade', + 'progress' => 'throbber', + // Since views options forms are complex, they're built by + // Drupal in a different way. To bypass this problem we need to + // provide the full path to the Ajax callback. + 'callback' => __CLASS__ . '::ajaxCallback', + 'wrapper' => 'bef-pager-configuration', + ], + ]; - // Used for numeric, non-date filters. - $bef_slider = FALSE; + if ($selected_plugin_id) { + $plugin_configuration = $bef_options['pager'] ?? []; + /** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */ + $plugin = $this->pagerWidgetManager->createInstance($selected_plugin_id, $plugin_configuration); + $plugin->setView($this->view); - // Check various filter types and determine what options are available. - if (is_a($filter, 'Drupal\views\Plugin\views\filter\String') || is_a($filter, 'Drupal\views\Plugin\views\filter\InOperator')) { - if (in_array($filter->operator, array('in', 'or', 'and'))) { - $bef_standard = TRUE; - } - if (in_array($filter->operator, array('empty', 'not empty'))) { - $bef_standard = TRUE; - if (!$filter->options['expose']['multiple']) { - $bef_single = TRUE; - } - } + $subform = &$form['bef']['pager']['configuration']; + $subform_state = SubformState::createForSubform($subform, $form, $form_state); + $subform += $plugin->buildConfigurationForm($subform, $subform_state); } + } - if (is_a($filter, 'Drupal\views\Plugin\views\filter\BooleanOperator')) { - $bef_standard = TRUE; - if (!$filter->options['expose']['multiple']) { - $bef_single = TRUE; - } + /* + * Add options for exposed filters. + */ + $documentation_uri = Url::fromUri('http://drupal.org/node/1701012')->toString(); + $form['bef']['filter']['bef_intro'] = [ + '#markup' => '<h3>' . $this->t('Exposed Filter Settings') . '</h3><p>' . $this->t('This section lets you select additional options for exposed filters. Some options are only available in certain situations. If you do not see the options you expect, please see the <a href=":link">BEF settings documentation page</a> for more details.', [':link' => $documentation_uri]) . '</p>', + ]; + + // Iterate over each filter and add BEF filter options. + /* @var \Drupal\views\Plugin\views\HandlerBase $filter */ + foreach ($this->view->display_handler->getHandlers('filter') as $filter_id => $filter) { + if (!$filter->isExposed()) { + continue; } - if (is_a($filter, 'Drupal\taxonomy\Plugin\views\filter\TaxonomyIndexTid')) { - // Autocomplete and dropdown taxonomy filter are both instances of - // TaxonomyIndexTid, but we can't show BEF options for the autocomplete - // widget. - if ($this->displayHandler->handlers['filter'][$label]->options['type'] != 'select') { - $bef_standard = FALSE; - } - elseif (!empty($this->displayHandler->handlers['filter'][$label]->options['hierarchy'])) { - $bef_nested = TRUE; - $bef_nested_links = TRUE; + $options = []; + foreach ($this->filterWidgetManager->getDefinitions() as $plugin_id => $definition) { + if ($definition['class']::isApplicable($filter, $this->displayHandler->handlers['filter'][$filter_id]->options)) { + $options[$plugin_id] = $definition['label']; } } - if (is_a($filter, 'Drupal\views\Plugin\views\filter\Date') || !empty($filter->date_handler)) { - $bef_datepicker = TRUE; - } + // Alter the list of available widgets for this filter. + $this->moduleHandler->alter('better_exposed_filters_display_options', $options, $filter); - // The date filter handler extends the numeric filter handler so we have - // to exclude it specifically. - if (is_a($filter, 'Drupal\views\Plugin\views\filter\NumericFilter') && !is_a($filter, 'Drupal\views\Plugin\views\filter\Date')) { - $bef_slider = TRUE; + // Get a descriptive label for the filter. + $label = $this->t('Exposed filter @filter', [ + '@filter' => $filter->options['expose']['identifier'], + ]); + if (!empty($filter->options['expose']['label'])) { + $label = $this->t('Exposed filter "@filter" with label "@label"', [ + '@filter' => $filter->options['expose']['identifier'], + '@label' => $filter->options['expose']['label'], + ]); } + $form['bef']['filter'][$filter_id] = [ + '#type' => 'details', + '#title' => $label, + '#collapsed' => FALSE, + '#collapsible' => TRUE, + ]; - // All filters can use the default filter exposed by Views. - $display_options = array('default' => $this->t('Default (Views render element)')); + $form['bef']['filter'][$filter_id]['configuration'] = [ + '#prefix' => "<div id='bef-filter-$filter_id-configuration'>", + '#suffix' => "</div>", + '#type' => 'container', + ]; - if ($bef_standard) { - // Main BEF option: radios/checkboxes. - $display_options['bef'] = $this->t('Checkboxes/Radio Buttons'); - } + // Get selected plugin_id on AJAX callback directly from the form state. + $selected_plugin_id = $bef_input['filter'][$filter_id]['configuration']['plugin_id'] ?? $bef_options['filter'][$filter_id]['plugin_id']; - if ($bef_nested) { - $display_options['bef_ul'] = $this->t('Nested Checkboxes/Radio Buttons'); - } + $form['bef']['filter'][$filter_id]['configuration']['plugin_id'] = [ + '#type' => 'select', + '#title' => $this->t('Exposed filter widget:'), + '#default_value' => $selected_plugin_id, + '#options' => $options, + '#ajax' => [ + 'event' => 'change', + 'effect' => 'fade', + 'progress' => 'throbber', + // Since views options forms are complex, they're built by + // Drupal in a different way. To bypass this problem we need to + // provide the full path to the Ajax callback. + 'callback' => __CLASS__ . '::ajaxCallback', + 'wrapper' => 'bef-filter-' . $filter_id . '-configuration', + ], + ]; - if ($bef_single) { - $display_options['bef_single'] = $this->t('Single on/off checkbox'); - } + if ($selected_plugin_id) { + $plugin_configuration = $bef_options['filter'][$filter_id] ?? []; + /** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */ + $plugin = $this->filterWidgetManager->createInstance($selected_plugin_id, $plugin_configuration); + $plugin->setView($this->view); + $plugin->setViewsHandler($filter); - if ($bef_datepicker) { - $display_options['bef_datepicker'] = $this->t('jQuery UI Datepicker'); + $subform = &$form['bef']['filter'][$filter_id]['configuration']; + $subform_state = SubformState::createForSubform($subform, $form, $form_state); + $subform += $plugin->buildConfigurationForm($subform, $subform_state); } + } + } - if ($bef_slider) { - $display_options['bef_slider'] = $this->t('jQuery UI slider'); - } + /** + * The form ajax callback. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * + * @return array + * The form element to return. + */ + public static function ajaxCallback(array $form, FormStateInterface $form_state) { + $triggering_element = $form_state->getTriggeringElement(); + return NestedArray::getValue($form, array_slice($triggering_element['#array_parents'], 0, -1)); + } - if ($bef_standard) { - // Less used BEF options, so put them last. - $display_options['bef_links'] = $this->t('Links'); - if ($bef_nested_links) { - $display_options['bef_ul_links'] = $this->t('Nested Links'); - } - $display_options['bef_hidden'] = $this->t('Hidden'); - } + /** + * {@inheritdoc} + */ + public function validateOptionsForm(&$form, FormStateInterface $form_state) { + // Drupal only passes in a part of the views form, but we need the complete + // form array for plugin subforms to work. + $parent_form = $form_state->getCompleteForm(); + // Save a shorthand to the BEF form. + $bef_form = &$form['bef']; + // Save a shorthand to the BEF options. + $bef_form_options = $form_state->getValue(['exposed_form_options', 'bef']); - // Alter the list of available display options for this filter. - \Drupal::moduleHandler()->alter('better_exposed_filters_display_options', $display_options, $filter); + parent::validateOptionsForm($form, $form_state); - $identifier = '"' . $filter->options['expose']['identifier'] . '"'; - if (!empty($filter->options['expose']['label'])) { - $identifier .= $this->t(' (Filter label: "@fl")', array('@fl' => $filter->options['expose']['label'])); - } - $bef_options[$label]['bef_format'] = array( - '#type' => 'select', - '#title' => $this->t('Display @identifier exposed filter as', array('@identifier' => $identifier)), - '#default_value' => $existing[$label]['bef_format'], - '#options' => $display_options, - ); - - if ($bef_slider) { - // Details element for jQuery slider options. - $bef_options[$label]['slider_options'] = array( - '#type' => 'details', - '#title' => $this->t('Slider options for @identifier', array('@identifier' => $identifier)), - '#open' => TRUE, - '#states' => array( - 'visible' => array( - ':input[name="exposed_form_options[bef][' . $label . '][bef_format]"]' => array('value' => 'bef_slider'), - ), - ), - ); - - $bef_options[$label]['slider_options']['bef_slider_min'] = array( - '#type' => 'textfield', - '#title' => $this->t('Range minimum'), - '#default_value' => $this->options['bef'][$label]['slider_options']['bef_slider_min'], - '#bef_filter_id' => $label, - '#states' => array( - 'required' => array( - ':input[name="exposed_form_options[bef][' . $label . '][bef_format]"]' => array('value' => 'bef_slider'), - ), - ), - '#description' => $this->t('The minimum allowed value for the jQuery range slider. It can be positive, negative, or zero and have up to 11 decimal places.'), - ); - $bef_options[$label]['slider_options']['bef_slider_max'] = array( - '#type' => 'textfield', - '#title' => $this->t('Range maximum'), - '#default_value' => $this->options['bef'][$label]['slider_options']['bef_slider_max'], - '#bef_filter_id' => $label, - '#states' => array( - 'required' => array( - ':input[name="exposed_form_options[bef][' . $label . '][bef_format]"]' => array('value' => 'bef_slider'), - ), - ), - '#description' => $this->t('The maximum allowed value for the jQuery range slider. It can be positive, negative, or zero and have up to 11 decimal places.'), - ); - $bef_options[$label]['slider_options']['bef_slider_step'] = array( - '#type' => 'textfield', - '#title' => $this->t('Step'), - '#default_value' => empty($this->options['bef'][$label]['slider_options']['bef_slider_step']) ? 1 : $this->options['bef'][$label]['slider_options']['bef_slider_step'], - '#bef_filter_id' => $label, - '#states' => array( - 'required' => array( - ':input[name="exposed_form_options[bef][' . $label . '][bef_format]"]' => array('value' => 'bef_slider'), - ), - ), - '#description' => $this->t('Determines the size or amount of each interval or step the slider takes between the min and max.') . '<br />' . - $this->t('The full specified value range of the slider (Range maximum - Range minimum) must be evenly divisible by the step.') . '<br />' . - $this->t('The step must be a positive number of up to 5 decimal places.'), - ); - $bef_options[$label]['slider_options']['bef_slider_animate'] = array( - '#type' => 'textfield', - '#title' => $this->t('Animate'), - '#default_value' => $this->options['bef'][$label]['slider_options']['bef_slider_animate'], - '#bef_filter_id' => $label, - '#description' => $this->t('Whether to slide handle smoothly when user click outside handle on the bar. Allowed values are "slow", "normal", "fast" or the number of milliseconds to run the animation (e.g. 1000). If left blank, there will be no animation, the slider will just jump to the new value instantly.'), - ); - $bef_options[$label]['slider_options']['bef_slider_orientation'] = array( - '#type' => 'select', - '#title' => $this->t('Orientation'), - '#options' => array( - 'horizontal' => $this->t('Horizontal'), - 'vertical' => $this->t('Vertical'), - ), - '#default_value' => $this->options['bef'][$label]['slider_options']['bef_slider_orientation'], - '#bef_filter_id' => $label, - '#states' => array( - 'required' => array( - ':input[name="exposed_form_options[bef][' . $label . '][bef_format]"]' => array('value' => 'bef_slider'), - ), - ), - '#description' => $this->t('The orientation of the jQuery range slider.'), - ); - } + // Skip plugin validation if we are switching between bef plugins. + $triggering_element = $form_state->getTriggeringElement(); + if ($triggering_element['#type'] !== 'submit') { + return; + } - // Details element to keep the UI from getting out of hand. - $bef_options[$label]['more_options'] = array( - '#type' => 'details', - '#title' => $this->t('More options for @identifier', array('@identifier' => $identifier)), - ); - - // Select all checkbox. - if ($bef_standard) { - $bef_options[$label]['more_options']['bef_select_all_none'] = array( - '#type' => 'checkbox', - '#title' => $this->t('Add select all/none links'), - '#default_value' => $existing[$label]['more_options']['bef_select_all_none'], - '#disabled' => !$filter->options['expose']['multiple'], - '#description' => $this->t( - 'Add a "Select All/None" link when rendering the exposed filter using - checkboxes. If this option is disabled, edit the filter and check the - "Allow multiple selections".' - ), - ); - - if ($bef_nested) { - $bef_options[$label]['more_options']['bef_select_all_none_nested'] = array( - '#type' => 'checkbox', - '#title' => $this->t('Add nested all/none selection'), - '#default_value' => $this->options['bef'][$label]['more_options']['bef_select_all_none_nested'], - '#disabled' => !$filter->options['expose']['multiple'] || !$filter->options['hierarchy'], - '#description' => $this->t( - 'When a parent checkbox is checked, check all its children. If this option - is disabled, edit the filter and check "Allow multiple selections" and - edit the filter settings and check "Show hierarchy in dropdown".' - ), - ); + // Shorthand for all filter handlers in this view. + /* @var \Drupal\views\Plugin\views\HandlerBase[] $filters */ + $filters = $this->view->display_handler->handlers['filter']; + + // Iterate over all filter, sort and pager plugins. + foreach ($bef_form_options as $type => $config) { + // Validate exposed filter configuration. + if ($type === 'filter') { + foreach ($config as $filter_id => $filter_options) { + $plugin_id = $filter_options['configuration']['plugin_id'] ?? NULL; + if (!$plugin_id) { + continue; + } + /** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */ + $plugin = $this->filterWidgetManager->createInstance($plugin_id); + $subform = &$bef_form[$type][$filter_id]['configuration']; + $subform_state = SubformState::createForSubform($subform, $parent_form, $form_state); + $plugin->setView($this->view); + $plugin->setViewsHandler($filters[$filter_id]); + $plugin->validateConfigurationForm($subform, $subform_state); } - - // Put filter in details element option. - // TODO: expand to all exposed filters. - $bef_options[$label]['more_options']['bef_collapsible'] = array( - '#type' => 'checkbox', - '#title' => $this->t('Make this filter collapsible'), - '#default_value' => $existing[$label]['more_options']['bef_collapsible'], - '#description' => $this->t( - 'Puts this filter in a collapsible details element' - ), - ); } + // Validate exposed pager/sort configuration. + elseif (in_array($type, ['pager', 'sort'])) { + $plugin_id = $config['configuration']['plugin_id'] ?? NULL; + if (!$plugin_id) { + continue; + } - // Allow any filter to be moved into the secondary options element. - $bef_options[$label]['more_options']['is_secondary'] = array( - '#type' => 'checkbox', - '#title' => $this->t('This is a secondary option'), - '#default_value' => $existing[$label]['more_options']['is_secondary'], - '#states' => array( - 'visible' => array( - ':input[name="exposed_form_options[bef][general][allow_secondary]"]' => array('checked' => TRUE), - ), - ), - '#description' => $this->t('Places this element in the secondary options portion of the exposed form.'), - ); - - $filter_form = array(); - $form_state = new FormState(); - - // Let form plugins know this is for exposed widgets. - $form_state->set('exposed', TRUE); - - /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase $filter */ - $filter->buildExposedForm($filter_form, $form_state); - - $supported_types = array('entity_autocomplete', 'textfield'); - - $filter_id = $filter->options['expose']['identifier']; - if (in_array($filter_form[$filter_id]['#type'], $supported_types) || in_array($filter_form[$filter_id]['value']['#type'], $supported_types)) { - // Allow users to specify placeholder text. - $bef_options[$label]['more_options']['placeholder_text'] = [ - '#type' => 'textfield', - '#title' => $this->t('Placeholder text'), - '#description' => $this->t('Text to be shown in the text field until it is edited. Leave blank for no placeholder to be set.'), - '#default_value' => $existing[$label]['more_options']['placeholder_text'], - ]; - } + // Use the correct widget manager. + if ($type === 'pager') { + /** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */ + $plugin = $this->pagerWidgetManager->createInstance($plugin_id); + } + else { + /** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */ + $plugin = $this->sortWidgetManager->createInstance($plugin_id); + } - // Allow rewriting of filter options for any filter. String and numeric - // filters allow unlimited filter options via textfields, so we can't - // offer rewriting for those. - // @TODO: check other core filter types to see if there are others that - // should be added to this list. - if (!$filter instanceof StringFilter && !$filter instanceof NumericFilter) { - $bef_options[$label]['more_options']['rewrite'] = array( - '#title' => $this->t('Rewrite filter options'), - '#type' => 'details', - ); - $bef_options[$label]['more_options']['rewrite']['filter_rewrite_values'] = array( - '#type' => 'textarea', - '#title' => $this->t('Rewrite the text displayed'), - '#default_value' => $existing[$label]['more_options']['rewrite']['filter_rewrite_values'], - '#description' => $this->t('Use this field to rewrite the filter options displayed. Use the format of current_text|replacement_text, one replacement per line. For example: <pre> - Current|Replacement - On|Yes - Off|No - </pre> Leave the replacement text blank to remove an option altogether. If using hierarchical taxonomy filters, do not including leading hyphens in the current text. - '), - ); + $subform = &$bef_form[$type]['configuration']; + $subform_state = SubformState::createForSubform($subform, $parent_form, $form_state); + $plugin->setView($this->view); + $plugin->validateConfigurationForm($subform, $subform_state); } } - /* Ends: foreach ($filters as $filter) { */ - - // Add BEF form elements to the exposed form options form. - $form['bef'] = $bef_options; } /** * {@inheritdoc} */ - public function validateOptionsForm(&$form, FormStateInterface $form_state) { - $bef = $form_state->getValue(['exposed_form_options', 'bef']); + public function submitOptionsForm(&$form, FormStateInterface $form_state) { + // Drupal only passes in a part of the views form, but we need the complete + // form array for plugin subforms to work. + $parent_form = $form_state->getCompleteForm(); + // Save a shorthand to the BEF form. + $bef_form = &$form['bef']; + + // Reorder options based on config - some keys may have shifted because of + // form alterations (@see \Drupal\better_exposed_filters\Plugin\views\exposed_form\BetterExposedFilters::buildOptionsForm). + $options = &$form_state->getValue('exposed_form_options'); + $options = array_replace_recursive($this->options, $options); + // Save a shorthand to the BEF options. + $bef_options = &$options['bef']; + + // Shorthand for all filter handlers in this view. + /* @var \Drupal\views\Plugin\views\HandlerBase[] $filters */ + $filters = $this->view->display_handler->handlers['filter']; + + parent::submitOptionsForm($form, $form_state); + + // Iterate over all filter, sort and pager plugins. + foreach ($bef_options as $type => $config) { + // Save exposed filter configuration. + if ($type === 'filter') { + foreach ($config as $filter_id => $filter_options) { + $plugin_id = $filter_options['configuration']['plugin_id'] ?? NULL; + /** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */ + if (!$plugin_id) { + unset($bef_options['filter'][$filter_id]); + continue; + } - // Validate slider settings. - foreach ($bef as $id => $settings) { - if ($id == 'general' || $settings['bef_format'] != 'bef_slider') { - continue; - } + $plugin = $this->filterWidgetManager->createInstance($plugin_id); + $subform = &$bef_form[$type][$filter_id]['configuration']; + $subform_state = SubformState::createForSubform($subform, $parent_form, $form_state); + $plugin->setView($this->view); + $plugin->setViewsHandler($filters[$filter_id]); + $plugin->submitConfigurationForm($subform, $subform_state); - // Max must be > min. - $min = $settings['slider_options']['bef_slider_min']; - $max = $settings['slider_options']['bef_slider_max']; - if ($max <= $min) { - $form_state->setError($form['bef'][$id], $this->t('The slider max value must be greater than the slider min value.')); + $plugin_configuration = $plugin->getConfiguration(); + $bef_options[$type][$filter_id] = $plugin_configuration; + } } + // Save exposed pager/sort configuration. + elseif (in_array($type, ['pager', 'sort'])) { + $plugin_id = $config['configuration']['plugin_id'] ?? NULL; + if (!$plugin_id) { + unset($bef_options[$type]); + continue; + } - // Validate the slider animation settings. - $animate = $settings['slider_options']['bef_slider_animate']; - if ($animate != '') { - if ((!is_numeric($animate) || intval($animate) != $animate) && !in_array($animate, ['slow', 'normal', 'fast'])) { - $form_state->setError($form['bef'][$id], $this->t('The slider animation option for %name must be "slow", "normal", or "fast", the number of milliseconds to run the animation (e.g. 1000), or blank.', ['%name' => $id])); + // Use the correct widget manager. + if ($type === 'pager') { + /** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */ + $plugin = $this->pagerWidgetManager->createInstance($plugin_id); + } + else { + /** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */ + $plugin = $this->sortWidgetManager->createInstance($plugin_id); } - } - // Validate the step setting: - // - Must be positive - // - No more than 5 decimal places - // - Slider range must be evenly divisible by step - $step = $settings['slider_options']['bef_slider_step']; - if ($step <= 0) { - $form_state->setError($form['bef'][$id], $this->t('The slider step option for %name must be a positive number.', ['%name' => $id])); - } - if (strlen(substr(strrchr((string) $step, '.'), 1)) > 5) { - $form_state->setError($form['bef'][$id], $this->t('The slider step option for %name cannot have more than 5 decimal places.', ['%name' => $id])); - } - // Very small step and a vary large range can go beyond the max value of - // an int in PHP. Thus we look for a decimal point when casting the result - // to a string. - if (strpos((string) ($max - $min) / $step, '.')) { - $form_state->setError($form['bef'][$id], $this->t('The slider range for %name must be evenly divisible by the step option.', ['%name' => $id])); + $subform = &$bef_form[$type]['configuration']; + $subform_state = SubformState::createForSubform($subform, $parent_form, $form_state); + $plugin->setView($this->view); + $plugin->submitConfigurationForm($subform, $subform_state); + + $plugin_configuration = $plugin->getConfiguration(); + $bef_options[$type] = $plugin_configuration; } } - - parent::validateOptionsForm($form, $form_state); } - - /** - * @inheritdoc + * {@inheritdoc} */ public function exposedFormAlter(&$form, FormStateInterface $form_state) { parent::exposedFormAlter($form, $form_state); - // If we have no visible elements, we don't show the Apply button. - $show_apply = FALSE; + // Mark form as Better Exposed Filter form for easier alterations. + $form['#context']['bef'] = TRUE; // These styles are used on all exposed forms. $form['#attached']['library'][] = 'better_exposed_filters/general'; @@ -641,660 +712,120 @@ public function exposedFormAlter(&$form, FormStateInterface $form_state) { // styling changes to just BEF forms. $form['#attributes']['class'][] = 'bef-exposed-form'; - // Collect BEF's Javascript settings, add to Drupal.settings at the end. - $bef_add_js = FALSE; - $bef_js = array( - 'datepicker' => FALSE, - 'slider' => FALSE, - 'settings' => array(), - ); - - // Grab BEF settings and allow modules/theme to modify them before + // Grab BEF options and allow modules/theme to modify them before // processing. - $settings = $this->getSettings(); - \Drupal::moduleHandler()->alter('better_exposed_filters_settings', $settings, $this->view, $this->displayHandler); - - // Some elements may be placed in a secondary details element (eg: "Advanced - // search options"). Place this after the exposed filters and before the - // rest of the items in the exposed form. - if ($allow_secondary = $settings['general']['allow_secondary']) { - $secondary = array( - '#type' => 'details', - '#title' => $settings['general']['secondary_label'], - ); - } - - // Apply autosubmit values. - if (!empty($settings['general']['autosubmit'])) { - $form = array_merge_recursive($form, array('#attributes' => array('data-bef-auto-submit-full-form' => ''))); + $bef_options = $this->options['bef']; + $this->moduleHandler->alter('better_exposed_filters_options', $bef_options, $this->view, $this->displayHandler); + + // Apply auto-submit values. + if (!empty($bef_options['general']['autosubmit'])) { + $form = array_merge_recursive($form, [ + '#attributes' => [ + 'data-bef-auto-submit-full-form' => '', + 'data-bef-auto-submit' => '', + 'data-bef-auto-submit-delay' => $bef_options['general']['autosubmit_textfield_delay'], + ], + ]); $form['actions']['submit']['#attributes']['data-bef-auto-submit-click'] = ''; $form['#attached']['library'][] = 'better_exposed_filters/auto_submit'; - if (!empty($settings['general']['autosubmit_exclude_textfield'])) { + if (!empty($bef_options['general']['autosubmit_exclude_textfield'])) { + $supported_types = ['entity_autocomplete', 'textfield']; foreach ($form as &$element) { - if (isset($element['#type']) && $element['#type'] == 'textfield') { - $element['#attributes'] = ['data-bef-auto-submit-exclude' => '']; + $element_type = $element['#type'] ?? NULL; + if (in_array($element_type, $supported_types)) { + $element['#attributes']['data-bef-auto-submit-exclude'] = ''; } } } - if (!empty($settings['general']['autosubmit_hide'])) { + if (!empty($bef_options['general']['autosubmit_hide'])) { $form['actions']['submit']['#attributes']['class'][] = 'js-hide'; } } + // Some elements may be placed in a secondary details element (eg: "Advanced + // search options"). Place this after the exposed filters and before the + // rest of the items in the exposed form. + $allow_secondary = $bef_options['general']['allow_secondary']; + if ($allow_secondary) { + $form['secondary'] = [ + '#attributes' => [ + 'class' => ['bef--secondary'], + ], + '#type' => 'details', + '#title' => $bef_options['general']['secondary_label'], + '#open' => $bef_options['general']['secondary_open'], + // Disable until fields are added to this fieldset. + '#access' => FALSE, + ]; + } + /* * Handle exposed sort elements. */ - if (isset($settings['sort']) && !empty($form['sort_by'])) { - $show_apply = TRUE; - - // If selected, collect all sort-related form elements and put them - // in a details element. - $collapse = $settings['sort']['advanced']['collapsible'] - && !empty($settings['sort']['advanced']['collapsible_label']); - $sort_elems = array(); - - // Check for combined sort_by and sort_order. - if ($settings['sort']['advanced']['combine'] && !empty($form['sort_order'])) { - $options = []; - - // Add reset sort option at the top of the list. - if ($settings['sort']['advanced']['reset']) { - $options[' '] = $this->t($settings['sort']['advanced']['reset_label']); - } - else { - $form['sort_bef_combine']['#default_value'] = ''; - } - - $selected = ''; - foreach ($form['sort_by']['#options'] as $by_key => $by_val) { - foreach ($form['sort_order']['#options'] as $order_key => $order_val) { - // Use a space to separate the two keys, we'll unpack them in our - // submit handler. - $options["$by_key $order_key"] = "$by_val $order_val"; - - if ($form['sort_order']['#default_value'] == $order_key && empty($selected)) { - // Respect default sort order set in Views. The default sort field - // will be the first one if there are multiple sort criteria. - $selected = "$by_key $order_key"; - } - } - } - - // Rewrite the option values if any were specified. - if (!empty($settings['sort']['advanced']['combine_rewrite'])) { - $options = $this->rewriteOptions($options, $settings['sort']['advanced']['combine_rewrite'], TRUE); - if (!isset($options[$selected])) { - // Avoid "illegal choice" errors if the selected option is - // eliminated by the rewrite. - $selected = NULL; - } - } + if (isset($bef_options['sort']['plugin_id']) && !empty($form['sort_by'])) { + $plugin_id = $bef_options['sort']['plugin_id']; + $plugin_configuration = $bef_options['sort']; - $form['sort_bef_combine'] = array( - '#options' => $options, - '#default_value' => $selected, - // Already sanitized by Views. - '#title' => $form['sort_by']['#title'], - ); - - // Handle display-specific details. - switch ($settings['sort']['bef_format']) { - case 'bef': - $form['sort_bef_combine']['#type'] = 'radios'; - $form['sort_bef_combine']['#theme'] = 'bef_radios'; - break; - - case 'bef_links': - $form['sort_bef_combine']['#theme'] = 'bef_links'; - - // Exposed form displayed as blocks can appear on pages other than - // the view results appear on. This can cause problems with - // select_as_links options as they will use the wrong path. We - // provide a hint for theme functions to correct this. - $form['sort_bef_combine']['#bef_path'] = $this->getExposedFormActionUrl(); - break; - - case 'default': - $form['sort_bef_combine']['#type'] = 'select'; - break; - } - - // Add our submit routine to process. - $form['#submit'][] = 'bef_sort_combine_submit'; - - // Pretend we're another exposed form widget. - $form['#info']['sort-sort_bef_combine'] = array( - 'value' => 'sort_bef_combine', - ); - - // Remove the existing sort_by and sort_order elements. - unset($form['sort_by']); - unset($form['sort_order']); - - if ($collapse) { - $sort_elems[] = 'sort_bef_combine'; - } - } - else { - // Leave sort_by and sort_order as separate elements. - if ('bef' == $settings['sort']['bef_format']) { - foreach (['sort_by', 'sort_order'] as $field) { - if (!empty($form[$field])) { - $form[$field]['#theme'] = 'bef_radios'; - $form[$field]['#type'] = 'radios'; - if (empty($form[$field]['#process'])) { - $form[$field]['#process'] = array(); - } - $form[$field]['#process'][] = ['\Drupal\Core\Render\Element\Radios', 'processRadios']; - } - } - } - elseif ('bef_links' == $settings['sort']['bef_format']) { - $form['sort_by']['#theme'] = 'bef_links'; - if(!empty($form['sort_order'])) { - $form['sort_order']['#theme'] = 'bef_links'; - } - - // Exposed form displayed as blocks can appear on pages other than the - // view results appear on. This can cause problems with - // select_as_links options as they will use the wrong path. We provide - // a hint for theme functions to correct this. - $form['sort_by']['#bef_path'] = $this->getExposedFormActionUrl(); - if(!empty($form['sort_order'])) { - $form['sort_order']['#bef_path'] = $this->getExposedFormActionUrl(); - } - } - - if ($collapse) { - $sort_elems[] = 'sort_by'; - if(!empty($form['sort_order'])) { - $sort_elems[] = 'sort_order'; - } - } - - // Add reset sort option if selected. - if ($settings['sort']['advanced']['reset']) { - array_unshift($form['sort_by']['#options'], $settings['sort']['advanced']['reset_label']); - } - } - /* Ends: if ($settings['sort']['advanced']['combine']) { ... } else { */ - - if ($collapse) { - $form['bef_sort_options'] = array( - '#type' => 'details', - '#title' => $settings['sort']['advanced']['collapsible_label'], - ); - foreach ($sort_elems as $elem) { - $form['bef_sort_options'][$elem] = $form[$elem]; - unset($form[$elem]); - } - } - - // Check if this is a secondary form element. - if ($allow_secondary && $settings['sort']['advanced']['is_secondary']) { - foreach (array('sort_bef_combine', 'sort_by', 'sort_order') as $elem) { - if (!empty($form[$elem])) { - $secondary[$elem] = $form[$elem]; - unset($form[$elem]); - } - } - } + /** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */ + $plugin = $this->sortWidgetManager->createInstance($plugin_id, $plugin_configuration); + $plugin->setView($this->view); + $plugin->exposedFormAlter($form, $form_state); } - /* Ends: if (isset($settings['sort'])) { */ /* * Handle exposed pager elements. */ - if (isset($settings['pager'])) { - $show_apply = TRUE; - - switch ($settings['pager']['bef_format']) { - case 'bef': - $form['items_per_page']['#type'] = 'radios'; - if (empty($form['items_per_page']['#process'])) { - $form['items_per_page']['#process'] = array(); - } - array_unshift($form['items_per_page']['#process'], 'form_process_radios'); - $form['items_per_page']['#prefix'] = '<div class="bef-sortby bef-select-as-radios">'; - $form['items_per_page']['#suffix'] = '</div>'; - break; - - case 'bef_links': - if (count($form['items_per_page']['#options']) > 1) { - $form['items_per_page']['#theme'] = 'select_as_links'; - $form['items_per_page']['#items_per_page'] = max($form['items_per_page']['#default_value'], key($form['items_per_page']['#options'])); - - // Exposed form displayed as blocks can appear on pages other than - // the view results appear on. This can cause problems with - // select_as_links options as they will use the wrong path. We - // provide a hint for theme functions to correct this. - $form['items_per_page']['#bef_path'] = $this->getExposedFormActionUrl(); - } - break; - } + $pager = $this->view->getPager(); + $is_pager_exposed = $pager && $pager->usesExposed(); + if ($is_pager_exposed && !empty($bef_options['pager']['plugin_id'])) { + $plugin_id = $bef_options['pager']['plugin_id']; + $plugin_configuration = $bef_options['pager']; - // Check if this is a secondary form element. - if ($allow_secondary && $settings['pager']['is_secondary']) { - foreach (array('items_per_page', 'offset') as $elem) { - if (!empty($form[$elem])) { - $secondary[$elem] = $form[$elem]; - unset($form[$elem]); - } - } - } + /** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */ + $plugin = $this->pagerWidgetManager->createInstance($plugin_id, $plugin_configuration); + $plugin->setView($this->view); + $plugin->exposedFormAlter($form, $form_state); } - // Shorthand for all filters in this view. - /* @var \Drupal\views\Plugin\views\HandlerBase[] $filters */ - $filters = $form_state->get('view')->display_handler->handlers['filter']; - - // Go through each saved option looking for Better Exposed Filter settings. - foreach ($settings as $label => $options) { - - // Sanity check: Ensure this filter is an exposed filter. - if (empty($filters[$label]) || !$filters[$label]->isExposed()) { - continue; - } - - // Form element is designated by the element ID which is user- - // configurable. - $field_id = $filters[$label]->options['expose']['identifier']; - - // Check for placeholder text. - if (!empty($settings[$label]['more_options']['placeholder_text'])) { - // @todo: Add token replacement for placeholder text. - $form[$field_id]['#placeholder'] = $settings[$label]['more_options']['placeholder_text']; - } - - // Handle filter value rewrites. - if (!empty($options['more_options']['rewrite']['filter_rewrite_values'])) { - $form[$field_id]['#options'] = $this->rewriteOptions($form[$field_id]['#options'] , $options['more_options']['rewrite']['filter_rewrite_values']); - if (isset($selected) && !isset($form[$field_id]['#options'][$selected])) { - // Avoid "Illegal choice" errors. - $form[$field_id]['#default_value'] = NULL; - } - } - - // @TODO: Is this conditional needed anymore after the existing settings - // array default values were added? - if (!isset($options['bef_format'])) { - $options['bef_format'] = ''; - } - - // These BEF options require a set of given options to work (namely, - // $form[$field_id]['#options'] needs to set). But it is possible to - // adjust settings elsewhere in the view that removes these options from - // the form (eg: changing a taxonomy term filter from dropdown to - // autocomplete). Check for that here and revert to Views' default filter - // in those cases. - $requires_options = array('bef', 'bef_ul', 'bef_links', 'bef_ul_links', 'bef_hidden'); - if (in_array($options['bef_format'], $requires_options)) { - if (empty($form[$field_id]['#options'])) { - $options['bef_format'] = 'default'; - } - else { - // Clean up filters that pass objects as options instead of strings. - $form[$field_id]['#options'] = $this->cleanOptions($form[$field_id]['#options']); - } - } - - switch ($options['bef_format']) { - case 'bef_datepicker': - $show_apply = TRUE; - $bef_add_js = TRUE; - $bef_js['datepicker'] = TRUE; - $bef_js['datepicker_options'] = array(); - - if (( - // Single Date API-based input element. - isset($form[$field_id]['value']['#type']) - && 'date_text' == $form[$field_id]['value']['#type'] - ) - // Double Date-API-based input elements such as "in-between". - || (isset($form[$field_id]['min']) && isset($form[$field_id]['max']) - && 'date_text' == $form[$field_id]['min']['#type'] - && 'date_text' == $form[$field_id]['max']['#type'] - )) { - /* - * Convert Date API formatting to jQuery formatDate formatting. - * - * @TODO: To be honest, I'm not sure this is needed. Can you set a - * Date API field to accept anything other than Y-m-d? Well, better - * safe than sorry... - * - * @see http://us3.php.net/manual/en/function.date.php - * @see http://docs.jquery.com/UI/Datepicker/formatDate - * - * Array format: PHP date format => jQuery formatDate format - * (comments are for the PHP format, lines that are commented out do - * not have a jQuery formatDate equivalent, but maybe someday they - * will...) - */ - $convert = array( - /* Day */ - - // Day of the month, 2 digits with leading zeros 01 to 31. - 'd' => 'dd', - // A textual representation of a day, three letters Mon through - // Sun. - 'D' => 'D', - // Day of the month without leading zeros 1 to 31. - 'j' => 'd', - // (lowercase 'L') A full textual representation of the day of the - // week Sunday through Saturday. - 'l' => 'DD', - // ISO-8601 numeric representation of the day of the week (added - // in PHP 5.1.0) 1 (for Monday) through 7 (for Sunday). - // 'N' => ' ', - // English ordinal suffix for the day of the month, 2 characters - // st, nd, rd or th. Works well with j. - // 'S' => ' ', - // Numeric representation of the day of the week 0 (for Sunday) - // through 6 (for Saturday). - // 'w' => ' ', - // The day of the year (starting from 0) 0 through 365. - 'z' => 'o', - - /* Week */ - // ISO-8601 week number of year, weeks starting on Monday (added - // in PHP 4.1.0) Example: 42 (the 42nd week in the year). - // 'W' => ' ', - // - /* Month */ - // A full textual representation of a month, such as January or - // March January through December. - 'F' => 'MM', - // Numeric representation of a month, with leading zeros 01 - // through 12. - 'm' => 'mm', - // A short textual representation of a month, three letters Jan - // through Dec. - 'M' => 'M', - // Numeric representation of a month, without leading zeros 1 - // through 12. - 'n' => 'm', - // Number of days in the given month 28 through 31. - // 't' => ' ', - // - /* Year */ - // Whether it's a leap year 1 if it is a leap year, 0 otherwise. - // 'L' => ' ', - // ISO-8601 year number. This has the same value as Y, except that - // if the ISO week number (W) belongs to the previous or next - // year, that year is used instead. (added in PHP 5.1.0). - // Examples: 1999 or 2003. - // 'o' => ' ', - // A full numeric representation of a year, 4 digits Examples: - // 1999 or 2003. - 'Y' => 'yy', - // A two digit representation of a year Examples: 99 or 03. - 'y' => 'y', - - /* Time */ - // Lowercase Ante meridiem and Post meridiem am or pm. - // 'a' => ' ', - // Uppercase Ante meridiem and Post meridiem AM or PM. - // 'A' => ' ', - // Swatch Internet time 000 through 999. - // 'B' => ' ', - // 12-hour format of an hour without leading zeros 1 through 12. - // 'g' => ' ', - // 24-hour format of an hour without leading zeros 0 through 23. - // 'G' => ' ', - // 12-hour format of an hour with leading zeros 01 through 12. - // 'h' => ' ', - // 24-hour format of an hour with leading zeros 00 through 23. - // 'H' => ' ', - // Minutes with leading zeros 00 to 59. - // 'i' => ' ', - // Seconds, with leading zeros 00 through 59. - // 's' => ' ', - // Microseconds (added in PHP 5.2.2) Example: 654321. - // 'u' => ' ', - ); - - $format = ''; - if (isset($form[$field_id]['value'])) { - $format = $form[$field_id]['value']['#date_format']; - $form[$field_id]['value']['#attributes']['class'][] = 'bef-datepicker'; - } - else { - // Both min and max share the same format. - $format = $form[$field_id]['min']['#date_format']; - $form[$field_id]['min']['#attributes']['class'][] = 'bef-datepicker'; - $form[$field_id]['max']['#attributes']['class'][] = 'bef-datepicker'; - } - $bef_js['datepicker_options']['dateformat'] = str_replace(array_keys($convert), array_values($convert), $format); - } - else { - /* - * Standard Drupal date field. Depending on the settings, the field - * can be at $form[$field_id] (single field) or - * $form[$field_id][subfield] for two-value date fields or filters - * with exposed operators. - */ - $fields = array('min', 'max', 'value'); - if (count(array_intersect($fields, array_keys($form[$field_id])))) { - foreach ($fields as $field) { - if (isset($form[$field_id][$field])) { - $form[$field_id][$field]['#attributes']['class'][] = 'bef-datepicker'; - } - } - } - else { - $form[$field_id]['#attributes']['class'][] = 'bef-datepicker'; - } - } - break; - - case 'bef_slider': - $show_apply = TRUE; - $form[$field_id]['#attached']['library'][] = 'core/jquery.ui.slider'; - $form[$field_id]['#attached']['library'][] = 'better_exposed_filters/sliders'; - - $form[$field_id]['#attached']['drupalSettings']['better_exposed_filters']['slider'] = TRUE; - $form[$field_id]['#attached']['drupalSettings']['better_exposed_filters']['slider_options'][$field_id] = [ - 'min' => $options['slider_options']['bef_slider_min'], - 'max' => $options['slider_options']['bef_slider_max'], - 'step' => $options['slider_options']['bef_slider_step'], - 'animate' => $options['slider_options']['bef_slider_animate'], - 'orientation' => $options['slider_options']['bef_slider_orientation'], - 'id' => Html::getUniqueId($field_id), - 'viewId' => $form['#id'], - ]; - break; - - case 'bef_links': - case 'bef_ul_links': - if ($options['bef_format'] == 'bef_ul_links') { - // Let the templates know this is a nested option. - $form[$field_id]['#bef_nested'] = TRUE; - } - - $form[$field_id]['#theme'] = 'bef_links'; - - // Exposed form displayed as blocks can appear on pages other than - // the view results appear on. This can cause problems with - // select_as_links options as they will use the wrong path. We provide - // a hint for theme functions to correct this. - $form[$field_id]['#bef_path'] = $this->getExposedFormActionUrl(); - break; - - case 'bef_single': - $show_apply = TRUE; - - // Use filter label as checkbox label. - $form[$field_id]['#title'] = $filters[$label]->options['expose']['label']; - $form[$field_id]['#type'] = 'checkbox'; - // Views populates missing values in $form_state['input'] with the - // defaults and a checkbox does not appear in $_GET (or $_POST) so it - // will appear to be missing when a user submits a form. Because of - // this, instead of unchecking the checkbox value will revert to the - // default. More, the default value for select values is reused which - // results in the checkbox always checked. So we need to add a form - // element to see whether the form is submitted or not and then we - // need to look at $_GET directly to see whether the checkbox is - // there. For security reasons, we must not copy the $_GET value. - - // First, let's figure out a short name for the signal element and - // then add it. - if (empty($signal)) { - for ($signal = 'a'; isset($form[$signal]); $signal++); - // This is all the signal element needs. - $form[$signal]['#type'] = 'hidden'; - } - $input = $form_state->getUserInput(); - $value = \Drupal::request()->query->get($field_id); - $checked = isset($input[$signal]) ? isset($value) : $form[$field_id]['#default_value']; - // For security, we check if value is valid and exist. - if ($checked) { - if (!in_array($value, array_keys($form[$field_id]['#options']))) { - $checked = FALSE; - } - } - // Now we know whether the checkbox is checked or not, set #value - // accordingly. - $form[$field_id]['#value'] = $checked ? $value : 0; - break; - - case 'bef': - case 'bef_ul': - if ($options['bef_format'] == 'bef_ul') { - $form[$field_id]['#bef_nested'] = TRUE; - } - $show_apply = TRUE; - - // Clean up objects from the options array (happens for taxonomy- - // based filters). - $form[$field_id]['#options'] = $this->cleanOptions($form[$field_id]['#options']); - - // Render as either radio buttons or checkboxes. - if (empty($form[$field_id]['#multiple'])) { - // Single-select -- display as radio buttons. - $form[$field_id]['#type'] = 'radios'; - $form[$field_id]['#theme'] = 'bef_radios'; - if (empty($form[$field_id]['#process'])) { - $form[$field_id]['#process'] = array(); - } - $form[$field_id]['#process'][] = ['\Drupal\Core\Render\Element\Radios', 'processRadios']; - } - else { - $form[$field_id]['#type'] = 'checkboxes'; - $form[$field_id]['#theme'] = 'bef_checkboxes'; - - if ($options['more_options']['bef_select_all_none'] || $options['more_options']['bef_select_all_none_nested']) { - $form[$field_id]['#attached']['library'][] = 'better_exposed_filters/select_all_none'; - - $form[$field_id]['#bef_select_all_none'] = $options['more_options']['bef_select_all_none']; - $form[$field_id]['#bef_select_all_none_nested'] = $options['more_options']['bef_select_all_none_nested']; - } - } - break; - - case 'bef_hidden': - if (empty($form[$field_id]['#multiple'])) { - // Single entry filters can simple be changed to a different element - // type. - $form[$field_id]['#type'] = 'hidden'; - } - else { - // Hide the label. - $form['#info']["filter-$label"]['label'] = ''; + /* + * Handle exposed filters. + */ - // Use BEF's preprocess and template to output the hidden elements. - $form[$field_id]['#theme'] = 'bef_hidden'; - } - break; + // Shorthand for all filter handlers in this view. + /* @var \Drupal\views\Plugin\views\HandlerBase[] $filters */ + $filters = $this->view->display_handler->handlers['filter']; - default: - $show_apply = TRUE; - break; - } - /* Ends switch ($options['bef_format']) */ - - // Collapsible filters are an option for all exposed filters. - if (!empty($options['more_options']['bef_collapsible'])) { - // Pass the description and title along in a way such that it does - // not get rendered as part of the exposed form widget. We render - // them as part of the details element instead. - $form[$field_id]['#theme_wrappers'] = [ - 'details' => [ - '#title' => $form['#info']["filter-$label"]['label'] ?: '', - '#description' => $form['#info']["filter-$label"]['description'] ?: '', - // Needed to keep styling consistent with other exposed options. - '#attributes' => [ - 'class' => ['form-item'], - ], - '#value' => NULL, - ], - ]; - $form['#info']["filter-$label"]['label'] = ''; - $form['#info']["filter-$label"]['description'] = ''; - - // Check if the operator is exposed for this filter. - if (isset($this->view->getHandlers('filter')[$label]) - && $this->view->getHandlers('filter')[$label]['expose']['use_operator'] - ) { - // Include the exposed operator with the filter. - $operator_id = $this->view->getHandlers('filter')[$label]['expose']['operator_id']; - $form[$field_id][$operator_id] = $form[$operator_id]; - unset($form[$operator_id]); + // Iterate over all exposed filters. + if (!empty($bef_options['filter'])) { + foreach ($bef_options['filter'] as $filter_id => $filter_options) { + // Sanity check: Ensure this filter is an exposed filter. + if (empty($filters[$filter_id]) || !$filters[$filter_id]->isExposed()) { + continue; } - } - // Check if this is a secondary form element. - if ($allow_secondary && $settings[$label]['more_options']['is_secondary']) { - if ($filters[$label]->options['is_grouped']) { - $identifier = $filters[$label]->options['group_info']['identifier']; - $filter_info_name = "filter-$identifier"; - } - else { - $identifier = $filters[$label]->options['expose']['identifier']; - $filter_info_name = "filter-$label"; - } - if (!empty($form[$identifier])) { - // Move exposed operators with exposed filters - if (!empty($this->view->getHandlers('filter')[$label]['expose']['use_operator'])) { - $op_id = $this->view->getHandlers('filter')[$label]['expose']['operator_id']; - $secondary[$op_id] = $form[$op_id]; - unset($form[$op_id]); - } - $secondary[$identifier] = $form[$identifier]; - unset($form[$identifier]); - $secondary[$identifier]['#title'] = $form['#info'][$filter_info_name]['label']; - $secondary[$identifier]['#description'] = $form['#info'][$filter_info_name]['description']; - unset($form['#info'][$filter_info_name]); + $plugin_id = $filter_options['plugin_id']; + if ($plugin_id) { + /** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */ + $plugin = $this->filterWidgetManager->createInstance($plugin_id, $filter_options); + $plugin->setView($this->view); + $plugin->setViewsHandler($filters[$filter_id]); + $plugin->exposedFormAlter($form, $form_state); } } } // If our form has no visible filters, hide the submit button. - $form['submit']['#access'] = $show_apply; - $form['reset']['#access'] = $show_apply; - - // Add Javascript as needed. - if ($bef_add_js) { - // Add jQuery UI library code as needed. - if ($bef_js['datepicker']) { - $form['#attached']['library'][] = 'core/jquery.ui.datepicker'; - $form['#attached']['library'][] = 'better_exposed_filters/datepickers'; - } - - $form['#attached']['drupalSettings']['better_exposed_filters'] = $bef_js; - } + $has_visible_filters = !empty(Element::getVisibleChildren($form)) ?: FALSE; + $form['actions']['submit']['#access'] = $has_visible_filters; + $form['actions']['reset']['#access'] = $has_visible_filters; - // Check for secondary elements. - if ($allow_secondary && !empty($secondary)) { - // Use weights to place the secondary elements just ahead of the - // submit/reset buttons, but after all the other expsoed elements. - foreach(Element::children($form) as $index => $child) { - $form[$child]['#weight'] = $index; - } - $form['secondary'] = $secondary; - $form['secondary']['#weight'] = count($form['#info']) -1; - $form['#info']['filter-secondary']['value'] = 'secondary'; + // Ensure default process/pre_render callbacks are included when a BEF + // widget has added their own. + foreach (Element::children($form) as $key) { + $element = &$form[$key]; + $this->addDefaultElementInfo($element); } } @@ -1304,7 +835,7 @@ public function exposedFormAlter(&$form, FormStateInterface $form_state) { protected function exposedFilterApplied() { // If the input required option is set, check to see if a filter option has // been set. - if (!empty($this->options['input_required'])) { + if (!empty($this->options['bef']['general']['input_required'])) { return parent::exposedFilterApplied(); } else { @@ -1313,325 +844,72 @@ protected function exposedFilterApplied() { } /** - * Rewrites a set of options given a string from the config form. + * Inserts a new form element before another element identified by $key. * - * Rewrites should be specified, one per line, using the format - * old_string|new_string. If new_string is empty, the option will be removed. + * This can be useful when reordering existing form elements without weights. * - * @param array $options - * An array of key => value pairs that may be rewritten. - * @param string $rewriteSettings - * String representing the entry in the settings form. - * @param bool $reorder - * Reorder $options based on the rewrite settings. + * @param array $form + * The form array to insert the element into. + * @param string $key + * The key of the form element you want to prepend the new form element. + * @param array $element + * The form element to insert. * * @return array - * Rewritten $options. + * The form array containing the newly inserted element. */ - protected function rewriteOptions(array $options, $rewriteSettings, $reorder = FALSE) { - if (empty($rewriteSettings) || !is_string($rewriteSettings)) { - return $options; - } - $rewrites = []; - $order = []; - if (!$reorder) { - $order = array_keys($options); - } - $lines = preg_split("/(\r\n|\n|\r)/", trim($rewriteSettings)); - foreach ($lines as $line) { - list($search, $replace) = explode('|', $line); - if (!empty($search)) { - $rewrites[$search] = trim($replace); - if ($reorder) { - // Reorder options in the order they are specified in rewrites. - // Collect the keys to use later. - // @TODO: need to handle taxonomy terms before exposing this option. - $key = array_search($search, $options); - if ($key !== FALSE) { - $order[] = $key; - } - } - } - } - - $return = []; - if ($reorder && !empty($order)) { - // Start with the items that were listed in the rewrite settings. - foreach ($order as $key) { - $return[$key] = $options[$key]; - unset($options[$key]); - } - - // Tack remaaining options on the end. - $return += $options; - } - else { - $return = $options; - } - - // Rewrite the option text. - foreach ($return as $index => $option) { - // Handle taxonomy terms which use a stdClass option object that doesn't - // implement __toString(). - if ($option instanceof \stdClass && isset($option->option) && is_array($option->option)) { - $option = array_values($option->option)[0]; - } - else if (!is_string($option)) { - // Some options, such as "- Any -", are passed as TranslatableMarkup - // objects. Convert them to strings for the comparison. - $option = (string) $option; - } - if (!is_string($option)) { - // We give up... - continue; - } - - if (isset($rewrites[$option])) { - if ('' == $rewrites[$option]) { - unset($return[$index]); - } - else { - if ($return[$index] instanceof \stdClass) { - $tid = key($return[$index]->option); - $text = current($return[$index]->option); - $return[$index]->option[$tid] = $rewrites[$text]; - } - else { - $return[$index] = $rewrites[$option]; - } - } - } - } - return $return; + protected function prependFormElement(array $form, $key, array $element) { + $pos = array_search($key, array_keys($form)) + 1; + return array_splice($form, 0, $pos - 1) + $element + $form; } /** - * Cleans up options being sent to radio button or checkbox renderers. + * Adds default element callbacks. * - * @param array $options - * The options array to clean. + * This is a workaround where adding process and pre-render functions are not + * results in replacing the default ones instead of merging. * - * @return array - * Cleaned options array. - */ - protected function cleanOptions(array $options) { - // Check the first element to see if we need to clean this array. If it is - // a scalar, then just return the array as-is. - if (is_scalar(reset($options))) { - return $options; - } - - $clean = []; - foreach ($options as $index => $value) { - // Some options, such as the "any" text, are really just plain - // text so we keep those as they are. Others, like taxonomy terms - // need to be converted to text. - if (is_object($value) && !is_a($value, 'Drupal\Core\StringTranslation\TranslatableMarkup')) { - reset($value->option); - $key = key($value->option); - $val = current($value->option); - $clean[$key] = $val; - } - else { - $clean[$index] = $value; - } - } - return $clean; - } - - /** - * Fills in missing settings with default values. + * @param array $element + * The render array for a single form element. * - * Similar to array_merge_recursive, but later numeric keys overwrites earlier - * values. Use this to set defaults for missing values in a multi-dimensional - * array. Eg: - * - * $existing = $this->setDefaults($defaults, $existing); - * - * @return array - * The resulting settings array + * @todo remove once the following issues are resolved. + * @see https://www.drupal.org/project/drupal/issues/2070131 + * @see https://www.drupal.org/project/drupal/issues/2190333 */ - protected function setDefaults() { - $count = func_num_args(); - if (!$count) { - return; - } - elseif (1 == $count) { - return (func_get_arg(0)); - } - - // First array is the default values. - $params = func_get_args(); - $return = array_shift($params); - - // Merge the rest of the arrays onto the default array. - foreach ($params as $array) { - foreach ($array as $key => $value) { - // Numeric keyed values are added (unless already there). - if (is_numeric($key) && !in_array($value, $return)) { - if (is_array($value)) { - $return[] = $this->setDefaults($return[$key], $value); - } - else { - $return[] = $value; - } - } - // String keyed values are replaced. - else { - if (isset($return[$key]) && is_array($value) && is_array($return[$key])) { - $return[$key] = $this->setDefaults($return[$key], $value); - } - else { - $return[$key] = $value; - } - } + protected function addDefaultElementInfo(array &$element) { + /** @var \Drupal\Core\Render\ElementInfoManager $element_info_manager */ + $element_info = \Drupal::service('element_info'); + if (isset($element['#type']) && empty($element['#defaults_loaded']) && ($info = $element_info->getInfo($element['#type']))) { + $element['#process'] = $element['#process'] ?? []; + $element['#pre_render'] = $element['#pre_render'] ?? []; + if (!empty($info['#process'])) { + $element['#process'] = array_merge($info['#process'], $element['#process']); } - } - return $return; - } - - /** - * Updates legacy settings to their current location. - * - * @param array $settings - * Array of BEF settings. - */ - protected function updateLegacySettings($settings) { - // There has got to be a better way... But for now, this works. - if (isset($settings['sort']['collapsible'])) { - $settings['sort']['advanced']['collapsible'] = $settings['sort']['collapsible']; - unset($settings['sort']['collapsible']); - } - if (isset($settings['sort']['collapsible_label'])) { - $settings['sort']['advanced']['collapsible_label'] = $settings['sort']['collapsible_label']; - unset($settings['sort']['collapsible_label']); - } - if (isset($settings['sort']['combine'])) { - $settings['sort']['advanced']['combine'] = $settings['sort']['combine']; - unset($settings['sort']['combine']); - } - if (isset($settings['sort']['reset'])) { - $settings['sort']['advanced']['reset'] = $settings['sort']['reset']; - unset($settings['sort']['reset']); - } - if (isset($settings['sort']['reset_label'])) { - $settings['sort']['advanced']['reset_label'] = $settings['sort']['reset_label']; - unset($settings['sort']['reset_label']); - } - - return $settings; - } - - /** - * Returns an array of default or current existing values for BEF settings. - * - * This helps us as we add new options and prevents a lot of - * @code - * if (isset($settings['new_settings'])) { ... } - * @endcode - * as there will be a default value at all positions in the settings array. - * Also updates legacy settings to their new locations via - * updateLegacySettings(). - * - * @return array - * Multi-dimensional settings array. - */ - protected function getSettings() { - // General, sort, pagers, etc. - $defaults = array( - 'general' => array( - 'allow_secondary' => FALSE, - 'secondary_label' => $this->t('Advanced options'), - 'autosubmit' => FALSE, - 'autosubmit_exclude_textfield' => FALSE, - 'autosubmit_hide' => FALSE, - ), - 'sort' => array( - 'bef_format' => 'default', - 'advanced' => array( - 'collapsible' => FALSE, - 'collapsible_label' => '', - 'combine' => FALSE, - 'combine_rewrite' => '', - 'reset' => FALSE, - 'reset_label' => '', - 'is_secondary' => FALSE, - ), - ), - 'pager' => array( - 'bef_format' => 'default', - 'is_secondary' => FALSE, - ), - ); - - // Update legacy settings in the exposed form settings form. This - // keep us from losing settings when an option is put into an - // 'advanced options' details element. - $current = []; - if (isset($this->options['bef'])) { - $current = $this->updateLegacySettings($this->options['bef']); - } - - // Collect existing values or use defaults. - $settings = $this->setDefaults($defaults, $current); - - // Filter default values. - $filter_defaults = array( - 'bef_format' => 'default', - 'more_options' => array( - 'placeholder_text' => '', - 'bef_select_all_none' => FALSE, - 'bef_select_all_none_nested' => FALSE, - 'bef_collapsible' => FALSE, - 'is_secondary' => FALSE, - 'rewrite' => array( - 'filter_rewrite_values' => '', - ), - ), - 'slider_options' => array( - 'bef_slider_min' => 0, - 'bef_slider_max' => 99999, - 'bef_slider_step' => 1, - 'bef_slider_animate' => '', - 'bef_slider_orientation' => 'horizontal', - ), - ); - - // Go through each exposed filter and collect settings. - /* @var \Drupal\views\Plugin\views\HandlerBase $filter */ - foreach ($this->view->display_handler->getHandlers('filter') as $label => $filter) { - if (!$filter->isExposed()) { - continue; + if (!empty($info['#pre_render'])) { + $element['#pre_render'] = array_merge($info['#pre_render'], $element['#pre_render']); } - // Get existing values or use defaults. - if (!isset($this->options['bef'][$label])) { - // First time opening the settings form with a new filter. - $settings[$label] = $filter_defaults; + // Some processing needs to happen prior to the default form element + // callbacks (e.g. sort). We use the custom '#pre_process' array for this. + if (!empty($element['#pre_process'])) { + $element['#process'] = array_merge($element['#pre_process'], $element['#process']); } - else { - $settings[$label] = $this->setDefaults($filter_defaults, $this->options['bef'][$label]); + + // Workaround to add support for #group FAPI to all elements currently not + // supported. + // @todo remove once core issue is resolved. + // @see https://www.drupal.org/project/drupal/issues/2190333 + if (!in_array('processGroup', array_column($element['#process'], 1))) { + $element['#process'][] = ['\Drupal\Core\Render\Element\RenderElement', 'processGroup']; + $element['#pre_render'][] = ['\Drupal\Core\Render\Element\RenderElement', 'preRenderGroup']; } } - return $settings; - } - /** - * Returns exposed form action URL object. - * - * @return \Drupal\Core\Url - * Url object. - */ - protected function getExposedFormActionUrl() { - if ($this->displayHandler->getRoutedDisplay()) { - return $this->displayHandler->getUrl(); + // Apply the same to any nested children. + foreach (Element::children($element) as $key) { + $child = &$element[$key]; + $this->addDefaultElementInfo($child); } - - $request = \Drupal::request(); - $url = Url::createFromRequest($request); - $url->setAbsolute(); - - return $url; } } diff --git a/web/modules/better_exposed_filters/templates/bef-checkboxes.html.twig b/web/modules/better_exposed_filters/templates/bef-checkboxes.html.twig index d966c1b823..db1cf0dbeb 100644 --- a/web/modules/better_exposed_filters/templates/bef-checkboxes.html.twig +++ b/web/modules/better_exposed_filters/templates/bef-checkboxes.html.twig @@ -2,6 +2,7 @@ Themes Views' default multi-select element as a set of checkboxes. Available variables: + - wrapper_attributes: attributes for the wrapper element. - element: The collection of checkboxes. - children: An array of keys for the children of element. - is_nested: TRUE if this is to be rendered as a nested list. @@ -9,19 +10,18 @@ child_id => nesting_level which defines the depth a given element should appear in the nested list. #} -{% - set classes = [ - 'form-checkboxes', - isNested ? 'bef-nested', - hasSelectAllNone ? 'bef-select-all-none', - hasSelectAllNoneNested ? 'bef-select-all-none-nested', - ] -%} -<div{{ attributes.addClass(classes) }}> +{% set classes = [ + 'form-checkboxes', + is_nested ? 'bef-nested', + show_select_all_none ? 'bef-select-all-none', + show_select_all_none_nested ? 'bef-select-all-none-nested', + display_inline ? 'form--inline' +] %} +<div{{ wrapper_attributes.addClass(classes) }}> {% set current_nesting_level = 0 %} {% for child in children %} {% set item = attribute(element, child) %} - {% if isNested %} + {% if is_nested %} {% set new_nesting_level = attribute(depth, child) %} {% include '@better_exposed_filters/bef-nested-elements.html.twig' %} {% set current_nesting_level = new_nesting_level %} diff --git a/web/modules/better_exposed_filters/templates/bef-hidden.html.twig b/web/modules/better_exposed_filters/templates/bef-hidden.html.twig index 1a91ac5d7b..4699c2ca1c 100644 --- a/web/modules/better_exposed_filters/templates/bef-hidden.html.twig +++ b/web/modules/better_exposed_filters/templates/bef-hidden.html.twig @@ -2,11 +2,11 @@ Themes Views' default multi-select element as hidden input elements. Available variables: - - hiddenElements: Array of hidden input elements for all possible filter + - hidden_elements: Array of hidden input elements for all possible filter options. - selected: Array of selected values. #} -{% for value, hidden in hiddenElements %} +{% for value, hidden in hidden_elements %} {% if value in selected %} {{ hidden }} {% endif %} diff --git a/web/modules/better_exposed_filters/templates/bef-links.html.twig b/web/modules/better_exposed_filters/templates/bef-links.html.twig index 5b75c41cf2..feeae01bc8 100644 --- a/web/modules/better_exposed_filters/templates/bef-links.html.twig +++ b/web/modules/better_exposed_filters/templates/bef-links.html.twig @@ -1,22 +1,18 @@ {% set classes = [ 'bef-links', + is_nested ? 'bef-nested' ] %} -<input type="hidden" name="{{ element["#name"] }}" value="{{ element["#value"] }}" /> +{% + set is_nested = true +%} <div{{ attributes.addClass(classes) }}> {% set current_nesting_level = 0 %} {% for child in children %} {% set item = attribute(element, child) %} - {% if child in selected %} - {% set item = item|merge({'#attributes': {'class': 'bef-link-active'}}) %} - {% endif %} - {% if isNested %} - {% set new_nesting_level = attribute(depth, child) %} - {% include '@better_exposed_filters/bef-nested-elements.html.twig' %} - {% set current_nesting_level = new_nesting_level %} - {% else %} - {{ item }} - {% endif %} + {% set new_nesting_level = attribute(depth, child) %} + {% include '@better_exposed_filters/bef-nested-elements.html.twig' %} + {% set current_nesting_level = new_nesting_level %} {% endfor %} </div> diff --git a/web/modules/better_exposed_filters/templates/bef-nested-elements.html.twig b/web/modules/better_exposed_filters/templates/bef-nested-elements.html.twig index 157b00e139..176664cc5c 100644 --- a/web/modules/better_exposed_filters/templates/bef-nested-elements.html.twig +++ b/web/modules/better_exposed_filters/templates/bef-nested-elements.html.twig @@ -12,7 +12,7 @@ - loop: The loop variable from the iterator that calls this template. Requires the loop.first and loop.last elements. #} -{% spaceless %} +{% apply spaceless %} {% set delta = (current_nesting_level - new_nesting_level) | abs %} {% if loop.first %} <ul> @@ -22,7 +22,7 @@ {% if new_nesting_level > current_nesting_level %} <ul> {% else %} - </li></ul> + </ul> {% endif %} {% endfor %} {% endif %} @@ -33,8 +33,7 @@ {% if loop.last %} {# Close any remaining <li> tags #} {% for i in new_nesting_level..0 %} - </li> + </li></ul> {% endfor %} - </ul> {% endif %} -{% endspaceless %} +{% endapply %} diff --git a/web/modules/better_exposed_filters/templates/bef-radios.html.twig b/web/modules/better_exposed_filters/templates/bef-radios.html.twig index 5a0d9d3efb..eedec626a8 100644 --- a/web/modules/better_exposed_filters/templates/bef-radios.html.twig +++ b/web/modules/better_exposed_filters/templates/bef-radios.html.twig @@ -2,6 +2,7 @@ Themes a single-select exposed form element as radio buttons. Available variables: + - wrapper_attributes: attributes for the wrapper element. - element: The collection of checkboxes. - children: An array of keys for the children of element. - is_nested: TRUE if this is to be rendered as a nested list. @@ -12,14 +13,15 @@ {% set classes = [ 'form-radios', - isNested ? 'bef_nested' + is_nested ? 'bef-nested', + display_inline ? 'form--inline' ] %} -<div{{ attributes.addClass(classes) }}> +<div{{ wrapper_attributes.addClass(classes) }}> {% set current_nesting_level = 0 %} {% for child in children %} {% set item = attribute(element, child) %} - {% if isNested %} + {% if is_nested %} {% set new_nesting_level = attribute(depth, child) %} {% include '@better_exposed_filters/bef-nested-elements.html.twig' %} {% set current_nesting_level = new_nesting_level %} diff --git a/web/modules/better_exposed_filters/tests/bef_test/bef_test.info.yml b/web/modules/better_exposed_filters/tests/bef_test/bef_test.info.yml deleted file mode 100644 index 6c45052899..0000000000 --- a/web/modules/better_exposed_filters/tests/bef_test/bef_test.info.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: BEF Test -type: module -description: Test module for Better Exposed Filters -# core: 8.x -package: Views -dependencies: - - better_exposed_filters - - datetime - - node - - options - - user - -# Information added by Drupal.org packaging script on 2019-02-18 -version: '8.x-3.0-alpha6' -core: '8.x' -project: 'better_exposed_filters' -datestamp: 1550449395 diff --git a/web/modules/better_exposed_filters/tests/better_exposed_filters.test b/web/modules/better_exposed_filters/tests/better_exposed_filters.test deleted file mode 100644 index 5648ed7553..0000000000 --- a/web/modules/better_exposed_filters/tests/better_exposed_filters.test +++ /dev/null @@ -1,476 +0,0 @@ -<?php -/** - * @file - * Tests for the Better Exposed Filters module. - */ - -use \Drupal\views\Tests\ViewTestBase; -use Drupal\Core\StringTranslation\StringTranslationTrait; - -/** - * Functional test for Better Exposed Filters - */ -class BEF_TestCase extends DrupalWebTestCase { - - use StringTranslationTrait; - protected $view_name; - protected $privileged_user; - protected $views_version; - - public static function getInfo() { - return array( - 'name' => 'BEF Basic functionality tests', - 'description' => 'Basic tests for Better Exposed Filters.', - 'group' => 'Better Exposed Filters', - ); - } - - /** - * After setup() runs, the test suite will have access to: - * - * - $this->privileged_user: User with 'administer views' privileges. - * - $this->view_name: A view with: - * - node->title, node->status and node->type exposed filters - * - node->title as a displayed field - */ - public function setUp() { - parent::setUp('views', 'views_ui', 'better_exposed_filters'); // Enable any modules required for the test - - // @TODO: Is there a better way? Hopefully... - $this->views_version = substr(views_api_version(), 0, 1); - - // User with edit views perms - $this->privileged_user = $this->drupalCreateUser(array('access content', 'administer views')); - $this->drupalLogin($this->privileged_user); - - // Build a basic view - $this->view_name = $this->randomName(8); - $this->drupalPost('admin/build/views/add', array('name' => $this->view_name), 'Next'); - - // Add filters to the default display - $this->_befAddFilter('node.title'); - $this->_befAddFilter('node.status'); - $this->_befAddFilter('node.type'); - - // Add field to default display - $this->_befAddField('node.title'); - - $this->_befSaveView(); - } - - /** - * Verify BEF options display for binary and unlimited option fields - */ - public function testsimpletest_befOptionsExist() { - // Check node->status (binary type) - $this->drupalGet("admin/build/views/nojs/config-item/$this->view_name/default/filter/status"); - $this->assertText( - $this->t('Display exposed filter as'), - 'Verify BEF settings dropdown label displays', - 'Better Exposed Filters' - ); - $this->assertFieldById('edit-options-expose-bef-format'); - - // Check node->status (binary type) - $this->drupalGet("admin/build/views/nojs/config-item/$this->view_name/default/filter/type"); - $this->assertText( - $this->t('Display exposed filter as'), - 'Verify BEF settings dropdown label displays', - 'Better Exposed Filters' - ); - $this->assertFieldById('edit-options-expose-bef-format'); - } - - /** - * Verify BEF options are saved and redisplayed properly - * - * @TODO: Need to extend this to cover filters in derived vs. default displays as well as editing - * default filter options from derived displays - */ - public function testsimpletest_befOptionsSave() { - $edit = array( - 'options[expose][bef_format]' => 'bef', - 'options[expose][bef_filter_description]' => $this->randomName(16), - 'options[expose][bef_select_all_none]' => TRUE, - 'options[expose][bef_collapsible]' => TRUE, - ); - $this->drupalPost("admin/build/views/nojs/config-item/$this->view_name/default/filter/status", $edit, 'Update'); - $this->drupalGet("admin/build/views/nojs/config-item/$this->view_name/default/filter/status"); - foreach ($edit as $name => $expected) { - $this->assertFieldByName($name, $expected); - } - } - - /** - * Verify BEF radio buttons are rendered correctly on a page view - */ - public function testsimpletest_befPageDisplayRadios() { - $bef_settings = array( - 'bef_format' => 'bef', - 'bef_filter_description' => $this->randomName(16), - 'bef_select_all_none' => TRUE, - ); - $this->_befExposedFilterSettings('status', $bef_settings); - - // Add a page view to the default view - $settings = array( - 'path' => array('path' => $this->randomName(8)), - ); - $this->_befCreateDisplay('page', $settings); - $this->_befSaveView(); - - $this->drupalGet($settings['path']['path']); - $this->assertText(t('Published'), 'Verify exposed filter label', 'Better Exposed Filters'); - $this->assertFieldByXpath( - '//input[@name="status" and @type="radio"]', - NULL, - 'Exposed filter is shown as radio buttons', - 'Better Exposed Filters' - ); - } - - /** - * Verify BEF radio buttons are rendered correctly in a fieldset (collapsible == TRUE) - */ - public function testsimpletest_befPageDisplayRadiosFieldset() { - $bef_settings = array( - 'bef_format' => 'bef', - 'bef_filter_description' => $this->randomName(16), - 'bef_select_all_none' => TRUE, - 'bef_collapsible' => TRUE, - ); - $this->_befExposedFilterSettings('status', $bef_settings); - - // Add a page view to the default view - $settings = array( - 'path' => array('path' => $this->randomName(8)), - ); - $this->_befCreateDisplay('page', $settings); - $this->_befSaveView(); - - $this->drupalGet($settings['path']['path']); - $this->assertText(t('Published'), 'Verify exposed filter label', 'Better Exposed Filters'); - $this->assertFieldByXpath( - '//fieldset//input[@name="status" and @type="radio"]', - NULL, - 'Radio buttons are enclosed by a fieldset', - 'Better Exposed Filters' - ); - $this->assertFieldByXpath( - '//fieldset/legend', - $this->t('Published'), - 'Filter label is used as the fieldset legend', - 'Better Exposed Filters' - ); - } - -/** - * Verify that checkboxes are rendered correctly on a page display - */ - public function testsimpletest_befPageDisplayCheckboxes() { - // Exposed the type filter such that it allows multiple selections - $bef_settings = array( - 'bef_format' => 'bef', - 'bef_filter_description' => $this->randomName(16), - 'bef_select_all_none' => TRUE, - 'single' => FALSE, - ); - $this->_befExposedFilterSettings('type', $bef_settings); - - // Add a page view to the default view - $settings = array( - 'path' => array('path' => $this->randomName(8)), - ); - $this->_befCreateDisplay('page', $settings); - $this->_befSaveView(); - - $this->drupalGet($settings['path']['path']); - $this->assertText(t('Node: Type'), 'Verify exposed filter label', 'Better Exposed Filters'); - $this->assertFieldByXpath( - '//input[@name="type[]" and @type="checkbox"]', - NULL, - 'Exposed filter is shown as checkboxes', - 'Better Exposed Filters' - ); - $this->assertFieldByXpath( - '//div[contains(@class, "bef-select-all-none")]', - NULL, - 'Class is set correctly for JS to build select all/none links', - 'Beter Exposed Filters' - ); - } - -/** - * Verify that checkboxes are rendered correctly on a page display - */ - public function testsimpletest_befPageDisplayCheckboxesFieldset() { - // Exposed the type filter such that it allows multiple selections - $bef_settings = array( - 'bef_format' => 'bef', - 'bef_filter_description' => $this->randomName(16), - 'bef_select_all_none' => TRUE, - 'bef_collapsible' => TRUE, - 'single' => FALSE, - ); - $this->_befExposedFilterSettings('type', $bef_settings); - - // Add a page view to the default view - $settings = array( - 'path' => array('path' => $this->randomName(8)), - ); - $this->_befCreateDisplay('page', $settings); - $this->_befSaveView(); - - $this->drupalGet($settings['path']['path']); - $this->assertText(t('Node: Type'), 'Verify exposed filter label', 'Better Exposed Filters'); - - $this->assertFieldByXpath( - '//fieldset//input[@name="type[]" and @type="checkbox"]', - NULL, - 'Radio buttons are enclosed by a fieldset', - 'Better Exposed Filters' - ); - $this->assertFieldByXpath( - '//fieldset//div[contains(@class, "bef-select-all-none")]', - NULL, - 'Class is set correctly for JS to build select all/none links', - 'Beter Exposed Filters' - ); - $this->assertFieldByXpath( - '//fieldset/legend', - $this->t('Node: Type'), - 'Filter label is used as the fieldset legend', - 'Better Exposed Filters' - ); - } - - - - /** - * Verify hidden exposed filters are rendered correctly on a page display - * @TODO: check the functionality of a multi-pass filter using hidden BEF settings. - */ - public function testsimpletest_befPageDisplayHidden() { - $this->_befExposedFilterSettings('type', array('bef_format' => 'bef_hidden')); - - // Add a page view to the default view - $settings = array( - 'path' => array('path' => $this->randomName(8)), - ); - $this->_befCreateDisplay('page', $settings); - $this->_befSaveView(); - - $this->drupalGet($settings['path']['path']); - $this->assertNoText(t('Node: Type'), 'Verify hidden label does not appear', 'Better Exposed Filters'); - } - - /** - * Verify taxonomy-based exposed filters display correctly as both radio buttons and checkboxes - */ - public function testsimpletest_befTaxonomyFilters() { - $this->taxonomy_user = $this->drupalCreateUser(array('access content', 'administer views', 'administer taxonomy')); - $this->drupalLogin($this->taxonomy_user); - - // Add a vocabulary and populate with a few terms - $edit = array( - 'name' => 'BEF test vocab', - 'nodes[page]' => TRUE, - 'nodes[story]' => TRUE, - ); - $this->drupalPost('admin/content/taxonomy/add/vocabulary', $edit, 'Save'); - $terms = array('test1', 'test2', 'test3', 'test4'); - foreach ($terms as $term) { - $this->drupalPost('admin/content/taxonomy/1/add/term', array('name' => $term), 'Save'); - } - - // Exposed the taxonomy filter - $addl = array( - 'options[vid]' => TRUE, - 'options[type]' => 'select', - 'options[hierarchy]' => FALSE, - ); - $this->_befAddFilter('term_node.tid', TRUE, 'default', $addl); - - // Exposed filter settings - $bef_settings = array( - 'bef_format' => 'bef', - 'bef_filter_description' => $this->randomName(16), - ); - $this->_befExposedFilterSettings('tid', $bef_settings); - - // Add a page view to the default view - $settings = array( - 'path' => array('path' => $this->randomName(8)), - ); - $this->_befCreateDisplay('page', $settings); - $this->_befSaveView(); - - // Verify taxonomy filter as radio buttons - $this->drupalGet($settings['path']['path']); - $this->assertText(t('Taxonomy: Term'), 'Verify exposed filter label', 'Better Exposed Filters'); - $this->assertFieldByXpath( - '//input[@name="tid" and @type="radio"]', - NULL, - 'Exposed filter is shown as radio buttons', - 'Better Exposed Filters' - ); - - // Set Force single to FALSE to display as checkboxes and set select all/none option - $bef_settings = array( - 'bef_select_all_none' => TRUE, - 'single' => FALSE, - ); - $this->_befExposedFilterSettings('tid', $bef_settings); - $this->_befSaveView(); - - // Verify taxonomy filter as checkboxes - $this->drupalGet($settings['path']['path']); - $this->assertText(t('Taxonomy: Term'), 'Verify exposed filter label', 'Better Exposed Filters'); - $this->assertFieldByXpath( - '//input[@name="tid[]" and @type="checkbox"]', - NULL, - 'Exposed filter is shown as checkboxes', - 'Better Exposed Filters' - ); - $this->assertFieldByXpath( - '//div[contains(@class, "bef-select-all-none")]', - NULL, - 'Class is set correctly for JS to build select all/none links', - 'Beter Exposed Filters' - ); - } - - - /******************************************************************************* - * Helper functions -- mostly to deal with differences between Views 2.x and 3.x - *******************************************************************************/ - - /** - * Creates a display of $type. Currently supports: - * 'page' - * - * Also, accepts an associative array of settings in the form of: - * - * 'setting_path' => array('form_element' => value, ...), - * 'setting_path' => array('form_element' => value, ...), - * ... - * - * where 'link_name' is the name of the setting link and the array is settings for - * resulting form. - * - * For example, to create a new page and set the path to a random name: - * - * $settings = array('path' => $this->randomName(16); - * $this->_befCreateDisplay('page', $settings); - * - */ - protected function _befCreateDisplay($type = 'page', $settings = NULL) { - $view_edit_url = "admin/build/views/edit/$this->view_name"; - - // Add a display of $type to the view - $this->drupalPost($view_edit_url, array('display' => $type), 'Add display'); - - // Grab the name of the newly created view out of the URL anchor text - $url = $this->getUrl(); - preg_match('/(.*)#(.*)/', $url, $matches); - $display_name = str_replace('views-tab-', '', $matches[2]); - - $display_edit_url = "admin/build/views/nojs/display/$this->view_name/$display_name"; - foreach ($settings as $path => $page_settings) { - $this->drupalPost("$display_edit_url/$path", $page_settings, 'Update'); - } - - // Save the view - $this->drupalPost($view_edit_url, array(), 'Save'); - } - - /** - * Adds a specified filter to the view. - * - * @param $field in the form of node.field or, for CCK fields ???.CCK_field_name - * @param $exposed - * @param $display - */ - protected function _befAddFilter($field, $exposed = TRUE, $display = 'default', $additional = array()) { - $edit = array( - "name[$field]" => TRUE, - ); - $field_name = preg_replace('/.*\./', '', $field); - $view = $this->view_name; - - $this->drupalPost("admin/build/views/nojs/add-item/$view/$display/filter", $edit, 'Add'); - - // For taxonomy filters, use info in $additional to configure the filter. - if (0 === strpos($field, 'term_node.')) { - $this->drupalPost ( - "admin/build/views/nojs/config-item-extra/$view/$display/filter/$field_name", - $additional, - 'Update' - ); - } - - if ($exposed) { - $this->drupalPost("admin/build/views/nojs/config-item/$view/$display/filter/$field_name", array(), 'Expose'); - } - $this->drupalPost("admin/build/views/nojs/config-item/$view/$display/filter/$field_name", array(), 'Update'); - } - - protected function _befAddField($field, $settings = array(), $display = 'default') { - $edit = array( - "name[$field]" => TRUE, - ); - $field_name = preg_replace('/.*\./', '', $field); - $view = $this->view_name; - - $this->drupalPost("admin/build/views/nojs/add-item/$view/$display/field", $edit, 'Add'); - $this->drupalPost("admin/build/views/nojs/config-item/$view/$display/field/$field_name", $settings, 'Update'); - } - - /** - * Specifies specific settings for an exposed filter for a given display - * - * NOTE: In Views 3.x, some of what used to be in the Configure Filteres dialog has moved to the - * Configure Exposed Form dialog. - * - * @param string $filter - name of the filter - * @param array $settings - array of form elements => value entries - * @return $settings array merged with default values - */ - protected function _befExposedFilterSettings($filter, $settings = array(), $display = 'default') { - $processed_settings = array(); - foreach ($settings as $elem => $setting) { - $processed_settings["options[expose][$elem]"] = $setting; - } - - //DEBUG: -// foreach ($processed_settings as $item) { -// $this->verbose("processed_setting: $item"); -// } - //END_DEBUG: - - if ($this->views_version == 2) { -// $this->drupalGet("admin/build/views/nojs/config-item/$this->view_name/$display/filter/$filter"); - $this->drupalPost( - "admin/build/views/nojs/config-item/$this->view_name/$display/filter/$filter", - $processed_settings, - 'Update' - ); - $this->drupalGet("admin/build/views/nojs/config-item/$this->view_name/$display/filter/$filter"); - } - else { - // All BEF settings are now in the Exposed Form dialog. - - //@TODO - } - return $settings; - } - - /** - * Saves the view - * @return unknown_type - */ - protected function _befSaveView() { - $this->drupalPost("admin/build/views/edit/$this->view_name", array(), 'Save'); - } -} - diff --git a/web/modules/better_exposed_filters/tests/modules/bef_test/bef_test.info.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/bef_test.info.yml new file mode 100644 index 0000000000..8e2798ca12 --- /dev/null +++ b/web/modules/better_exposed_filters/tests/modules/bef_test/bef_test.info.yml @@ -0,0 +1,16 @@ +name: BEF Test +type: module +description: Test module for Better Exposed Filters +core_version_requirement: ^8.8 || ^9 +package: Views +dependencies: + - better_exposed_filters:better_exposed_filters + - drupal:datetime + - drupal:node + - drupal:options + - drupal:user + +# Information added by Drupal.org packaging script on 2020-07-07 +version: '8.x-5.0-beta1' +project: 'better_exposed_filters' +datestamp: 1594141894 diff --git a/web/modules/better_exposed_filters/tests/bef_test/bef_test.install b/web/modules/better_exposed_filters/tests/modules/bef_test/bef_test.install similarity index 55% rename from web/modules/better_exposed_filters/tests/bef_test/bef_test.install rename to web/modules/better_exposed_filters/tests/modules/bef_test/bef_test.install index e43bc07da7..0ce4eea395 100644 --- a/web/modules/better_exposed_filters/tests/bef_test/bef_test.install +++ b/web/modules/better_exposed_filters/tests/modules/bef_test/bef_test.install @@ -1,46 +1,53 @@ <?php + /** + * @file * Provides install hooks for the BEF Test module. */ -use Drupal\taxonomy\Entity; - /** * Adds terms to the hierarchical "location" vocabulary. + * + * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException + * Thrown if the entity type doesn't exist. + * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException + * Thrown if the storage handler couldn't be loaded. + * @throws \Drupal\Core\Entity\EntityStorageException + * In case of failures an exception is thrown. */ function bef_test_install() { // Set up an example hierarchical terms in the "Location" vocab. - $locations = array( - 'United States' => array( - 'California' => array( + $locations = [ + 'United States' => [ + 'California' => [ 'San Francisco', 'San Diego', 'Santa Barbara', - ), - 'Oregon' => array( + ], + 'Oregon' => [ 'Portland', 'Eugene', - ), - 'Washington' => array( + ], + 'Washington' => [ 'Seattle', 'Spokane', 'Walla Walla', - ), - ), - 'Canada' => array( - 'British Columbia' => array( + ], + ], + 'Canada' => [ + 'British Columbia' => [ 'Vancouver', 'Victoria', 'Whistler', - ), - 'Alberta' => array( + ], + 'Alberta' => [ 'Calgary', 'Edmonton', 'Lake Louise', - ), - ), - 'Mexico' => array(), - ); + ], + ], + 'Mexico' => [], + ]; foreach ($locations as $country => $states) { $country_tid = _bef_test_add_term($country); if ($country_tid && !empty($states)) { @@ -57,8 +64,10 @@ function bef_test_install() { } /** - * Adds a new term to the bef_test-location vocabulary. If a TID is specified - * in $parent, the new term is added as a child of that term. + * Adds a new term to the bef_test-location vocabulary. + * + * If a TID is specified in $parent, the new term is added as a child of that + * term. * * @param string $name * The name of the new term. @@ -67,6 +76,13 @@ function bef_test_install() { * * @return int * TID of the newly created term. + * + * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException + * Thrown if the entity type doesn't exist. + * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException + * Thrown if the storage handler couldn't be loaded. + * @throws \Drupal\Core\Entity\EntityStorageException + * In case of failures an exception is thrown. */ function _bef_test_add_term($name, $parent = 0) { $term = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->create([ diff --git a/web/modules/better_exposed_filters/tests/bef_test/bef_test.module b/web/modules/better_exposed_filters/tests/modules/bef_test/bef_test.module similarity index 100% rename from web/modules/better_exposed_filters/tests/bef_test/bef_test.module rename to web/modules/better_exposed_filters/tests/modules/bef_test/bef_test.module diff --git a/web/modules/better_exposed_filters/tests/bef_test/config/install/core.entity_form_display.node.bef_test.default.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/core.entity_form_display.node.bef_test.default.yml similarity index 96% rename from web/modules/better_exposed_filters/tests/bef_test/config/install/core.entity_form_display.node.bef_test.default.yml rename to web/modules/better_exposed_filters/tests/modules/bef_test/config/install/core.entity_form_display.node.bef_test.default.yml index 393a0bbf1c..d9b75914b1 100644 --- a/web/modules/better_exposed_filters/tests/bef_test/config/install/core.entity_form_display.node.bef_test.default.yml +++ b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/core.entity_form_display.node.bef_test.default.yml @@ -14,8 +14,6 @@ dependencies: enforced: module: - bef_test -_core: - default_config_hash: R0o7gr9BG9vh0chu8DVw6qiEdviHNnz3BBYqkG8No6Q id: node.bef_test.default targetEntityType: node bundle: bef_test diff --git a/web/modules/better_exposed_filters/tests/bef_test/config/install/core.entity_view_display.node.bef_test.default.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/core.entity_view_display.node.bef_test.default.yml similarity index 100% rename from web/modules/better_exposed_filters/tests/bef_test/config/install/core.entity_view_display.node.bef_test.default.yml rename to web/modules/better_exposed_filters/tests/modules/bef_test/config/install/core.entity_view_display.node.bef_test.default.yml diff --git a/web/modules/better_exposed_filters/tests/bef_test/config/install/core.entity_view_display.node.bef_test.teaser.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/core.entity_view_display.node.bef_test.teaser.yml similarity index 100% rename from web/modules/better_exposed_filters/tests/bef_test/config/install/core.entity_view_display.node.bef_test.teaser.yml rename to web/modules/better_exposed_filters/tests/modules/bef_test/config/install/core.entity_view_display.node.bef_test.teaser.yml diff --git a/web/modules/better_exposed_filters/tests/bef_test/config/install/field.field.node.bef_test.body.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.field.node.bef_test.body.yml similarity index 100% rename from web/modules/better_exposed_filters/tests/bef_test/config/install/field.field.node.bef_test.body.yml rename to web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.field.node.bef_test.body.yml diff --git a/web/modules/better_exposed_filters/tests/bef_test/config/install/field.field.node.bef_test.field_bef_boolean.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.field.node.bef_test.field_bef_boolean.yml similarity index 100% rename from web/modules/better_exposed_filters/tests/bef_test/config/install/field.field.node.bef_test.field_bef_boolean.yml rename to web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.field.node.bef_test.field_bef_boolean.yml diff --git a/web/modules/better_exposed_filters/tests/bef_test/config/install/field.field.node.bef_test.field_bef_email.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.field.node.bef_test.field_bef_email.yml similarity index 100% rename from web/modules/better_exposed_filters/tests/bef_test/config/install/field.field.node.bef_test.field_bef_email.yml rename to web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.field.node.bef_test.field_bef_email.yml diff --git a/web/modules/better_exposed_filters/tests/bef_test/config/install/field.field.node.bef_test.field_bef_integer.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.field.node.bef_test.field_bef_integer.yml similarity index 100% rename from web/modules/better_exposed_filters/tests/bef_test/config/install/field.field.node.bef_test.field_bef_integer.yml rename to web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.field.node.bef_test.field_bef_integer.yml diff --git a/web/modules/better_exposed_filters/tests/bef_test/config/install/field.field.node.bef_test.field_bef_letters.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.field.node.bef_test.field_bef_letters.yml similarity index 100% rename from web/modules/better_exposed_filters/tests/bef_test/config/install/field.field.node.bef_test.field_bef_letters.yml rename to web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.field.node.bef_test.field_bef_letters.yml diff --git a/web/modules/better_exposed_filters/tests/bef_test/config/install/field.field.node.bef_test.field_bef_location.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.field.node.bef_test.field_bef_location.yml similarity index 100% rename from web/modules/better_exposed_filters/tests/bef_test/config/install/field.field.node.bef_test.field_bef_location.yml rename to web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.field.node.bef_test.field_bef_location.yml diff --git a/web/modules/better_exposed_filters/tests/bef_test/config/install/field.field.node.bef_test.field_bef_price.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.field.node.bef_test.field_bef_price.yml similarity index 100% rename from web/modules/better_exposed_filters/tests/bef_test/config/install/field.field.node.bef_test.field_bef_price.yml rename to web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.field.node.bef_test.field_bef_price.yml diff --git a/web/modules/better_exposed_filters/tests/bef_test/config/install/field.storage.node.field_bef_boolean.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.storage.node.field_bef_boolean.yml similarity index 100% rename from web/modules/better_exposed_filters/tests/bef_test/config/install/field.storage.node.field_bef_boolean.yml rename to web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.storage.node.field_bef_boolean.yml diff --git a/web/modules/better_exposed_filters/tests/bef_test/config/install/field.storage.node.field_bef_email.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.storage.node.field_bef_email.yml similarity index 100% rename from web/modules/better_exposed_filters/tests/bef_test/config/install/field.storage.node.field_bef_email.yml rename to web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.storage.node.field_bef_email.yml diff --git a/web/modules/better_exposed_filters/tests/bef_test/config/install/field.storage.node.field_bef_integer.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.storage.node.field_bef_integer.yml similarity index 100% rename from web/modules/better_exposed_filters/tests/bef_test/config/install/field.storage.node.field_bef_integer.yml rename to web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.storage.node.field_bef_integer.yml diff --git a/web/modules/better_exposed_filters/tests/bef_test/config/install/field.storage.node.field_bef_letters.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.storage.node.field_bef_letters.yml similarity index 100% rename from web/modules/better_exposed_filters/tests/bef_test/config/install/field.storage.node.field_bef_letters.yml rename to web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.storage.node.field_bef_letters.yml diff --git a/web/modules/better_exposed_filters/tests/bef_test/config/install/field.storage.node.field_bef_location.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.storage.node.field_bef_location.yml similarity index 100% rename from web/modules/better_exposed_filters/tests/bef_test/config/install/field.storage.node.field_bef_location.yml rename to web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.storage.node.field_bef_location.yml diff --git a/web/modules/better_exposed_filters/tests/bef_test/config/install/field.storage.node.field_bef_price.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.storage.node.field_bef_price.yml similarity index 100% rename from web/modules/better_exposed_filters/tests/bef_test/config/install/field.storage.node.field_bef_price.yml rename to web/modules/better_exposed_filters/tests/modules/bef_test/config/install/field.storage.node.field_bef_price.yml diff --git a/web/modules/better_exposed_filters/tests/bef_test/config/install/node.type.bef_test.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/node.type.bef_test.yml similarity index 69% rename from web/modules/better_exposed_filters/tests/bef_test/config/install/node.type.bef_test.yml rename to web/modules/better_exposed_filters/tests/modules/bef_test/config/install/node.type.bef_test.yml index 07680c0cd0..d5817a13c3 100644 --- a/web/modules/better_exposed_filters/tests/bef_test/config/install/node.type.bef_test.yml +++ b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/node.type.bef_test.yml @@ -1,16 +1,9 @@ langcode: en status: true dependencies: - module: - - menu_ui enforced: module: - bef_test -third_party_settings: - menu_ui: - available_menus: - - main - parent: 'main:' name: 'BEF Test' type: bef_test description: 'Test content type for the Better Exposed Filters module.' diff --git a/web/modules/better_exposed_filters/tests/bef_test/config/install/taxonomy.vocabulary.bef_test_location.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/taxonomy.vocabulary.bef_test_location.yml similarity index 100% rename from web/modules/better_exposed_filters/tests/bef_test/config/install/taxonomy.vocabulary.bef_test_location.yml rename to web/modules/better_exposed_filters/tests/modules/bef_test/config/install/taxonomy.vocabulary.bef_test_location.yml diff --git a/web/modules/better_exposed_filters/tests/bef_test/config/install/views.view.bef_test.yml b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/views.view.bef_test.yml similarity index 87% rename from web/modules/better_exposed_filters/tests/bef_test/config/install/views.view.bef_test.yml rename to web/modules/better_exposed_filters/tests/modules/bef_test/config/install/views.view.bef_test.yml index a2d0d822bb..07e59bad42 100644 --- a/web/modules/better_exposed_filters/tests/bef_test/config/install/views.view.bef_test.yml +++ b/web/modules/better_exposed_filters/tests/modules/bef_test/config/install/views.view.bef_test.yml @@ -18,8 +18,6 @@ dependencies: - options - taxonomy - user -_core: - default_config_hash: vBlYQqgp3xw3tYE67_Y9dy18ibAGMS9HYTop3QyDoEk id: bef_test label: 'BEF Test' module: views @@ -27,7 +25,6 @@ description: '' tag: '' base_table: node_field_data base_field: nid -core: 8.x display: default: display_plugin: default @@ -62,77 +59,81 @@ display: sort_desc_label: Desc bef: general: + autosubmit: false + autosubmit_exclude_textfield: false + autosubmit_hide: false + input_required: false allow_secondary: false secondary_label: 'Advanced options' - status: - bef_format: default - more_options: - bef_select_all_none: false - bef_collapsible: false + sort: + plugin_id: default + advanced: + combine: false + combine_rewrite: '' + reset: false + reset_label: '' + collapsible: false + collapsible_label: '' is_secondary: false - bef_filter_description: '' - tokens: - available: - - global_types - rewrite: - filter_rewrite_values: '' - field_bef_boolean_value: - bef_format: default - more_options: + pager: + plugin_id: default + advanced: is_secondary: false - bef_filter_description: '' - tokens: - available: - - global_types - rewrite: - filter_rewrite_values: '' - field_bef_email_value: - bef_format: default - more_options: - is_secondary: false - bef_filter_description: '' - tokens: - available: - - global_types - rewrite: - filter_rewrite_values: '' - field_bef_integer_value: - bef_format: default - more_options: - bef_select_all_none: false - bef_collapsible: false - is_secondary: false - bef_filter_description: '' - tokens: - available: - - global_types - rewrite: - filter_rewrite_values: '' - field_bef_letters_value: - bef_format: default - more_options: - bef_select_all_none: false - bef_collapsible: false - is_secondary: false - bef_filter_description: '' - tokens: - available: - - global_types - rewrite: - filter_rewrite_values: '' - field_bef_location_target_id: - bef_format: bef - more_options: - bef_select_all_none: false - bef_collapsible: false - is_secondary: false - bef_filter_description: '' - tokens: - available: - - global_types - - vocabulary - rewrite: - filter_rewrite_values: '' + filter: + status: + plugin_id: default + advanced: + collapsible: false + is_secondary: false + placeholder_text: '' + rewrite: + filter_rewrite_values: '' + sort_options: false + field_bef_boolean_value: + plugin_id: default + advanced: + collapsible: false + is_secondary: false + placeholder_text: '' + rewrite: + filter_rewrite_values: '' + sort_options: false + field_bef_email_value: + plugin_id: default + advanced: + collapsible: false + is_secondary: false + placeholder_text: '' + rewrite: + filter_rewrite_values: '' + sort_options: false + field_bef_integer_value: + plugin_id: default + advanced: + collapsible: false + is_secondary: false + placeholder_text: '' + rewrite: + filter_rewrite_values: '' + sort_options: false + field_bef_letters_value: + plugin_id: default + advanced: + collapsible: false + is_secondary: false + placeholder_text: '' + rewrite: + filter_rewrite_values: '' + sort_options: false + field_bef_location_target_id: + plugin_id: default + advanced: + collapsible: false + is_secondary: false + placeholder_text: '' + rewrite: + filter_rewrite_values: '' + sort_options: false pager: type: full options: @@ -141,12 +142,12 @@ display: id: 0 total_pages: null expose: - items_per_page: false + items_per_page: true items_per_page_label: 'Items per page' items_per_page_options: '5, 10, 25, 50' items_per_page_options_all: false items_per_page_options_all_label: '- All -' - offset: false + offset: true offset_label: Offset tags: previous: '‹ Previous' @@ -694,44 +695,6 @@ display: plugin_id: boolean entity_type: node entity_field: status - field_bef_boolean_value: - id: field_bef_boolean_value - table: node__field_bef_boolean - field: field_bef_boolean_value - relationship: none - group_type: group - admin_label: '' - operator: '=' - value: All - group: 1 - exposed: true - expose: - operator_id: field_bef_boolean_value_op - label: 'bef_boolean (field_bef_boolean)' - description: '' - use_operator: false - operator: field_bef_boolean_value_op - identifier: field_bef_boolean_value - required: false - remember: false - multiple: false - remember_roles: - authenticated: authenticated - anonymous: '0' - administrator: '0' - is_grouped: false - group_info: - label: '' - description: '' - identifier: '' - optional: true - widget: select - multiple: false - remember: false - default_group: All - default_group_multiple: { } - group_items: { } - plugin_id: numeric field_bef_email_value: id: field_bef_email_value table: node__field_bef_email @@ -938,6 +901,49 @@ display: default_group_multiple: { } group_items: { } plugin_id: numeric + field_bef_boolean_value: + id: field_bef_boolean_value + table: node__field_bef_boolean + field: field_bef_boolean_value + relationship: none + group_type: group + admin_label: '' + operator: '=' + value: All + group: 1 + exposed: true + expose: + operator_id: '' + label: 'bef_boolean (field_bef_boolean)' + description: '' + use_operator: false + operator: field_bef_boolean_value_op + identifier: field_bef_boolean_value + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + marketing_editor: '0' + vendor: '0' + partner: '0' + partner_admin: '0' + editor: '0' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + plugin_id: boolean sorts: created: id: created @@ -950,9 +956,9 @@ display: relationship: none group_type: group admin_label: '' - exposed: false + exposed: true expose: - label: '' + label: 'Created' granularity: second title: 'BEF Test' header: { } @@ -961,23 +967,6 @@ display: relationships: { } arguments: { } display_extenders: { } - cache_metadata: - max-age: 0 - contexts: - - 'languages:language_content' - - 'languages:language_interface' - - url - - url.query_args - - user - - 'user.node_grants:view' - - user.permissions - tags: - - 'config:field.storage.node.field_bef_boolean' - - 'config:field.storage.node.field_bef_email' - - 'config:field.storage.node.field_bef_integer' - - 'config:field.storage.node.field_bef_letters' - - 'config:field.storage.node.field_bef_location' - - 'config:field.storage.node.field_bef_price' page_1: display_plugin: page id: page_1 @@ -986,20 +975,12 @@ display: display_options: display_extenders: { } path: bef-test - cache_metadata: - max-age: 0 - contexts: - - 'languages:language_content' - - 'languages:language_interface' - - url - - url.query_args - - user - - 'user.node_grants:view' - - user.permissions - tags: - - 'config:field.storage.node.field_bef_boolean' - - 'config:field.storage.node.field_bef_email' - - 'config:field.storage.node.field_bef_integer' - - 'config:field.storage.node.field_bef_letters' - - 'config:field.storage.node.field_bef_location' - - 'config:field.storage.node.field_bef_price' + page_2: + display_plugin: page + id: page_2 + display_title: Page + position: 2 + display_options: + display_extenders: { } + path: bef-test-with-block + exposed_block: true diff --git a/web/modules/better_exposed_filters/tests/src/FunctionalJavascript/BetterExposedFiltersTest.php b/web/modules/better_exposed_filters/tests/src/FunctionalJavascript/BetterExposedFiltersTest.php new file mode 100644 index 0000000000..115d083879 --- /dev/null +++ b/web/modules/better_exposed_filters/tests/src/FunctionalJavascript/BetterExposedFiltersTest.php @@ -0,0 +1,244 @@ +<?php + +namespace Drupal\Tests\better_exposed_filters\FunctionalJavascript; + +use Drupal\FunctionalJavascriptTests\WebDriverTestBase; +use Drupal\Tests\better_exposed_filters\Traits\BetterExposedFiltersTrait; +use Drupal\Tests\node\Traits\ContentTypeCreationTrait; +use Drupal\Tests\node\Traits\NodeCreationTrait; +use Drupal\views\Views; + +/** + * Tests the basic AJAX functionality of BEF exposed forms. + * + * @group better_exposed_filters + */ +class BetterExposedFiltersTest extends WebDriverTestBase { + + use BetterExposedFiltersTrait; + use ContentTypeCreationTrait; + use NodeCreationTrait; + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * {@inheritdoc} + */ + public static $modules = [ + 'block', + 'node', + 'views', + 'taxonomy', + 'better_exposed_filters', + 'bef_test', + ]; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + // Enable AJAX on the our test view. + \Drupal::configFactory()->getEditable('views.view.bef_test') + ->set('display.default.display_options.use_ajax', TRUE) + ->save(); + + // Create a few test nodes. + $this->createNode([ + 'title' => 'Page One', + 'field_bef_boolean' => '', + 'field_bef_email' => 'bef-test@drupal.org', + 'field_bef_integer' => '1', + 'field_bef_letters' => 'Aardvark', + // Seattle. + 'field_bef_location' => '10', + 'type' => 'bef_test', + ]); + $this->createNode([ + 'title' => 'Page Two', + 'field_bef_boolean' => '', + 'field_bef_email' => 'bef-test@drupal.org', + 'field_bef_integer' => '2', + 'field_bef_letters' => 'Bumble & the Bee', + // Vancouver. + 'field_bef_location' => '15', + 'type' => 'bef_test', + ]); + + } + + /** + * Tests if filtering via auto-submit works. + */ + public function testAutoSubmit() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Enable auto-submit, but disable for text fields. + $this->setBetterExposedOptions($view, [ + 'general' => [ + 'autosubmit' => TRUE, + 'autosubmit_exclude_textfield' => TRUE, + ], + ]); + + // Visit the bef-test page. + $this->drupalGet('bef-test'); + + $session = $this->getSession(); + $page = $session->getPage(); + + // Ensure that the content we're testing for is present. + $html = $page->getHtml(); + $this->assertStringContainsString('Page One', $html); + $this->assertStringContainsString('Page Two', $html); + + // Search for "Page One". + $field_bef_integer = $page->findField('field_bef_integer_value'); + $field_bef_integer->setValue('1'); + $field_bef_integer->blur(); + $this->assertSession()->assertWaitOnAjaxRequest(); + + // Verify that only the "Page One" Node is present. + $html = $page->getHtml(); + $this->assertStringContainsString('Page One', $html); + $this->assertStringNotContainsString('Page Two', $html); + + // Enter value in email field. + $field_bef_email = $page->find('css', '.form-item-field-bef-email-value input'); + $field_bef_email->setValue('qwerty@test.com'); + $this->assertSession()->assertWaitOnAjaxRequest(); + + // Verify nothing has changed. + $this->assertStringContainsString('Page One', $html); + $this->assertStringNotContainsString('Page Two', $html); + + // Submit form. + $this->submitForm([], 'Apply'); + $this->assertSession()->assertWaitOnAjaxRequest(); + + // Verify no results are visible. + $html = $page->getHtml(); + $this->assertStringNotContainsString('Page One', $html); + $this->assertStringNotContainsString('Page Two', $html); + } + + /** + * Tests if filtering via auto-submit works if exposed form is a block. + */ + public function testAutoSubmitWithExposedFormBlock() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + $block = $this->drupalPlaceBlock('views_exposed_filter_block:bef_test-page_2'); + + // Enable auto-submit, but disable for text fields. + $this->setBetterExposedOptions($view, [ + 'general' => [ + 'autosubmit' => TRUE, + 'autosubmit_exclude_textfield' => TRUE, + ], + ]); + + // Visit the bef-test page. + $this->drupalGet('bef-test-with-block'); + + $session = $this->getSession(); + $page = $session->getPage(); + + // Ensure that the content we're testing for is present. + $html = $page->getHtml(); + $this->assertStringContainsString('Page One', $html); + $this->assertStringContainsString('Page Two', $html); + + // Search for "Page One". + $field_bef_integer = $page->findField('field_bef_integer_value'); + $field_bef_integer->setValue('1'); + $field_bef_integer->blur(); + $this->assertSession()->assertWaitOnAjaxRequest(); + + // Verify that only the "Page One" Node is present. + $html = $page->getHtml(); + $this->assertStringContainsString('Page One', $html); + $this->assertStringNotContainsString('Page Two', $html); + + // Enter value in email field. + $field_bef_email = $page->find('css', '.form-item-field-bef-email-value input'); + $field_bef_email->setValue('qwerty@test.com'); + $this->assertSession()->assertWaitOnAjaxRequest(); + + // Verify nothing has changed. + $this->assertStringContainsString('Page One', $html); + $this->assertStringNotContainsString('Page Two', $html); + + // Submit form. + $this->submitForm([], 'Apply'); + $this->assertSession()->assertWaitOnAjaxRequest(); + + // Verify no results are visible. + $html = $page->getHtml(); + $this->assertStringNotContainsString('Page One', $html); + $this->assertStringNotContainsString('Page Two', $html); + } + + /** + * Tests placing exposed filters inside a collapsible field-set. + */ + public function testSecondaryOptions() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Enable auto-submit, but disable for text fields. + $this->setBetterExposedOptions($view, [ + 'general' => [ + 'allow_secondary' => TRUE, + 'secondary_label' => 'Secondary Options TEST', + ], + 'sort' => [ + 'plugin_id' => 'default', + 'advanced' => [ + 'is_secondary' => TRUE, + ], + ], + 'pager' => [ + 'plugin_id' => 'default', + 'advanced' => [ + 'is_secondary' => TRUE, + ], + ], + 'filter' => [ + 'field_bef_boolean_value' => [ + 'plugin_id' => 'default', + 'advanced' => [ + 'is_secondary' => TRUE, + ], + ], + ], + ]); + + // Visit the bef-test page. + $this->drupalGet('bef-test'); + + $session = $this->getSession(); + $page = $session->getPage(); + + // Assert our fields are initially hidden inside the collapsible field-set. + $secondary_options = $page->find('css', '.bef--secondary'); + $this->assertFalse($secondary_options->hasAttribute('open')); + $secondary_options->hasField('field_bef_boolean_value'); + + // Submit form and set a value for the boolean field. + $secondary_options->click(); + $this->submitForm(['field_bef_boolean_value' => 1], 'Apply'); + $session = $this->getSession(); + $page = $session->getPage(); + + // Verify our field-set is open and our fields visible. + $secondary_options = $page->find('css', '.bef--secondary'); + $this->assertTrue($secondary_options->hasAttribute('open')); + } + +} diff --git a/web/modules/better_exposed_filters/tests/src/Kernel/BetterExposedFiltersKernelTest.php b/web/modules/better_exposed_filters/tests/src/Kernel/BetterExposedFiltersKernelTest.php new file mode 100644 index 0000000000..3bae009601 --- /dev/null +++ b/web/modules/better_exposed_filters/tests/src/Kernel/BetterExposedFiltersKernelTest.php @@ -0,0 +1,123 @@ +<?php + +namespace Drupal\Tests\better_exposed_filters\Kernel; + +use Drupal\Tests\better_exposed_filters\Kernel\BetterExposedFiltersKernelTestBase; +use Drupal\views\Views; + +/** + * Tests the radio buttons/checkboxes filter widget (i.e. "bef"). + * + * @group better_exposed_filters + * + * @see \Drupal\better_exposed_filters\Plugin\better_exposed_filters\filter\RadioButtons + */ +class BetterExposedFiltersKernelTest extends BetterExposedFiltersKernelTestBase { + + /** + * {@inheritdoc} + */ + public static $testViews = ['bef_test']; + + /** + * Tests hiding the submit button when auto-submit is enabled. + */ + public function testHideSubmitButtonOnAutoSubmit() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Enable auto-submit and hide auto-submit button. + $this->setBetterExposedOptions($view, [ + 'general' => [ + 'autosubmit' => TRUE, + 'autosubmit_hide' => TRUE, + ], + ]); + + // Render the exposed form. + $this->renderExposedForm($view); + + // Check our "submit" button is hidden. + $actual = $this->xpath("//form//input[@type='submit'][contains(concat(' ',normalize-space(@class),' '),' js-hide ')]"); + $this->assertCount(1, $actual, 'Submit button was hidden successfully.'); + + $view->destroy(); + } + + /** + * Tests moving sorts, filters and pager options into secondary fieldset. + */ + public function testSecondaryOptions() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Enable secondary options and set label. + $this->setBetterExposedOptions($view, [ + 'general' => [ + 'allow_secondary' => TRUE, + 'secondary_label' => 'Secondary Options TEST', + ], + ]); + + // Render the exposed form. + $this->renderExposedForm($view); + + // Assert our "secondary" options detail is hidden if no fields are placed. + $actual = $this->xpath("//form//details[@data-drupal-selector='edit-secondary']"); + $this->assertCount(0, $actual, 'Secondary options are hidden because no fields were placed.'); + + $view->destroy(); + + // Move sort, pager and "field_bef_boolean" into secondary options. + $view = Views::getView('bef_test'); + + $this->setBetterExposedOptions($view, [ + 'general' => [ + 'allow_secondary' => TRUE, + 'secondary_label' => 'Secondary Options TEST', + ], + 'sort' => [ + 'plugin_id' => 'default', + 'advanced' => [ + 'is_secondary' => TRUE, + ], + ], + 'pager' => [ + 'plugin_id' => 'default', + 'advanced' => [ + 'is_secondary' => TRUE, + ], + ], + 'filter' => [ + 'field_bef_boolean_value' => [ + 'plugin_id' => 'default', + 'advanced' => [ + 'is_secondary' => TRUE, + ], + ], + ], + ]); + + // Render the exposed form. + $this->renderExposedForm($view); + + // Assert our "secondary" options detail is visible. + $actual = $this->xpath("//form//details[@data-drupal-selector='edit-secondary']"); + $this->assertCount(1, $actual, 'Secondary options is visible.'); + + // Assert sort option was placed in secondary details. + $actual = $this->xpath("//form//details[@data-drupal-selector='edit-secondary']//select[@name='sort_by']"); + $this->assertCount(1, $actual, 'Exposed sort was placed in secondary fieldset.'); + + // Assert pager option was placed in secondary details. + $actual = $this->xpath("//form//details[@data-drupal-selector='edit-secondary']//select[@name='items_per_page']"); + $this->assertCount(1, $actual, 'Exposed pager was placed in secondary fieldset.'); + + // Assert filter option was placed in secondary details. + $actual = $this->xpath("//form//details[@data-drupal-selector='edit-secondary']//select[@name='field_bef_boolean_value']"); + $this->assertCount(1, $actual, 'Exposed filter "field_bef_boolean" was placed in secondary fieldset.'); + + $view->destroy(); + } + +} diff --git a/web/modules/better_exposed_filters/tests/src/Kernel/BetterExposedFiltersKernelTestBase.php b/web/modules/better_exposed_filters/tests/src/Kernel/BetterExposedFiltersKernelTestBase.php new file mode 100644 index 0000000000..e2b0f1d0d2 --- /dev/null +++ b/web/modules/better_exposed_filters/tests/src/Kernel/BetterExposedFiltersKernelTestBase.php @@ -0,0 +1,77 @@ +<?php + +namespace Drupal\Tests\better_exposed_filters\Kernel; + +use Drupal\Tests\better_exposed_filters\Traits\BetterExposedFiltersTrait; +use Drupal\Tests\views\Kernel\ViewsKernelTestBase; +use Drupal\views\ViewExecutable; + +/** + * Defines a base class for Better Exposed Filters kernel testing. + */ +abstract class BetterExposedFiltersKernelTestBase extends ViewsKernelTestBase { + + use BetterExposedFiltersTrait; + + /** + * {@inheritdoc} + */ + public static $modules = [ + 'system', + 'field', + 'views', + 'node', + 'filter', + 'options', + 'text', + 'taxonomy', + 'better_exposed_filters', + 'bef_test', + 'user', + ]; + + /** + * {@inheritdoc} + */ + protected function setUp($import_test_views = TRUE) { + parent::setUp(); + + $this->installSchema('node', ['node_access']); + + $this->installEntitySchema('node'); + $this->installEntitySchema('taxonomy_vocabulary'); + $this->installEntitySchema('taxonomy_term'); + + \Drupal::moduleHandler()->loadInclude('bef_test', 'install'); + bef_test_install(); + + $this->installConfig(['system', 'field', 'node', 'taxonomy', 'bef_test']); + } + + /** + * Gets the render array for the views exposed form. + * + * @param \Drupal\views\ViewExecutable $view + * The view object. + * + * @return array + * The render array. + */ + public function getExposedFormRenderArray(ViewExecutable $view) { + $this->executeView($view); + $exposed_form = $view->display_handler->getPlugin('exposed_form'); + return $exposed_form->renderExposedForm(); + } + + /** + * Renders the views exposed form. + * + * @param \Drupal\views\ViewExecutable $view + * The view object. + */ + protected function renderExposedForm(ViewExecutable $view) { + $output = $this->getExposedFormRenderArray($view); + $this->setRawContent(\Drupal::service('renderer')->renderRoot($output)); + } + +} diff --git a/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/FilterWidgetKernelTest.php b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/FilterWidgetKernelTest.php new file mode 100644 index 0000000000..0e528590a9 --- /dev/null +++ b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/FilterWidgetKernelTest.php @@ -0,0 +1,145 @@ +<?php + +namespace Drupal\Tests\better_exposed_filters\Kernel\Plugin\filter; + +use Drupal\Tests\better_exposed_filters\Kernel\BetterExposedFiltersKernelTestBase; +use Drupal\views\Views; + +/** + * Tests the advanced options of a filter widget. + * + * @group better_exposed_filters + * + * @see \Drupal\better_exposed_filters\Plugin\better_exposed_filters\filter\FilterWidgetBase + */ +class FilterWidgetKernelTest extends BetterExposedFiltersKernelTestBase { + + /** + * {@inheritdoc} + */ + public static $testViews = ['bef_test']; + + /** + * Tests grouping filter options. + * + * There is a bug in views where changing the identifier of an exposed + * grouped filter will cause an undefined index notice. + * + * @todo Enable test once https://www.drupal.org/project/drupal/issues/2884296 + * is fixed + */ + /*public function testGroupedFilters() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Ensure our filter "field_bef_boolean_value" is grouped. + $display['display_options']['filters']['field_bef_boolean_value']['is_grouped'] = TRUE; + $display['display_options']['filters']['field_bef_boolean_value']['group_info'] = [ + 'plugin_id' => 'boolean', + 'label' => 'bef_boolean (field_bef_boolean)', + 'description' => '', + 'identifier' => 'field_bef_boolean_value2', + 'optional' => TRUE, + 'widget' => 'select', + 'multiple' => FALSE, + 'remember' => FALSE, + 'default_group' => 'All', + 'default_group_multiple' => [], + 'group_items' => [ + 1 => [ + 'title' => 'YES', + 'operator' => '=', + 'value' => '1', + ], + 2 => [ + 'title' => 'NO', + 'operator' => '=', + 'value' => '0', + ], + ], + ]; + + // Render the exposed form. + $output = $this->getExposedFormRenderArray($view); + + // Check our "FIELD_BEF_BOOLEAN" filter is rendered with id + // "field_bef_boolean_value2". + $this->assertTrue(isset($output['field_bef_boolean_value2']), 'Exposed filter "FIELD_BEF_BOOLEAN" is exposed with id "field_bef_boolean_value2".'); + + $view->destroy(); + }*/ + + /** + * Tests sorting filter options alphabetically. + */ + public function testSortFilterOptions() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Get the exposed form render array. + $output = $this->getExposedFormRenderArray($view); + + // Assert our "field_bef_integer" filter options are not sorted + // alphabetically, but by key. + $sorted_options = $options = $output['field_bef_integer_value']['#options']; + asort($sorted_options); + + $this->assertNotEqual(array_keys($options), array_keys($sorted_options), '"Field BEF integer" options are not sorted alphabetically.'); + + $view->destroy(); + + // Enable sort for filter options. + $this->setBetterExposedOptions($view, [ + 'filter' => [ + 'field_bef_integer_value' => [ + 'plugin_id' => 'default', + 'advanced' => [ + 'sort_options' => TRUE, + ], + ], + ], + ]); + + // Get the exposed form render array. + $output = $this->getExposedFormRenderArray($view); + + // Assert our "field_bef_integer" filter options are sorted alphabetically. + $sorted_options = $options = $output['field_bef_integer_value']['#options']; + asort($sorted_options); + + // Assert our "collapsible" options detail is visible. + $this->assertEqual(array_keys($options), array_keys($sorted_options), '"Field BEF integer" options are sorted alphabetically.'); + + $view->destroy(); + } + + /** + * Tests moving filter option into collapsible fieldset. + */ + public function testCollapsibleOption() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Enable collapsible options. + $this->setBetterExposedOptions($view, [ + 'filter' => [ + 'field_bef_email_value' => [ + 'plugin_id' => 'default', + 'advanced' => [ + 'collapsible' => TRUE, + ], + ], + ], + ]); + + // Render the exposed form. + $this->renderExposedForm($view); + + // Assert our "collapsible" options detail is visible. + $actual = $this->xpath("//form//details[@data-drupal-selector='edit-field-bef-email-value-collapsible']"); + $this->assertCount(1, $actual, '"Field BEF Email" option is displayed as collapsible fieldset.'); + + $view->destroy(); + } + +} diff --git a/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/HiddenFilterWidgetKernelTest.php b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/HiddenFilterWidgetKernelTest.php new file mode 100644 index 0000000000..9d1e6814a5 --- /dev/null +++ b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/HiddenFilterWidgetKernelTest.php @@ -0,0 +1,78 @@ +<?php + +namespace Drupal\Tests\better_exposed_filters\Kernel\Plugin\filter; + +use Drupal\Tests\better_exposed_filters\Kernel\BetterExposedFiltersKernelTestBase; +use Drupal\views\Views; + +/** + * Tests the options of a hidden filter widget. + * + * @group better_exposed_filters + * + * @see \Drupal\better_exposed_filters\Plugin\better_exposed_filters\filter\FilterWidgetBase + */ +class HiddenFilterWidgetKernelTest extends BetterExposedFiltersKernelTestBase { + + /** + * {@inheritdoc} + */ + public static $testViews = ['bef_test']; + + /** + * Tests hiding element with single option. + */ + public function testSingleExposedHiddenElement() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Change exposed filter "field_bef_boolean" to hidden (i.e. 'bef_hidden'). + $this->setBetterExposedOptions($view, [ + 'filter' => [ + 'field_bef_boolean_value' => [ + 'plugin_id' => 'bef_hidden', + ], + ], + ]); + + // Render the exposed form. + $this->renderExposedForm($view); + + // Check our "FIELD_BEF_BOOLEAN" filter is rendered as a hidden element. + $actual = $this->xpath('//form//input[@type="hidden" and starts-with(@name, "field_bef_boolean_value")]'); + $this->assertCount(1, $actual, 'Exposed filter "FIELD_BEF_BOOLEAN" is hidden.'); + + $view->destroy(); + } + + /** + * Tests hiding element with multiple options. + */ + public function testMultipleExposedHiddenElement() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Set filter to "multiple". + $display['display_options']['filters']['field_bef_integer_value']['expose']['multiple'] = TRUE; + + // Change exposed filter "field_bef_integer" to hidden (i.e. 'bef_hidden'). + $this->setBetterExposedOptions($view, [ + 'filter' => [ + 'field_bef_integer_value' => [ + 'plugin_id' => 'bef_hidden', + ], + ], + ]); + + // Render the exposed form. + $this->renderExposedForm($view); + + // Check our "FIELD_BEF_INTEGER" filter is rendered as a hidden element. + $actual = $this->xpath('//form//label[@type="label" and starts-with(@for, "edit-field-bef-integer-value")]'); + $this->assertCount(0, $actual, 'Exposed filter "FIELD_BEF_INTEGER" is hidden.'); + + $actual = $this->xpath('//form//input[@type="hidden" and starts-with(@name, "field_bef_integer_value")]'); + $this->assertCount(0, $actual, 'Exposed filter "FIELD_BEF_INTEGER" has no selected values.'); + } + +} diff --git a/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/LinksFilterWidgetKernelTest.php b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/LinksFilterWidgetKernelTest.php new file mode 100644 index 0000000000..c106427a77 --- /dev/null +++ b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/LinksFilterWidgetKernelTest.php @@ -0,0 +1,68 @@ +<?php + +namespace Drupal\Tests\better_exposed_filters\Kernel\Plugin\filter; + +use Drupal\Tests\better_exposed_filters\Kernel\BetterExposedFiltersKernelTestBase; +use Drupal\views\Views; + +/** + * Tests the links filter widget (i.e. "bef_links"). + * + * @group better_exposed_filters + * + * @see \Drupal\better_exposed_filters\Plugin\better_exposed_filters\filter\Links + */ +class LinksFilterWidgetKernelTest extends BetterExposedFiltersKernelTestBase { + + /** + * {@inheritdoc} + */ + public static $testViews = ['bef_test']; + + /** + * Tests the exposed links filter widget. + */ + public function testExposedLinks() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Ensure our filter "term_node_tid_depth" has show hierarchy enabled. + $display['display_options']['filters']['term_node_tid_depth']['hierarchy'] = TRUE; + + // Change exposed filter "field_bef_integer" and "term_node_tid_depth" to + // links (i.e. 'bef_links'). + $this->setBetterExposedOptions($view, [ + 'filter' => [ + 'field_bef_integer_value' => [ + 'plugin_id' => 'bef_links', + ], + 'term_node_tid_depth' => [ + 'plugin_id' => 'bef_links', + ], + ], + ]); + + // Render the exposed form. + $this->renderExposedForm($view); + + // Check our "FIELD_BEF_INTEGER" filter is rendered as links. + $actual = $this->xpath('//form//a[starts-with(@name, "field_bef_integer_value")]'); + $this->assertCount(6, $actual, 'Exposed filter "FIELD_BEF_INTEGER" has correct number of exposed links.'); + + // Check our "TERM_NODE_TID_DEPTH" filter is rendered as nested links. + $actual = $this->xpath("//form//div[contains(concat(' ',normalize-space(@class),' '),' bef-nested ')]"); + $this->assertCount(1, $actual, 'Exposed filter "TERM_NODE_TID_DEPTH" has bef-nested class'); + + $actual = $this->xpath('//form//div[@id="edit-term-node-tid-depth--2"]/ul/li/a[starts-with(@name, "term_node_tid_depth")]'); + $this->assertCount(4, $actual, 'Exposed filter "TERM_NODE_TID_DEPTH" has correct number of exposed top-level links.'); + + $actual = $this->xpath('//form//div[@id="edit-term-node-tid-depth--2"]/ul/li/ul/li/a[starts-with(@name, "term_node_tid_depth")]'); + $this->assertCount(5, $actual, 'Exposed filter "TERM_NODE_TID_DEPTH" has correct number of exposed second-level links.'); + + $actual = $this->xpath('//form//div[@id="edit-term-node-tid-depth--2"]/ul/li/ul/li/ul/li/a[starts-with(@name, "term_node_tid_depth")]'); + $this->assertCount(14, $actual, 'Exposed filter "TERM_NODE_TID_DEPTH" has correct number of exposed third-level links.'); + + $view->destroy(); + } + +} diff --git a/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/RadioButtonsFilterWidgetKernelTest.php b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/RadioButtonsFilterWidgetKernelTest.php new file mode 100644 index 0000000000..e29afda81b --- /dev/null +++ b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/RadioButtonsFilterWidgetKernelTest.php @@ -0,0 +1,120 @@ +<?php + +namespace Drupal\Tests\better_exposed_filters\Kernel\Plugin\filter; + +use Drupal\Tests\better_exposed_filters\Kernel\BetterExposedFiltersKernelTestBase; +use Drupal\views\Views; + +/** + * Tests the radio buttons/checkboxes filter widget (i.e. "bef"). + * + * @group better_exposed_filters + * + * @see \Drupal\better_exposed_filters\Plugin\better_exposed_filters\filter\RadioButtons + */ +class RadioButtonsFilterWidgetKernelTest extends BetterExposedFiltersKernelTestBase { + + /** + * {@inheritdoc} + */ + public static $testViews = ['bef_test']; + + /** + * Tests the exposed checkboxes filter widget. + */ + public function testExposedCheckboxes() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Ensure our filter "field_bef_integer" allows multiple values. + $display['display_options']['filters']['field_bef_integer_value']['expose']['multiple'] = TRUE; + // Ensure our filter "term_node_tid_depth" has show hierarchy enabled. + $display['display_options']['filters']['term_node_tid_depth']['expose']['multiple'] = TRUE; + $display['display_options']['filters']['term_node_tid_depth']['hierarchy'] = TRUE; + + // Change exposed filter "field_bef_integer" and "term_node_tid_depth" to + // checkboxes (i.e. 'bef'). + $this->setBetterExposedOptions($view, [ + 'filter' => [ + 'field_bef_integer_value' => [ + 'plugin_id' => 'bef', + ], + 'term_node_tid_depth' => [ + 'plugin_id' => 'bef', + ], + ], + ]); + + // Render the exposed form. + $this->renderExposedForm($view); + + // Check our "FIELD_BEF_INTEGER" filter is rendered as checkboxes. + $actual = $this->xpath('//form//input[@type="checkbox" and starts-with(@name, "field_bef_integer_value")]'); + $this->assertCount(5, $actual, 'Exposed filter "FIELD_BEF_INTEGER" has correct number of exposed checkboxes.'); + + // Check our "TERM_NODE_TID_DEPTH" filter is rendered as nested checkboxes. + $actual = $this->xpath("//form//div[contains(concat(' ',normalize-space(@class),' '),' bef-nested ')]"); + $this->assertCount(1, $actual, 'Exposed filter "TERM_NODE_TID_DEPTH" has bef-nested class'); + + $actual = $this->xpath('//form//div[@id="edit-term-node-tid-depth--2"]/div/ul/li/div/input[@type="checkbox" and starts-with(@name, "term_node_tid_depth")]'); + $this->assertCount(3, $actual, 'Exposed filter "TERM_NODE_TID_DEPTH" has correct number of exposed top-level checkboxes.'); + + $actual = $this->xpath('//form//div[@id="edit-term-node-tid-depth--2"]/div/ul/li/ul/li/div/input[@type="checkbox" and starts-with(@name, "term_node_tid_depth")]'); + $this->assertCount(5, $actual, 'Exposed filter "TERM_NODE_TID_DEPTH" has correct number of exposed second-level checkboxes.'); + + $actual = $this->xpath('//form//div[@id="edit-term-node-tid-depth--2"]/div/ul/li/ul/li/ul/li/div/input[@type="checkbox" and starts-with(@name, "term_node_tid_depth")]'); + $this->assertCount(14, $actual, 'Exposed filter "TERM_NODE_TID_DEPTH" has correct number of exposed third-level checkboxes.'); + + $view->destroy(); + } + + /** + * Tests the exposed radio buttons filter widget. + */ + public function testExposedRadioButtons() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Ensure our filter "term_node_tid_depth" has show hierarchy enabled. + $display['display_options']['filters']['term_node_tid_depth']['hierarchy'] = TRUE; + + // Change exposed filter "field_bef_integer" and "term_node_tid_depth" to + // radio buttons (i.e. 'bef'). + $this->setBetterExposedOptions($view, [ + 'filter' => [ + 'field_bef_boolean_value' => [ + 'plugin_id' => 'bef', + ], + 'term_node_tid_depth' => [ + 'plugin_id' => 'bef', + ], + ], + ]); + + // Render the exposed form. + $this->renderExposedForm($view); + + // Check our filter is rendered as radio buttons (i.e. Any, true, false). + $actual = $this->xpath('//form//input[@type="radio" and @name="field_bef_boolean_value"]'); + $this->assertCount(3, $actual, 'Exposed filter "FIELD_BEF_BOOLEAN" renders as radio buttons.'); + + // Check our "TERM_NODE_TID_DEPTH" filter is rendered as nested radio + // buttons. + $actual = $this->xpath("//form//div[contains(concat(' ',normalize-space(@class),' '),' bef-nested ')]"); + $this->assertCount(1, $actual, 'Exposed filter "TERM_NODE_TID_DEPTH" has bef-nested class'); + + // The difference with checkboxes is that radio buttons render an additoinal + // top level option (i.e. any). + $actual = $this->xpath('//form//div[@id="edit-term-node-tid-depth--2"]/div/ul/li/div/input[@type="radio" and starts-with(@name, "term_node_tid_depth")]'); + $this->assertCount(4, $actual, 'Exposed filter "TERM_NODE_TID_DEPTH" has correct number of exposed top-level radio buttons.'); + + $actual = $this->xpath('//form//div[@id="edit-term-node-tid-depth--2"]/div/ul/li/ul/li/div/input[@type="radio" and starts-with(@name, "term_node_tid_depth")]'); + $this->assertCount(5, $actual, 'Exposed filter "TERM_NODE_TID_DEPTH" has correct number of exposed second-level radio buttons.'); + + $actual = $this->xpath('//form//div[@id="edit-term-node-tid-depth--2"]/div/ul/li/ul/li/ul/li/div/input[@type="radio" and starts-with(@name, "term_node_tid_depth")]'); + $this->assertCount(14, $actual, 'Exposed filter "TERM_NODE_TID_DEPTH" has correct number of exposed third-level radio buttons.'); + + $view->destroy(); + } + +} diff --git a/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/SingleFilterWidgetKernelTest.php b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/SingleFilterWidgetKernelTest.php new file mode 100644 index 0000000000..23950bf5b7 --- /dev/null +++ b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/filter/SingleFilterWidgetKernelTest.php @@ -0,0 +1,49 @@ +<?php + +namespace Drupal\Tests\better_exposed_filters\Kernel\Plugin\filter; + +use Drupal\Tests\better_exposed_filters\Kernel\BetterExposedFiltersKernelTestBase; +use Drupal\views\Views; + +/** + * Tests the options of a single on/off filter widget. + * + * @group better_exposed_filters + * + * @see \Drupal\better_exposed_filters\Plugin\better_exposed_filters\filter\FilterWidgetBase + */ +class SingleFilterWidgetKernelTest extends BetterExposedFiltersKernelTestBase { + + /** + * {@inheritdoc} + */ + public static $testViews = ['bef_test']; + + /** + * Tests hiding element with single option. + */ + public function testSingleExposedCheckbox() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Change exposed filter "field_bef_boolean" to single on/off (i.e. + // 'bef_single'). + $this->setBetterExposedOptions($view, [ + 'filter' => [ + 'field_bef_boolean_value' => [ + 'plugin_id' => 'bef_single', + ], + ], + ]); + + // Render the exposed form. + $this->renderExposedForm($view); + + // Check our "FIELD_BEF_BOOLEAN" filter is rendered as a single checkbox. + $actual = $this->xpath('//form//input[@type="checkbox" and starts-with(@name, "field_bef_boolean_value")]'); + $this->assertCount(1, $actual, 'Exposed filter "FIELD_BEF_BOOLEAN" is rendered as a checkbox.'); + + $view->destroy(); + } + +} diff --git a/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/pager/LinksPagerWidgetKernelTest.php b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/pager/LinksPagerWidgetKernelTest.php new file mode 100644 index 0000000000..cb2fd2b89a --- /dev/null +++ b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/pager/LinksPagerWidgetKernelTest.php @@ -0,0 +1,50 @@ +<?php + +namespace Drupal\Tests\better_exposed_filters\Kernel\Plugin\pager; + +use Drupal\Tests\better_exposed_filters\Kernel\BetterExposedFiltersKernelTestBase; +use Drupal\views\Views; + +/** + * Tests the links pager widget (i.e. "bef"). + * + * @group better_exposed_filters + * + * @see \Drupal\better_exposed_filters\Plugin\better_exposed_filters\pager\RadioButtons + */ +class LinksPagerWidgetKernelTest extends BetterExposedFiltersKernelTestBase { + + /** + * {@inheritdoc} + */ + public static $testViews = ['bef_test']; + + /** + * Tests the exposed links pager widget. + */ + public function testExposedLinks() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Ensure our pager exposes all items (i.e. items_per_page and offset). + $display['display_options']['pager']['options']['expose']['items_per_page'] = TRUE; + $display['display_options']['pager']['options']['expose']['offset'] = TRUE; + + // Change exposed pager to radio buttons (i.e. 'bef'). + $this->setBetterExposedOptions($view, [ + 'pager' => [ + 'plugin_id' => 'bef_links', + ], + ]); + + // Render the exposed form. + $this->renderExposedForm($view); + + // Check our pager item "items_per_page" is rendered as links. + $actual = $this->xpath('//form//a[starts-with(@name, "items_per_page")]'); + $this->assertCount(4, $actual, 'Exposed pager "items_per_page" has correct number of exposed radio buttons.'); + + $view->destroy(); + } + +} diff --git a/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/pager/RadioButtonsPagerWidgetKernelTest.php b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/pager/RadioButtonsPagerWidgetKernelTest.php new file mode 100644 index 0000000000..81a62230b5 --- /dev/null +++ b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/pager/RadioButtonsPagerWidgetKernelTest.php @@ -0,0 +1,50 @@ +<?php + +namespace Drupal\Tests\better_exposed_filters\Kernel\Plugin\pager; + +use Drupal\Tests\better_exposed_filters\Kernel\BetterExposedFiltersKernelTestBase; +use Drupal\views\Views; + +/** + * Tests the radio buttons pager widget (i.e. "bef"). + * + * @group better_exposed_filters + * + * @see \Drupal\better_exposed_filters\Plugin\better_exposed_filters\pager\RadioButtons + */ +class RadioButtonsPagerWidgetKernelTest extends BetterExposedFiltersKernelTestBase { + + /** + * {@inheritdoc} + */ + public static $testViews = ['bef_test']; + + /** + * Tests the exposed radio buttons pager widget. + */ + public function testExposedRadioButtons() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Ensure our pager exposes all items (i.e. items_per_page and offset). + $display['display_options']['pager']['options']['expose']['items_per_page'] = TRUE; + $display['display_options']['pager']['options']['expose']['offset'] = TRUE; + + // Change exposed pager to radio buttons (i.e. 'bef'). + $this->setBetterExposedOptions($view, [ + 'pager' => [ + 'plugin_id' => 'bef', + ], + ]); + + // Render the exposed form. + $this->renderExposedForm($view); + + // Check our pager item "items_per_page" is rendered as radio buttons. + $actual = $this->xpath('//form//input[@type="radio" and starts-with(@name, "items_per_page")]'); + $this->assertCount(4, $actual, 'Exposed pager "items_per_page" has correct number of exposed radio buttons.'); + + $view->destroy(); + } + +} diff --git a/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/sort/LinksSortWidgetKernelTest.php b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/sort/LinksSortWidgetKernelTest.php new file mode 100644 index 0000000000..b3cb0f03f7 --- /dev/null +++ b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/sort/LinksSortWidgetKernelTest.php @@ -0,0 +1,50 @@ +<?php + +namespace Drupal\Tests\better_exposed_filters\Kernel\Plugin\sort; + +use Drupal\Tests\better_exposed_filters\Kernel\BetterExposedFiltersKernelTestBase; +use Drupal\views\Views; + +/** + * Tests the links sort widget (i.e. "bef_links"). + * + * @group better_exposed_filters + * + * @see \Drupal\better_exposed_filters\Plugin\better_exposed_filters\sort\Links + */ +class LinksSortWidgetKernelTest extends BetterExposedFiltersKernelTestBase { + + /** + * {@inheritdoc} + */ + public static $testViews = ['bef_test']; + + /** + * Tests the exposed links sort widget. + */ + public function testExposedLinks() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Change exposed sort to links (i.e. 'bef_links'). + $this->setBetterExposedOptions($view, [ + 'sort' => [ + 'plugin_id' => 'bef_links', + ], + ]); + + // Render the exposed form. + $this->renderExposedForm($view); + + // Check our sort item "sort_by" is rendered as links. + $actual = $this->xpath('//form//a[starts-with(@id, "edit-sort-by")]'); + $this->assertCount(1, $actual, 'Exposed sort "sort_by" has correct number of exposed links.'); + + // Check our sort item "sort_order" is rendered as links. + $actual = $this->xpath('//form//a[starts-with(@id, "edit-sort-order")]'); + $this->assertCount(2, $actual, 'Exposed sort "sort_order" has correct number of exposed links.'); + + $view->destroy(); + } + +} diff --git a/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/sort/RadioButtonsSortWidgetKernelTest.php b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/sort/RadioButtonsSortWidgetKernelTest.php new file mode 100644 index 0000000000..fd9ed9252b --- /dev/null +++ b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/sort/RadioButtonsSortWidgetKernelTest.php @@ -0,0 +1,50 @@ +<?php + +namespace Drupal\Tests\better_exposed_filters\Kernel\Plugin\sort; + +use Drupal\Tests\better_exposed_filters\Kernel\BetterExposedFiltersKernelTestBase; +use Drupal\views\Views; + +/** + * Tests the radio buttons sort widget (i.e. "bef"). + * + * @group better_exposed_filters + * + * @see \Drupal\better_exposed_filters\Plugin\better_exposed_filters\sort\RadioButtons + */ +class RadioButtonsSortWidgetKernelTest extends BetterExposedFiltersKernelTestBase { + + /** + * {@inheritdoc} + */ + public static $testViews = ['bef_test']; + + /** + * Tests the exposed radio buttons sort widget. + */ + public function testExposedRadioButtons() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Change exposed sort to radio buttons (i.e. 'bef'). + $this->setBetterExposedOptions($view, [ + 'sort' => [ + 'plugin_id' => 'bef', + ], + ]); + + // Render the exposed form. + $this->renderExposedForm($view); + + // Check our sort item "sort_by" is rendered as links. + $actual = $this->xpath('//form//input[@type="radio" and starts-with(@id, "edit-sort-by")]'); + $this->assertCount(1, $actual, 'Exposed sort "sort_by" has correct number of exposed radio buttons.'); + + // Check our sort item "sort_order" is rendered as links. + $actual = $this->xpath('//form//input[@type="radio" and starts-with(@id, "edit-sort-order")]'); + $this->assertCount(2, $actual, 'Exposed sort "sort_order" has correct number of exposed radio buttons.'); + + $view->destroy(); + } + +} diff --git a/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/sort/SortWidgetKernelTest.php b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/sort/SortWidgetKernelTest.php new file mode 100644 index 0000000000..b6adf083f5 --- /dev/null +++ b/web/modules/better_exposed_filters/tests/src/Kernel/Plugin/sort/SortWidgetKernelTest.php @@ -0,0 +1,136 @@ +<?php + +namespace Drupal\Tests\better_exposed_filters\Kernel\Plugin\sort; + +use Drupal\Tests\better_exposed_filters\Kernel\BetterExposedFiltersKernelTestBase; +use Drupal\views\Views; + +/** + * Tests the advanced options of a sort widget. + * + * @group better_exposed_filters + * + * @see \Drupal\better_exposed_filters\Plugin\better_exposed_filters\sort\SortWidgetBase + */ +class SortWidgetKernelTest extends BetterExposedFiltersKernelTestBase { + + /** + * {@inheritdoc} + */ + public static $testViews = ['bef_test']; + + /** + * Tests combining sort options (sort order and sort by). + */ + public function testCombineSortOptions() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Ensure our sort "created" is exposed. + $display['display_options']['sorts']['created']['exposed'] = TRUE; + $display['display_options']['sorts']['created']['expose']['label'] = 'Created'; + + // Enable combined sort. + $this->setBetterExposedOptions($view, [ + 'sort' => [ + 'advanced' => [ + 'combine' => TRUE, + ], + ], + ]); + + // Get the exposed form render array. + $output = $this->getExposedFormRenderArray($view); + + // Assert our "sort_bef_combine" contains both sort by and sort order + // options. + $options = $output['sort_bef_combine']['#options']; + $assert = [ + 'created_ASC' => 'Created Asc', + 'created_DESC' => 'Created Desc', + ]; + + // Assert our combined sort options are added. + $this->assertEqual($options, $assert, 'Sort options are combined.'); + + $view->destroy(); + } + + /** + * Tests combining and rewriting sort options (sort order and sort by). + */ + public function testCombineRewriteSortOptions() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Ensure our sort "created" is exposed. + $display['display_options']['sorts']['created']['exposed'] = TRUE; + $display['display_options']['sorts']['created']['expose']['label'] = 'Created'; + + // Enable combined sort and rewrite options. + $this->setBetterExposedOptions($view, [ + 'sort' => [ + 'advanced' => [ + 'combine' => TRUE, + 'combine_rewrite' => "Created Desc|down\r\nCreated Asc|up", + ], + ], + ]); + + // Get the exposed form render array. + $output = $this->getExposedFormRenderArray($view); + + // Assert our "sort_bef_combine" contains both sort by and sort order + // options, and has its options rewritten. + $options = $output['sort_bef_combine']['#options']; + $assert = [ + 'created_DESC' => 'down', + 'created_ASC' => 'up', + ]; + + // Assert our combined sort options are added. + $this->assertEqual($options, $assert, 'Sort options are combined and rewritten.'); + + $view->destroy(); + } + + /** + * Tests adding a reset sort option. + */ + public function testResetSortOptions() { + $view = Views::getView('bef_test'); + $display = &$view->storage->getDisplay('default'); + + // Ensure our sort "created" is exposed. + $display['display_options']['sorts']['created']['exposed'] = TRUE; + $display['display_options']['sorts']['created']['expose']['label'] = 'Created'; + + // Enable combined sort and rewrite options. + $this->setBetterExposedOptions($view, [ + 'sort' => [ + 'advanced' => [ + 'combine' => TRUE, + 'reset' => TRUE, + 'reset_label' => 'Reset sort', + ], + ], + ]); + + // Get the exposed form render array. + $output = $this->getExposedFormRenderArray($view); + + // Assert our "sort_bef_combine" contains a reset option at the top. + $options = $output['sort_bef_combine']['#options']; + $assert = [ + ' ' => 'Reset sort', + 'created_ASC' => 'Created Asc', + 'created_DESC' => 'Created Desc', + ]; + + // Assert our combined sort options are added. + $this->assertEqual($options, $assert, 'Reset sort option was added.'); + + $view->destroy(); + } + +} diff --git a/web/modules/better_exposed_filters/tests/src/Traits/BetterExposedFiltersTrait.php b/web/modules/better_exposed_filters/tests/src/Traits/BetterExposedFiltersTrait.php new file mode 100644 index 0000000000..5926298e9b --- /dev/null +++ b/web/modules/better_exposed_filters/tests/src/Traits/BetterExposedFiltersTrait.php @@ -0,0 +1,44 @@ +<?php + +namespace Drupal\Tests\better_exposed_filters\Traits; + +use Drupal\Component\Utility\NestedArray; +use Drupal\views\ViewExecutable; + +/** + * Makes Drupal's test API forward compatible with multiple versions of PHPUnit. + */ +trait BetterExposedFiltersTrait { + + /** + * Returns the configured BEF options. + * + * @param \Drupal\views\ViewExecutable $view + * The view object. + * + * @return array + * Array of BEF options. + */ + protected function &getBetterExposedOptions(ViewExecutable $view) { + return $view->storage->getDisplay('default')['display_options']['exposed_form']['options']['bef']; + } + + /** + * Merges options into existing BEF configuration. + * + * @param \Drupal\views\ViewExecutable $view + * The view object. + * @param array $options + * The list of options (e.g. ['sort' => ['plugin_id' => 'default']]). + * + * @throws \Drupal\Core\Entity\EntityStorageException + * In case of failures an exception is thrown. + */ + protected function setBetterExposedOptions(ViewExecutable $view, array $options) { + $bef_options = &$this->getBetterExposedOptions($view); + $bef_options = NestedArray::mergeDeep($bef_options, $options); + + $view->storage->save(); + } + +} diff --git a/web/modules/better_exposed_filters/tests/src/Unit/BetterExposedFiltersHelperUnitTest.php b/web/modules/better_exposed_filters/tests/src/Unit/BetterExposedFiltersHelperUnitTest.php new file mode 100644 index 0000000000..6ae459ba0f --- /dev/null +++ b/web/modules/better_exposed_filters/tests/src/Unit/BetterExposedFiltersHelperUnitTest.php @@ -0,0 +1,356 @@ +<?php + +namespace Drupal\Tests\better_exposed_filters\Unit; + +use Drupal\better_exposed_filters\BetterExposedFiltersHelper; +use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\Tests\UnitTestCase; + +/** + * Tests the helper functions for better exposed filters. + * + * @coversDefaultClass \Drupal\better_exposed_filters\BetterExposedFiltersHelper + * + * @group better_exposed_filters + */ +class BetterExposedFiltersHelperUnitTest extends UnitTestCase { + + use StringTranslationTrait; + + /** + * Tests options are rewritten correctly. + * + * @dataProvider providerTestRewriteOptions + * + * @covers ::rewriteOptions + */ + public function testRewriteOptions($options, $settings, $expected) { + $actual = BetterExposedFiltersHelper::rewriteOptions($options, $settings); + $this->assertArrayEquals(array_values($expected), array_values($actual)); + } + + /** + * Data provider for ::testRewriteOptions. + */ + public function providerTestRewriteOptions() { + $data = []; + + // Super basic rewrite. + $data[] = [ + ['foo' => 'bar'], + "bar|baz", + ['foo' => 'baz'], + ]; + + // Removes an option. + $data[] = [ + ['foo' => 'bar'], + "bar|", + [], + ]; + + // An option in the middle is removed -- preserves order. + $data[] = [ + ['foo' => '1', 'bar' => '2', 'baz' => '3'], + "2|", + ['foo' => '1', 'baz' => '3'], + ]; + + // Ensure order is preserved. + $data[] = [ + ['foo' => '1', 'bar' => '2', 'baz' => '3'], + "2|Two", + ['foo' => '1', 'bar' => 'Two', 'baz' => '3'], + ]; + + // No options are replaced. + $data[] = [ + ['foo' => '1', 'bar' => '2', 'baz' => '3'], + "4|Two", + ['foo' => '1', 'bar' => '2', 'baz' => '3'], + ]; + + // All options are replaced. + $data[] = [ + ['foo' => '1', 'bar' => '2', 'baz' => '3'], + "1|One\n2|Two\n3|Three", + ['foo' => 'One', 'bar' => 'Two', 'baz' => 'Three'], + ]; + + return $data; + } + + /** + * Tests options are rewritten correctly. + * + * @dataProvider providerTestRewriteReorderOptions + * + * @covers ::rewriteOptions + */ + public function testRewriteReorderOptions($options, $settings, $expected) { + $actual = BetterExposedFiltersHelper::rewriteOptions($options, $settings, TRUE); + $this->assertArrayEquals(array_values($expected), array_values($actual)); + } + + /** + * Data provider for ::testRewriteReorderOptions. + */ + public function providerTestRewriteReorderOptions() { + $data = []; + + // Basic use case. + $data[] = [ + ['foo' => '1', 'bar' => '2', 'baz' => '3'], + '2|Two', + ['bar' => 'Two', 'foo' => '1', 'baz' => '3'], + ]; + + // No option replaced should not change the order. + $data[] = [ + ['foo' => '1', 'bar' => '2', 'baz' => '3'], + '4|Four', + ['foo' => '1', 'bar' => '2', 'baz' => '3'], + ]; + + // Completely reorder options. + $data[] = [ + ['foo' => '1', 'bar' => '2', 'baz' => '3'], + "3|Three\n2|Two\n1|One", + ['baz' => 'Three', 'bar' => 'Two', 'foo' => 'One'], + ]; + + return $data; + } + + /** + * Tests options are rewritten correctly. + * + * @dataProvider providerTestRewriteTaxonomy + * + * @covers ::rewriteOptions + */ + public function testRewriteTaxonomy($options, $settings, $expected, $reorder = FALSE) { + $actual = BetterExposedFiltersHelper::rewriteOptions($options, $settings, $reorder); + $this->assertArrayEquals(array_values($expected), array_values($actual)); + } + + /** + * Data provider for ::testRewriteTaxonomy. + */ + public function providerTestRewriteTaxonomy() { + $data = []; + + // Replace a single item, no change in order. + $data[] = [ + [ + (object) ['option' => [123 => 'term1']], + (object) ['option' => [456 => 'term2']], + (object) ['option' => [789 => 'term3']], + ], + "term2|Two", + [ + (object) ['option' => [123 => 'term1']], + (object) ['option' => [456 => 'Two']], + (object) ['option' => [789 => 'term3']], + ], + ]; + + // Replace all items, no change in order. + $data[] = [ + [ + (object) ['option' => [123 => 'term1']], + (object) ['option' => [456 => 'term2']], + (object) ['option' => [789 => 'term3']], + ], + "term2|Two\nterm3|Three\nterm1|One", + [ + (object) ['option' => [123 => 'One']], + (object) ['option' => [456 => 'Two']], + (object) ['option' => [789 => 'Three']], + ], + ]; + + // Replace a single item, with change in order. + $data[] = [ + [ + (object) ['option' => [123 => 'term1']], + (object) ['option' => [456 => 'term2']], + (object) ['option' => [789 => 'term3']], + ], "term2|Two", + [ + (object) ['option' => [456 => 'Two']], + (object) ['option' => [123 => 'term1']], + (object) ['option' => [789 => 'term3']], + ], TRUE, + ]; + + // Replace all items, with change in order. + $data[] = [ + [ + (object) ['option' => [123 => 'term1']], + (object) ['option' => [456 => 'term2']], + (object) ['option' => [789 => 'term3']], + ], "term2|Two\nterm3|Three\nterm1|One", + [ + (object) ['option' => [456 => 'Two']], + (object) ['option' => [789 => 'Three']], + (object) ['option' => [123 => 'One']], + ], TRUE, + ]; + + return $data; + } + + /** + * Tests options are rewritten correctly. + * + * @dataProvider providerTestSortOptions + * + * @covers ::sortOptions + */ + public function testSortOptions($unsorted, $expected) { + $sorted = BetterExposedFiltersHelper::sortOptions($unsorted); + $this->assertArrayEquals(array_values($sorted), array_values($expected)); + } + + /** + * Data provider for ::testSortOptions. + */ + public function providerTestSortOptions() { + // Data providers run before ::setUp. We rely on the stringTranslationTrait + // for some of our option values so call it here instead. + $this->stringTranslation = $this->getStringTranslationStub(); + + $data = []; + + // List of strings. + $data[] = [ + [ + 'e', + 'a', + 'b', + 'd', + 'c', + ], [ + 'a', + 'b', + 'c', + 'd', + 'e', + ], + ]; + + // List of mixed values. + $data[] = [ + [ + '1', + 'a', + '2', + 'b', + '3', + ], [ + '1', + '2', + '3', + 'a', + 'b', + ], + ]; + + // List of taxonomy terms. + $data[] = [ + [ + (object) ['option' => [555 => 'term5']], + (object) ['option' => [222 => 'term2']], + (object) ['option' => [444 => 'term4']], + (object) ['option' => [333 => 'term3']], + (object) ['option' => [111 => 'term1']], + ], [ + (object) ['option' => [111 => 'term1']], + (object) ['option' => [222 => 'term2']], + (object) ['option' => [333 => 'term3']], + (object) ['option' => [444 => 'term4']], + (object) ['option' => [555 => 'term5']], + ], + ]; + + return $data; + } + + /** + * Tests options are rewritten correctly. + * + * @dataProvider providerTestSortNestedOptions + * + * @covers ::sortNestedOptions + */ + public function testSortNestedOptions($unsorted, $expected) { + $sorted = BetterExposedFiltersHelper::sortNestedOptions($unsorted); + $this->assertArrayEquals(array_values($sorted), array_values($expected)); + } + + /** + * Data provider for ::testSortNestedOptions. + */ + public function providerTestSortNestedOptions() { + // Data providers run before ::setUp. We rely on the stringTranslationTrait + // for some of our option values so call it here instead. + $this->stringTranslation = $this->getStringTranslationStub(); + + $data = []; + + // List of nested taxonomy terms. + $data[] = [ + [ + (object) ['option' => [2303 => 'United States']], + (object) ['option' => [2311 => '-Washington']], + (object) ['option' => [2312 => '--Seattle']], + (object) ['option' => [2313 => '--Spokane']], + (object) ['option' => [2314 => '--Walla Walla']], + (object) ['option' => [2304 => '-California']], + (object) ['option' => [2307 => '--Santa Barbara']], + (object) ['option' => [2306 => '--San Diego']], + (object) ['option' => [2305 => '--San Francisco']], + (object) ['option' => [2308 => '-Oregon']], + (object) ['option' => [2310 => '--Eugene']], + (object) ['option' => [2309 => '--Portland']], + (object) ['option' => [2324 => 'Mexico']], + (object) ['option' => [2315 => 'Canada']], + (object) ['option' => [2316 => '-British Columbia']], + (object) ['option' => [2319 => '--Whistler']], + (object) ['option' => [2317 => '--Vancouver']], + (object) ['option' => [2318 => '--Victoria']], + (object) ['option' => [2320 => '-Alberta']], + (object) ['option' => [2321 => '--Calgary']], + (object) ['option' => [2323 => '--Lake Louise']], + (object) ['option' => [2322 => '--Edmonton']], + ], [ + (object) ['option' => [2315 => 'Canada']], + (object) ['option' => [2320 => '-Alberta']], + (object) ['option' => [2321 => '--Calgary']], + (object) ['option' => [2322 => '--Edmonton']], + (object) ['option' => [2323 => '--Lake Louise']], + (object) ['option' => [2316 => '-British Columbia']], + (object) ['option' => [2317 => '--Vancouver']], + (object) ['option' => [2318 => '--Victoria']], + (object) ['option' => [2319 => '--Whistler']], + (object) ['option' => [2324 => 'Mexico']], + (object) ['option' => [2303 => 'United States']], + (object) ['option' => [2304 => '-California']], + (object) ['option' => [2306 => '--San Diego']], + (object) ['option' => [2305 => '--San Francisco']], + (object) ['option' => [2307 => '--Santa Barbara']], + (object) ['option' => [2308 => '-Oregon']], + (object) ['option' => [2310 => '--Eugene']], + (object) ['option' => [2309 => '--Portland']], + (object) ['option' => [2311 => '-Washington']], + (object) ['option' => [2312 => '--Seattle']], + (object) ['option' => [2313 => '--Spokane']], + (object) ['option' => [2314 => '--Walla Walla']], + ], + ]; + + return $data; + } + +} diff --git a/web/modules/better_exposed_filters/tests/src/Unit/BetterExposedFiltersTest.php b/web/modules/better_exposed_filters/tests/src/Unit/BetterExposedFiltersTest.php deleted file mode 100644 index cade986a7b..0000000000 --- a/web/modules/better_exposed_filters/tests/src/Unit/BetterExposedFiltersTest.php +++ /dev/null @@ -1,222 +0,0 @@ -<?php - -namespace Drupal\better_exposed_filters\Tests; - -use Drupal\better_exposed_filters\Plugin\views\exposed_form\BetterExposedFilters; -use Drupal\Tests\UnitTestCase; - -/** - * @coversDefaultClass \Drupal\better_exposed_filters\Plugin\views\exposed_form\BetterExposedFilters - * @group bef - */ -class BetterExposedFiltersTest extends UnitTestCase { - - /** - * Order in the returned array matters for BEF rewrites. But assertEquals - * don't take array order into account. - * - * @param $expected - * Expectedd value. - * @param $actual - * Actual value. - * @param $message - * Optionsal error message on failure. - */ - protected function assertRewrite($expected, $actual, $message = NULL) { - // Order matters. - foreach ($actual as $actual_item) { - $expected_item = array_shift($expected); - $this->assertEquals($expected_item, $actual_item, $message); - } - } - - public function providerTestRewriteOptions() { - $data = []; - - // Super basic rewrite. - $data[] = [ - ['foo' => 'bar'], - "bar|baz", - ['foo' => 'baz'], - ]; - - // Removes an option. - $data[] = [ - ['foo' => 'bar'], - "bar|", - [], - ]; - - // An option in the middle is removed -- preserves order. - $data[] = [ - ['foo' => '1', 'bar' => '2', 'baz' => '3'], - "2|", - ['foo' => '1', 'baz' => '3'], - ]; - - // Ensure order is preserved. - $data[] = [ - ['foo' => '1', 'bar' => '2', 'baz' => '3'], - "2|Two", - ['foo' => '1', 'bar' => 'Two', 'baz' => '3'], - ]; - - // No options are replaced. - $data[] = [ - ['foo' => '1', 'bar' => '2', 'baz' => '3'], - "4|Two", - ['foo' => '1', 'bar' => '2', 'baz' => '3'], - ]; - - // All options are replaced. - $data[] = [ - ['foo' => '1', 'bar' => '2', 'baz' => '3'], - "1|One\n2|Two\n3|Three", - ['foo' => 'One', 'bar' => 'Two', 'baz' => 'Three'], - ]; - - return $data; - } - - /** - * Tests options are rewritten correctly. - * - * @dataProvider providerTestRewriteOptions - * @covers ::rewriteOptions - */ - public function testRewriteOptions($options, $rewriteSettings, $expected) { - $bef = new TestBEF([], 'default', []); - $actual = $bef->testRewriteOptions($options, $rewriteSettings); - $this->assertRewrite($expected, $actual); - } - - public function providerTestRewriteReorderOptions() { - $data = []; - - // Basic use case. - $data[] = [ - ['foo' => '1', 'bar' => '2', 'baz' => '3'], - '2|Two', - ['bar' => 'Two', 'foo' => '1', 'baz' => '3'], - ]; - - // No option replaced should not change the order - $data[] = [ - ['foo' => '1', 'bar' => '2', 'baz' => '3'], - '4|Four', - ['foo' => '1', 'bar' => '2', 'baz' => '3'], - ]; - - // Completely reorder options - $data[] = [ - ['foo' => '1', 'bar' => '2', 'baz' => '3'], - "3|Three\n2|Two\n1|One", - ['baz' => 'Three', 'bar' => 'Two', 'foo' => 'One'], - ]; - - return $data; - } - - /** - * Tests options are rewritten correctly. - * - * @dataProvider providerTestRewriteReorderOptions - * @covers ::rewriteOptions - */ - public function testRewriteReorderOptions($options, $rewriteSettings, $expected) { - $bef = new TestBEF([], 'default', []); - $actual = $bef->testRewriteOptions($options, $rewriteSettings, TRUE); - $this->assertRewrite($expected, $actual); - } - - public function providerTestRewriteTaxonomy() { - $data = []; - - // Replace a single item, no change in order. - $data[] = [ - [ - (object)['option' => [123 => 'term1']], - (object)['option' => [456 => 'term2']], - (object)['option' => [789 => 'term3']], - ], - "term2|Two", - [ - (object)['option' => [123 => 'term1']], - (object)['option' => [456 => 'Two']], - (object)['option' => [789 => 'term3']], - ], - ]; - - // Replace all items, no change in order. - $data[] = [ - [ - (object)['option' => [123 => 'term1']], - (object)['option' => [456 => 'term2']], - (object)['option' => [789 => 'term3']], - ], - "term2|Two\nterm3|Three\nterm1|One", - [ - (object)['option' => [123 => 'One']], - (object)['option' => [456 => 'Two']], - (object)['option' => [789 => 'Three']], - ], - ]; - - // @TODO: - // Replace a single item, with change in order. - //$data[] = [ - // [ - // (object)['option' => [123 => 'term1']], - // (object)['option' => [456 => 'term2']], - // (object)['option' => [789 => 'term3']], - // ], - // "term2|Two", - // [ - // (object)['option' => [456 => 'Two']], - // (object)['option' => [123 => 'term1']], - // (object)['option' => [789 => 'term3']], - // ], - // TRUE, - //]; - - //// Replace all items, with change in order. - //$data[] = [ - // [ - // (object)['option' => [123 => 'term1']], - // (object)['option' => [456 => 'term2']], - // (object)['option' => [789 => 'term3']], - // ], - // "term2|Two\nterm3|Three\nterm1|One", - // [ - // (object)['option' => [456 => 'Two']], - // (object)['option' => [789 => 'Three']], - // (object)['option' => [123 => 'One']], - // ], - // TRUE, - //]; - - return $data; - } - - /** - * Tests options are rewritten correctly. - * - * @dataProvider providerTestRewriteTaxonomy - * @covers ::rewriteOptions - */ - public function testRewriteTaxonomy($options, $rewriteSettings, $expected, $reorder = FALSE) { - $bef = new TestBEF([], 'default', []); - $actual = $bef->testRewriteOptions($options, $rewriteSettings, $reorder); - $this->assertRewrite($expected, $actual); - } - -} - -// Allows access to otherwise protected methods in BEF. -class TestBEF extends BetterExposedFilters { - - public function testRewriteOptions($options, $rewriteSettings, $reorder = FALSE) { - return $this->rewriteOptions($options, $rewriteSettings, $reorder); - } - -} diff --git a/web/modules/jquery_ui/CHANGELOG.txt b/web/modules/jquery_ui/CHANGELOG.txt new file mode 100644 index 0000000000..858694195c --- /dev/null +++ b/web/modules/jquery_ui/CHANGELOG.txt @@ -0,0 +1,32 @@ + +jQuery UI 8.x-1.x, 2019-07-11 +----------------------------- +- Refactored to provide jQuery UI libraries deprecated from Drupal core. + +jQuery UI 7.x-1.x, xxxx-xx-xx +----------------------------- +#559582 by sun, drupal-id.com: Fixed installation instructions. +#325831 by sun: Fixed url() in hook_requirements() breaks install profiles. +#325831 by hanoii: Fixed drupal_get_path() in hook_requirements() breaks install + profiles. +#758910 by fp, sun: Fixed jQuery UI download link. +#542908 by sun: Clarified installation instructions. +#503848 by G�bor Hojtsy: Added default weight of JS_LIBRARY for JavaScripts. +#325831 by quicksketch, James Andres, ksenzee: Fixed drupal_get_path() fails + during Drupal installation. +#357780 by G�bor Hojtsy: Ported to 7.x. +#362509 by Rob Loach, Deciphered, sun, and adrinux: New release system with + jQuery UI 1.6. +#322731 by sun: Fixed improper use of t() in module install file. +#227687 by sun: Fixed improperly capitalized package name. + + +jQuery UI 6.x-1.2, 2008-06-23 +----------------------------- +- Updated to work with jQuery UI 1.5 release. +- Uses jQuery Update's compression settings rather than its own. + +jQuery UI 6.x-1.1, 2008-03-27 +----------------------------- +- Initial release of jQuery UI. +- Works with jQuery UI 1.5b4. diff --git a/web/modules/jquery_ui/LICENSE.txt b/web/modules/jquery_ui/LICENSE.txt new file mode 100644 index 0000000000..d159169d10 --- /dev/null +++ b/web/modules/jquery_ui/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/web/modules/jquery_ui/README.txt b/web/modules/jquery_ui/README.txt new file mode 100644 index 0000000000..5b741e25e7 --- /dev/null +++ b/web/modules/jquery_ui/README.txt @@ -0,0 +1,44 @@ +## Summary + +Drupal 8 includes jQuery UI in core, however it is no longer actively +maintained and has been marked deprecated. This module provides the +jQuery UI library for any themes and modules that require it. + + - jQuery UI [documentation](https://jqueryui.com/) + - jQuery UI [API documentation](https://api.jqueryui.com/) + +**Caution**: jQuery UI was deprecated from core because it is no longer +actively maintained, and has been marked “End of Life” by the OpenJS +Foundation. It is not recommended to depend on jQuery UI in your own +code, and instead to select a replacement solution as soon as possible. + +## Instructions + +1. Install this module. +2. Change any references in your theme or module from `core/jquery.ui` + to `jquery_ui/core` + +### Related modules + + - [jQuery UI Accordion](https://www.drupal.org/project/jquery_ui_accordion) + - [jQuery UI Button](https://www.drupal.org/project/jquery_ui_button) + - [jQuery UI Checkboxradio](https://www.drupal.org/project/jquery_ui_checkboxradio) + - [jQuery UI Controlgroup](https://www.drupal.org/project/jquery_ui_controlgroup) + - [jQuery UI Draggable](https://www.drupal.org/project/jquery_ui_draggable) + - [jQuery UI Droppable](https://www.drupal.org/project/jquery_ui_droppable) + - [jQuery UI Effects](https://www.drupal.org/project/jquery_ui_effects) + - [jQuery UI Menu](https://www.drupal.org/project/jquery_ui_menu) + - [jQuery UI Progressbar](https://www.drupal.org/project/jquery_ui_progressbar) + - [jQuery UI Selectable](https://www.drupal.org/project/jquery_ui_selectable) + - [jQuery UI Selectmenu](https://www.drupal.org/project/jquery_ui_selectmenu) + - [jQuery UI Slider](https://www.drupal.org/project/jquery_ui_slider) + - [jQuery UI Spinner](https://www.drupal.org/project/jquery_ui_spinner) + - [jQuery UI Tooltip](https://www.drupal.org/project/jquery_ui_tooltip) + +## Current maintainers: +* Jeff Robbins (jjeff) +* Angela Byron (webchick) +* Addison Berry (add1sun) +* Daniel F. Kudwien (sun) - http://drupal.org/user/54136 +* Lauri Eskola (lauriii) +* Peter Weber (zrpnr) diff --git a/web/modules/jquery_ui/jquery.ui/themes/base/core.css b/web/modules/jquery_ui/jquery.ui/themes/base/core.css new file mode 100644 index 0000000000..a4c1b2de87 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/themes/base/core.css @@ -0,0 +1,97 @@ +/*! + * jQuery UI CSS Framework 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/category/theming/ + */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { + display: none; +} +.ui-helper-hidden-accessible { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} +.ui-helper-reset { + margin: 0; + padding: 0; + border: 0; + outline: 0; + line-height: 1.3; + text-decoration: none; + font-size: 100%; + list-style: none; +} +.ui-helper-clearfix:before, +.ui-helper-clearfix:after { + content: ""; + display: table; + border-collapse: collapse; +} +.ui-helper-clearfix:after { + clear: both; +} +.ui-helper-zfix { + width: 100%; + height: 100%; + top: 0; + left: 0; + position: absolute; + opacity: 0; + filter:Alpha(Opacity=0); /* support: IE8 */ +} + +.ui-front { + z-index: 100; +} + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { + cursor: default !important; + pointer-events: none; +} + + +/* Icons +----------------------------------*/ +.ui-icon { + display: inline-block; + vertical-align: middle; + margin-top: -.25em; + position: relative; + text-indent: -99999px; + overflow: hidden; + background-repeat: no-repeat; +} + +.ui-widget-icon-block { + left: 50%; + margin-left: -8px; + display: block; +} + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; +} diff --git a/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png b/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png new file mode 100644 index 0000000000..a2e6bfc085 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png @@ -0,0 +1,3 @@ +�PNG + +��� IHDR���(���d���Ϊ�9���PLTE���~�����IDATxc����X�\�R����IEND�B`� \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_444444_256x240.png b/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_444444_256x240.png new file mode 100644 index 0000000000..9221438927 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_444444_256x240.png @@ -0,0 +1,11 @@ +�PNG + +��� IHDR�����������IJ���PLTEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD�r ���ZtRNS��3P���/"Uq@f`2� !<BHK Z#'1S,�4���j���8E���|��������)��Q$� +��b�J��mߜGc?o�h�#�-���IDATx���r�:�ax|���O�0A�I�%��=��b ���eY����~_�|��Q�d ��@P�(UM�ZI�Z~ &?ׁ��Q��0�A��`ꇚ�^���=�C��� ���c�m +�y M0�v���C<w���%K0�w���,�FhY�eY��O� �w݈��#����_����f���|#�݀P����J݀������r"�f?Ŀ0��,��}@N~��0���> OK0�^�eY�eY���K���gqƽ�����3��zHM״��i����zr88��@��u۱�Z�*�:>���c����s��l5��2[4�0'{���y��#�*��ajA��E��;瀣*^�W�X�eY�e�$/���0h@�jro��`���y��g%Y�9=@�H���(��(8� ��dզ"G���[���c��h�G(�<䈹����`}�8@W�:�������e��� y���/$���̰ +��ޗ�p���@�w���ҏo8��4�f0 +��7���i^�ѐ��rt�k8� ����FdY�eY�N�|a�3?b��*I�C���ܞ2��j�`�U�l{J-�~*S��O b2�����@+��(:�[m!3&:���o4 �E):�;@����Z�c�� �*:x>�&z�S~7��xۥ@rz�� +C�ݟ�!]�����j�X/�C{��,˲,��t��kV��O3g�L�h�����^~k���؈��� �I��<ls����T�{P��j|���.j�g���w�`0��w3�O��_�%} |{�xP z�@����Ц�'����=���� +�P�/0�Z�����q����_;����хї3�X�b�81i��{%������,���>���eu�T�����?00000�zs_��>������(W`_��O�f��UA�6��W�Ȼ������i]�l�@��WIL�^�6�-M�^��;+t{��uQQTE�� + f]1`r�-��u.p��;$ Pl�u������:�L�`P��=@�l6�/��N:ְ�Y �v�o�u�%� ��=�0o8{��N�?���t�s����$ ��0 +��6 ��7d;`Z"QNٹ!J� @�� ��������LEQh&����������=�`�^��h:!��b��NK(�i�R@b�5�k�셳�A�̕���@.���*��*P���-�����Pd; N�r��.<��ЖQŃU��u�B;xr�!(|���NL��L�.𧶸�+�\��H�)���1YU�Y�bG�����!���A-���=w�������A�&���/a�w�k���\��@��c�%������uN�50000pix�i�%@P�@��D�x�gLK �Ǟp��ݞ��p�YPJ]ks��K�4i�1��p^����&W���f���$�� ������^n�Ԙ�X&�@��<���E[?� ��ǻx~�qJZ�Jb2� [0zA/�%!�J�긺>/扫���� ��� ��SIḧ��\��v�|�W�V&�S*�o�����㍴@�r�Tꅊ�_�b�n���M�q`�iC��JdK��@)qNy��I�'�w�zKo��\ד0W�:��5 ����.[ޕ�X�{��X<�����o���Z�����H�q���t;~�<� �����i�(OQjԹ�";\�n{.��?|����}��);Nz��/��N�{��M�3��H!H(X��Y�I�r��>.W���I$�x哉4���ӆ�gy��Q:����u�����a$��7���4h������\��:(G����V/��v20000PB-��.ϧ2�RT��[&���"��9� <�g�}�9'�s�����c��2�-3�[��(���ܿ+T���N�_h�Tј+����s���X�Tj�͍�;E����N�!�un�K+��`� `��_�h���O?��kSQ���F��:���H���m,@�L ���t�+Ve6��\�F琢R�@Z���NL�-0��=@�9��)�dB�R�6�mHZ�Z.��JX�R��(@FA�\n�\��tx��5|+��;�3��pg��(Yn���W_����\;���4>\����n���j�e!s�0�0��;t��Śןt�Yeǀ�rs� �⋲SzH�_� �JX,d�:�<I�Igɰ���S�O� �1����%�������9��Z]V�ɻ�Q�:� ��a����Y�1�rK�z����(@|�S����#� �d��PDb$���;��~8K�9 �§��9����0000P�����O?���3�,�v���£���� �G3fJ���u(f�̎��!`-�[�o}���QSg�셼3���������$�������f���g=����}�0���e�V�A���~��0�����X�bH�Y���B�e($� 2�͈:m���<��V��:ฃ` V�m�?�ȯ��d0=~�{�W�����u��ј�Ú��?����{���-��aq`````�(�x=&���8$W����zL���`�t�z6���71gr1S����Z]G^�u,�"�$�`;Tö<?C�M��U0�*� �&��`,;�ƀ�! ��d+�+���R�+���ɏ��Y�-w�Y`���%&�%��0Hp.Q����5AFU�d��A�CY��[�����?�M�I�E��Z/�-����n�R̋b��`���yvs�^P`~�9׆s�vT ,����Fӑ8b4ڼ�Mg����L��wfH�����[�)_@�i�3�Hj���,J�=;V����P����[>�W�~~�t���u�U��;�������1����w������݃�7�~}������C������?��we�����䝽�g�R�;����?M�����s~��w��t���O�������i }�_�8H����/(3�������m����\��������,uQ?�;�&�C=��l���RKz`��� �� RxWfzf��w����K��4��w���.d����y�})��w,���(6���������� tw����$�9:`�����������[@h/D���?���H����L���\��wg0��L��zQ���1 ��G�~F���~2�_���Y���/�u����Q�50p��?�����W�����IEND�B`� \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_555555_256x240.png b/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_555555_256x240.png new file mode 100644 index 0000000000..4c37296071 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_555555_256x240.png @@ -0,0 +1,11 @@ +�PNG + +��� IHDR�����������IJ���PLTEUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUy�����ZtRNS��3P���/"Uq@f`2� !<BHK Z#'1S,�4���j���8E���|��������)��Q$� +��b�J��mߜGc?o�h�#�-���IDATx���r�:�ax|���O�0A�I�%��=��b ���eY����~_�|��Q�d ��@P�(UM�ZI�Z~ &?ׁ��Q��0�A��`ꇚ�^���=�C��� ���c�m +�y M0�v���C<w���%K0�w���,�FhY�eY��O� �w݈��#����_����f���|#�݀P����J݀������r"�f?Ŀ0��,��}@N~��0���> OK0�^�eY�eY���K���gqƽ�����3��zHM״��i����zr88��@��u۱�Z�*�:>���c����s��l5��2[4�0'{���y��#�*��ajA��E��;瀣*^�W�X�eY�e�$/���0h@�jro��`���y��g%Y�9=@�H���(��(8� ��dզ"G���[���c��h�G(�<䈹����`}�8@W�:�������e��� y���/$���̰ +��ޗ�p���@�w���ҏo8��4�f0 +��7���i^�ѐ��rt�k8� ����FdY�eY�N�|a�3?b��*I�C���ܞ2��j�`�U�l{J-�~*S��O b2�����@+��(:�[m!3&:���o4 �E):�;@����Z�c�� �*:x>�&z�S~7��xۥ@rz�� +C�ݟ�!]�����j�X/�C{��,˲,��t��kV��O3g�L�h�����^~k���؈��� �I��<ls����T�{P��j|���.j�g���w�`0��w3�O��_�%} |{�xP z�@����Ц�'����=���� +�P�/0�Z�����q����_;����хї3�X�b�81i��{%������,���>���eu�T�����?00000�zs_��>������(W`_��O�f��UA�6��W�Ȼ������i]�l�@��WIL�^�6�-M�^��;+t{��uQQTE�� + f]1`r�-��u.p��;$ Pl�u������:�L�`P��=@�l6�/��N:ְ�Y �v�o�u�%� ��=�0o8{��N�?���t�s����$ ��0 +��6 ��7d;`Z"QNٹ!J� @�� ��������LEQh&����������=�`�^��h:!��b��NK(�i�R@b�5�k�셳�A�̕���@.���*��*P���-�����Pd; N�r��.<��ЖQŃU��u�B;xr�!(|���NL��L�.𧶸�+�\��H�)���1YU�Y�bG�����!���A-���=w�������A�&���/a�w�k���\��@��c�%������uN�50000pix�i�%@P�@��D�x�gLK �Ǟp��ݞ��p�YPJ]ks��K�4i�1��p^����&W���f���$�� ������^n�Ԙ�X&�@��<���E[?� ��ǻx~�qJZ�Jb2� [0zA/�%!�J�긺>/扫���� ��� ��SIḧ��\��v�|�W�V&�S*�o�����㍴@�r�Tꅊ�_�b�n���M�q`�iC��JdK��@)qNy��I�'�w�zKo��\ד0W�:��5 ����.[ޕ�X�{��X<�����o���Z�����H�q���t;~�<� �����i�(OQjԹ�";\�n{.��?|����}��);Nz��/��N�{��M�3��H!H(X��Y�I�r��>.W���I$�x哉4���ӆ�gy��Q:����u�����a$��7���4h������\��:(G����V/��v20000PB-��.ϧ2�RT��[&���"��9� <�g�}�9'�s�����c��2�-3�[��(���ܿ+T���N�_h�Tј+����s���X�Tj�͍�;E����N�!�un�K+��`� `��_�h���O?��kSQ���F��:���H���m,@�L ���t�+Ve6��\�F琢R�@Z���NL�-0��=@�9��)�dB�R�6�mHZ�Z.��JX�R��(@FA�\n�\��tx��5|+��;�3��pg��(Yn���W_����\;���4>\����n���j�e!s�0�0��;t��Śןt�Yeǀ�rs� �⋲SzH�_� �JX,d�:�<I�Igɰ���S�O� �1����%�������9��Z]V�ɻ�Q�:� ��a����Y�1�rK�z����(@|�S����#� �d��PDb$���;��~8K�9 �§��9����0000P�����O?���3�,�v���£���� �G3fJ���u(f�̎��!`-�[�o}���QSg�셼3���������$�������f���g=����}�0���e�V�A���~��0�����X�bH�Y���B�e($� 2�͈:m���<��V��:ฃ` V�m�?�ȯ��d0=~�{�W�����u��ј�Ú��?����{���-��aq`````�(�x=&���8$W����zL���`�t�z6���71gr1S����Z]G^�u,�"�$�`;Tö<?C�M��U0�*� �&��`,;�ƀ�! ��d+�+���R�+���ɏ��Y�-w�Y`���%&�%��0Hp.Q����5AFU�d��A�CY��[�����?�M�I�E��Z/�-����n�R̋b��`���yvs�^P`~�9׆s�vT ,����Fӑ8b4ڼ�Mg����L��wfH�����[�)_@�i�3�Hj���,J�=;V����P����[>�W�~~�t���u�U��;�������1����w������݃�7�~}������C������?��we�����䝽�g�R�;����?M�����s~��w��t���O�������i }�_�8H����/(3�������m����\��������,uQ?�;�&�C=��l���RKz`��� �� RxWfzf��w����K��4��w���.d����y�})��w,���(6���������� tw����$�9:`�����������[@h/D���?���H����L���\��wg0��L��zQ���1 ��G�~F���~2�_���Y���/�u����Q�50p��?�����W�����IEND�B`� \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_777620_256x240.png b/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_777620_256x240.png new file mode 100644 index 0000000000..3b4ce6868a --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_777620_256x240.png @@ -0,0 +1,11 @@ +�PNG + +��� IHDR�����������IJ���PLTEwv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv wv U.G����ZtRNS��3P���/"Uq@f`2� !<BHK Z#'1S,�4���j���8E���|��������)��Q$� +��b�J��mߜGc?o�h�#�-���IDATx���r�:�ax|���O�0A�I�%��=��b ���eY����~_�|��Q�d ��@P�(UM�ZI�Z~ &?ׁ��Q��0�A��`ꇚ�^���=�C��� ���c�m +�y M0�v���C<w���%K0�w���,�FhY�eY��O� �w݈��#����_����f���|#�݀P����J݀������r"�f?Ŀ0��,��}@N~��0���> OK0�^�eY�eY���K���gqƽ�����3��zHM״��i����zr88��@��u۱�Z�*�:>���c����s��l5��2[4�0'{���y��#�*��ajA��E��;瀣*^�W�X�eY�e�$/���0h@�jro��`���y��g%Y�9=@�H���(��(8� ��dզ"G���[���c��h�G(�<䈹����`}�8@W�:�������e��� y���/$���̰ +��ޗ�p���@�w���ҏo8��4�f0 +��7���i^�ѐ��rt�k8� ����FdY�eY�N�|a�3?b��*I�C���ܞ2��j�`�U�l{J-�~*S��O b2�����@+��(:�[m!3&:���o4 �E):�;@����Z�c�� �*:x>�&z�S~7��xۥ@rz�� +C�ݟ�!]�����j�X/�C{��,˲,��t��kV��O3g�L�h�����^~k���؈��� �I��<ls����T�{P��j|���.j�g���w�`0��w3�O��_�%} |{�xP z�@����Ц�'����=���� +�P�/0�Z�����q����_;����хї3�X�b�81i��{%������,���>���eu�T�����?00000�zs_��>������(W`_��O�f��UA�6��W�Ȼ������i]�l�@��WIL�^�6�-M�^��;+t{��uQQTE�� + f]1`r�-��u.p��;$ Pl�u������:�L�`P��=@�l6�/��N:ְ�Y �v�o�u�%� ��=�0o8{��N�?���t�s����$ ��0 +��6 ��7d;`Z"QNٹ!J� @�� ��������LEQh&����������=�`�^��h:!��b��NK(�i�R@b�5�k�셳�A�̕���@.���*��*P���-�����Pd; N�r��.<��ЖQŃU��u�B;xr�!(|���NL��L�.𧶸�+�\��H�)���1YU�Y�bG�����!���A-���=w�������A�&���/a�w�k���\��@��c�%������uN�50000pix�i�%@P�@��D�x�gLK �Ǟp��ݞ��p�YPJ]ks��K�4i�1��p^����&W���f���$�� ������^n�Ԙ�X&�@��<���E[?� ��ǻx~�qJZ�Jb2� [0zA/�%!�J�긺>/扫���� ��� ��SIḧ��\��v�|�W�V&�S*�o�����㍴@�r�Tꅊ�_�b�n���M�q`�iC��JdK��@)qNy��I�'�w�zKo��\ד0W�:��5 ����.[ޕ�X�{��X<�����o���Z�����H�q���t;~�<� �����i�(OQjԹ�";\�n{.��?|����}��);Nz��/��N�{��M�3��H!H(X��Y�I�r��>.W���I$�x哉4���ӆ�gy��Q:����u�����a$��7���4h������\��:(G����V/��v20000PB-��.ϧ2�RT��[&���"��9� <�g�}�9'�s�����c��2�-3�[��(���ܿ+T���N�_h�Tј+����s���X�Tj�͍�;E����N�!�un�K+��`� `��_�h���O?��kSQ���F��:���H���m,@�L ���t�+Ve6��\�F琢R�@Z���NL�-0��=@�9��)�dB�R�6�mHZ�Z.��JX�R��(@FA�\n�\��tx��5|+��;�3��pg��(Yn���W_����\;���4>\����n���j�e!s�0�0��;t��Śןt�Yeǀ�rs� �⋲SzH�_� �JX,d�:�<I�Igɰ���S�O� �1����%�������9��Z]V�ɻ�Q�:� ��a����Y�1�rK�z����(@|�S����#� �d��PDb$���;��~8K�9 �§��9����0000P�����O?���3�,�v���£���� �G3fJ���u(f�̎��!`-�[�o}���QSg�셼3���������$�������f���g=����}�0���e�V�A���~��0�����X�bH�Y���B�e($� 2�͈:m���<��V��:ฃ` V�m�?�ȯ��d0=~�{�W�����u��ј�Ú��?����{���-��aq`````�(�x=&���8$W����zL���`�t�z6���71gr1S����Z]G^�u,�"�$�`;Tö<?C�M��U0�*� �&��`,;�ƀ�! ��d+�+���R�+���ɏ��Y�-w�Y`���%&�%��0Hp.Q����5AFU�d��A�CY��[�����?�M�I�E��Z/�-����n�R̋b��`���yvs�^P`~�9׆s�vT ,����Fӑ8b4ڼ�Mg����L��wfH�����[�)_@�i�3�Hj���,J�=;V����P����[>�W�~~�t���u�U��;�������1����w������݃�7�~}������C������?��we�����䝽�g�R�;����?M�����s~��w��t���O�������i }�_�8H����/(3�������m����\��������,uQ?�;�&�C=��l���RKz`��� �� RxWfzf��w����K��4��w���.d����y�})��w,���(6���������� tw����$�9:`�����������[@h/D���?���H����L���\��wg0��L��zQ���1 ��G�~F���~2�_���Y���/�u����Q�50p��?�����W�����IEND�B`� \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_777777_256x240.png b/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_777777_256x240.png new file mode 100644 index 0000000000..de6cf086bb --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_777777_256x240.png @@ -0,0 +1,11 @@ +�PNG + +��� IHDR�����������IJ���PLTEwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww3<�>���ZtRNS��3P���/"Uq@f`2� !<BHK Z#'1S,�4���j���8E���|��������)��Q$� +��b�J��mߜGc?o�h�#�-���IDATx���r�:�ax|���O�0A�I�%��=��b ���eY����~_�|��Q�d ��@P�(UM�ZI�Z~ &?ׁ��Q��0�A��`ꇚ�^���=�C��� ���c�m +�y M0�v���C<w���%K0�w���,�FhY�eY��O� �w݈��#����_����f���|#�݀P����J݀������r"�f?Ŀ0��,��}@N~��0���> OK0�^�eY�eY���K���gqƽ�����3��zHM״��i����zr88��@��u۱�Z�*�:>���c����s��l5��2[4�0'{���y��#�*��ajA��E��;瀣*^�W�X�eY�e�$/���0h@�jro��`���y��g%Y�9=@�H���(��(8� ��dզ"G���[���c��h�G(�<䈹����`}�8@W�:�������e��� y���/$���̰ +��ޗ�p���@�w���ҏo8��4�f0 +��7���i^�ѐ��rt�k8� ����FdY�eY�N�|a�3?b��*I�C���ܞ2��j�`�U�l{J-�~*S��O b2�����@+��(:�[m!3&:���o4 �E):�;@����Z�c�� �*:x>�&z�S~7��xۥ@rz�� +C�ݟ�!]�����j�X/�C{��,˲,��t��kV��O3g�L�h�����^~k���؈��� �I��<ls����T�{P��j|���.j�g���w�`0��w3�O��_�%} |{�xP z�@����Ц�'����=���� +�P�/0�Z�����q����_;����хї3�X�b�81i��{%������,���>���eu�T�����?00000�zs_��>������(W`_��O�f��UA�6��W�Ȼ������i]�l�@��WIL�^�6�-M�^��;+t{��uQQTE�� + f]1`r�-��u.p��;$ Pl�u������:�L�`P��=@�l6�/��N:ְ�Y �v�o�u�%� ��=�0o8{��N�?���t�s����$ ��0 +��6 ��7d;`Z"QNٹ!J� @�� ��������LEQh&����������=�`�^��h:!��b��NK(�i�R@b�5�k�셳�A�̕���@.���*��*P���-�����Pd; N�r��.<��ЖQŃU��u�B;xr�!(|���NL��L�.𧶸�+�\��H�)���1YU�Y�bG�����!���A-���=w�������A�&���/a�w�k���\��@��c�%������uN�50000pix�i�%@P�@��D�x�gLK �Ǟp��ݞ��p�YPJ]ks��K�4i�1��p^����&W���f���$�� ������^n�Ԙ�X&�@��<���E[?� ��ǻx~�qJZ�Jb2� [0zA/�%!�J�긺>/扫���� ��� ��SIḧ��\��v�|�W�V&�S*�o�����㍴@�r�Tꅊ�_�b�n���M�q`�iC��JdK��@)qNy��I�'�w�zKo��\ד0W�:��5 ����.[ޕ�X�{��X<�����o���Z�����H�q���t;~�<� �����i�(OQjԹ�";\�n{.��?|����}��);Nz��/��N�{��M�3��H!H(X��Y�I�r��>.W���I$�x哉4���ӆ�gy��Q:����u�����a$��7���4h������\��:(G����V/��v20000PB-��.ϧ2�RT��[&���"��9� <�g�}�9'�s�����c��2�-3�[��(���ܿ+T���N�_h�Tј+����s���X�Tj�͍�;E����N�!�un�K+��`� `��_�h���O?��kSQ���F��:���H���m,@�L ���t�+Ve6��\�F琢R�@Z���NL�-0��=@�9��)�dB�R�6�mHZ�Z.��JX�R��(@FA�\n�\��tx��5|+��;�3��pg��(Yn���W_����\;���4>\����n���j�e!s�0�0��;t��Śןt�Yeǀ�rs� �⋲SzH�_� �JX,d�:�<I�Igɰ���S�O� �1����%�������9��Z]V�ɻ�Q�:� ��a����Y�1�rK�z����(@|�S����#� �d��PDb$���;��~8K�9 �§��9����0000P�����O?���3�,�v���£���� �G3fJ���u(f�̎��!`-�[�o}���QSg�셼3���������$�������f���g=����}�0���e�V�A���~��0�����X�bH�Y���B�e($� 2�͈:m���<��V��:ฃ` V�m�?�ȯ��d0=~�{�W�����u��ј�Ú��?����{���-��aq`````�(�x=&���8$W����zL���`�t�z6���71gr1S����Z]G^�u,�"�$�`;Tö<?C�M��U0�*� �&��`,;�ƀ�! ��d+�+���R�+���ɏ��Y�-w�Y`���%&�%��0Hp.Q����5AFU�d��A�CY��[�����?�M�I�E��Z/�-����n�R̋b��`���yvs�^P`~�9׆s�vT ,����Fӑ8b4ڼ�Mg����L��wfH�����[�)_@�i�3�Hj���,J�=;V����P����[>�W�~~�t���u�U��;�������1����w������݃�7�~}������C������?��we�����䝽�g�R�;����?M�����s~��w��t���O�������i }�_�8H����/(3�������m����\��������,uQ?�;�&�C=��l���RKz`��� �� RxWfzf��w����K��4��w���.d����y�})��w,���(6���������� tw����$�9:`�����������[@h/D���?���H����L���\��wg0��L��zQ���1 ��G�~F���~2�_���Y���/�u����Q�50p��?�����W�����IEND�B`� \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_cc0000_256x240.png b/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_cc0000_256x240.png new file mode 100644 index 0000000000..6c64c85e20 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_cc0000_256x240.png @@ -0,0 +1,11 @@ +�PNG + +��� IHDR�����������IJ���PLTE��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������r����ZtRNS��3P���/"Uq@f`2� !<BHK Z#'1S,�4���j���8E���|��������)��Q$� +��b�J��mߜGc?o�h�#�-���IDATx���r�:�ax|���O�0A�I�%��=��b ���eY����~_�|��Q�d ��@P�(UM�ZI�Z~ &?ׁ��Q��0�A��`ꇚ�^���=�C��� ���c�m +�y M0�v���C<w���%K0�w���,�FhY�eY��O� �w݈��#����_����f���|#�݀P����J݀������r"�f?Ŀ0��,��}@N~��0���> OK0�^�eY�eY���K���gqƽ�����3��zHM״��i����zr88��@��u۱�Z�*�:>���c����s��l5��2[4�0'{���y��#�*��ajA��E��;瀣*^�W�X�eY�e�$/���0h@�jro��`���y��g%Y�9=@�H���(��(8� ��dզ"G���[���c��h�G(�<䈹����`}�8@W�:�������e��� y���/$���̰ +��ޗ�p���@�w���ҏo8��4�f0 +��7���i^�ѐ��rt�k8� ����FdY�eY�N�|a�3?b��*I�C���ܞ2��j�`�U�l{J-�~*S��O b2�����@+��(:�[m!3&:���o4 �E):�;@����Z�c�� �*:x>�&z�S~7��xۥ@rz�� +C�ݟ�!]�����j�X/�C{��,˲,��t��kV��O3g�L�h�����^~k���؈��� �I��<ls����T�{P��j|���.j�g���w�`0��w3�O��_�%} |{�xP z�@����Ц�'����=���� +�P�/0�Z�����q����_;����хї3�X�b�81i��{%������,���>���eu�T�����?00000�zs_��>������(W`_��O�f��UA�6��W�Ȼ������i]�l�@��WIL�^�6�-M�^��;+t{��uQQTE�� + f]1`r�-��u.p��;$ Pl�u������:�L�`P��=@�l6�/��N:ְ�Y �v�o�u�%� ��=�0o8{��N�?���t�s����$ ��0 +��6 ��7d;`Z"QNٹ!J� @�� ��������LEQh&����������=�`�^��h:!��b��NK(�i�R@b�5�k�셳�A�̕���@.���*��*P���-�����Pd; N�r��.<��ЖQŃU��u�B;xr�!(|���NL��L�.𧶸�+�\��H�)���1YU�Y�bG�����!���A-���=w�������A�&���/a�w�k���\��@��c�%������uN�50000pix�i�%@P�@��D�x�gLK �Ǟp��ݞ��p�YPJ]ks��K�4i�1��p^����&W���f���$�� ������^n�Ԙ�X&�@��<���E[?� ��ǻx~�qJZ�Jb2� [0zA/�%!�J�긺>/扫���� ��� ��SIḧ��\��v�|�W�V&�S*�o�����㍴@�r�Tꅊ�_�b�n���M�q`�iC��JdK��@)qNy��I�'�w�zKo��\ד0W�:��5 ����.[ޕ�X�{��X<�����o���Z�����H�q���t;~�<� �����i�(OQjԹ�";\�n{.��?|����}��);Nz��/��N�{��M�3��H!H(X��Y�I�r��>.W���I$�x哉4���ӆ�gy��Q:����u�����a$��7���4h������\��:(G����V/��v20000PB-��.ϧ2�RT��[&���"��9� <�g�}�9'�s�����c��2�-3�[��(���ܿ+T���N�_h�Tј+����s���X�Tj�͍�;E����N�!�un�K+��`� `��_�h���O?��kSQ���F��:���H���m,@�L ���t�+Ve6��\�F琢R�@Z���NL�-0��=@�9��)�dB�R�6�mHZ�Z.��JX�R��(@FA�\n�\��tx��5|+��;�3��pg��(Yn���W_����\;���4>\����n���j�e!s�0�0��;t��Śןt�Yeǀ�rs� �⋲SzH�_� �JX,d�:�<I�Igɰ���S�O� �1����%�������9��Z]V�ɻ�Q�:� ��a����Y�1�rK�z����(@|�S����#� �d��PDb$���;��~8K�9 �§��9����0000P�����O?���3�,�v���£���� �G3fJ���u(f�̎��!`-�[�o}���QSg�셼3���������$�������f���g=����}�0���e�V�A���~��0�����X�bH�Y���B�e($� 2�͈:m���<��V��:ฃ` V�m�?�ȯ��d0=~�{�W�����u��ј�Ú��?����{���-��aq`````�(�x=&���8$W����zL���`�t�z6���71gr1S����Z]G^�u,�"�$�`;Tö<?C�M��U0�*� �&��`,;�ƀ�! ��d+�+���R�+���ɏ��Y�-w�Y`���%&�%��0Hp.Q����5AFU�d��A�CY��[�����?�M�I�E��Z/�-����n�R̋b��`���yvs�^P`~�9׆s�vT ,����Fӑ8b4ڼ�Mg����L��wfH�����[�)_@�i�3�Hj���,J�=;V����P����[>�W�~~�t���u�U��;�������1����w������݃�7�~}������C������?��we�����䝽�g�R�;����?M�����s~��w��t���O�������i }�_�8H����/(3�������m����\��������,uQ?�;�&�C=��l���RKz`��� �� RxWfzf��w����K��4��w���.d����y�})��w,���(6���������� tw����$�9:`�����������[@h/D���?���H����L���\��wg0��L��zQ���1 ��G�~F���~2�_���Y���/�u����Q�50p��?�����W�����IEND�B`� \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_ffffff_256x240.png b/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_ffffff_256x240.png new file mode 100644 index 0000000000..4ab379a1f7 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/themes/base/images/ui-icons_ffffff_256x240.png @@ -0,0 +1,11 @@ +�PNG + +��� IHDR�����������IJ���PLTE�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������)[V���ZtRNS��3P���/"Uq@f`2� !<BHK Z#'1S,�4���j���8E���|��������)��Q$� +��b�J��mߜGc?o�h�#�-���IDATx���r�:�ax|���O�0A�I�%��=��b ���eY����~_�|��Q�d ��@P�(UM�ZI�Z~ &?ׁ��Q��0�A��`ꇚ�^���=�C��� ���c�m +�y M0�v���C<w���%K0�w���,�FhY�eY��O� �w݈��#����_����f���|#�݀P����J݀������r"�f?Ŀ0��,��}@N~��0���> OK0�^�eY�eY���K���gqƽ�����3��zHM״��i����zr88��@��u۱�Z�*�:>���c����s��l5��2[4�0'{���y��#�*��ajA��E��;瀣*^�W�X�eY�e�$/���0h@�jro��`���y��g%Y�9=@�H���(��(8� ��dզ"G���[���c��h�G(�<䈹����`}�8@W�:�������e��� y���/$���̰ +��ޗ�p���@�w���ҏo8��4�f0 +��7���i^�ѐ��rt�k8� ����FdY�eY�N�|a�3?b��*I�C���ܞ2��j�`�U�l{J-�~*S��O b2�����@+��(:�[m!3&:���o4 �E):�;@����Z�c�� �*:x>�&z�S~7��xۥ@rz�� +C�ݟ�!]�����j�X/�C{��,˲,��t��kV��O3g�L�h�����^~k���؈��� �I��<ls����T�{P��j|���.j�g���w�`0��w3�O��_�%} |{�xP z�@����Ц�'����=���� +�P�/0�Z�����q����_;����хї3�X�b�81i��{%������,���>���eu�T�����?00000�zs_��>������(W`_��O�f��UA�6��W�Ȼ������i]�l�@��WIL�^�6�-M�^��;+t{��uQQTE�� + f]1`r�-��u.p��;$ Pl�u������:�L�`P��=@�l6�/��N:ְ�Y �v�o�u�%� ��=�0o8{��N�?���t�s����$ ��0 +��6 ��7d;`Z"QNٹ!J� @�� ��������LEQh&����������=�`�^��h:!��b��NK(�i�R@b�5�k�셳�A�̕���@.���*��*P���-�����Pd; N�r��.<��ЖQŃU��u�B;xr�!(|���NL��L�.𧶸�+�\��H�)���1YU�Y�bG�����!���A-���=w�������A�&���/a�w�k���\��@��c�%������uN�50000pix�i�%@P�@��D�x�gLK �Ǟp��ݞ��p�YPJ]ks��K�4i�1��p^����&W���f���$�� ������^n�Ԙ�X&�@��<���E[?� ��ǻx~�qJZ�Jb2� [0zA/�%!�J�긺>/扫���� ��� ��SIḧ��\��v�|�W�V&�S*�o�����㍴@�r�Tꅊ�_�b�n���M�q`�iC��JdK��@)qNy��I�'�w�zKo��\ד0W�:��5 ����.[ޕ�X�{��X<�����o���Z�����H�q���t;~�<� �����i�(OQjԹ�";\�n{.��?|����}��);Nz��/��N�{��M�3��H!H(X��Y�I�r��>.W���I$�x哉4���ӆ�gy��Q:����u�����a$��7���4h������\��:(G����V/��v20000PB-��.ϧ2�RT��[&���"��9� <�g�}�9'�s�����c��2�-3�[��(���ܿ+T���N�_h�Tј+����s���X�Tj�͍�;E����N�!�un�K+��`� `��_�h���O?��kSQ���F��:���H���m,@�L ���t�+Ve6��\�F琢R�@Z���NL�-0��=@�9��)�dB�R�6�mHZ�Z.��JX�R��(@FA�\n�\��tx��5|+��;�3��pg��(Yn���W_����\;���4>\����n���j�e!s�0�0��;t��Śןt�Yeǀ�rs� �⋲SzH�_� �JX,d�:�<I�Igɰ���S�O� �1����%�������9��Z]V�ɻ�Q�:� ��a����Y�1�rK�z����(@|�S����#� �d��PDb$���;��~8K�9 �§��9����0000P�����O?���3�,�v���£���� �G3fJ���u(f�̎��!`-�[�o}���QSg�셼3���������$�������f���g=����}�0���e�V�A���~��0�����X�bH�Y���B�e($� 2�͈:m���<��V��:ฃ` V�m�?�ȯ��d0=~�{�W�����u��ј�Ú��?����{���-��aq`````�(�x=&���8$W����zL���`�t�z6���71gr1S����Z]G^�u,�"�$�`;Tö<?C�M��U0�*� �&��`,;�ƀ�! ��d+�+���R�+���ɏ��Y�-w�Y`���%&�%��0Hp.Q����5AFU�d��A�CY��[�����?�M�I�E��Z/�-����n�R̋b��`���yvs�^P`~�9׆s�vT ,����Fӑ8b4ڼ�Mg����L��wfH�����[�)_@�i�3�Hj���,J�=;V����P����[>�W�~~�t���u�U��;�������1����w������݃�7�~}������C������?��we�����䝽�g�R�;����?M�����s~��w��t���O�������i }�_�8H����/(3�������m����\��������,uQ?�;�&�C=��l���RKz`��� �� RxWfzf��w����K��4��w���.d����y�})��w,���(6���������� tw����$�9:`�����������[@h/D���?���H����L���\��wg0��L��zQ���1 ��G�~F���~2�_���Y���/�u����Q�50p��?�����W�����IEND�B`� \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/themes/base/theme.css b/web/modules/jquery_ui/jquery.ui/themes/base/theme.css new file mode 100644 index 0000000000..79937075d9 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/themes/base/theme.css @@ -0,0 +1,443 @@ +/*! + * jQuery UI CSS Framework 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/category/theming/ + * + * To view and modify this theme, visit http://jqueryui.com/themeroller/ + */ + + +/* Component containers +----------------------------------*/ +.ui-widget { + font-family: Arial,Helvetica,sans-serif/*{ffDefault}*/; + font-size: 1em/*{fsDefault}*/; +} +.ui-widget .ui-widget { + font-size: 1em; +} +.ui-widget input, +.ui-widget select, +.ui-widget textarea, +.ui-widget button { + font-family: Arial,Helvetica,sans-serif/*{ffDefault}*/; + font-size: 1em; +} +.ui-widget.ui-widget-content { + border: 1px solid #c5c5c5/*{borderColorDefault}*/; +} +.ui-widget-content { + border: 1px solid #dddddd/*{borderColorContent}*/; + background: #ffffff/*{bgColorContent}*/ /*{bgImgUrlContent}*/ /*{bgContentXPos}*/ /*{bgContentYPos}*/ /*{bgContentRepeat}*/; + color: #333333/*{fcContent}*/; +} +.ui-widget-content a { + color: #333333/*{fcContent}*/; +} +.ui-widget-header { + border: 1px solid #dddddd/*{borderColorHeader}*/; + background: #e9e9e9/*{bgColorHeader}*/ /*{bgImgUrlHeader}*/ /*{bgHeaderXPos}*/ /*{bgHeaderYPos}*/ /*{bgHeaderRepeat}*/; + color: #333333/*{fcHeader}*/; + font-weight: bold; +} +.ui-widget-header a { + color: #333333/*{fcHeader}*/; +} + +/* Interaction states +----------------------------------*/ +.ui-state-default, +.ui-widget-content .ui-state-default, +.ui-widget-header .ui-state-default, +.ui-button, + +/* We use html here because we need a greater specificity to make sure disabled +works properly when clicked or hovered */ +html .ui-button.ui-state-disabled:hover, +html .ui-button.ui-state-disabled:active { + border: 1px solid #c5c5c5/*{borderColorDefault}*/; + background: #f6f6f6/*{bgColorDefault}*/ /*{bgImgUrlDefault}*/ /*{bgDefaultXPos}*/ /*{bgDefaultYPos}*/ /*{bgDefaultRepeat}*/; + font-weight: normal/*{fwDefault}*/; + color: #454545/*{fcDefault}*/; +} +.ui-state-default a, +.ui-state-default a:link, +.ui-state-default a:visited, +a.ui-button, +a:link.ui-button, +a:visited.ui-button, +.ui-button { + color: #454545/*{fcDefault}*/; + text-decoration: none; +} +.ui-state-hover, +.ui-widget-content .ui-state-hover, +.ui-widget-header .ui-state-hover, +.ui-state-focus, +.ui-widget-content .ui-state-focus, +.ui-widget-header .ui-state-focus, +.ui-button:hover, +.ui-button:focus { + border: 1px solid #cccccc/*{borderColorHover}*/; + background: #ededed/*{bgColorHover}*/ /*{bgImgUrlHover}*/ /*{bgHoverXPos}*/ /*{bgHoverYPos}*/ /*{bgHoverRepeat}*/; + font-weight: normal/*{fwDefault}*/; + color: #2b2b2b/*{fcHover}*/; +} +.ui-state-hover a, +.ui-state-hover a:hover, +.ui-state-hover a:link, +.ui-state-hover a:visited, +.ui-state-focus a, +.ui-state-focus a:hover, +.ui-state-focus a:link, +.ui-state-focus a:visited, +a.ui-button:hover, +a.ui-button:focus { + color: #2b2b2b/*{fcHover}*/; + text-decoration: none; +} + +.ui-visual-focus { + box-shadow: 0 0 3px 1px rgb(94, 158, 214); +} +.ui-state-active, +.ui-widget-content .ui-state-active, +.ui-widget-header .ui-state-active, +a.ui-button:active, +.ui-button:active, +.ui-button.ui-state-active:hover { + border: 1px solid #003eff/*{borderColorActive}*/; + background: #007fff/*{bgColorActive}*/ /*{bgImgUrlActive}*/ /*{bgActiveXPos}*/ /*{bgActiveYPos}*/ /*{bgActiveRepeat}*/; + font-weight: normal/*{fwDefault}*/; + color: #ffffff/*{fcActive}*/; +} +.ui-icon-background, +.ui-state-active .ui-icon-background { + border: #003eff/*{borderColorActive}*/; + background-color: #ffffff/*{fcActive}*/; +} +.ui-state-active a, +.ui-state-active a:link, +.ui-state-active a:visited { + color: #ffffff/*{fcActive}*/; + text-decoration: none; +} + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, +.ui-widget-content .ui-state-highlight, +.ui-widget-header .ui-state-highlight { + border: 1px solid #dad55e/*{borderColorHighlight}*/; + background: #fffa90/*{bgColorHighlight}*/ /*{bgImgUrlHighlight}*/ /*{bgHighlightXPos}*/ /*{bgHighlightYPos}*/ /*{bgHighlightRepeat}*/; + color: #777620/*{fcHighlight}*/; +} +.ui-state-checked { + border: 1px solid #dad55e/*{borderColorHighlight}*/; + background: #fffa90/*{bgColorHighlight}*/; +} +.ui-state-highlight a, +.ui-widget-content .ui-state-highlight a, +.ui-widget-header .ui-state-highlight a { + color: #777620/*{fcHighlight}*/; +} +.ui-state-error, +.ui-widget-content .ui-state-error, +.ui-widget-header .ui-state-error { + border: 1px solid #f1a899/*{borderColorError}*/; + background: #fddfdf/*{bgColorError}*/ /*{bgImgUrlError}*/ /*{bgErrorXPos}*/ /*{bgErrorYPos}*/ /*{bgErrorRepeat}*/; + color: #5f3f3f/*{fcError}*/; +} +.ui-state-error a, +.ui-widget-content .ui-state-error a, +.ui-widget-header .ui-state-error a { + color: #5f3f3f/*{fcError}*/; +} +.ui-state-error-text, +.ui-widget-content .ui-state-error-text, +.ui-widget-header .ui-state-error-text { + color: #5f3f3f/*{fcError}*/; +} +.ui-priority-primary, +.ui-widget-content .ui-priority-primary, +.ui-widget-header .ui-priority-primary { + font-weight: bold; +} +.ui-priority-secondary, +.ui-widget-content .ui-priority-secondary, +.ui-widget-header .ui-priority-secondary { + opacity: .7; + filter:Alpha(Opacity=70); /* support: IE8 */ + font-weight: normal; +} +.ui-state-disabled, +.ui-widget-content .ui-state-disabled, +.ui-widget-header .ui-state-disabled { + opacity: .35; + filter:Alpha(Opacity=35); /* support: IE8 */ + background-image: none; +} +.ui-state-disabled .ui-icon { + filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */ +} + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { + width: 16px; + height: 16px; +} +.ui-icon, +.ui-widget-content .ui-icon { + background-image: url("images/ui-icons_444444_256x240.png")/*{iconsContent}*/; +} +.ui-widget-header .ui-icon { + background-image: url("images/ui-icons_444444_256x240.png")/*{iconsHeader}*/; +} +.ui-state-hover .ui-icon, +.ui-state-focus .ui-icon, +.ui-button:hover .ui-icon, +.ui-button:focus .ui-icon { + background-image: url("images/ui-icons_555555_256x240.png")/*{iconsHover}*/; +} +.ui-state-active .ui-icon, +.ui-button:active .ui-icon { + background-image: url("images/ui-icons_ffffff_256x240.png")/*{iconsActive}*/; +} +.ui-state-highlight .ui-icon, +.ui-button .ui-state-highlight.ui-icon { + background-image: url("images/ui-icons_777620_256x240.png")/*{iconsHighlight}*/; +} +.ui-state-error .ui-icon, +.ui-state-error-text .ui-icon { + background-image: url("images/ui-icons_cc0000_256x240.png")/*{iconsError}*/; +} +.ui-button .ui-icon { + background-image: url("images/ui-icons_777777_256x240.png")/*{iconsDefault}*/; +} + +/* positioning */ +.ui-icon-blank { background-position: 16px 16px; } +.ui-icon-caret-1-n { background-position: 0 0; } +.ui-icon-caret-1-ne { background-position: -16px 0; } +.ui-icon-caret-1-e { background-position: -32px 0; } +.ui-icon-caret-1-se { background-position: -48px 0; } +.ui-icon-caret-1-s { background-position: -65px 0; } +.ui-icon-caret-1-sw { background-position: -80px 0; } +.ui-icon-caret-1-w { background-position: -96px 0; } +.ui-icon-caret-1-nw { background-position: -112px 0; } +.ui-icon-caret-2-n-s { background-position: -128px 0; } +.ui-icon-caret-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -65px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -65px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 1px -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-on { background-position: -96px -144px; } +.ui-icon-radio-off { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-all, +.ui-corner-top, +.ui-corner-left, +.ui-corner-tl { + border-top-left-radius: 3px/*{cornerRadius}*/; +} +.ui-corner-all, +.ui-corner-top, +.ui-corner-right, +.ui-corner-tr { + border-top-right-radius: 3px/*{cornerRadius}*/; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-left, +.ui-corner-bl { + border-bottom-left-radius: 3px/*{cornerRadius}*/; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-right, +.ui-corner-br { + border-bottom-right-radius: 3px/*{cornerRadius}*/; +} + +/* Overlays */ +.ui-widget-overlay { + background: #aaaaaa/*{bgColorOverlay}*/ /*{bgImgUrlOverlay}*/ /*{bgOverlayXPos}*/ /*{bgOverlayYPos}*/ /*{bgOverlayRepeat}*/; + opacity: .3/*{opacityOverlay}*/; + filter: Alpha(Opacity=30)/*{opacityFilterOverlay}*/; /* support: IE8 */ +} +.ui-widget-shadow { + -webkit-box-shadow: 0/*{offsetLeftShadow}*/ 0/*{offsetTopShadow}*/ 5px/*{thicknessShadow}*/ #666666/*{bgColorShadow}*/; + box-shadow: 0/*{offsetLeftShadow}*/ 0/*{offsetTopShadow}*/ 5px/*{thicknessShadow}*/ #666666/*{bgColorShadow}*/; +} diff --git a/web/modules/jquery_ui/jquery.ui/ui/data-min.js b/web/modules/jquery_ui/jquery.ui/ui/data-min.js new file mode 100644 index 0000000000..abd417845c --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/data-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./version"],a):a(jQuery)}(function(a){return a.extend(a.expr[":"],{data:a.expr.createPseudo?a.expr.createPseudo(function(b){return function(c){return!!a.data(c,b)}}):function(b,c,d){return!!a.data(b,d[3])}})}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/ui/disable-selection-min.js b/web/modules/jquery_ui/jquery.ui/ui/disable-selection-min.js new file mode 100644 index 0000000000..2dc0d14ff7 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/disable-selection-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./version"],a):a(jQuery)}(function(a){return a.fn.extend({disableSelection:function(){var a="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.on(a+".ui-disableSelection",function(a){a.preventDefault()})}}(),enableSelection:function(){return this.off(".ui-disableSelection")}})}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/ui/escape-selector-min.js b/web/modules/jquery_ui/jquery.ui/ui/escape-selector-min.js new file mode 100644 index 0000000000..9b7a16064c --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/escape-selector-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./version"],a):a(jQuery)}(function(a){return a.ui.escapeSelector=function(){var a=/([!"#$%&'()*+,.\/:;<=>?@[\]^`{|}~])/g;return function(b){return b.replace(a,"\\$1")}}()}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/ui/focusable-min.js b/web/modules/jquery_ui/jquery.ui/ui/focusable-min.js new file mode 100644 index 0000000000..e28dd4a048 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/focusable-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./version"],a):a(jQuery)}(function(a){function b(a){for(var b=a.css("visibility");"inherit"===b;)a=a.parent(),b=a.css("visibility");return"hidden"!==b}return a.ui.focusable=function(c,d){var e,f,g,h,i,j=c.nodeName.toLowerCase();return"area"===j?(e=c.parentNode,f=e.name,!(!c.href||!f||"map"!==e.nodeName.toLowerCase())&&(g=a("img[usemap='#"+f+"']"),g.length>0&&g.is(":visible"))):(/^(input|select|textarea|button|object)$/.test(j)?(h=!c.disabled,h&&(i=a(c).closest("fieldset")[0],i&&(h=!i.disabled))):h="a"===j?c.href||d:d,h&&a(c).is(":visible")&&b(a(c)))},a.extend(a.expr[":"],{focusable:function(b){return a.ui.focusable(b,null!=a.attr(b,"tabindex"))}}),a.ui.focusable}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/ui/form-min.js b/web/modules/jquery_ui/jquery.ui/ui/form-min.js new file mode 100644 index 0000000000..6dd8000437 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/form-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./version"],a):a(jQuery)}(function(a){return a.fn.form=function(){return"string"==typeof this[0].form?this.closest("form"):a(this[0].form)}}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/ui/form-reset-mixin-min.js b/web/modules/jquery_ui/jquery.ui/ui/form-reset-mixin-min.js new file mode 100644 index 0000000000..f337b9abc9 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/form-reset-mixin-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./form","./version"],a):a(jQuery)}(function(a){return a.ui.formResetMixin={_formResetHandler:function(){var b=a(this);setTimeout(function(){var c=b.data("ui-form-reset-instances");a.each(c,function(){this.refresh()})})},_bindFormResetHandler:function(){if(this.form=this.element.form(),this.form.length){var a=this.form.data("ui-form-reset-instances")||[];a.length||this.form.on("reset.ui-form-reset",this._formResetHandler),a.push(this),this.form.data("ui-form-reset-instances",a)}},_unbindFormResetHandler:function(){if(this.form.length){var b=this.form.data("ui-form-reset-instances");b.splice(a.inArray(this,b),1),b.length?this.form.data("ui-form-reset-instances",b):this.form.removeData("ui-form-reset-instances").off("reset.ui-form-reset")}}}}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/ui/ie-min.js b/web/modules/jquery_ui/jquery.ui/ui/ie-min.js new file mode 100644 index 0000000000..64fd40e5eb --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/ie-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./version"],a):a(jQuery)}(function(a){return a.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase())}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/ui/jquery-1-7-min.js b/web/modules/jquery_ui/jquery.ui/ui/jquery-1-7-min.js new file mode 100644 index 0000000000..feb34df305 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/jquery-1-7-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./version"],a):a(jQuery)}(function(a){"1.7"===a.fn.jquery.substring(0,3)&&(a.each(["Width","Height"],function(b,c){function d(b,c,d,f){return a.each(e,function(){c-=parseFloat(a.css(b,"padding"+this))||0,d&&(c-=parseFloat(a.css(b,"border"+this+"Width"))||0),f&&(c-=parseFloat(a.css(b,"margin"+this))||0)}),c}var e="Width"===c?["Left","Right"]:["Top","Bottom"],f=c.toLowerCase(),g={innerWidth:a.fn.innerWidth,innerHeight:a.fn.innerHeight,outerWidth:a.fn.outerWidth,outerHeight:a.fn.outerHeight};a.fn["inner"+c]=function(b){return void 0===b?g["inner"+c].call(this):this.each(function(){a(this).css(f,d(this,b)+"px")})},a.fn["outer"+c]=function(b,e){return"number"!=typeof b?g["outer"+c].call(this,b):this.each(function(){a(this).css(f,d(this,b,!0,e)+"px")})}}),a.fn.addBack=function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))})}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/ui/keycode-min.js b/web/modules/jquery_ui/jquery.ui/ui/keycode-min.js new file mode 100644 index 0000000000..93f156f244 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/keycode-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./version"],a):a(jQuery)}(function(a){return a.ui.keyCode={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/ui/labels-min.js b/web/modules/jquery_ui/jquery.ui/ui/labels-min.js new file mode 100644 index 0000000000..13e9c91e04 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/labels-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./version","./escape-selector"],a):a(jQuery)}(function(a){return a.fn.labels=function(){var b,c,d,e,f;return this[0].labels&&this[0].labels.length?this.pushStack(this[0].labels):(e=this.eq(0).parents("label"),d=this.attr("id"),d&&(b=this.eq(0).parents().last(),f=b.add(b.length?b.siblings():this.siblings()),c="label[for='"+a.ui.escapeSelector(d)+"']",e=e.add(f.find(c).addBack(c))),this.pushStack(e))}}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/ui/plugin-min.js b/web/modules/jquery_ui/jquery.ui/ui/plugin-min.js new file mode 100644 index 0000000000..1c04174f09 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/plugin-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./version"],a):a(jQuery)}(function(a){return a.ui.plugin={add:function(b,c,d){var e,f=a.ui[b].prototype;for(e in d)f.plugins[e]=f.plugins[e]||[],f.plugins[e].push([c,d[e]])},call:function(a,b,c,d){var e,f=a.plugins[b];if(f&&(d||a.element[0].parentNode&&11!==a.element[0].parentNode.nodeType))for(e=0;e<f.length;e++)a.options[f[e][0]]&&f[e][1].apply(a.element,c)}}}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/ui/position-min.js b/web/modules/jquery_ui/jquery.ui/ui/position-min.js new file mode 100644 index 0000000000..2f4626486f --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/position-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./version"],a):a(jQuery)}(function(a){return function(){function b(a,b,c){return[parseFloat(a[0])*(l.test(a[0])?b/100:1),parseFloat(a[1])*(l.test(a[1])?c/100:1)]}function c(b,c){return parseInt(a.css(b,c),10)||0}function d(b){var c=b[0];return 9===c.nodeType?{width:b.width(),height:b.height(),offset:{top:0,left:0}}:a.isWindow(c)?{width:b.width(),height:b.height(),offset:{top:b.scrollTop(),left:b.scrollLeft()}}:c.preventDefault?{width:0,height:0,offset:{top:c.pageY,left:c.pageX}}:{width:b.outerWidth(),height:b.outerHeight(),offset:b.offset()}}var e,f=Math.max,g=Math.abs,h=/left|center|right/,i=/top|center|bottom/,j=/[\+\-]\d+(\.[\d]+)?%?/,k=/^\w+/,l=/%$/,m=a.fn.position;a.position={scrollbarWidth:function(){if(void 0!==e)return e;var b,c,d=a("<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>"),f=d.children()[0];return a("body").append(d),b=f.offsetWidth,d.css("overflow","scroll"),c=f.offsetWidth,b===c&&(c=d[0].clientWidth),d.remove(),e=b-c},getScrollInfo:function(b){var c=b.isWindow||b.isDocument?"":b.element.css("overflow-x"),d=b.isWindow||b.isDocument?"":b.element.css("overflow-y"),e="scroll"===c||"auto"===c&&b.width<b.element[0].scrollWidth,f="scroll"===d||"auto"===d&&b.height<b.element[0].scrollHeight;return{width:f?a.position.scrollbarWidth():0,height:e?a.position.scrollbarWidth():0}},getWithinInfo:function(b){var c=a(b||window),d=a.isWindow(c[0]),e=!!c[0]&&9===c[0].nodeType,f=!d&&!e;return{element:c,isWindow:d,isDocument:e,offset:f?a(b).offset():{left:0,top:0},scrollLeft:c.scrollLeft(),scrollTop:c.scrollTop(),width:c.outerWidth(),height:c.outerHeight()}}},a.fn.position=function(e){if(!e||!e.of)return m.apply(this,arguments);e=a.extend({},e);var l,n,o,p,q,r,s=a(e.of),t=a.position.getWithinInfo(e.within),u=a.position.getScrollInfo(t),v=(e.collision||"flip").split(" "),w={};return r=d(s),s[0].preventDefault&&(e.at="left top"),n=r.width,o=r.height,p=r.offset,q=a.extend({},p),a.each(["my","at"],function(){var a,b,c=(e[this]||"").split(" ");1===c.length&&(c=h.test(c[0])?c.concat(["center"]):i.test(c[0])?["center"].concat(c):["center","center"]),c[0]=h.test(c[0])?c[0]:"center",c[1]=i.test(c[1])?c[1]:"center",a=j.exec(c[0]),b=j.exec(c[1]),w[this]=[a?a[0]:0,b?b[0]:0],e[this]=[k.exec(c[0])[0],k.exec(c[1])[0]]}),1===v.length&&(v[1]=v[0]),"right"===e.at[0]?q.left+=n:"center"===e.at[0]&&(q.left+=n/2),"bottom"===e.at[1]?q.top+=o:"center"===e.at[1]&&(q.top+=o/2),l=b(w.at,n,o),q.left+=l[0],q.top+=l[1],this.each(function(){var d,h,i=a(this),j=i.outerWidth(),k=i.outerHeight(),m=c(this,"marginLeft"),r=c(this,"marginTop"),x=j+m+c(this,"marginRight")+u.width,y=k+r+c(this,"marginBottom")+u.height,z=a.extend({},q),A=b(w.my,i.outerWidth(),i.outerHeight());"right"===e.my[0]?z.left-=j:"center"===e.my[0]&&(z.left-=j/2),"bottom"===e.my[1]?z.top-=k:"center"===e.my[1]&&(z.top-=k/2),z.left+=A[0],z.top+=A[1],d={marginLeft:m,marginTop:r},a.each(["left","top"],function(b,c){a.ui.position[v[b]]&&a.ui.position[v[b]][c](z,{targetWidth:n,targetHeight:o,elemWidth:j,elemHeight:k,collisionPosition:d,collisionWidth:x,collisionHeight:y,offset:[l[0]+A[0],l[1]+A[1]],my:e.my,at:e.at,within:t,elem:i})}),e.using&&(h=function(a){var b=p.left-z.left,c=b+n-j,d=p.top-z.top,h=d+o-k,l={target:{element:s,left:p.left,top:p.top,width:n,height:o},element:{element:i,left:z.left,top:z.top,width:j,height:k},horizontal:c<0?"left":b>0?"right":"center",vertical:h<0?"top":d>0?"bottom":"middle"};n<j&&g(b+c)<n&&(l.horizontal="center"),o<k&&g(d+h)<o&&(l.vertical="middle"),f(g(b),g(c))>f(g(d),g(h))?l.important="horizontal":l.important="vertical",e.using.call(this,a,l)}),i.offset(a.extend(z,{using:h}))})},a.ui.position={fit:{left:function(a,b){var c,d=b.within,e=d.isWindow?d.scrollLeft:d.offset.left,g=d.width,h=a.left-b.collisionPosition.marginLeft,i=e-h,j=h+b.collisionWidth-g-e;b.collisionWidth>g?i>0&&j<=0?(c=a.left+i+b.collisionWidth-g-e,a.left+=i-c):j>0&&i<=0?a.left=e:i>j?a.left=e+g-b.collisionWidth:a.left=e:i>0?a.left+=i:j>0?a.left-=j:a.left=f(a.left-h,a.left)},top:function(a,b){var c,d=b.within,e=d.isWindow?d.scrollTop:d.offset.top,g=b.within.height,h=a.top-b.collisionPosition.marginTop,i=e-h,j=h+b.collisionHeight-g-e;b.collisionHeight>g?i>0&&j<=0?(c=a.top+i+b.collisionHeight-g-e,a.top+=i-c):j>0&&i<=0?a.top=e:i>j?a.top=e+g-b.collisionHeight:a.top=e:i>0?a.top+=i:j>0?a.top-=j:a.top=f(a.top-h,a.top)}},flip:{left:function(a,b){var c,d,e=b.within,f=e.offset.left+e.scrollLeft,h=e.width,i=e.isWindow?e.scrollLeft:e.offset.left,j=a.left-b.collisionPosition.marginLeft,k=j-i,l=j+b.collisionWidth-h-i,m="left"===b.my[0]?-b.elemWidth:"right"===b.my[0]?b.elemWidth:0,n="left"===b.at[0]?b.targetWidth:"right"===b.at[0]?-b.targetWidth:0,o=-2*b.offset[0];k<0?(c=a.left+m+n+o+b.collisionWidth-h-f,(c<0||c<g(k))&&(a.left+=m+n+o)):l>0&&(d=a.left-b.collisionPosition.marginLeft+m+n+o-i,(d>0||g(d)<l)&&(a.left+=m+n+o))},top:function(a,b){var c,d,e=b.within,f=e.offset.top+e.scrollTop,h=e.height,i=e.isWindow?e.scrollTop:e.offset.top,j=a.top-b.collisionPosition.marginTop,k=j-i,l=j+b.collisionHeight-h-i,m="top"===b.my[1],n=m?-b.elemHeight:"bottom"===b.my[1]?b.elemHeight:0,o="top"===b.at[1]?b.targetHeight:"bottom"===b.at[1]?-b.targetHeight:0,p=-2*b.offset[1];k<0?(d=a.top+n+o+p+b.collisionHeight-h-f,(d<0||d<g(k))&&(a.top+=n+o+p)):l>0&&(c=a.top-b.collisionPosition.marginTop+n+o+p-i,(c>0||g(c)<l)&&(a.top+=n+o+p))}},flipfit:{left:function(){a.ui.position.flip.left.apply(this,arguments),a.ui.position.fit.left.apply(this,arguments)},top:function(){a.ui.position.flip.top.apply(this,arguments),a.ui.position.fit.top.apply(this,arguments)}}}}(),a.ui.position}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/ui/safe-active-element-min.js b/web/modules/jquery_ui/jquery.ui/ui/safe-active-element-min.js new file mode 100644 index 0000000000..4989d7704e --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/safe-active-element-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./version"],a):a(jQuery)}(function(a){return a.ui.safeActiveElement=function(a){var b;try{b=a.activeElement}catch(c){b=a.body}return b||(b=a.body),b.nodeName||(b=a.body),b}}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/ui/safe-blur-min.js b/web/modules/jquery_ui/jquery.ui/ui/safe-blur-min.js new file mode 100644 index 0000000000..56802b8ca7 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/safe-blur-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./version"],a):a(jQuery)}(function(a){return a.ui.safeBlur=function(b){b&&"body"!==b.nodeName.toLowerCase()&&a(b).trigger("blur")}}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/ui/scroll-parent-min.js b/web/modules/jquery_ui/jquery.ui/ui/scroll-parent-min.js new file mode 100644 index 0000000000..0c1440ec2e --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/scroll-parent-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./version"],a):a(jQuery)}(function(a){return a.fn.scrollParent=function(b){var c=this.css("position"),d="absolute"===c,e=b?/(auto|scroll|hidden)/:/(auto|scroll)/,f=this.parents().filter(function(){var b=a(this);return(!d||"static"!==b.css("position"))&&e.test(b.css("overflow")+b.css("overflow-y")+b.css("overflow-x"))}).eq(0);return"fixed"!==c&&f.length?f:a(this[0].ownerDocument||document)}}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/ui/tabbable-min.js b/web/modules/jquery_ui/jquery.ui/ui/tabbable-min.js new file mode 100644 index 0000000000..17f7743401 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/tabbable-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./version","./focusable"],a):a(jQuery)}(function(a){return a.extend(a.expr[":"],{tabbable:function(b){var c=a.attr(b,"tabindex"),d=null!=c;return(!d||c>=0)&&a.ui.focusable(b,d)}})}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/ui/unique-id-min.js b/web/modules/jquery_ui/jquery.ui/ui/unique-id-min.js new file mode 100644 index 0000000000..509f2ab0e8 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/unique-id-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./version"],a):a(jQuery)}(function(a){return a.fn.extend({uniqueId:function(){var a=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++a)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&a(this).removeAttr("id")})}})}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/ui/version-min.js b/web/modules/jquery_ui/jquery.ui/ui/version-min.js new file mode 100644 index 0000000000..a7f99728a0 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/version-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a(jQuery)}(function(a){return a.ui=a.ui||{},a.ui.version="1.12.1"}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/ui/widget-min.js b/web/modules/jquery_ui/jquery.ui/ui/widget-min.js new file mode 100644 index 0000000000..3b13f75cee --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/widget-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./version"],a):a(jQuery)}(function(a){var b=0,c=Array.prototype.slice;return a.cleanData=function(b){return function(c){var d,e,f;for(f=0;null!=(e=c[f]);f++)try{d=a._data(e,"events"),d&&d.remove&&a(e).triggerHandler("remove")}catch(g){}b(c)}}(a.cleanData),a.widget=function(b,c,d){var e,f,g,h={},i=b.split(".")[0];b=b.split(".")[1];var j=i+"-"+b;return d||(d=c,c=a.Widget),a.isArray(d)&&(d=a.extend.apply(null,[{}].concat(d))),a.expr[":"][j.toLowerCase()]=function(b){return!!a.data(b,j)},a[i]=a[i]||{},e=a[i][b],f=a[i][b]=function(a,b){return this._createWidget?void(arguments.length&&this._createWidget(a,b)):new f(a,b)},a.extend(f,e,{version:d.version,_proto:a.extend({},d),_childConstructors:[]}),g=new c,g.options=a.widget.extend({},g.options),a.each(d,function(b,d){return a.isFunction(d)?void(h[b]=function(){function a(){return c.prototype[b].apply(this,arguments)}function e(a){return c.prototype[b].apply(this,a)}return function(){var b,c=this._super,f=this._superApply;return this._super=a,this._superApply=e,b=d.apply(this,arguments),this._super=c,this._superApply=f,b}}()):void(h[b]=d)}),f.prototype=a.widget.extend(g,{widgetEventPrefix:e?g.widgetEventPrefix||b:b},h,{constructor:f,namespace:i,widgetName:b,widgetFullName:j}),e?(a.each(e._childConstructors,function(b,c){var d=c.prototype;a.widget(d.namespace+"."+d.widgetName,f,c._proto)}),delete e._childConstructors):c._childConstructors.push(f),a.widget.bridge(b,f),f},a.widget.extend=function(b){for(var d,e,f=c.call(arguments,1),g=0,h=f.length;g<h;g++)for(d in f[g])e=f[g][d],f[g].hasOwnProperty(d)&&void 0!==e&&(a.isPlainObject(e)?b[d]=a.isPlainObject(b[d])?a.widget.extend({},b[d],e):a.widget.extend({},e):b[d]=e);return b},a.widget.bridge=function(b,d){var e=d.prototype.widgetFullName||b;a.fn[b]=function(f){var g="string"==typeof f,h=c.call(arguments,1),i=this;return g?this.length||"instance"!==f?this.each(function(){var c,d=a.data(this,e);return"instance"===f?(i=d,!1):d?a.isFunction(d[f])&&"_"!==f.charAt(0)?(c=d[f].apply(d,h),c!==d&&void 0!==c?(i=c&&c.jquery?i.pushStack(c.get()):c,!1):void 0):a.error("no such method '"+f+"' for "+b+" widget instance"):a.error("cannot call methods on "+b+" prior to initialization; attempted to call method '"+f+"'")}):i=void 0:(h.length&&(f=a.widget.extend.apply(null,[f].concat(h))),this.each(function(){var b=a.data(this,e);b?(b.option(f||{}),b._init&&b._init()):a.data(this,e,new d(f,this))})),i}},a.Widget=function(){},a.Widget._childConstructors=[],a.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{classes:{},disabled:!1,create:null},_createWidget:function(c,d){d=a(d||this.defaultElement||this)[0],this.element=a(d),this.uuid=b++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=a(),this.hoverable=a(),this.focusable=a(),this.classesElementLookup={},d!==this&&(a.data(d,this.widgetFullName,this),this._on(!0,this.element,{remove:function(a){a.target===d&&this.destroy()}}),this.document=a(d.style?d.ownerDocument:d.document||d),this.window=a(this.document[0].defaultView||this.document[0].parentWindow)),this.options=a.widget.extend({},this.options,this._getCreateOptions(),c),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:a.noop,_create:a.noop,_init:a.noop,destroy:function(){var b=this;this._destroy(),a.each(this.classesElementLookup,function(a,c){b._removeClass(c,a)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:a.noop,widget:function(){return this.element},option:function(b,c){var d,e,f,g=b;if(0===arguments.length)return a.widget.extend({},this.options);if("string"==typeof b)if(g={},d=b.split("."),b=d.shift(),d.length){for(e=g[b]=a.widget.extend({},this.options[b]),f=0;f<d.length-1;f++)e[d[f]]=e[d[f]]||{},e=e[d[f]];if(b=d.pop(),1===arguments.length)return void 0===e[b]?null:e[b];e[b]=c}else{if(1===arguments.length)return void 0===this.options[b]?null:this.options[b];g[b]=c}return this._setOptions(g),this},_setOptions:function(a){var b;for(b in a)this._setOption(b,a[b]);return this},_setOption:function(a,b){return"classes"===a&&this._setOptionClasses(b),this.options[a]=b,"disabled"===a&&this._setOptionDisabled(b),this},_setOptionClasses:function(b){var c,d,e;for(c in b)e=this.classesElementLookup[c],b[c]!==this.options.classes[c]&&e&&e.length&&(d=a(e.get()),this._removeClass(e,c),d.addClass(this._classes({element:d,keys:c,classes:b,add:!0})))},_setOptionDisabled:function(a){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!a),a&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(b){function c(c,f){var g,h;for(h=0;h<c.length;h++)g=e.classesElementLookup[c[h]]||a(),g=a(b.add?a.unique(g.get().concat(b.element.get())):g.not(b.element).get()),e.classesElementLookup[c[h]]=g,d.push(c[h]),f&&b.classes[c[h]]&&d.push(b.classes[c[h]])}var d=[],e=this;return b=a.extend({element:this.element,classes:this.options.classes||{}},b),this._on(b.element,{remove:"_untrackClassesElement"}),b.keys&&c(b.keys.match(/\S+/g)||[],!0),b.extra&&c(b.extra.match(/\S+/g)||[]),d.join(" ")},_untrackClassesElement:function(b){var c=this;a.each(c.classesElementLookup,function(d,e){a.inArray(b.target,e)!==-1&&(c.classesElementLookup[d]=a(e.not(b.target).get()))})},_removeClass:function(a,b,c){return this._toggleClass(a,b,c,!1)},_addClass:function(a,b,c){return this._toggleClass(a,b,c,!0)},_toggleClass:function(a,b,c,d){d="boolean"==typeof d?d:c;var e="string"==typeof a||null===a,f={extra:e?b:c,keys:e?a:b,element:e?this.element:a,add:d};return f.element.toggleClass(this._classes(f),d),this},_on:function(b,c,d){var e,f=this;"boolean"!=typeof b&&(d=c,c=b,b=!1),d?(c=e=a(c),this.bindings=this.bindings.add(c)):(d=c,c=this.element,e=this.widget()),a.each(d,function(d,g){function h(){if(b||f.options.disabled!==!0&&!a(this).hasClass("ui-state-disabled"))return("string"==typeof g?f[g]:g).apply(f,arguments)}"string"!=typeof g&&(h.guid=g.guid=g.guid||h.guid||a.guid++);var i=d.match(/^([\w:-]*)\s*(.*)$/),j=i[1]+f.eventNamespace,k=i[2];k?e.on(j,k,h):c.on(j,h)})},_off:function(b,c){c=(c||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,b.off(c).off(c),this.bindings=a(this.bindings.not(b).get()),this.focusable=a(this.focusable.not(b).get()),this.hoverable=a(this.hoverable.not(b).get())},_delay:function(a,b){function c(){return("string"==typeof a?d[a]:a).apply(d,arguments)}var d=this;return setTimeout(c,b||0)},_hoverable:function(b){this.hoverable=this.hoverable.add(b),this._on(b,{mouseenter:function(b){this._addClass(a(b.currentTarget),null,"ui-state-hover")},mouseleave:function(b){this._removeClass(a(b.currentTarget),null,"ui-state-hover")}})},_focusable:function(b){this.focusable=this.focusable.add(b),this._on(b,{focusin:function(b){this._addClass(a(b.currentTarget),null,"ui-state-focus")},focusout:function(b){this._removeClass(a(b.currentTarget),null,"ui-state-focus")}})},_trigger:function(b,c,d){var e,f,g=this.options[b];if(d=d||{},c=a.Event(c),c.type=(b===this.widgetEventPrefix?b:this.widgetEventPrefix+b).toLowerCase(),c.target=this.element[0],f=c.originalEvent)for(e in f)e in c||(c[e]=f[e]);return this.element.trigger(c,d),!(a.isFunction(g)&&g.apply(this.element[0],[c].concat(d))===!1||c.isDefaultPrevented())}},a.each({show:"fadeIn",hide:"fadeOut"},function(b,c){a.Widget.prototype["_"+b]=function(d,e,f){"string"==typeof e&&(e={effect:e});var g,h=e?e===!0||"number"==typeof e?c:e.effect||c:b;e=e||{},"number"==typeof e&&(e={duration:e}),g=!a.isEmptyObject(e),e.complete=f,e.delay&&d.delay(e.delay),g&&a.effects&&a.effects.effect[h]?d[b](e):h!==b&&d[h]?d[h](e.duration,e.easing,f):d.queue(function(c){a(this)[b](),f&&f.call(d[0]),c()})}}),a.widget}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery.ui/ui/widgets/mouse-min.js b/web/modules/jquery_ui/jquery.ui/ui/widgets/mouse-min.js new file mode 100644 index 0000000000..9af7c1e903 --- /dev/null +++ b/web/modules/jquery_ui/jquery.ui/ui/widgets/mouse-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","../ie","../version","../widget"],a):a(jQuery)}(function(a){var b=!1;return a(document).on("mouseup",function(){b=!1}),a.widget("ui.mouse",{version:"1.12.1",options:{cancel:"input, textarea, button, select, option",distance:1,delay:0},_mouseInit:function(){var b=this;this.element.on("mousedown."+this.widgetName,function(a){return b._mouseDown(a)}).on("click."+this.widgetName,function(c){if(!0===a.data(c.target,b.widgetName+".preventClickEvent"))return a.removeData(c.target,b.widgetName+".preventClickEvent"),c.stopImmediatePropagation(),!1}),this.started=!1},_mouseDestroy:function(){this.element.off("."+this.widgetName),this._mouseMoveDelegate&&this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(c){if(!b){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(c),this._mouseDownEvent=c;var d=this,e=1===c.which,f=!("string"!=typeof this.options.cancel||!c.target.nodeName)&&a(c.target).closest(this.options.cancel).length;return!(e&&!f&&this._mouseCapture(c))||(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){d.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(c)&&this._mouseDelayMet(c)&&(this._mouseStarted=this._mouseStart(c)!==!1,!this._mouseStarted)?(c.preventDefault(),!0):(!0===a.data(c.target,this.widgetName+".preventClickEvent")&&a.removeData(c.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(a){return d._mouseMove(a)},this._mouseUpDelegate=function(a){return d._mouseUp(a)},this.document.on("mousemove."+this.widgetName,this._mouseMoveDelegate).on("mouseup."+this.widgetName,this._mouseUpDelegate),c.preventDefault(),b=!0,!0))}},_mouseMove:function(b){if(this._mouseMoved){if(a.ui.ie&&(!document.documentMode||document.documentMode<9)&&!b.button)return this._mouseUp(b);if(!b.which)if(b.originalEvent.altKey||b.originalEvent.ctrlKey||b.originalEvent.metaKey||b.originalEvent.shiftKey)this.ignoreMissingWhich=!0;else if(!this.ignoreMissingWhich)return this._mouseUp(b)}return(b.which||b.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(b),b.preventDefault()):(this._mouseDistanceMet(b)&&this._mouseDelayMet(b)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,b)!==!1,this._mouseStarted?this._mouseDrag(b):this._mouseUp(b)),!this._mouseStarted)},_mouseUp:function(c){this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,c.target===this._mouseDownEvent.target&&a.data(c.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(c)),this._mouseDelayTimer&&(clearTimeout(this._mouseDelayTimer),delete this._mouseDelayTimer),this.ignoreMissingWhich=!1,b=!1,c.preventDefault()},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}})}); \ No newline at end of file diff --git a/web/modules/jquery_ui/jquery_ui.info.yml b/web/modules/jquery_ui/jquery_ui.info.yml new file mode 100644 index 0000000000..4593dbc39b --- /dev/null +++ b/web/modules/jquery_ui/jquery_ui.info.yml @@ -0,0 +1,11 @@ +name: jQuery UI +type: module +description: 'Provides jQuery UI library.' +package: jQuery UI +core: 8.x +core_version_requirement: ^8 || ^9 + +# Information added by Drupal.org packaging script on 2020-02-19 +version: '8.x-1.4' +project: 'jquery_ui' +datestamp: 1582149959 diff --git a/web/modules/jquery_ui/jquery_ui.libraries.yml b/web/modules/jquery_ui/jquery_ui.libraries.yml new file mode 100644 index 0000000000..a6c3ff79cd --- /dev/null +++ b/web/modules/jquery_ui/jquery_ui.libraries.yml @@ -0,0 +1,32 @@ +core: + remote: https://github.com/jquery/jquery-ui + version: &jquery_ui_version 1.12.1 + license: &jquery_ui_license + name: Public Domain + url: https://github.com/jquery/jquery-ui/blob/1.12.1/LICENSE.txt + gpl-compatible: true + js: {} + dependencies: + - core/jquery + - core/jquery.ui + +widget: + version: *jquery_ui_version + license: *jquery_ui_license + js: {} + dependencies: + - core/jquery.ui.widget + +mouse: + version: *jquery_ui_version + license: *jquery_ui_license + js: {} + dependencies: + - core/jquery.ui.mouse + +position: + version: *jquery_ui_version + license: *jquery_ui_license + js: {} + dependencies: + - core/jquery.ui.position diff --git a/web/modules/jquery_ui/jquery_ui.module b/web/modules/jquery_ui/jquery_ui.module new file mode 100644 index 0000000000..0f7e694042 --- /dev/null +++ b/web/modules/jquery_ui/jquery_ui.module @@ -0,0 +1,23 @@ +<?php + +/** + * @file + * Contains jquery_ui.module. + */ + +use Drupal\Core\Routing\RouteMatchInterface; + +/** + * Implements hook_help(). + */ +function jquery_ui_help($route_name, RouteMatchInterface $route_match) { + switch ($route_name) { + case 'help.page.jquery_ui': + $output = '<h3>' . t('About') . '</h3>'; + $output .= '<p>' . t('Drupal 8 includes jQuery UI in core, however it is no longer actively maintained and has been marked deprecated. This module provides the jQuery UI asset library for any themes and modules that require it.') . '</p>'; + $output .= '<p>' . t('For more information about the deprecation of jQuery UI, see this <a href=":change-record">change record</a>', [':change-record' => 'https://www.drupal.org/node/3067969']) . '</p>'; + $output .= '<p>' . t('Visit the <a href=":project_link">jQuery UI project page</a> on Drupal.org for more information about this module.', [':project_link' => 'https://www.drupal.org/project/jquery_ui']) . '</p>'; + + return $output; + } +} diff --git a/web/modules/jquery_ui_datepicker/LICENSE.txt b/web/modules/jquery_ui_datepicker/LICENSE.txt new file mode 100644 index 0000000000..d159169d10 --- /dev/null +++ b/web/modules/jquery_ui_datepicker/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/web/modules/jquery_ui_datepicker/README.md b/web/modules/jquery_ui_datepicker/README.md new file mode 100644 index 0000000000..6a4d0883b5 --- /dev/null +++ b/web/modules/jquery_ui_datepicker/README.md @@ -0,0 +1,40 @@ +# jQuery UI Datepicker + +Drupal 8 includes jQuery UI in core, however it is no longer actively +maintained and has been marked deprecated. This module provides the +jQuery UI Datepicker library for any themes and modules that require it. + + - jQuery UI [Datepicker documentation](https://jqueryui.com/datepicker/) + - jQuery UI [Datepicker API + documentation](https://api.jqueryui.com/datepicker/) + +**Caution**: jQuery UI was deprecated from core because it is no longer +actively maintained, and has been marked “End of Life” by the OpenJS +Foundation. It is not recommended to depend on jQuery UI in your own +code, and instead to select a replacement solution as soon as possible. + +## Instructions + +1. Install this module and the jQuery UI module. +2. Change any references in your theme or module from + `core/jquery.ui.datepicker` to `jquery_ui_datepicker/datepicker` + +### Requirements + + - [jQuery UI](https://www.drupal.org/project/jquery_ui) + +### Related modules + + - [jQuery UI Accordion](https://www.drupal.org/project/jquery_ui_accordion) + - [jQuery UI Button](https://www.drupal.org/project/jquery_ui_button) + - [jQuery UI Checkboxradio](https://www.drupal.org/project/jquery_ui_checkboxradio) + - [jQuery UI Controlgroup](https://www.drupal.org/project/jquery_ui_controlgroup) + - [jQuery UI Draggable](https://www.drupal.org/project/jquery_ui_draggable) + - [jQuery UI Droppable](https://www.drupal.org/project/jquery_ui_droppable) + - [jQuery UI Effects](https://www.drupal.org/project/jquery_ui_effects) + - [jQuery UI Menu](https://www.drupal.org/project/jquery_ui_menu) + - [jQuery UI Progressbar](https://www.drupal.org/project/jquery_ui_progressbar) + - [jQuery UI Selectable](https://www.drupal.org/project/jquery_ui_selectable) + - [jQuery UI Selectmenu](https://www.drupal.org/project/jquery_ui_selectmenu) + - [jQuery UI Slider](https://www.drupal.org/project/jquery_ui_slider) + - [jQuery UI Spinner](https://www.drupal.org/project/jquery_ui_spinner) diff --git a/web/modules/jquery_ui_datepicker/jquery.ui/themes/base/datepicker.css b/web/modules/jquery_ui_datepicker/jquery.ui/themes/base/datepicker.css new file mode 100644 index 0000000000..c82ae33fba --- /dev/null +++ b/web/modules/jquery_ui_datepicker/jquery.ui/themes/base/datepicker.css @@ -0,0 +1,185 @@ +/*! + * jQuery UI Datepicker 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/datepicker/#theming + */ +.ui-datepicker { + width: 17em; + padding: .2em .2em 0; + display: none; +} +.ui-datepicker .ui-datepicker-header { + position: relative; + padding: .2em 0; +} +.ui-datepicker .ui-datepicker-prev, +.ui-datepicker .ui-datepicker-next { + position: absolute; + top: 2px; + width: 1.8em; + height: 1.8em; +} +.ui-datepicker .ui-datepicker-prev-hover, +.ui-datepicker .ui-datepicker-next-hover { + top: 1px; +} +.ui-datepicker .ui-datepicker-prev { + left: 2px; +} +.ui-datepicker .ui-datepicker-next { + right: 2px; +} +.ui-datepicker .ui-datepicker-prev-hover { + left: 1px; +} +.ui-datepicker .ui-datepicker-next-hover { + right: 1px; +} +.ui-datepicker .ui-datepicker-prev span, +.ui-datepicker .ui-datepicker-next span { + display: block; + position: absolute; + left: 50%; + margin-left: -8px; + top: 50%; + margin-top: -8px; +} +.ui-datepicker .ui-datepicker-title { + margin: 0 2.3em; + line-height: 1.8em; + text-align: center; +} +.ui-datepicker .ui-datepicker-title select { + font-size: 1em; + margin: 1px 0; +} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { + width: 45%; +} +.ui-datepicker table { + width: 100%; + font-size: .9em; + border-collapse: collapse; + margin: 0 0 .4em; +} +.ui-datepicker th { + padding: .7em .3em; + text-align: center; + font-weight: bold; + border: 0; +} +.ui-datepicker td { + border: 0; + padding: 1px; +} +.ui-datepicker td span, +.ui-datepicker td a { + display: block; + padding: .2em; + text-align: right; + text-decoration: none; +} +.ui-datepicker .ui-datepicker-buttonpane { + background-image: none; + margin: .7em 0 0 0; + padding: 0 .2em; + border-left: 0; + border-right: 0; + border-bottom: 0; +} +.ui-datepicker .ui-datepicker-buttonpane button { + float: right; + margin: .5em .2em .4em; + cursor: pointer; + padding: .2em .6em .3em .6em; + width: auto; + overflow: visible; +} +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { + float: left; +} + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { + width: auto; +} +.ui-datepicker-multi .ui-datepicker-group { + float: left; +} +.ui-datepicker-multi .ui-datepicker-group table { + width: 95%; + margin: 0 auto .4em; +} +.ui-datepicker-multi-2 .ui-datepicker-group { + width: 50%; +} +.ui-datepicker-multi-3 .ui-datepicker-group { + width: 33.3%; +} +.ui-datepicker-multi-4 .ui-datepicker-group { + width: 25%; +} +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header, +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { + border-left-width: 0; +} +.ui-datepicker-multi .ui-datepicker-buttonpane { + clear: left; +} +.ui-datepicker-row-break { + clear: both; + width: 100%; + font-size: 0; +} + +/* RTL support */ +.ui-datepicker-rtl { + direction: rtl; +} +.ui-datepicker-rtl .ui-datepicker-prev { + right: 2px; + left: auto; +} +.ui-datepicker-rtl .ui-datepicker-next { + left: 2px; + right: auto; +} +.ui-datepicker-rtl .ui-datepicker-prev:hover { + right: 1px; + left: auto; +} +.ui-datepicker-rtl .ui-datepicker-next:hover { + left: 1px; + right: auto; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane { + clear: right; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane button { + float: left; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current, +.ui-datepicker-rtl .ui-datepicker-group { + float: right; +} +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header, +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { + border-right-width: 0; + border-left-width: 1px; +} + +/* Icons */ +.ui-datepicker .ui-icon { + display: block; + text-indent: -99999px; + overflow: hidden; + background-repeat: no-repeat; + left: .5em; + top: .3em; +} diff --git a/web/modules/jquery_ui_datepicker/jquery.ui/ui/widgets/datepicker-min.js b/web/modules/jquery_ui_datepicker/jquery.ui/ui/widgets/datepicker-min.js new file mode 100644 index 0000000000..3469b7d40f --- /dev/null +++ b/web/modules/jquery_ui_datepicker/jquery.ui/ui/widgets/datepicker-min.js @@ -0,0 +1,5 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","../version","../keycode"],a):a(jQuery)}(function(a){function b(a){for(var b,c;a.length&&a[0]!==document;){if(b=a.css("position"),("absolute"===b||"relative"===b||"fixed"===b)&&(c=parseInt(a.css("zIndex"),10),!isNaN(c)&&0!==c))return c;a=a.parent()}return 0}function c(){this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},a.extend(this._defaults,this.regional[""]),this.regional.en=a.extend(!0,{},this.regional[""]),this.regional["en-US"]=a.extend(!0,{},this.regional.en),this.dpDiv=d(a("<div id='"+this._mainDivId+"' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"))}function d(b){var c="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return b.on("mouseout",c,function(){a(this).removeClass("ui-state-hover"),this.className.indexOf("ui-datepicker-prev")!==-1&&a(this).removeClass("ui-datepicker-prev-hover"),this.className.indexOf("ui-datepicker-next")!==-1&&a(this).removeClass("ui-datepicker-next-hover")}).on("mouseover",c,e)}function e(){a.datepicker._isDisabledDatepicker(g.inline?g.dpDiv.parent()[0]:g.input[0])||(a(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),a(this).addClass("ui-state-hover"),this.className.indexOf("ui-datepicker-prev")!==-1&&a(this).addClass("ui-datepicker-prev-hover"),this.className.indexOf("ui-datepicker-next")!==-1&&a(this).addClass("ui-datepicker-next-hover"))}function f(b,c){a.extend(b,c);for(var d in c)null==c[d]&&(b[d]=c[d]);return b}a.extend(a.ui,{datepicker:{version:"1.12.1"}});var g;return a.extend(c.prototype,{markerClassName:"hasDatepicker",maxRows:4,_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){return f(this._defaults,a||{}),this},_attachDatepicker:function(b,c){var d,e,f;d=b.nodeName.toLowerCase(),e="div"===d||"span"===d,b.id||(this.uuid+=1,b.id="dp"+this.uuid),f=this._newInst(a(b),e),f.settings=a.extend({},c||{}),"input"===d?this._connectDatepicker(b,f):e&&this._inlineDatepicker(b,f)},_newInst:function(b,c){var e=b[0].id.replace(/([^A-Za-z0-9_\-])/g,"\\\\$1");return{id:e,input:b,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:c,dpDiv:c?d(a("<div class='"+this._inlineClass+" ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")):this.dpDiv}},_connectDatepicker:function(b,c){var d=a(b);c.append=a([]),c.trigger=a([]),d.hasClass(this.markerClassName)||(this._attachments(d,c),d.addClass(this.markerClassName).on("keydown",this._doKeyDown).on("keypress",this._doKeyPress).on("keyup",this._doKeyUp),this._autoSize(c),a.data(b,"datepicker",c),c.settings.disabled&&this._disableDatepicker(b))},_attachments:function(b,c){var d,e,f,g=this._get(c,"appendText"),h=this._get(c,"isRTL");c.append&&c.append.remove(),g&&(c.append=a("<span class='"+this._appendClass+"'>"+g+"</span>"),b[h?"before":"after"](c.append)),b.off("focus",this._showDatepicker),c.trigger&&c.trigger.remove(),d=this._get(c,"showOn"),"focus"!==d&&"both"!==d||b.on("focus",this._showDatepicker),"button"!==d&&"both"!==d||(e=this._get(c,"buttonText"),f=this._get(c,"buttonImage"),c.trigger=a(this._get(c,"buttonImageOnly")?a("<img/>").addClass(this._triggerClass).attr({src:f,alt:e,title:e}):a("<button type='button'></button>").addClass(this._triggerClass).html(f?a("<img/>").attr({src:f,alt:e,title:e}):e)),b[h?"before":"after"](c.trigger),c.trigger.on("click",function(){return a.datepicker._datepickerShowing&&a.datepicker._lastInput===b[0]?a.datepicker._hideDatepicker():a.datepicker._datepickerShowing&&a.datepicker._lastInput!==b[0]?(a.datepicker._hideDatepicker(),a.datepicker._showDatepicker(b[0])):a.datepicker._showDatepicker(b[0]),!1}))},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b,c,d,e,f=new Date(2009,11,20),g=this._get(a,"dateFormat");g.match(/[DM]/)&&(b=function(a){for(c=0,d=0,e=0;e<a.length;e++)a[e].length>c&&(c=a[e].length,d=e);return d},f.setMonth(b(this._get(a,g.match(/MM/)?"monthNames":"monthNamesShort"))),f.setDate(b(this._get(a,g.match(/DD/)?"dayNames":"dayNamesShort"))+20-f.getDay())),a.input.attr("size",this._formatDate(a,f).length)}},_inlineDatepicker:function(b,c){var d=a(b);d.hasClass(this.markerClassName)||(d.addClass(this.markerClassName).append(c.dpDiv),a.data(b,"datepicker",c),this._setDate(c,this._getDefaultDate(c),!0),this._updateDatepicker(c),this._updateAlternate(c),c.settings.disabled&&this._disableDatepicker(b),c.dpDiv.css("display","block"))},_dialogDatepicker:function(b,c,d,e,g){var h,i,j,k,l,m=this._dialogInst;return m||(this.uuid+=1,h="dp"+this.uuid,this._dialogInput=a("<input type='text' id='"+h+"' style='position: absolute; top: -100px; width: 0px;'/>"),this._dialogInput.on("keydown",this._doKeyDown),a("body").append(this._dialogInput),m=this._dialogInst=this._newInst(this._dialogInput,!1),m.settings={},a.data(this._dialogInput[0],"datepicker",m)),f(m.settings,e||{}),c=c&&c.constructor===Date?this._formatDate(m,c):c,this._dialogInput.val(c),this._pos=g?g.length?g:[g.pageX,g.pageY]:null,this._pos||(i=document.documentElement.clientWidth,j=document.documentElement.clientHeight,k=document.documentElement.scrollLeft||document.body.scrollLeft,l=document.documentElement.scrollTop||document.body.scrollTop,this._pos=[i/2-100+k,j/2-150+l]),this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),m.settings.onSelect=d,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),a.blockUI&&a.blockUI(this.dpDiv),a.data(this._dialogInput[0],"datepicker",m),this},_destroyDatepicker:function(b){var c,d=a(b),e=a.data(b,"datepicker");d.hasClass(this.markerClassName)&&(c=b.nodeName.toLowerCase(),a.removeData(b,"datepicker"),"input"===c?(e.append.remove(),e.trigger.remove(),d.removeClass(this.markerClassName).off("focus",this._showDatepicker).off("keydown",this._doKeyDown).off("keypress",this._doKeyPress).off("keyup",this._doKeyUp)):"div"!==c&&"span"!==c||d.removeClass(this.markerClassName).empty(),g===e&&(g=null))},_enableDatepicker:function(b){var c,d,e=a(b),f=a.data(b,"datepicker");e.hasClass(this.markerClassName)&&(c=b.nodeName.toLowerCase(),"input"===c?(b.disabled=!1,f.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""})):"div"!==c&&"span"!==c||(d=e.children("."+this._inlineClass),d.children().removeClass("ui-state-disabled"),d.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!1)),this._disabledInputs=a.map(this._disabledInputs,function(a){return a===b?null:a}))},_disableDatepicker:function(b){var c,d,e=a(b),f=a.data(b,"datepicker");e.hasClass(this.markerClassName)&&(c=b.nodeName.toLowerCase(),"input"===c?(b.disabled=!0,f.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"})):"div"!==c&&"span"!==c||(d=e.children("."+this._inlineClass),d.children().addClass("ui-state-disabled"),d.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!0)),this._disabledInputs=a.map(this._disabledInputs,function(a){return a===b?null:a}),this._disabledInputs[this._disabledInputs.length]=b)},_isDisabledDatepicker:function(a){if(!a)return!1;for(var b=0;b<this._disabledInputs.length;b++)if(this._disabledInputs[b]===a)return!0;return!1},_getInst:function(b){try{return a.data(b,"datepicker")}catch(c){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(b,c,d){var e,g,h,i,j=this._getInst(b);return 2===arguments.length&&"string"==typeof c?"defaults"===c?a.extend({},a.datepicker._defaults):j?"all"===c?a.extend({},j.settings):this._get(j,c):null:(e=c||{},"string"==typeof c&&(e={},e[c]=d),void(j&&(this._curInst===j&&this._hideDatepicker(),g=this._getDateDatepicker(b,!0),h=this._getMinMaxDate(j,"min"),i=this._getMinMaxDate(j,"max"),f(j.settings,e),null!==h&&void 0!==e.dateFormat&&void 0===e.minDate&&(j.settings.minDate=this._formatDate(j,h)),null!==i&&void 0!==e.dateFormat&&void 0===e.maxDate&&(j.settings.maxDate=this._formatDate(j,i)),"disabled"in e&&(e.disabled?this._disableDatepicker(b):this._enableDatepicker(b)),this._attachments(a(b),j),this._autoSize(j),this._setDate(j,g),this._updateAlternate(j),this._updateDatepicker(j))))},_changeDatepicker:function(a,b,c){this._optionDatepicker(a,b,c)},_refreshDatepicker:function(a){var b=this._getInst(a);b&&this._updateDatepicker(b)},_setDateDatepicker:function(a,b){var c=this._getInst(a);c&&(this._setDate(c,b),this._updateDatepicker(c),this._updateAlternate(c))},_getDateDatepicker:function(a,b){var c=this._getInst(a);return c&&!c.inline&&this._setDateFromField(c,b),c?this._getDate(c):null},_doKeyDown:function(b){var c,d,e,f=a.datepicker._getInst(b.target),g=!0,h=f.dpDiv.is(".ui-datepicker-rtl");if(f._keyEvent=!0,a.datepicker._datepickerShowing)switch(b.keyCode){case 9:a.datepicker._hideDatepicker(),g=!1;break;case 13:return e=a("td."+a.datepicker._dayOverClass+":not(."+a.datepicker._currentClass+")",f.dpDiv),e[0]&&a.datepicker._selectDay(b.target,f.selectedMonth,f.selectedYear,e[0]),c=a.datepicker._get(f,"onSelect"),c?(d=a.datepicker._formatDate(f),c.apply(f.input?f.input[0]:null,[d,f])):a.datepicker._hideDatepicker(),!1;case 27:a.datepicker._hideDatepicker();break;case 33:a.datepicker._adjustDate(b.target,b.ctrlKey?-a.datepicker._get(f,"stepBigMonths"):-a.datepicker._get(f,"stepMonths"),"M");break;case 34:a.datepicker._adjustDate(b.target,b.ctrlKey?+a.datepicker._get(f,"stepBigMonths"):+a.datepicker._get(f,"stepMonths"),"M");break;case 35:(b.ctrlKey||b.metaKey)&&a.datepicker._clearDate(b.target),g=b.ctrlKey||b.metaKey;break;case 36:(b.ctrlKey||b.metaKey)&&a.datepicker._gotoToday(b.target),g=b.ctrlKey||b.metaKey;break;case 37:(b.ctrlKey||b.metaKey)&&a.datepicker._adjustDate(b.target,h?1:-1,"D"),g=b.ctrlKey||b.metaKey,b.originalEvent.altKey&&a.datepicker._adjustDate(b.target,b.ctrlKey?-a.datepicker._get(f,"stepBigMonths"):-a.datepicker._get(f,"stepMonths"),"M");break;case 38:(b.ctrlKey||b.metaKey)&&a.datepicker._adjustDate(b.target,-7,"D"),g=b.ctrlKey||b.metaKey;break;case 39:(b.ctrlKey||b.metaKey)&&a.datepicker._adjustDate(b.target,h?-1:1,"D"),g=b.ctrlKey||b.metaKey,b.originalEvent.altKey&&a.datepicker._adjustDate(b.target,b.ctrlKey?+a.datepicker._get(f,"stepBigMonths"):+a.datepicker._get(f,"stepMonths"),"M");break;case 40:(b.ctrlKey||b.metaKey)&&a.datepicker._adjustDate(b.target,7,"D"),g=b.ctrlKey||b.metaKey;break;default:g=!1}else 36===b.keyCode&&b.ctrlKey?a.datepicker._showDatepicker(this):g=!1;g&&(b.preventDefault(),b.stopPropagation())},_doKeyPress:function(b){var c,d,e=a.datepicker._getInst(b.target);if(a.datepicker._get(e,"constrainInput"))return c=a.datepicker._possibleChars(a.datepicker._get(e,"dateFormat")),d=String.fromCharCode(null==b.charCode?b.keyCode:b.charCode),b.ctrlKey||b.metaKey||d<" "||!c||c.indexOf(d)>-1},_doKeyUp:function(b){var c,d=a.datepicker._getInst(b.target);if(d.input.val()!==d.lastVal)try{c=a.datepicker.parseDate(a.datepicker._get(d,"dateFormat"),d.input?d.input.val():null,a.datepicker._getFormatConfig(d)),c&&(a.datepicker._setDateFromField(d),a.datepicker._updateAlternate(d),a.datepicker._updateDatepicker(d))}catch(e){}return!0},_showDatepicker:function(c){if(c=c.target||c,"input"!==c.nodeName.toLowerCase()&&(c=a("input",c.parentNode)[0]),!a.datepicker._isDisabledDatepicker(c)&&a.datepicker._lastInput!==c){var d,e,g,h,i,j,k;d=a.datepicker._getInst(c),a.datepicker._curInst&&a.datepicker._curInst!==d&&(a.datepicker._curInst.dpDiv.stop(!0,!0),d&&a.datepicker._datepickerShowing&&a.datepicker._hideDatepicker(a.datepicker._curInst.input[0])),e=a.datepicker._get(d,"beforeShow"),g=e?e.apply(c,[c,d]):{},g!==!1&&(f(d.settings,g),d.lastVal=null,a.datepicker._lastInput=c,a.datepicker._setDateFromField(d),a.datepicker._inDialog&&(c.value=""),a.datepicker._pos||(a.datepicker._pos=a.datepicker._findPos(c),a.datepicker._pos[1]+=c.offsetHeight),h=!1,a(c).parents().each(function(){return h|="fixed"===a(this).css("position"),!h}),i={left:a.datepicker._pos[0],top:a.datepicker._pos[1]},a.datepicker._pos=null,d.dpDiv.empty(),d.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),a.datepicker._updateDatepicker(d),i=a.datepicker._checkOffset(d,i,h),d.dpDiv.css({position:a.datepicker._inDialog&&a.blockUI?"static":h?"fixed":"absolute",display:"none",left:i.left+"px",top:i.top+"px"}),d.inline||(j=a.datepicker._get(d,"showAnim"),k=a.datepicker._get(d,"duration"),d.dpDiv.css("z-index",b(a(c))+1),a.datepicker._datepickerShowing=!0,a.effects&&a.effects.effect[j]?d.dpDiv.show(j,a.datepicker._get(d,"showOptions"),k):d.dpDiv[j||"show"](j?k:null),a.datepicker._shouldFocusInput(d)&&d.input.trigger("focus"),a.datepicker._curInst=d))}},_updateDatepicker:function(b){this.maxRows=4,g=b,b.dpDiv.empty().append(this._generateHTML(b)),this._attachHandlers(b);var c,d=this._getNumberOfMonths(b),f=d[1],h=17,i=b.dpDiv.find("."+this._dayOverClass+" a");i.length>0&&e.apply(i.get(0)),b.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),f>1&&b.dpDiv.addClass("ui-datepicker-multi-"+f).css("width",h*f+"em"),b.dpDiv[(1!==d[0]||1!==d[1]?"add":"remove")+"Class"]("ui-datepicker-multi"),b.dpDiv[(this._get(b,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),b===a.datepicker._curInst&&a.datepicker._datepickerShowing&&a.datepicker._shouldFocusInput(b)&&b.input.trigger("focus"),b.yearshtml&&(c=b.yearshtml,setTimeout(function(){c===b.yearshtml&&b.yearshtml&&b.dpDiv.find("select.ui-datepicker-year:first").replaceWith(b.yearshtml),c=b.yearshtml=null},0))},_shouldFocusInput:function(a){return a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&!a.input.is(":focus")},_checkOffset:function(b,c,d){var e=b.dpDiv.outerWidth(),f=b.dpDiv.outerHeight(),g=b.input?b.input.outerWidth():0,h=b.input?b.input.outerHeight():0,i=document.documentElement.clientWidth+(d?0:a(document).scrollLeft()),j=document.documentElement.clientHeight+(d?0:a(document).scrollTop());return c.left-=this._get(b,"isRTL")?e-g:0,c.left-=d&&c.left===b.input.offset().left?a(document).scrollLeft():0,c.top-=d&&c.top===b.input.offset().top+h?a(document).scrollTop():0,c.left-=Math.min(c.left,c.left+e>i&&i>e?Math.abs(c.left+e-i):0),c.top-=Math.min(c.top,c.top+f>j&&j>f?Math.abs(f+h):0),c},_findPos:function(b){for(var c,d=this._getInst(b),e=this._get(d,"isRTL");b&&("hidden"===b.type||1!==b.nodeType||a.expr.filters.hidden(b));)b=b[e?"previousSibling":"nextSibling"];return c=a(b).offset(),[c.left,c.top]},_hideDatepicker:function(b){var c,d,e,f,g=this._curInst;!g||b&&g!==a.data(b,"datepicker")||this._datepickerShowing&&(c=this._get(g,"showAnim"),d=this._get(g,"duration"),e=function(){a.datepicker._tidyDialog(g)},a.effects&&(a.effects.effect[c]||a.effects[c])?g.dpDiv.hide(c,a.datepicker._get(g,"showOptions"),d,e):g.dpDiv["slideDown"===c?"slideUp":"fadeIn"===c?"fadeOut":"hide"](c?d:null,e),c||e(),this._datepickerShowing=!1,f=this._get(g,"onClose"),f&&f.apply(g.input?g.input[0]:null,[g.input?g.input.val():"",g]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),a.blockUI&&(a.unblockUI(),a("body").append(this.dpDiv))),this._inDialog=!1)},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).off(".ui-datepicker-calendar")},_checkExternalClick:function(b){if(a.datepicker._curInst){var c=a(b.target),d=a.datepicker._getInst(c[0]);(c[0].id===a.datepicker._mainDivId||0!==c.parents("#"+a.datepicker._mainDivId).length||c.hasClass(a.datepicker.markerClassName)||c.closest("."+a.datepicker._triggerClass).length||!a.datepicker._datepickerShowing||a.datepicker._inDialog&&a.blockUI)&&(!c.hasClass(a.datepicker.markerClassName)||a.datepicker._curInst===d)||a.datepicker._hideDatepicker()}},_adjustDate:function(b,c,d){var e=a(b),f=this._getInst(e[0]);this._isDisabledDatepicker(e[0])||(this._adjustInstDate(f,c+("M"===d?this._get(f,"showCurrentAtPos"):0),d),this._updateDatepicker(f))},_gotoToday:function(b){var c,d=a(b),e=this._getInst(d[0]);this._get(e,"gotoCurrent")&&e.currentDay?(e.selectedDay=e.currentDay,e.drawMonth=e.selectedMonth=e.currentMonth,e.drawYear=e.selectedYear=e.currentYear):(c=new Date,e.selectedDay=c.getDate(),e.drawMonth=e.selectedMonth=c.getMonth(),e.drawYear=e.selectedYear=c.getFullYear()),this._notifyChange(e),this._adjustDate(d)},_selectMonthYear:function(b,c,d){var e=a(b),f=this._getInst(e[0]);f["selected"+("M"===d?"Month":"Year")]=f["draw"+("M"===d?"Month":"Year")]=parseInt(c.options[c.selectedIndex].value,10),this._notifyChange(f),this._adjustDate(e)},_selectDay:function(b,c,d,e){var f,g=a(b);a(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(g[0])||(f=this._getInst(g[0]),f.selectedDay=f.currentDay=a("a",e).html(),f.selectedMonth=f.currentMonth=c,f.selectedYear=f.currentYear=d,this._selectDate(b,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear)))},_clearDate:function(b){var c=a(b);this._selectDate(c,"")},_selectDate:function(b,c){var d,e=a(b),f=this._getInst(e[0]);c=null!=c?c:this._formatDate(f),f.input&&f.input.val(c),this._updateAlternate(f),d=this._get(f,"onSelect"),d?d.apply(f.input?f.input[0]:null,[c,f]):f.input&&f.input.trigger("change"),f.inline?this._updateDatepicker(f):(this._hideDatepicker(),this._lastInput=f.input[0],"object"!=typeof f.input[0]&&f.input.trigger("focus"),this._lastInput=null)},_updateAlternate:function(b){var c,d,e,f=this._get(b,"altField");f&&(c=this._get(b,"altFormat")||this._get(b,"dateFormat"),d=this._getDate(b),e=this.formatDate(c,d,this._getFormatConfig(b)),a(f).val(e))},noWeekends:function(a){var b=a.getDay();return[b>0&&b<6,""]},iso8601Week:function(a){var b,c=new Date(a.getTime());return c.setDate(c.getDate()+4-(c.getDay()||7)),b=c.getTime(),c.setMonth(0),c.setDate(1),Math.floor(Math.round((b-c)/864e5)/7)+1},parseDate:function(b,c,d){if(null==b||null==c)throw"Invalid arguments";if(c="object"==typeof c?c.toString():c+"",""===c)return null;var e,f,g,h,i=0,j=(d?d.shortYearCutoff:null)||this._defaults.shortYearCutoff,k="string"!=typeof j?j:(new Date).getFullYear()%100+parseInt(j,10),l=(d?d.dayNamesShort:null)||this._defaults.dayNamesShort,m=(d?d.dayNames:null)||this._defaults.dayNames,n=(d?d.monthNamesShort:null)||this._defaults.monthNamesShort,o=(d?d.monthNames:null)||this._defaults.monthNames,p=-1,q=-1,r=-1,s=-1,t=!1,u=function(a){var c=e+1<b.length&&b.charAt(e+1)===a;return c&&e++,c},v=function(a){var b=u(a),d="@"===a?14:"!"===a?20:"y"===a&&b?4:"o"===a?3:2,e="y"===a?d:1,f=new RegExp("^\\d{"+e+","+d+"}"),g=c.substring(i).match(f);if(!g)throw"Missing number at position "+i;return i+=g[0].length,parseInt(g[0],10)},w=function(b,d,e){var f=-1,g=a.map(u(b)?e:d,function(a,b){return[[b,a]]}).sort(function(a,b){return-(a[1].length-b[1].length)});if(a.each(g,function(a,b){var d=b[1];if(c.substr(i,d.length).toLowerCase()===d.toLowerCase())return f=b[0],i+=d.length,!1}),f!==-1)return f+1;throw"Unknown name at position "+i},x=function(){if(c.charAt(i)!==b.charAt(e))throw"Unexpected literal at position "+i;i++};for(e=0;e<b.length;e++)if(t)"'"!==b.charAt(e)||u("'")?x():t=!1;else switch(b.charAt(e)){case"d":r=v("d");break;case"D":w("D",l,m);break;case"o":s=v("o");break;case"m":q=v("m");break;case"M":q=w("M",n,o);break;case"y":p=v("y");break;case"@":h=new Date(v("@")),p=h.getFullYear(),q=h.getMonth()+1,r=h.getDate();break;case"!":h=new Date((v("!")-this._ticksTo1970)/1e4),p=h.getFullYear(),q=h.getMonth()+1,r=h.getDate();break;case"'":u("'")?x():t=!0;break;default:x()}if(i<c.length&&(g=c.substr(i),!/^\s+/.test(g)))throw"Extra/unparsed characters found in date: "+g;if(p===-1?p=(new Date).getFullYear():p<100&&(p+=(new Date).getFullYear()-(new Date).getFullYear()%100+(p<=k?0:-100)),s>-1)for(q=1,r=s;;){if(f=this._getDaysInMonth(p,q-1),r<=f)break;q++,r-=f}if(h=this._daylightSavingAdjust(new Date(p,q-1,r)),h.getFullYear()!==p||h.getMonth()+1!==q||h.getDate()!==r)throw"Invalid date";return h},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:24*(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*60*60*1e7,formatDate:function(a,b,c){if(!b)return"";var d,e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,g=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,h=(c?c.monthNames:null)||this._defaults.monthNames,i=function(b){var c=d+1<a.length&&a.charAt(d+1)===b;return c&&d++,c},j=function(a,b,c){var d=""+b;if(i(a))for(;d.length<c;)d="0"+d;return d},k=function(a,b,c,d){return i(a)?d[b]:c[b]},l="",m=!1;if(b)for(d=0;d<a.length;d++)if(m)"'"!==a.charAt(d)||i("'")?l+=a.charAt(d):m=!1;else switch(a.charAt(d)){case"d":l+=j("d",b.getDate(),2);break;case"D":l+=k("D",b.getDay(),e,f);break;case"o":l+=j("o",Math.round((new Date(b.getFullYear(),b.getMonth(),b.getDate()).getTime()-new Date(b.getFullYear(),0,0).getTime())/864e5),3);break;case"m":l+=j("m",b.getMonth()+1,2);break;case"M":l+=k("M",b.getMonth(),g,h);break;case"y":l+=i("y")?b.getFullYear():(b.getFullYear()%100<10?"0":"")+b.getFullYear()%100;break;case"@":l+=b.getTime();break;case"!":l+=1e4*b.getTime()+this._ticksTo1970;break;case"'":i("'")?l+="'":m=!0;break;default:l+=a.charAt(d)}return l},_possibleChars:function(a){var b,c="",d=!1,e=function(c){var d=b+1<a.length&&a.charAt(b+1)===c;return d&&b++,d};for(b=0;b<a.length;b++)if(d)"'"!==a.charAt(b)||e("'")?c+=a.charAt(b):d=!1;else switch(a.charAt(b)){case"d":case"m":case"y":case"@":c+="0123456789";break;case"D":case"M":return null;case"'":e("'")?c+="'":d=!0;break;default:c+=a.charAt(b)}return c},_get:function(a,b){return void 0!==a.settings[b]?a.settings[b]:this._defaults[b]},_setDateFromField:function(a,b){if(a.input.val()!==a.lastVal){var c=this._get(a,"dateFormat"),d=a.lastVal=a.input?a.input.val():null,e=this._getDefaultDate(a),f=e,g=this._getFormatConfig(a);try{f=this.parseDate(c,d,g)||e}catch(h){d=b?"":d}a.selectedDay=f.getDate(),a.drawMonth=a.selectedMonth=f.getMonth(),a.drawYear=a.selectedYear=f.getFullYear(),a.currentDay=d?f.getDate():0,a.currentMonth=d?f.getMonth():0,a.currentYear=d?f.getFullYear():0,this._adjustInstDate(a)}},_getDefaultDate:function(a){return this._restrictMinMax(a,this._determineDate(a,this._get(a,"defaultDate"),new Date))},_determineDate:function(b,c,d){var e=function(a){var b=new Date;return b.setDate(b.getDate()+a),b},f=function(c){try{return a.datepicker.parseDate(a.datepicker._get(b,"dateFormat"),c,a.datepicker._getFormatConfig(b))}catch(d){}for(var e=(c.toLowerCase().match(/^c/)?a.datepicker._getDate(b):null)||new Date,f=e.getFullYear(),g=e.getMonth(),h=e.getDate(),i=/([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,j=i.exec(c);j;){switch(j[2]||"d"){case"d":case"D":h+=parseInt(j[1],10);break;case"w":case"W":h+=7*parseInt(j[1],10);break;case"m":case"M":g+=parseInt(j[1],10),h=Math.min(h,a.datepicker._getDaysInMonth(f,g));break;case"y":case"Y":f+=parseInt(j[1],10),h=Math.min(h,a.datepicker._getDaysInMonth(f,g))}j=i.exec(c)}return new Date(f,g,h)},g=null==c||""===c?d:"string"==typeof c?f(c):"number"==typeof c?isNaN(c)?d:e(c):new Date(c.getTime());return g=g&&"Invalid Date"===g.toString()?d:g,g&&(g.setHours(0),g.setMinutes(0),g.setSeconds(0),g.setMilliseconds(0)),this._daylightSavingAdjust(g)},_daylightSavingAdjust:function(a){return a?(a.setHours(a.getHours()>12?a.getHours()+2:0),a):null},_setDate:function(a,b,c){var d=!b,e=a.selectedMonth,f=a.selectedYear,g=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=g.getDate(),a.drawMonth=a.selectedMonth=a.currentMonth=g.getMonth(),a.drawYear=a.selectedYear=a.currentYear=g.getFullYear(),e===a.selectedMonth&&f===a.selectedYear||c||this._notifyChange(a),this._adjustInstDate(a),a.input&&a.input.val(d?"":this._formatDate(a))},_getDate:function(a){var b=!a.currentYear||a.input&&""===a.input.val()?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return b},_attachHandlers:function(b){var c=this._get(b,"stepMonths"),d="#"+b.id.replace(/\\\\/g,"\\");b.dpDiv.find("[data-handler]").map(function(){var b={prev:function(){a.datepicker._adjustDate(d,-c,"M")},next:function(){a.datepicker._adjustDate(d,+c,"M")},hide:function(){a.datepicker._hideDatepicker()},today:function(){a.datepicker._gotoToday(d)},selectDay:function(){return a.datepicker._selectDay(d,+this.getAttribute("data-month"),+this.getAttribute("data-year"),this),!1},selectMonth:function(){return a.datepicker._selectMonthYear(d,this,"M"),!1},selectYear:function(){return a.datepicker._selectMonthYear(d,this,"Y"),!1}};a(this).on(this.getAttribute("data-event"),b[this.getAttribute("data-handler")])})},_generateHTML:function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O=new Date,P=this._daylightSavingAdjust(new Date(O.getFullYear(),O.getMonth(),O.getDate())),Q=this._get(a,"isRTL"),R=this._get(a,"showButtonPanel"),S=this._get(a,"hideIfNoPrevNext"),T=this._get(a,"navigationAsDateFormat"),U=this._getNumberOfMonths(a),V=this._get(a,"showCurrentAtPos"),W=this._get(a,"stepMonths"),X=1!==U[0]||1!==U[1],Y=this._daylightSavingAdjust(a.currentDay?new Date(a.currentYear,a.currentMonth,a.currentDay):new Date(9999,9,9)),Z=this._getMinMaxDate(a,"min"),$=this._getMinMaxDate(a,"max"),_=a.drawMonth-V,aa=a.drawYear;if(_<0&&(_+=12,aa--),$)for(b=this._daylightSavingAdjust(new Date($.getFullYear(),$.getMonth()-U[0]*U[1]+1,$.getDate())),b=Z&&b<Z?Z:b;this._daylightSavingAdjust(new Date(aa,_,1))>b;)_--,_<0&&(_=11,aa--);for(a.drawMonth=_,a.drawYear=aa,c=this._get(a,"prevText"),c=T?this.formatDate(c,this._daylightSavingAdjust(new Date(aa,_-W,1)),this._getFormatConfig(a)):c,d=this._canAdjustMonth(a,-1,aa,_)?"<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click' title='"+c+"'><span class='ui-icon ui-icon-circle-triangle-"+(Q?"e":"w")+"'>"+c+"</span></a>":S?"":"<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='"+c+"'><span class='ui-icon ui-icon-circle-triangle-"+(Q?"e":"w")+"'>"+c+"</span></a>",e=this._get(a,"nextText"),e=T?this.formatDate(e,this._daylightSavingAdjust(new Date(aa,_+W,1)),this._getFormatConfig(a)):e,f=this._canAdjustMonth(a,1,aa,_)?"<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click' title='"+e+"'><span class='ui-icon ui-icon-circle-triangle-"+(Q?"w":"e")+"'>"+e+"</span></a>":S?"":"<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='"+e+"'><span class='ui-icon ui-icon-circle-triangle-"+(Q?"w":"e")+"'>"+e+"</span></a>",g=this._get(a,"currentText"),h=this._get(a,"gotoCurrent")&&a.currentDay?Y:P,g=T?this.formatDate(g,h,this._getFormatConfig(a)):g,i=a.inline?"":"<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>"+this._get(a,"closeText")+"</button>",j=R?"<div class='ui-datepicker-buttonpane ui-widget-content'>"+(Q?i:"")+(this._isInRange(a,h)?"<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'>"+g+"</button>":"")+(Q?"":i)+"</div>":"",k=parseInt(this._get(a,"firstDay"),10),k=isNaN(k)?0:k,l=this._get(a,"showWeek"),m=this._get(a,"dayNames"),n=this._get(a,"dayNamesMin"),o=this._get(a,"monthNames"),p=this._get(a,"monthNamesShort"),q=this._get(a,"beforeShowDay"),r=this._get(a,"showOtherMonths"),s=this._get(a,"selectOtherMonths"),t=this._getDefaultDate(a),u="",w=0;w<U[0];w++){for(x="",this.maxRows=4,y=0;y<U[1];y++){if(z=this._daylightSavingAdjust(new Date(aa,_,a.selectedDay)),A=" ui-corner-all",B="",X){if(B+="<div class='ui-datepicker-group",U[1]>1)switch(y){case 0:B+=" ui-datepicker-group-first",A=" ui-corner-"+(Q?"right":"left");break;case U[1]-1:B+=" ui-datepicker-group-last",A=" ui-corner-"+(Q?"left":"right");break;default:B+=" ui-datepicker-group-middle",A=""}B+="'>"}for(B+="<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix"+A+"'>"+(/all|left/.test(A)&&0===w?Q?f:d:"")+(/all|right/.test(A)&&0===w?Q?d:f:"")+this._generateMonthYearHeader(a,_,aa,Z,$,w>0||y>0,o,p)+"</div><table class='ui-datepicker-calendar'><thead><tr>",C=l?"<th class='ui-datepicker-week-col'>"+this._get(a,"weekHeader")+"</th>":"",v=0;v<7;v++)D=(v+k)%7,C+="<th scope='col'"+((v+k+6)%7>=5?" class='ui-datepicker-week-end'":"")+"><span title='"+m[D]+"'>"+n[D]+"</span></th>";for(B+=C+"</tr></thead><tbody>",E=this._getDaysInMonth(aa,_),aa===a.selectedYear&&_===a.selectedMonth&&(a.selectedDay=Math.min(a.selectedDay,E)),F=(this._getFirstDayOfMonth(aa,_)-k+7)%7,G=Math.ceil((F+E)/7),H=X&&this.maxRows>G?this.maxRows:G,this.maxRows=H,I=this._daylightSavingAdjust(new Date(aa,_,1-F)),J=0;J<H;J++){for(B+="<tr>",K=l?"<td class='ui-datepicker-week-col'>"+this._get(a,"calculateWeek")(I)+"</td>":"",v=0;v<7;v++)L=q?q.apply(a.input?a.input[0]:null,[I]):[!0,""],M=I.getMonth()!==_,N=M&&!s||!L[0]||Z&&I<Z||$&&I>$,K+="<td class='"+((v+k+6)%7>=5?" ui-datepicker-week-end":"")+(M?" ui-datepicker-other-month":"")+(I.getTime()===z.getTime()&&_===a.selectedMonth&&a._keyEvent||t.getTime()===I.getTime()&&t.getTime()===z.getTime()?" "+this._dayOverClass:"")+(N?" "+this._unselectableClass+" ui-state-disabled":"")+(M&&!r?"":" "+L[1]+(I.getTime()===Y.getTime()?" "+this._currentClass:"")+(I.getTime()===P.getTime()?" ui-datepicker-today":""))+"'"+(M&&!r||!L[2]?"":" title='"+L[2].replace(/'/g,"'")+"'")+(N?"":" data-handler='selectDay' data-event='click' data-month='"+I.getMonth()+"' data-year='"+I.getFullYear()+"'")+">"+(M&&!r?" ":N?"<span class='ui-state-default'>"+I.getDate()+"</span>":"<a class='ui-state-default"+(I.getTime()===P.getTime()?" ui-state-highlight":"")+(I.getTime()===Y.getTime()?" ui-state-active":"")+(M?" ui-priority-secondary":"")+"' href='#'>"+I.getDate()+"</a>")+"</td>",I.setDate(I.getDate()+1),I=this._daylightSavingAdjust(I);B+=K+"</tr>"}_++,_>11&&(_=0,aa++),B+="</tbody></table>"+(X?"</div>"+(U[0]>0&&y===U[1]-1?"<div class='ui-datepicker-row-break'></div>":""):""),x+=B}u+=x}return u+=j,a._keyEvent=!1,u},_generateMonthYearHeader:function(a,b,c,d,e,f,g,h){var i,j,k,l,m,n,o,p,q=this._get(a,"changeMonth"),r=this._get(a,"changeYear"),s=this._get(a,"showMonthAfterYear"),t="<div class='ui-datepicker-title'>",u="";if(f||!q)u+="<span class='ui-datepicker-month'>"+g[b]+"</span>";else{ +for(i=d&&d.getFullYear()===c,j=e&&e.getFullYear()===c,u+="<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>",k=0;k<12;k++)(!i||k>=d.getMonth())&&(!j||k<=e.getMonth())&&(u+="<option value='"+k+"'"+(k===b?" selected='selected'":"")+">"+h[k]+"</option>");u+="</select>"}if(s||(t+=u+(!f&&q&&r?"":" ")),!a.yearshtml)if(a.yearshtml="",f||!r)t+="<span class='ui-datepicker-year'>"+c+"</span>";else{for(l=this._get(a,"yearRange").split(":"),m=(new Date).getFullYear(),n=function(a){var b=a.match(/c[+\-].*/)?c+parseInt(a.substring(1),10):a.match(/[+\-].*/)?m+parseInt(a,10):parseInt(a,10);return isNaN(b)?m:b},o=n(l[0]),p=Math.max(o,n(l[1]||"")),o=d?Math.max(o,d.getFullYear()):o,p=e?Math.min(p,e.getFullYear()):p,a.yearshtml+="<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";o<=p;o++)a.yearshtml+="<option value='"+o+"'"+(o===c?" selected='selected'":"")+">"+o+"</option>";a.yearshtml+="</select>",t+=a.yearshtml,a.yearshtml=null}return t+=this._get(a,"yearSuffix"),s&&(t+=(!f&&q&&r?"":" ")+u),t+="</div>"},_adjustInstDate:function(a,b,c){var d=a.selectedYear+("Y"===c?b:0),e=a.selectedMonth+("M"===c?b:0),f=Math.min(a.selectedDay,this._getDaysInMonth(d,e))+("D"===c?b:0),g=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(d,e,f)));a.selectedDay=g.getDate(),a.drawMonth=a.selectedMonth=g.getMonth(),a.drawYear=a.selectedYear=g.getFullYear(),"M"!==c&&"Y"!==c||this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min"),d=this._getMinMaxDate(a,"max"),e=c&&b<c?c:b;return d&&e>d?d:e},_notifyChange:function(a){var b=this._get(a,"onChangeMonthYear");b&&b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){var b=this._get(a,"numberOfMonths");return null==b?[1,1]:"number"==typeof b?[1,b]:b},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return new Date(a,b,1).getDay()},_canAdjustMonth:function(a,b,c,d){var e=this._getNumberOfMonths(a),f=this._daylightSavingAdjust(new Date(c,d+(b<0?b:e[0]*e[1]),1));return b<0&&f.setDate(this._getDaysInMonth(f.getFullYear(),f.getMonth())),this._isInRange(a,f)},_isInRange:function(a,b){var c,d,e=this._getMinMaxDate(a,"min"),f=this._getMinMaxDate(a,"max"),g=null,h=null,i=this._get(a,"yearRange");return i&&(c=i.split(":"),d=(new Date).getFullYear(),g=parseInt(c[0],10),h=parseInt(c[1],10),c[0].match(/[+\-].*/)&&(g+=d),c[1].match(/[+\-].*/)&&(h+=d)),(!e||b.getTime()>=e.getTime())&&(!f||b.getTime()<=f.getTime())&&(!g||b.getFullYear()>=g)&&(!h||b.getFullYear()<=h)},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");return b="string"!=typeof b?b:(new Date).getFullYear()%100+parseInt(b,10),{shortYearCutoff:b,dayNamesShort:this._get(a,"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,d){b||(a.currentDay=a.selectedDay,a.currentMonth=a.selectedMonth,a.currentYear=a.selectedYear);var e=b?"object"==typeof b?b:this._daylightSavingAdjust(new Date(d,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),e,this._getFormatConfig(a))}}),a.fn.datepicker=function(b){if(!this.length)return this;a.datepicker.initialized||(a(document).on("mousedown",a.datepicker._checkExternalClick),a.datepicker.initialized=!0),0===a("#"+a.datepicker._mainDivId).length&&a("body").append(a.datepicker.dpDiv);var c=Array.prototype.slice.call(arguments,1);return"string"!=typeof b||"isDisabled"!==b&&"getDate"!==b&&"widget"!==b?"option"===b&&2===arguments.length&&"string"==typeof arguments[1]?a.datepicker["_"+b+"Datepicker"].apply(a.datepicker,[this[0]].concat(c)):this.each(function(){"string"==typeof b?a.datepicker["_"+b+"Datepicker"].apply(a.datepicker,[this].concat(c)):a.datepicker._attachDatepicker(this,b)}):a.datepicker["_"+b+"Datepicker"].apply(a.datepicker,[this[0]].concat(c))},a.datepicker=new c,a.datepicker.initialized=!1,a.datepicker.uuid=(new Date).getTime(),a.datepicker.version="1.12.1",a.datepicker}); \ No newline at end of file diff --git a/web/modules/jquery_ui_datepicker/jquery_ui_datepicker.info.yml b/web/modules/jquery_ui_datepicker/jquery_ui_datepicker.info.yml new file mode 100644 index 0000000000..42af1c4a75 --- /dev/null +++ b/web/modules/jquery_ui_datepicker/jquery_ui_datepicker.info.yml @@ -0,0 +1,12 @@ +name: jQuery UI Datepicker +type: module +description: 'Provides jQuery UI Datepicker library.' +package: jQuery UI +core_version_requirement: ^8 || ^9 +dependencies: + - jquery_ui:jquery_ui + +# Information added by Drupal.org packaging script on 2020-05-17 +version: '8.x-1.0' +project: 'jquery_ui_datepicker' +datestamp: 1589684303 diff --git a/web/modules/jquery_ui_datepicker/jquery_ui_datepicker.libraries.yml b/web/modules/jquery_ui_datepicker/jquery_ui_datepicker.libraries.yml new file mode 100644 index 0000000000..6b25d15acc --- /dev/null +++ b/web/modules/jquery_ui_datepicker/jquery_ui_datepicker.libraries.yml @@ -0,0 +1,13 @@ +datepicker: + version: 1.12.1 + license: + name: Public Domain + url: https://github.com/jquery/jquery-ui/blob/1.12.1/LICENSE.txt + gpl-compatible: true + js: + jquery.ui/ui/widgets/datepicker-min.js: { minified: true } + css: + component: + jquery.ui/themes/base/datepicker.css: {} + dependencies: + - jquery_ui/core diff --git a/web/modules/jquery_ui_datepicker/jquery_ui_datepicker.module b/web/modules/jquery_ui_datepicker/jquery_ui_datepicker.module new file mode 100644 index 0000000000..33dad2fa25 --- /dev/null +++ b/web/modules/jquery_ui_datepicker/jquery_ui_datepicker.module @@ -0,0 +1,23 @@ +<?php + +/** + * @file + * Contains jquery_ui_datepicker.module. + */ + +use Drupal\Core\Routing\RouteMatchInterface; + +/** + * Implements hook_help(). + */ +function jquery_ui_datepicker_help($route_name, RouteMatchInterface $route_match) { + switch ($route_name) { + case 'help.page.jquery_ui_datepicker': + $output = '<h3>' . t('About') . '</h3>'; + $output .= '<p>' . t('Drupal 8 includes jQuery UI in core, however it is no longer actively maintained and has been marked deprecated. This module provides the jQuery UI Datepicker library for any themes and modules that require it.') . '</p>'; + $output .= '<p>' . t('For more information about the deprecation of jQuery UI, see this <a href=":change-record">change record</a>', [':change-record' => 'https://www.drupal.org/node/3067969']) . '</p>'; + $output .= '<p>' . t('Visit the <a href=":project_link">jQuery UI Datepicker project page</a> on Drupal.org for more information on this module.', [':project_link' => 'https://www.drupal.org/project/jquery_ui_datepicker']) . '</p>'; + + return $output; + } +} diff --git a/web/modules/jquery_ui_slider/LICENSE.txt b/web/modules/jquery_ui_slider/LICENSE.txt new file mode 100644 index 0000000000..d159169d10 --- /dev/null +++ b/web/modules/jquery_ui_slider/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/web/modules/jquery_ui_slider/README.md b/web/modules/jquery_ui_slider/README.md new file mode 100644 index 0000000000..52b45d5817 --- /dev/null +++ b/web/modules/jquery_ui_slider/README.md @@ -0,0 +1,39 @@ +# jQuery UI Slider + +Drupal 8 includes jQuery UI in core, however it is no longer actively +maintained and has been marked deprecated. This module provides the +jQuery UI Slider library for any themes and modules that require it. + + - jQuery UI [Slider documentation](https://jqueryui.com/slider/) + - jQuery UI [Slider API documentation](https://api.jqueryui.com/slider/) + +**Caution**: jQuery UI was deprecated from core because it is no longer +actively maintained, and has been marked “End of Life” by the OpenJS +Foundation. It is not recommended to depend on jQuery UI in your own +code, and instead to select a replacement solution as soon as possible. + +## Instructions + +1. Install this module and the jQuery UI module. +2. Change any references in your theme or module from + `core/jquery.ui.slider` to `jquery_ui_slider/slider` + +### Requirements + + - [jQuery UI](https://www.drupal.org/project/jquery_ui) + +### Related modules + + - [jQuery UI Accordion](https://www.drupal.org/project/jquery_ui_accordion) + - [jQuery UI Button](https://www.drupal.org/project/jquery_ui_button) + - [jQuery UI Checkboxradio](https://www.drupal.org/project/jquery_ui_checkboxradio) + - [jQuery UI Controlgroup](https://www.drupal.org/project/jquery_ui_controlgroup) + - [jQuery UI Draggable](https://www.drupal.org/project/jquery_ui_draggable) + - [jQuery UI Droppable](https://www.drupal.org/project/jquery_ui_droppable) + - [jQuery UI Effects](https://www.drupal.org/project/jquery_ui_effects) + - [jQuery UI Menu](https://www.drupal.org/project/jquery_ui_menu) + - [jQuery UI Progressbar](https://www.drupal.org/project/jquery_ui_progressbar) + - [jQuery UI Selectable](https://www.drupal.org/project/jquery_ui_selectable) + - [jQuery UI Selectmenu](https://www.drupal.org/project/jquery_ui_selectmenu) + - [jQuery UI Spinner](https://www.drupal.org/project/jquery_ui_spinner) + - [jQuery UI Tooltip](https://www.drupal.org/project/jquery_ui_tooltip) diff --git a/web/modules/jquery_ui_slider/jquery.ui/themes/base/slider.css b/web/modules/jquery_ui_slider/jquery.ui/themes/base/slider.css new file mode 100644 index 0000000000..e1f5030eea --- /dev/null +++ b/web/modules/jquery_ui_slider/jquery.ui/themes/base/slider.css @@ -0,0 +1,75 @@ +/*! + * jQuery UI Slider 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/slider/#theming + */ +.ui-slider { + position: relative; + text-align: left; +} +.ui-slider .ui-slider-handle { + position: absolute; + z-index: 2; + width: 1.2em; + height: 1.2em; + cursor: default; + -ms-touch-action: none; + touch-action: none; +} +.ui-slider .ui-slider-range { + position: absolute; + z-index: 1; + font-size: .7em; + display: block; + border: 0; + background-position: 0 0; +} + +/* support: IE8 - See #6727 */ +.ui-slider.ui-state-disabled .ui-slider-handle, +.ui-slider.ui-state-disabled .ui-slider-range { + filter: inherit; +} + +.ui-slider-horizontal { + height: .8em; +} +.ui-slider-horizontal .ui-slider-handle { + top: -.3em; + margin-left: -.6em; +} +.ui-slider-horizontal .ui-slider-range { + top: 0; + height: 100%; +} +.ui-slider-horizontal .ui-slider-range-min { + left: 0; +} +.ui-slider-horizontal .ui-slider-range-max { + right: 0; +} + +.ui-slider-vertical { + width: .8em; + height: 100px; +} +.ui-slider-vertical .ui-slider-handle { + left: -.3em; + margin-left: 0; + margin-bottom: -.6em; +} +.ui-slider-vertical .ui-slider-range { + left: 0; + width: 100%; +} +.ui-slider-vertical .ui-slider-range-min { + bottom: 0; +} +.ui-slider-vertical .ui-slider-range-max { + top: 0; +} diff --git a/web/modules/jquery_ui_slider/jquery.ui/ui/widgets/slider-min.js b/web/modules/jquery_ui_slider/jquery.ui/ui/widgets/slider-min.js new file mode 100644 index 0000000000..25fbdc2dc3 --- /dev/null +++ b/web/modules/jquery_ui_slider/jquery.ui/ui/widgets/slider-min.js @@ -0,0 +1,4 @@ +/*! jQuery UI - v1.12.1 - 2017-03-31 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./mouse","../keycode","../version","../widget"],a):a(jQuery)}(function(a){return a.widget("ui.slider",a.ui.mouse,{version:"1.12.1",widgetEventPrefix:"slide",options:{animate:!1,classes:{"ui-slider":"ui-corner-all","ui-slider-handle":"ui-corner-all","ui-slider-range":"ui-corner-all ui-widget-header"},distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null,change:null,slide:null,start:null,stop:null},numPages:5,_create:function(){this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this._calculateNewMax(),this._addClass("ui-slider ui-slider-"+this.orientation,"ui-widget ui-widget-content"),this._refresh(),this._animateOff=!1},_refresh:function(){this._createRange(),this._createHandles(),this._setupEvents(),this._refreshValue()},_createHandles:function(){var b,c,d=this.options,e=this.element.find(".ui-slider-handle"),f="<span tabindex='0'></span>",g=[];for(c=d.values&&d.values.length||1,e.length>c&&(e.slice(c).remove(),e=e.slice(0,c)),b=e.length;b<c;b++)g.push(f);this.handles=e.add(a(g.join("")).appendTo(this.element)),this._addClass(this.handles,"ui-slider-handle","ui-state-default"),this.handle=this.handles.eq(0),this.handles.each(function(b){a(this).data("ui-slider-handle-index",b).attr("tabIndex",0)})},_createRange:function(){var b=this.options;b.range?(b.range===!0&&(b.values?b.values.length&&2!==b.values.length?b.values=[b.values[0],b.values[0]]:a.isArray(b.values)&&(b.values=b.values.slice(0)):b.values=[this._valueMin(),this._valueMin()]),this.range&&this.range.length?(this._removeClass(this.range,"ui-slider-range-min ui-slider-range-max"),this.range.css({left:"",bottom:""})):(this.range=a("<div>").appendTo(this.element),this._addClass(this.range,"ui-slider-range")),"min"!==b.range&&"max"!==b.range||this._addClass(this.range,"ui-slider-range-"+b.range)):(this.range&&this.range.remove(),this.range=null)},_setupEvents:function(){this._off(this.handles),this._on(this.handles,this._handleEvents),this._hoverable(this.handles),this._focusable(this.handles)},_destroy:function(){this.handles.remove(),this.range&&this.range.remove(),this._mouseDestroy()},_mouseCapture:function(b){var c,d,e,f,g,h,i,j,k=this,l=this.options;return!l.disabled&&(this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()},this.elementOffset=this.element.offset(),c={x:b.pageX,y:b.pageY},d=this._normValueFromMouse(c),e=this._valueMax()-this._valueMin()+1,this.handles.each(function(b){var c=Math.abs(d-k.values(b));(e>c||e===c&&(b===k._lastChangedValue||k.values(b)===l.min))&&(e=c,f=a(this),g=b)}),h=this._start(b,g),h!==!1&&(this._mouseSliding=!0,this._handleIndex=g,this._addClass(f,null,"ui-state-active"),f.trigger("focus"),i=f.offset(),j=!a(b.target).parents().addBack().is(".ui-slider-handle"),this._clickOffset=j?{left:0,top:0}:{left:b.pageX-i.left-f.width()/2,top:b.pageY-i.top-f.height()/2-(parseInt(f.css("borderTopWidth"),10)||0)-(parseInt(f.css("borderBottomWidth"),10)||0)+(parseInt(f.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(b,g,d),this._animateOff=!0,!0))},_mouseStart:function(){return!0},_mouseDrag:function(a){var b={x:a.pageX,y:a.pageY},c=this._normValueFromMouse(b);return this._slide(a,this._handleIndex,c),!1},_mouseStop:function(a){return this._removeClass(this.handles,null,"ui-state-active"),this._mouseSliding=!1,this._stop(a,this._handleIndex),this._change(a,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1,!1},_detectOrientation:function(){this.orientation="vertical"===this.options.orientation?"vertical":"horizontal"},_normValueFromMouse:function(a){var b,c,d,e,f;return"horizontal"===this.orientation?(b=this.elementSize.width,c=a.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(b=this.elementSize.height,c=a.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),d=c/b,d>1&&(d=1),d<0&&(d=0),"vertical"===this.orientation&&(d=1-d),e=this._valueMax()-this._valueMin(),f=this._valueMin()+d*e,this._trimAlignValue(f)},_uiHash:function(a,b,c){var d={handle:this.handles[a],handleIndex:a,value:void 0!==b?b:this.value()};return this._hasMultipleValues()&&(d.value=void 0!==b?b:this.values(a),d.values=c||this.values()),d},_hasMultipleValues:function(){return this.options.values&&this.options.values.length},_start:function(a,b){return this._trigger("start",a,this._uiHash(b))},_slide:function(a,b,c){var d,e,f=this.value(),g=this.values();this._hasMultipleValues()&&(e=this.values(b?0:1),f=this.values(b),2===this.options.values.length&&this.options.range===!0&&(c=0===b?Math.min(e,c):Math.max(e,c)),g[b]=c),c!==f&&(d=this._trigger("slide",a,this._uiHash(b,c,g)),d!==!1&&(this._hasMultipleValues()?this.values(b,c):this.value(c)))},_stop:function(a,b){this._trigger("stop",a,this._uiHash(b))},_change:function(a,b){this._keySliding||this._mouseSliding||(this._lastChangedValue=b,this._trigger("change",a,this._uiHash(b)))},value:function(a){return arguments.length?(this.options.value=this._trimAlignValue(a),this._refreshValue(),void this._change(null,0)):this._value()},values:function(b,c){var d,e,f;if(arguments.length>1)return this.options.values[b]=this._trimAlignValue(c),this._refreshValue(),void this._change(null,b);if(!arguments.length)return this._values();if(!a.isArray(arguments[0]))return this._hasMultipleValues()?this._values(b):this.value();for(d=this.options.values,e=arguments[0],f=0;f<d.length;f+=1)d[f]=this._trimAlignValue(e[f]),this._change(null,f);this._refreshValue()},_setOption:function(b,c){var d,e=0;switch("range"===b&&this.options.range===!0&&("min"===c?(this.options.value=this._values(0),this.options.values=null):"max"===c&&(this.options.value=this._values(this.options.values.length-1),this.options.values=null)),a.isArray(this.options.values)&&(e=this.options.values.length),this._super(b,c),b){case"orientation":this._detectOrientation(),this._removeClass("ui-slider-horizontal ui-slider-vertical")._addClass("ui-slider-"+this.orientation),this._refreshValue(),this.options.range&&this._refreshRange(c),this.handles.css("horizontal"===c?"bottom":"left","");break;case"value":this._animateOff=!0,this._refreshValue(),this._change(null,0),this._animateOff=!1;break;case"values":for(this._animateOff=!0,this._refreshValue(),d=e-1;d>=0;d--)this._change(null,d);this._animateOff=!1;break;case"step":case"min":case"max":this._animateOff=!0,this._calculateNewMax(),this._refreshValue(),this._animateOff=!1;break;case"range":this._animateOff=!0,this._refresh(),this._animateOff=!1}},_setOptionDisabled:function(a){this._super(a),this._toggleClass(null,"ui-state-disabled",!!a)},_value:function(){var a=this.options.value;return a=this._trimAlignValue(a)},_values:function(a){var b,c,d;if(arguments.length)return b=this.options.values[a],b=this._trimAlignValue(b);if(this._hasMultipleValues()){for(c=this.options.values.slice(),d=0;d<c.length;d+=1)c[d]=this._trimAlignValue(c[d]);return c}return[]},_trimAlignValue:function(a){if(a<=this._valueMin())return this._valueMin();if(a>=this._valueMax())return this._valueMax();var b=this.options.step>0?this.options.step:1,c=(a-this._valueMin())%b,d=a-c;return 2*Math.abs(c)>=b&&(d+=c>0?b:-b),parseFloat(d.toFixed(5))},_calculateNewMax:function(){var a=this.options.max,b=this._valueMin(),c=this.options.step,d=Math.round((a-b)/c)*c;a=d+b,a>this.options.max&&(a-=c),this.max=parseFloat(a.toFixed(this._precision()))},_precision:function(){var a=this._precisionOf(this.options.step);return null!==this.options.min&&(a=Math.max(a,this._precisionOf(this.options.min))),a},_precisionOf:function(a){var b=a.toString(),c=b.indexOf(".");return c===-1?0:b.length-c-1},_valueMin:function(){return this.options.min},_valueMax:function(){return this.max},_refreshRange:function(a){"vertical"===a&&this.range.css({width:"",left:""}),"horizontal"===a&&this.range.css({height:"",bottom:""})},_refreshValue:function(){var b,c,d,e,f,g=this.options.range,h=this.options,i=this,j=!this._animateOff&&h.animate,k={};this._hasMultipleValues()?this.handles.each(function(d){c=(i.values(d)-i._valueMin())/(i._valueMax()-i._valueMin())*100,k["horizontal"===i.orientation?"left":"bottom"]=c+"%",a(this).stop(1,1)[j?"animate":"css"](k,h.animate),i.options.range===!0&&("horizontal"===i.orientation?(0===d&&i.range.stop(1,1)[j?"animate":"css"]({left:c+"%"},h.animate),1===d&&i.range[j?"animate":"css"]({width:c-b+"%"},{queue:!1,duration:h.animate})):(0===d&&i.range.stop(1,1)[j?"animate":"css"]({bottom:c+"%"},h.animate),1===d&&i.range[j?"animate":"css"]({height:c-b+"%"},{queue:!1,duration:h.animate}))),b=c}):(d=this.value(),e=this._valueMin(),f=this._valueMax(),c=f!==e?(d-e)/(f-e)*100:0,k["horizontal"===this.orientation?"left":"bottom"]=c+"%",this.handle.stop(1,1)[j?"animate":"css"](k,h.animate),"min"===g&&"horizontal"===this.orientation&&this.range.stop(1,1)[j?"animate":"css"]({width:c+"%"},h.animate),"max"===g&&"horizontal"===this.orientation&&this.range.stop(1,1)[j?"animate":"css"]({width:100-c+"%"},h.animate),"min"===g&&"vertical"===this.orientation&&this.range.stop(1,1)[j?"animate":"css"]({height:c+"%"},h.animate),"max"===g&&"vertical"===this.orientation&&this.range.stop(1,1)[j?"animate":"css"]({height:100-c+"%"},h.animate))},_handleEvents:{keydown:function(b){var c,d,e,f,g=a(b.target).data("ui-slider-handle-index");switch(b.keyCode){case a.ui.keyCode.HOME:case a.ui.keyCode.END:case a.ui.keyCode.PAGE_UP:case a.ui.keyCode.PAGE_DOWN:case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:if(b.preventDefault(),!this._keySliding&&(this._keySliding=!0,this._addClass(a(b.target),null,"ui-state-active"),c=this._start(b,g),c===!1))return}switch(f=this.options.step,d=e=this._hasMultipleValues()?this.values(g):this.value(),b.keyCode){case a.ui.keyCode.HOME:e=this._valueMin();break;case a.ui.keyCode.END:e=this._valueMax();break;case a.ui.keyCode.PAGE_UP:e=this._trimAlignValue(d+(this._valueMax()-this._valueMin())/this.numPages);break;case a.ui.keyCode.PAGE_DOWN:e=this._trimAlignValue(d-(this._valueMax()-this._valueMin())/this.numPages);break;case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:if(d===this._valueMax())return;e=this._trimAlignValue(d+f);break;case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:if(d===this._valueMin())return;e=this._trimAlignValue(d-f)}this._slide(b,g,e)},keyup:function(b){var c=a(b.target).data("ui-slider-handle-index");this._keySliding&&(this._keySliding=!1,this._stop(b,c),this._change(b,c),this._removeClass(a(b.target),null,"ui-state-active"))}}})}); \ No newline at end of file diff --git a/web/modules/jquery_ui_slider/jquery_ui_slider.info.yml b/web/modules/jquery_ui_slider/jquery_ui_slider.info.yml new file mode 100644 index 0000000000..56c7bc4c95 --- /dev/null +++ b/web/modules/jquery_ui_slider/jquery_ui_slider.info.yml @@ -0,0 +1,12 @@ +name: jQuery UI Slider +type: module +description: 'Provides jQuery UI Slider library.' +package: jQuery UI +core_version_requirement: ^8 || ^9 +dependencies: + - jquery_ui:jquery_ui + +# Information added by Drupal.org packaging script on 2020-03-13 +version: '8.x-1.1' +project: 'jquery_ui_slider' +datestamp: 1584107819 diff --git a/web/modules/jquery_ui_slider/jquery_ui_slider.libraries.yml b/web/modules/jquery_ui_slider/jquery_ui_slider.libraries.yml new file mode 100644 index 0000000000..473238cc87 --- /dev/null +++ b/web/modules/jquery_ui_slider/jquery_ui_slider.libraries.yml @@ -0,0 +1,14 @@ +slider: + license: + name: Public Domain + url: https://github.com/jquery/jquery-ui/blob/1.12.1/LICENSE.txt + gpl-compatible: true + js: + jquery.ui/ui/widgets/slider-min.js: { minified: true } + css: + component: + jquery.ui/themes/base/slider.css: {} + dependencies: + - jquery_ui/core + - jquery_ui/mouse + - jquery_ui/widget diff --git a/web/modules/jquery_ui_slider/jquery_ui_slider.module b/web/modules/jquery_ui_slider/jquery_ui_slider.module new file mode 100644 index 0000000000..6a9523e027 --- /dev/null +++ b/web/modules/jquery_ui_slider/jquery_ui_slider.module @@ -0,0 +1,23 @@ +<?php + +/** + * @file + * Contains jquery_ui_slider.module. + */ + +use Drupal\Core\Routing\RouteMatchInterface; + +/** + * Implements hook_help(). + */ +function jquery_ui_slider_help($route_name, RouteMatchInterface $route_match) { + switch ($route_name) { + case 'help.page.jquery_ui_slider': + $output = '<h3>' . t('About') . '</h3>'; + $output .= '<p>' . t('Drupal 8 includes jQuery UI in core, however it is no longer actively maintained and has been marked deprecated. This module provides the jQuery UI Slider library for any themes and modules that require it.') . '</p>'; + $output .= '<p>' . t('For more information about the deprecation of jQuery UI, see this <a href=":change-record">change record</a>', [':change-record' => 'https://www.drupal.org/node/3067969']) . '</p>'; + $output .= '<p>' . t('Visit the <a href=":project_link">jQuery UI Slider project page</a> on Drupal.org for more information on this module.', [':project_link' => 'https://www.drupal.org/project/jquery_ui_slider']) . '</p>'; + + return $output; + } +} diff --git a/web/modules/jquery_ui_touch_punch/LICENSE.txt b/web/modules/jquery_ui_touch_punch/LICENSE.txt new file mode 100644 index 0000000000..d159169d10 --- /dev/null +++ b/web/modules/jquery_ui_touch_punch/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/web/modules/jquery_ui_touch_punch/README.md b/web/modules/jquery_ui_touch_punch/README.md new file mode 100644 index 0000000000..e29294bc77 --- /dev/null +++ b/web/modules/jquery_ui_touch_punch/README.md @@ -0,0 +1,43 @@ +# jQuery UI Touch Punch + +Drupal 8 includes jQuery UI in core, however it is no longer actively +maintained and has been marked deprecated. This module provides the +jQuery UI Touch Punch library for any themes and modules that require it. + + - jQuery UI [Touch Punch documentation](http://touchpunch.furf.com/) + - jQuery UI [Touch Punch API + documentation](https://github.com/furf/jquery-ui-touch-punch) + +**Caution**: jQuery UI was deprecated from core because it is no longer +actively maintained, and has been marked “End of Life” by the OpenJS +Foundation. It is not recommended to depend on jQuery UI in your own +code, and instead to select a replacement solution as soon as possible. + +## Instructions + +1. Download this module and the jQuery UI module. +2. [Download Jquery Touch Punch](https://github.com/furf/jquery-ui-touch-punch) library and place it in the libraries folder (<drupal root>/libraries). You need the full library which is easily available from the Github repo (the full path to the required js file should be: <drupal root>/libraries/jquery-ui-touch-punch/jquery.ui.touch-punch.min.js). +3. Install module the [usual way](https://www.drupal.org/documentation/install/modules-themes/modules-8). +4. Change any references in your theme or module from + `core/jquery.ui.touch-punch` to `jquery_ui_touch_punch/touch-punch` + +### Requirements + + - [jQuery UI](https://www.drupal.org/project/jquery_ui) + +### Related modules + + - [jQuery UI Accordion](https://www.drupal.org/project/jquery_ui_accordion) + - [jQuery UI Button](https://www.drupal.org/project/jquery_ui_button) + - [jQuery UI Checkboxradio](https://www.drupal.org/project/jquery_ui_checkboxradio) + - [jQuery UI Controlgroup](https://www.drupal.org/project/jquery_ui_controlgroup) + - [jQuery UI Draggable](https://www.drupal.org/project/jquery_ui_draggable) + - [jQuery UI Droppable](https://www.drupal.org/project/jquery_ui_droppable) + - [jQuery UI Effects](https://www.drupal.org/project/jquery_ui_effects) + - [jQuery UI Menu](https://www.drupal.org/project/jquery_ui_menu) + - [jQuery UI Progressbar](https://www.drupal.org/project/jquery_ui_progressbar) + - [jQuery UI Selectable](https://www.drupal.org/project/jquery_ui_selectable) + - [jQuery UI Selectmenu](https://www.drupal.org/project/jquery_ui_selectmenu) + - [jQuery UI Slider](https://www.drupal.org/project/jquery_ui_slider) + - [jQuery UI Spinner](https://www.drupal.org/project/jquery_ui_spinner) + - [jQuery UI DatePicker](https://www.drupal.org/project/jquery_ui_datepicker) diff --git a/web/modules/jquery_ui_touch_punch/composer.json b/web/modules/jquery_ui_touch_punch/composer.json new file mode 100644 index 0000000000..876ce1c71f --- /dev/null +++ b/web/modules/jquery_ui_touch_punch/composer.json @@ -0,0 +1,28 @@ +{ + "name": "drupal/jquery_ui_touch_punch", + "description": "Provides jQuery UI Touch Punch library.", + "type": "drupal-module", + "homepage": "https://www.drupal.org/project/jquery_ui_touch_punch", + "keywords": ["Drupal", "jquery_ui_touch_punch"], + "license": "GPL-2.0+", + "minimum-stability": "dev", + "prefer-stable": true, + "support": { + "issues": "https://www.drupal.org/project/issues/jquery_ui_touch_punch", + "irc": "irc://irc.freenode.org/drupal-contribute", + "source": "https://www.drupal.org/project/jquery_ui_touch_punch" + }, + "authors": [ + { + "name": "Naveen Valecha", + "homepage": "https://drupal.org/u/naveenvalecha", + "role": "Maintainer" + } + ], + "require": { + "drupal/jquery_ui": "^1.0" + }, + "suggest": { + "furf/jquery-ui-touch-punch": "Required to use drupal/jquery_ui_touch_punch module." + } +} diff --git a/web/modules/jquery_ui_touch_punch/jquery_ui_touch_punch.info.yml b/web/modules/jquery_ui_touch_punch/jquery_ui_touch_punch.info.yml new file mode 100644 index 0000000000..1b5a5dce2b --- /dev/null +++ b/web/modules/jquery_ui_touch_punch/jquery_ui_touch_punch.info.yml @@ -0,0 +1,12 @@ +name: jQuery UI Touch Punch +type: module +description: 'Provides jQuery UI Touch Punch library.' +package: jQuery UI +core_version_requirement: ^8 || ^9 +dependencies: + - jquery_ui:jquery_ui + +# Information added by Drupal.org packaging script on 2020-06-11 +version: '1.0.0' +project: 'jquery_ui_touch_punch' +datestamp: 1591893294 diff --git a/web/modules/jquery_ui_touch_punch/jquery_ui_touch_punch.install b/web/modules/jquery_ui_touch_punch/jquery_ui_touch_punch.install new file mode 100644 index 0000000000..d7fe6233ad --- /dev/null +++ b/web/modules/jquery_ui_touch_punch/jquery_ui_touch_punch.install @@ -0,0 +1,52 @@ +<?php + +/** + * @file + * Install, update and uninstall functions for the jquery_ui_touch_punch module. + */ + +/** + * Implements hook_requirements(). + */ +function jquery_ui_touch_punch_requirements($phase) { + + $requirements = []; + + // @todo Remove this conditional structure in favor of using the libraries + // directory file finder service when Drupal 8.9 is the minimum supported + // version of core. + if (\Drupal::hasService('library.libraries_directory_file_finder')) { + /** @var \Drupal\Core\Asset\LibrariesDirectoryFileFinder $library_file_finder */ + $library_file_finder = \Drupal::service('library.libraries_directory_file_finder'); + $library_found = (bool) $library_file_finder->find('jquery-ui-touch-punch/jquery.ui.touch-punch.min.js'); + } + else { + $path = DRUPAL_ROOT . '/libraries/jquery-ui-touch-punch/jquery.ui.touch-punch.min.js'; + if (\Drupal::moduleHandler()->moduleExists('libraries')) { + $path = libraries_get_path('jquery-ui-touch-punch') . '/jquery.ui.touch-punch.min.js'; + } + + // Is the library found in the root libraries path. + $library_found = file_exists($path); + + // If library is not found, then look in the current profile libraries path. + if (!$library_found) { + $profile_path = drupal_get_path('profile', \Drupal::installProfile()); + $profile_path .= '/libraries/jquery-ui-touch-punch/jquery.ui.touch-punch.min.js'; + // Is the library found in the current profile libraries path. + $library_found = file_exists($profile_path); + } + } + + if (!$library_found) { + $requirements['jquery_ui_touch_punch_library'] = [ + 'title' => t('jQuery UI Touch Punch library missing'), + 'description' => t('jQuery UI Touch Punch requires the jquery.ui.touch-punch.min.js library. + Download it (https://github.com/furf/jquery-ui-touch-punch) and place it in the + libraries folder (/libraries)'), + 'severity' => REQUIREMENT_ERROR, + ]; + } + + return $requirements; +} diff --git a/web/modules/jquery_ui_touch_punch/jquery_ui_touch_punch.libraries.yml b/web/modules/jquery_ui_touch_punch/jquery_ui_touch_punch.libraries.yml new file mode 100644 index 0000000000..127cd2045e --- /dev/null +++ b/web/modules/jquery_ui_touch_punch/jquery_ui_touch_punch.libraries.yml @@ -0,0 +1,8 @@ +touch-punch: + title: 'jQuery Touch Punch' + website: https://github.com/furf/jquery-ui-touch-punch + version: 0.0 + js: + /libraries/jquery-ui-touch-punch/jquery.ui.touch-punch.min.js: { minified: true } + dependencies: + - jquery_ui/core diff --git a/web/modules/jquery_ui_touch_punch/jquery_ui_touch_punch.module b/web/modules/jquery_ui_touch_punch/jquery_ui_touch_punch.module new file mode 100644 index 0000000000..0e40e2a2ae --- /dev/null +++ b/web/modules/jquery_ui_touch_punch/jquery_ui_touch_punch.module @@ -0,0 +1,23 @@ +<?php + +/** + * @file + * Contains jquery_ui_touch_punch.module. + */ + +use Drupal\Core\Routing\RouteMatchInterface; + +/** + * Implements hook_help(). + */ +function jquery_ui_touch_punch_help($route_name, RouteMatchInterface $route_match) { + switch ($route_name) { + case 'help.page.jquery_ui_touch_punch': + $output = '<h3>' . t('About') . '</h3>'; + $output .= '<p>' . t('Drupal 8 includes jQuery UI in core, however it is no longer actively maintained and has been marked deprecated. This module provides the jQuery UI Touch Punch library for any themes and modules that require it.') . '</p>'; + $output .= '<p>' . t('For more information about the deprecation of jQuery UI, see this <a href=":change-record">change record</a>', [':change-record' => 'https://www.drupal.org/node/3067969']) . '</p>'; + $output .= '<p>' . t('Visit the <a href=":project_link">jQuery UI Touch Punch project page</a> on Drupal.org for more information on this module.', [':project_link' => 'https://www.drupal.org/project/jquery_ui_touch_punch']) . '</p>'; + + return $output; + } +} -- GitLab