3 * Polyfill for HTML5 details elements.
6 (function ($, Modernizr, Drupal) {
11 * The collapsible details object represents a single details element.
13 * @constructor Drupal.CollapsibleDetails
15 * @param {HTMLElement} node
16 * The details element.
18 function CollapsibleDetails(node) {
20 this.$node.data('details', this);
21 // Expand details if there are errors inside, or if it contains an
22 // element that is targeted by the URI fragment identifier.
23 var anchor = location.hash && location.hash !== '#' ? ', ' + location.hash : '';
24 if (this.$node.find('.error' + anchor).length) {
25 this.$node.attr('open', true);
27 // Initialize and setup the summary,
29 // Initialize and setup the legend.
33 $.extend(CollapsibleDetails, /** @lends Drupal.CollapsibleDetails */{
36 * Holds references to instantiated CollapsibleDetails objects.
38 * @type {Array.<Drupal.CollapsibleDetails>}
43 $.extend(CollapsibleDetails.prototype, /** @lends Drupal.CollapsibleDetails# */{
46 * Initialize and setup summary events and markup.
48 * @fires event:summaryUpdated
50 * @listens event:summaryUpdated
52 setupSummary: function () {
53 this.$summary = $('<span class="summary"></span>');
55 .on('summaryUpdated', $.proxy(this.onSummaryUpdated, this))
56 .trigger('summaryUpdated');
60 * Initialize and setup legend markup.
62 setupLegend: function () {
63 // Turn the summary into a clickable link.
64 var $legend = this.$node.find('> summary');
66 $('<span class="details-summary-prefix visually-hidden"></span>')
67 .append(this.$node.attr('open') ? Drupal.t('Hide') : Drupal.t('Show'))
69 .after(document.createTextNode(' '));
71 // .wrapInner() does not retain bound events.
72 $('<a class="details-title"></a>')
73 .attr('href', '#' + this.$node.attr('id'))
74 .prepend($legend.contents())
78 .append(this.$summary)
79 .on('click', $.proxy(this.onLegendClick, this));
83 * Handle legend clicks.
85 * @param {jQuery.Event} e
86 * The event triggered.
88 onLegendClick: function (e) {
96 onSummaryUpdated: function () {
97 var text = $.trim(this.$node.drupalGetSummary());
98 this.$summary.html(text ? ' (' + text + ')' : '');
102 * Toggle the visibility of a details element using smooth animations.
104 toggle: function () {
105 var isOpen = !!this.$node.attr('open');
106 var $summaryPrefix = this.$node.find('> summary span.details-summary-prefix');
108 $summaryPrefix.html(Drupal.t('Show'));
111 $summaryPrefix.html(Drupal.t('Hide'));
113 // Delay setting the attribute to emulate chrome behavior and make
114 // details-aria.js work as expected with this polyfill.
115 setTimeout(function () {
116 this.$node.attr('open', !isOpen);
122 * Polyfill HTML5 details element.
124 * @type {Drupal~behavior}
126 * @prop {Drupal~behaviorAttach} attach
127 * Attaches behavior for the details element.
129 Drupal.behaviors.collapse = {
130 attach: function (context) {
131 if (Modernizr.details) {
134 var $collapsibleDetails = $(context).find('details').once('collapse').addClass('collapse-processed');
135 if ($collapsibleDetails.length) {
136 for (var i = 0; i < $collapsibleDetails.length; i++) {
137 CollapsibleDetails.instances.push(new CollapsibleDetails($collapsibleDetails[i]));
143 // Expose constructor in the public space.
144 Drupal.CollapsibleDetails = CollapsibleDetails;
146 })(jQuery, Modernizr, Drupal);