Mercurial > hg > isophonics-drupal-site
comparison core/misc/collapse.es6.js @ 17:129ea1e6d783
Update, including to Drupal core 8.6.10
author | Chris Cannam |
---|---|
date | Thu, 28 Feb 2019 13:21:36 +0000 |
parents | 4c8ae668cc8c |
children |
comparison
equal
deleted
inserted
replaced
16:c2387f117808 | 17:129ea1e6d783 |
---|---|
1 /** | 1 /** |
2 * @file | 2 * @file |
3 * Polyfill for HTML5 details elements. | 3 * Polyfill for HTML5 details elements. |
4 */ | 4 */ |
5 | 5 |
6 (function ($, Modernizr, Drupal) { | 6 (function($, Modernizr, Drupal) { |
7 /** | 7 /** |
8 * The collapsible details object represents a single details element. | 8 * The collapsible details object represents a single details element. |
9 * | 9 * |
10 * @constructor Drupal.CollapsibleDetails | 10 * @constructor Drupal.CollapsibleDetails |
11 * | 11 * |
15 function CollapsibleDetails(node) { | 15 function CollapsibleDetails(node) { |
16 this.$node = $(node); | 16 this.$node = $(node); |
17 this.$node.data('details', this); | 17 this.$node.data('details', this); |
18 // Expand details if there are errors inside, or if it contains an | 18 // Expand details if there are errors inside, or if it contains an |
19 // element that is targeted by the URI fragment identifier. | 19 // element that is targeted by the URI fragment identifier. |
20 const anchor = location.hash && location.hash !== '#' ? `, ${location.hash}` : ''; | 20 const anchor = |
21 window.location.hash && window.location.hash !== '#' | |
22 ? `, ${window.location.hash}` | |
23 : ''; | |
21 if (this.$node.find(`.error${anchor}`).length) { | 24 if (this.$node.find(`.error${anchor}`).length) { |
22 this.$node.attr('open', true); | 25 this.$node.attr('open', true); |
23 } | 26 } |
24 // Initialize and setup the summary, | 27 // Initialize and setup the summary, |
25 this.setupSummary(); | 28 this.setupSummary(); |
26 // Initialize and setup the legend. | 29 // Initialize and setup the legend. |
27 this.setupLegend(); | 30 this.setupLegend(); |
28 } | 31 } |
29 | 32 |
30 $.extend(CollapsibleDetails, /** @lends Drupal.CollapsibleDetails */{ | 33 $.extend( |
34 CollapsibleDetails, | |
35 /** @lends Drupal.CollapsibleDetails */ { | |
36 /** | |
37 * Holds references to instantiated CollapsibleDetails objects. | |
38 * | |
39 * @type {Array.<Drupal.CollapsibleDetails>} | |
40 */ | |
41 instances: [], | |
42 }, | |
43 ); | |
31 | 44 |
32 /** | 45 $.extend( |
33 * Holds references to instantiated CollapsibleDetails objects. | 46 CollapsibleDetails.prototype, |
34 * | 47 /** @lends Drupal.CollapsibleDetails# */ { |
35 * @type {Array.<Drupal.CollapsibleDetails>} | 48 /** |
36 */ | 49 * Initialize and setup summary events and markup. |
37 instances: [], | 50 * |
38 }); | 51 * @fires event:summaryUpdated |
52 * | |
53 * @listens event:summaryUpdated | |
54 */ | |
55 setupSummary() { | |
56 this.$summary = $('<span class="summary"></span>'); | |
57 this.$node | |
58 .on('summaryUpdated', $.proxy(this.onSummaryUpdated, this)) | |
59 .trigger('summaryUpdated'); | |
60 }, | |
39 | 61 |
40 $.extend(CollapsibleDetails.prototype, /** @lends Drupal.CollapsibleDetails# */{ | 62 /** |
63 * Initialize and setup legend markup. | |
64 */ | |
65 setupLegend() { | |
66 // Turn the summary into a clickable link. | |
67 const $legend = this.$node.find('> summary'); | |
41 | 68 |
42 /** | 69 $('<span class="details-summary-prefix visually-hidden"></span>') |
43 * Initialize and setup summary events and markup. | 70 .append(this.$node.attr('open') ? Drupal.t('Hide') : Drupal.t('Show')) |
44 * | 71 .prependTo($legend) |
45 * @fires event:summaryUpdated | 72 .after(document.createTextNode(' ')); |
46 * | 73 |
47 * @listens event:summaryUpdated | 74 // .wrapInner() does not retain bound events. |
48 */ | 75 $('<a class="details-title"></a>') |
49 setupSummary() { | 76 .attr('href', `#${this.$node.attr('id')}`) |
50 this.$summary = $('<span class="summary"></span>'); | 77 .prepend($legend.contents()) |
51 this.$node | 78 .appendTo($legend); |
52 .on('summaryUpdated', $.proxy(this.onSummaryUpdated, this)) | 79 |
53 .trigger('summaryUpdated'); | 80 $legend |
81 .append(this.$summary) | |
82 .on('click', $.proxy(this.onLegendClick, this)); | |
83 }, | |
84 | |
85 /** | |
86 * Handle legend clicks. | |
87 * | |
88 * @param {jQuery.Event} e | |
89 * The event triggered. | |
90 */ | |
91 onLegendClick(e) { | |
92 this.toggle(); | |
93 e.preventDefault(); | |
94 }, | |
95 | |
96 /** | |
97 * Update summary. | |
98 */ | |
99 onSummaryUpdated() { | |
100 const text = $.trim(this.$node.drupalGetSummary()); | |
101 this.$summary.html(text ? ` (${text})` : ''); | |
102 }, | |
103 | |
104 /** | |
105 * Toggle the visibility of a details element using smooth animations. | |
106 */ | |
107 toggle() { | |
108 const isOpen = !!this.$node.attr('open'); | |
109 const $summaryPrefix = this.$node.find( | |
110 '> summary span.details-summary-prefix', | |
111 ); | |
112 if (isOpen) { | |
113 $summaryPrefix.html(Drupal.t('Show')); | |
114 } else { | |
115 $summaryPrefix.html(Drupal.t('Hide')); | |
116 } | |
117 // Delay setting the attribute to emulate chrome behavior and make | |
118 // details-aria.js work as expected with this polyfill. | |
119 setTimeout(() => { | |
120 this.$node.attr('open', !isOpen); | |
121 }, 0); | |
122 }, | |
54 }, | 123 }, |
55 | 124 ); |
56 /** | |
57 * Initialize and setup legend markup. | |
58 */ | |
59 setupLegend() { | |
60 // Turn the summary into a clickable link. | |
61 const $legend = this.$node.find('> summary'); | |
62 | |
63 $('<span class="details-summary-prefix visually-hidden"></span>') | |
64 .append(this.$node.attr('open') ? Drupal.t('Hide') : Drupal.t('Show')) | |
65 .prependTo($legend) | |
66 .after(document.createTextNode(' ')); | |
67 | |
68 // .wrapInner() does not retain bound events. | |
69 $('<a class="details-title"></a>') | |
70 .attr('href', `#${this.$node.attr('id')}`) | |
71 .prepend($legend.contents()) | |
72 .appendTo($legend); | |
73 | |
74 $legend | |
75 .append(this.$summary) | |
76 .on('click', $.proxy(this.onLegendClick, this)); | |
77 }, | |
78 | |
79 /** | |
80 * Handle legend clicks. | |
81 * | |
82 * @param {jQuery.Event} e | |
83 * The event triggered. | |
84 */ | |
85 onLegendClick(e) { | |
86 this.toggle(); | |
87 e.preventDefault(); | |
88 }, | |
89 | |
90 /** | |
91 * Update summary. | |
92 */ | |
93 onSummaryUpdated() { | |
94 const text = $.trim(this.$node.drupalGetSummary()); | |
95 this.$summary.html(text ? ` (${text})` : ''); | |
96 }, | |
97 | |
98 /** | |
99 * Toggle the visibility of a details element using smooth animations. | |
100 */ | |
101 toggle() { | |
102 const isOpen = !!this.$node.attr('open'); | |
103 const $summaryPrefix = this.$node.find('> summary span.details-summary-prefix'); | |
104 if (isOpen) { | |
105 $summaryPrefix.html(Drupal.t('Show')); | |
106 } | |
107 else { | |
108 $summaryPrefix.html(Drupal.t('Hide')); | |
109 } | |
110 // Delay setting the attribute to emulate chrome behavior and make | |
111 // details-aria.js work as expected with this polyfill. | |
112 setTimeout(() => { | |
113 this.$node.attr('open', !isOpen); | |
114 }, 0); | |
115 }, | |
116 }); | |
117 | 125 |
118 /** | 126 /** |
119 * Polyfill HTML5 details element. | 127 * Polyfill HTML5 details element. |
120 * | 128 * |
121 * @type {Drupal~behavior} | 129 * @type {Drupal~behavior} |
126 Drupal.behaviors.collapse = { | 134 Drupal.behaviors.collapse = { |
127 attach(context) { | 135 attach(context) { |
128 if (Modernizr.details) { | 136 if (Modernizr.details) { |
129 return; | 137 return; |
130 } | 138 } |
131 const $collapsibleDetails = $(context).find('details').once('collapse').addClass('collapse-processed'); | 139 const $collapsibleDetails = $(context) |
140 .find('details') | |
141 .once('collapse') | |
142 .addClass('collapse-processed'); | |
132 if ($collapsibleDetails.length) { | 143 if ($collapsibleDetails.length) { |
133 for (let i = 0; i < $collapsibleDetails.length; i++) { | 144 for (let i = 0; i < $collapsibleDetails.length; i++) { |
134 CollapsibleDetails.instances.push(new CollapsibleDetails($collapsibleDetails[i])); | 145 CollapsibleDetails.instances.push( |
146 new CollapsibleDetails($collapsibleDetails[i]), | |
147 ); | |
135 } | 148 } |
136 } | 149 } |
137 }, | 150 }, |
138 }; | 151 }; |
139 | 152 |
149 * The event triggered. | 162 * The event triggered. |
150 * @param {jQuery} $target | 163 * @param {jQuery} $target |
151 * The targeted node as a jQuery object. | 164 * The targeted node as a jQuery object. |
152 */ | 165 */ |
153 const handleFragmentLinkClickOrHashChange = (e, $target) => { | 166 const handleFragmentLinkClickOrHashChange = (e, $target) => { |
154 $target.parents('details').not('[open]').find('> summary').trigger('click'); | 167 $target |
168 .parents('details') | |
169 .not('[open]') | |
170 .find('> summary') | |
171 .trigger('click'); | |
155 }; | 172 }; |
156 | 173 |
157 /** | 174 /** |
158 * Binds a listener to handle fragment link clicks and URL hash changes. | 175 * Binds a listener to handle fragment link clicks and URL hash changes. |
159 */ | 176 */ |
160 $('body').on('formFragmentLinkClickOrHashChange.details', handleFragmentLinkClickOrHashChange); | 177 $('body').on( |
178 'formFragmentLinkClickOrHashChange.details', | |
179 handleFragmentLinkClickOrHashChange, | |
180 ); | |
161 | 181 |
162 // Expose constructor in the public space. | 182 // Expose constructor in the public space. |
163 Drupal.CollapsibleDetails = CollapsibleDetails; | 183 Drupal.CollapsibleDetails = CollapsibleDetails; |
164 }(jQuery, Modernizr, Drupal)); | 184 })(jQuery, Modernizr, Drupal); |