2015-08-17 17:00:26 -07:00
/ * *
* @ file
* Polyfill for HTML5 details elements .
* /
( function ( $ , Modernizr , Drupal ) {
2015-10-21 21:44:50 -07:00
'use strict' ;
2015-08-17 17:00:26 -07:00
/ * *
* The collapsible details object represents a single details element .
*
* @ constructor Drupal . CollapsibleDetails
*
* @ param { HTMLElement } node
* /
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.
var anchor = location . hash && location . hash !== '#' ? ', ' + 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 : function ( ) {
this . $summary = $ ( '<span class="summary"></span>' ) ;
this . $node
. on ( 'summaryUpdated' , $ . proxy ( this . onSummaryUpdated , this ) )
. trigger ( 'summaryUpdated' ) ;
} ,
/ * *
* Initialize and setup legend markup .
* /
setupLegend : function ( ) {
// Turn the summary into a clickable link.
var $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
* /
onLegendClick : function ( e ) {
this . toggle ( ) ;
e . preventDefault ( ) ;
} ,
/ * *
* Update summary .
* /
onSummaryUpdated : function ( ) {
var text = $ . trim ( this . $node . drupalGetSummary ( ) ) ;
this . $summary . html ( text ? ' (' + text + ')' : '' ) ;
} ,
/ * *
* Toggle the visibility of a details element using smooth animations .
* /
toggle : function ( ) {
var isOpen = ! ! this . $node . attr ( 'open' ) ;
var $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 ( function ( ) {
this . $node . attr ( 'open' , ! isOpen ) ;
} . bind ( this ) , 0 ) ;
}
} ) ;
/ * *
* Polyfill HTML5 details element .
*
* @ type { Drupal ~ behavior }
* /
Drupal . behaviors . collapse = {
attach : function ( context ) {
if ( Modernizr . details ) {
return ;
}
var $collapsibleDetails = $ ( context ) . find ( 'details' ) . once ( 'collapse' ) . addClass ( 'collapse-processed' ) ;
if ( $collapsibleDetails . length ) {
for ( var i = 0 ; i < $collapsibleDetails . length ; i ++ ) {
CollapsibleDetails . instances . push ( new CollapsibleDetails ( $collapsibleDetails [ i ] ) ) ;
}
}
}
} ;
// Expose constructor in the public space.
Drupal . CollapsibleDetails = CollapsibleDetails ;
} ) ( jQuery , Modernizr , Drupal ) ;