123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- /**
- * @file
- * Polyfill for HTML5 details elements.
- */
- (function($, Modernizr, Drupal) {
- /**
- * The collapsible details object represents a single details element.
- *
- * @constructor Drupal.CollapsibleDetails
- *
- * @param {HTMLElement} node
- * The details element.
- */
- function CollapsibleDetails(node) {
- this.$node = $(node);
- this.$node.data('details', this);
- // Expand details if there are errors inside, or if it contains an
- // element that is targeted by the URI fragment identifier.
- const anchor =
- window.location.hash && window.location.hash !== '#'
- ? `, ${window.location.hash}`
- : '';
- if (this.$node.find(`.error${anchor}`).length) {
- this.$node.attr('open', true);
- }
- // Initialize and setup the summary,
- this.setupSummary();
- // Initialize and setup the legend.
- this.setupLegend();
- }
- $.extend(
- CollapsibleDetails,
- /** @lends Drupal.CollapsibleDetails */ {
- /**
- * Holds references to instantiated CollapsibleDetails objects.
- *
- * @type {Array.<Drupal.CollapsibleDetails>}
- */
- instances: [],
- },
- );
- $.extend(
- CollapsibleDetails.prototype,
- /** @lends Drupal.CollapsibleDetails# */ {
- /**
- * Initialize and setup summary events and markup.
- *
- * @fires event:summaryUpdated
- *
- * @listens event:summaryUpdated
- */
- setupSummary() {
- this.$summary = $('<span class="summary"></span>');
- this.$node
- .on('summaryUpdated', $.proxy(this.onSummaryUpdated, this))
- .trigger('summaryUpdated');
- },
- /**
- * Initialize and setup legend markup.
- */
- setupLegend() {
- // Turn the summary into a clickable link.
- const $legend = this.$node.find('> summary');
- $('<span class="details-summary-prefix visually-hidden"></span>')
- .append(this.$node.attr('open') ? Drupal.t('Hide') : Drupal.t('Show'))
- .prependTo($legend)
- .after(document.createTextNode(' '));
- // .wrapInner() does not retain bound events.
- $('<a class="details-title"></a>')
- .attr('href', `#${this.$node.attr('id')}`)
- .prepend($legend.contents())
- .appendTo($legend);
- $legend
- .append(this.$summary)
- .on('click', $.proxy(this.onLegendClick, this));
- },
- /**
- * Handle legend clicks.
- *
- * @param {jQuery.Event} e
- * The event triggered.
- */
- onLegendClick(e) {
- this.toggle();
- e.preventDefault();
- },
- /**
- * Update summary.
- */
- onSummaryUpdated() {
- const text = $.trim(this.$node.drupalGetSummary());
- this.$summary.html(text ? ` (${text})` : '');
- },
- /**
- * Toggle the visibility of a details element using smooth animations.
- */
- toggle() {
- const isOpen = !!this.$node.attr('open');
- const $summaryPrefix = this.$node.find(
- '> summary span.details-summary-prefix',
- );
- if (isOpen) {
- $summaryPrefix.html(Drupal.t('Show'));
- } else {
- $summaryPrefix.html(Drupal.t('Hide'));
- }
- // Delay setting the attribute to emulate chrome behavior and make
- // details-aria.js work as expected with this polyfill.
- setTimeout(() => {
- this.$node.attr('open', !isOpen);
- }, 0);
- },
- },
- );
- /**
- * Polyfill HTML5 details element.
- *
- * @type {Drupal~behavior}
- *
- * @prop {Drupal~behaviorAttach} attach
- * Attaches behavior for the details element.
- */
- Drupal.behaviors.collapse = {
- attach(context) {
- if (Modernizr.details) {
- return;
- }
- const $collapsibleDetails = $(context)
- .find('details')
- .once('collapse')
- .addClass('collapse-processed');
- if ($collapsibleDetails.length) {
- for (let i = 0; i < $collapsibleDetails.length; i++) {
- CollapsibleDetails.instances.push(
- new CollapsibleDetails($collapsibleDetails[i]),
- );
- }
- }
- },
- };
- /**
- * Open parent details elements of a targeted page fragment.
- *
- * Opens all (nested) details element on a hash change or fragment link click
- * when the target is a child element, in order to make sure the targeted
- * element is visible. Aria attributes on the summary
- * are set by triggering the click event listener in details-aria.js.
- *
- * @param {jQuery.Event} e
- * The event triggered.
- * @param {jQuery} $target
- * The targeted node as a jQuery object.
- */
- const handleFragmentLinkClickOrHashChange = (e, $target) => {
- $target
- .parents('details')
- .not('[open]')
- .find('> summary')
- .trigger('click');
- };
- /**
- * Binds a listener to handle fragment link clicks and URL hash changes.
- */
- $('body').on(
- 'formFragmentLinkClickOrHashChange.details',
- handleFragmentLinkClickOrHashChange,
- );
- // Expose constructor in the public space.
- Drupal.CollapsibleDetails = CollapsibleDetails;
- })(jQuery, Modernizr, Drupal);
|