Upgraded drupal core with security updates
[yaffs-website] / web / core / misc / collapse.js
1 /**
2  * @file
3  * Polyfill for HTML5 details elements.
4  */
5
6 (function ($, Modernizr, Drupal) {
7
8   'use strict';
9
10   /**
11    * The collapsible details object represents a single details element.
12    *
13    * @constructor Drupal.CollapsibleDetails
14    *
15    * @param {HTMLElement} node
16    *   The details element.
17    */
18   function CollapsibleDetails(node) {
19     this.$node = $(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);
26     }
27     // Initialize and setup the summary,
28     this.setupSummary();
29     // Initialize and setup the legend.
30     this.setupLegend();
31   }
32
33   $.extend(CollapsibleDetails, /** @lends Drupal.CollapsibleDetails */{
34
35     /**
36      * Holds references to instantiated CollapsibleDetails objects.
37      *
38      * @type {Array.<Drupal.CollapsibleDetails>}
39      */
40     instances: []
41   });
42
43   $.extend(CollapsibleDetails.prototype, /** @lends Drupal.CollapsibleDetails# */{
44
45     /**
46      * Initialize and setup summary events and markup.
47      *
48      * @fires event:summaryUpdated
49      *
50      * @listens event:summaryUpdated
51      */
52     setupSummary: function () {
53       this.$summary = $('<span class="summary"></span>');
54       this.$node
55         .on('summaryUpdated', $.proxy(this.onSummaryUpdated, this))
56         .trigger('summaryUpdated');
57     },
58
59     /**
60      * Initialize and setup legend markup.
61      */
62     setupLegend: function () {
63       // Turn the summary into a clickable link.
64       var $legend = this.$node.find('> summary');
65
66       $('<span class="details-summary-prefix visually-hidden"></span>')
67         .append(this.$node.attr('open') ? Drupal.t('Hide') : Drupal.t('Show'))
68         .prependTo($legend)
69         .after(document.createTextNode(' '));
70
71       // .wrapInner() does not retain bound events.
72       $('<a class="details-title"></a>')
73         .attr('href', '#' + this.$node.attr('id'))
74         .prepend($legend.contents())
75         .appendTo($legend);
76
77       $legend
78         .append(this.$summary)
79         .on('click', $.proxy(this.onLegendClick, this));
80     },
81
82     /**
83      * Handle legend clicks.
84      *
85      * @param {jQuery.Event} e
86      *   The event triggered.
87      */
88     onLegendClick: function (e) {
89       this.toggle();
90       e.preventDefault();
91     },
92
93     /**
94      * Update summary.
95      */
96     onSummaryUpdated: function () {
97       var text = $.trim(this.$node.drupalGetSummary());
98       this.$summary.html(text ? ' (' + text + ')' : '');
99     },
100
101     /**
102      * Toggle the visibility of a details element using smooth animations.
103      */
104     toggle: function () {
105       var isOpen = !!this.$node.attr('open');
106       var $summaryPrefix = this.$node.find('> summary span.details-summary-prefix');
107       if (isOpen) {
108         $summaryPrefix.html(Drupal.t('Show'));
109       }
110       else {
111         $summaryPrefix.html(Drupal.t('Hide'));
112       }
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);
117       }.bind(this), 0);
118     }
119   });
120
121   /**
122    * Polyfill HTML5 details element.
123    *
124    * @type {Drupal~behavior}
125    *
126    * @prop {Drupal~behaviorAttach} attach
127    *   Attaches behavior for the details element.
128    */
129   Drupal.behaviors.collapse = {
130     attach: function (context) {
131       if (Modernizr.details) {
132         return;
133       }
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]));
138         }
139       }
140     }
141   };
142
143   // Expose constructor in the public space.
144   Drupal.CollapsibleDetails = CollapsibleDetails;
145
146 })(jQuery, Modernizr, Drupal);