2015-08-17 17:00:26 -07:00
/ * *
* @ file
* A Backbone view for the toolbar element . Listens to mouse & touch .
* /
( function ( $ , Drupal , drupalSettings , Backbone ) {
2015-10-21 21:44:50 -07:00
'use strict' ;
2015-08-17 17:00:26 -07:00
Drupal . toolbar . ToolbarVisualView = Backbone . View . extend ( /** @lends Drupal.toolbar.ToolbarVisualView# */ {
/ * *
2015-09-04 13:20:09 -07:00
* Event map for the ` ToolbarVisualView ` .
*
2015-08-17 17:00:26 -07:00
* @ return { object }
2015-09-04 13:20:09 -07:00
* A map of events .
2015-08-17 17:00:26 -07:00
* /
events : function ( ) {
// Prevents delay and simulated mouse events.
var touchEndToClick = function ( event ) {
event . preventDefault ( ) ;
event . target . click ( ) ;
} ;
return {
2016-10-06 15:16:20 -07:00
'click .toolbar-bar .toolbar-tab .trigger' : 'onTabClick' ,
2015-08-17 17:00:26 -07:00
'click .toolbar-toggle-orientation button' : 'onOrientationToggleClick' ,
2016-10-06 15:16:20 -07:00
'touchend .toolbar-bar .toolbar-tab .trigger' : touchEndToClick ,
2015-08-17 17:00:26 -07:00
'touchend .toolbar-toggle-orientation button' : touchEndToClick
} ;
} ,
/ * *
* Backbone view for the toolbar element . Listens to mouse & touch .
*
* @ constructs
*
* @ augments Backbone . View
*
* @ param { object } options
2015-09-04 13:20:09 -07:00
* Options for the view object .
2015-08-17 17:00:26 -07:00
* @ param { object } options . strings
2015-09-04 13:20:09 -07:00
* Various strings to use in the view .
2015-08-17 17:00:26 -07:00
* /
initialize : function ( options ) {
this . strings = options . strings ;
this . listenTo ( this . model , 'change:activeTab change:orientation change:isOriented change:isTrayToggleVisible' , this . render ) ;
this . listenTo ( this . model , 'change:mqMatches' , this . onMediaQueryChange ) ;
this . listenTo ( this . model , 'change:offsets' , this . adjustPlacement ) ;
// Add the tray orientation toggles.
this . $el
. find ( '.toolbar-tray .toolbar-lining' )
. append ( Drupal . theme ( 'toolbarOrientationToggle' ) ) ;
// Trigger an activeTab change so that listening scripts can respond on
// page load. This will call render.
this . model . trigger ( 'change:activeTab' ) ;
} ,
/ * *
* @ inheritdoc
*
* @ return { Drupal . toolbar . ToolbarVisualView }
2015-09-04 13:20:09 -07:00
* The ` ToolbarVisualView ` instance .
2015-08-17 17:00:26 -07:00
* /
render : function ( ) {
this . updateTabs ( ) ;
this . updateTrayOrientation ( ) ;
this . updateBarAttributes ( ) ;
// Load the subtrees if the orientation of the toolbar is changed to
// vertical. This condition responds to the case that the toolbar switches
// from horizontal to vertical orientation. The toolbar starts in a
// vertical orientation by default and then switches to horizontal during
// initialization if the media query conditions are met. Simply checking
// that the orientation is vertical here would result in the subtrees
// always being loaded, even when the toolbar initialization ultimately
// results in a horizontal orientation.
//
// @see Drupal.behaviors.toolbar.attach() where admin menu subtrees
// loading is invoked during initialization after media query conditions
// have been processed.
if ( this . model . changed . orientation === 'vertical' || this . model . changed . activeTab ) {
this . loadSubtrees ( ) ;
}
// Trigger a recalculation of viewport displacing elements. Use setTimeout
// to ensure this recalculation happens after changes to visual elements
// have processed.
window . setTimeout ( function ( ) {
Drupal . displace ( true ) ;
} , 0 ) ;
return this ;
} ,
/ * *
* Responds to a toolbar tab click .
*
* @ param { jQuery . Event } event
2015-09-04 13:20:09 -07:00
* The event triggered .
2015-08-17 17:00:26 -07:00
* /
onTabClick : function ( event ) {
// If this tab has a tray associated with it, it is considered an
// activatable tab.
if ( event . target . hasAttribute ( 'data-toolbar-tray' ) ) {
var activeTab = this . model . get ( 'activeTab' ) ;
var clickedTab = event . target ;
// Set the event target as the active item if it is not already.
this . model . set ( 'activeTab' , ( ! activeTab || clickedTab !== activeTab ) ? clickedTab : null ) ;
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
}
} ,
/ * *
* Toggles the orientation of a toolbar tray .
*
* @ param { jQuery . Event } event
2015-09-04 13:20:09 -07:00
* The event triggered .
2015-08-17 17:00:26 -07:00
* /
onOrientationToggleClick : function ( event ) {
var orientation = this . model . get ( 'orientation' ) ;
// Determine the toggle-to orientation.
var antiOrientation = ( orientation === 'vertical' ) ? 'horizontal' : 'vertical' ;
2015-11-17 13:42:33 -08:00
var locked = antiOrientation === 'vertical' ;
2015-08-17 17:00:26 -07:00
// Remember the locked state.
if ( locked ) {
localStorage . setItem ( 'Drupal.toolbar.trayVerticalLocked' , 'true' ) ;
}
else {
localStorage . removeItem ( 'Drupal.toolbar.trayVerticalLocked' ) ;
}
// Update the model.
this . model . set ( {
locked : locked ,
orientation : antiOrientation
} , {
validate : true ,
override : true
} ) ;
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
} ,
/ * *
* Updates the display of the tabs : toggles a tab and the associated tray .
* /
updateTabs : function ( ) {
var $tab = $ ( this . model . get ( 'activeTab' ) ) ;
// Deactivate the previous tab.
$ ( this . model . previous ( 'activeTab' ) )
. removeClass ( 'is-active' )
. prop ( 'aria-pressed' , false ) ;
// Deactivate the previous tray.
$ ( this . model . previous ( 'activeTray' ) )
. removeClass ( 'is-active' ) ;
// Activate the selected tab.
if ( $tab . length > 0 ) {
$tab
. addClass ( 'is-active' )
// Mark the tab as pressed.
. prop ( 'aria-pressed' , true ) ;
var name = $tab . attr ( 'data-toolbar-tray' ) ;
// Store the active tab name or remove the setting.
var id = $tab . get ( 0 ) . id ;
if ( id ) {
localStorage . setItem ( 'Drupal.toolbar.activeTabID' , JSON . stringify ( id ) ) ;
}
// Activate the associated tray.
var $tray = this . $el . find ( '[data-toolbar-tray="' + name + '"].toolbar-tray' ) ;
if ( $tray . length ) {
$tray . addClass ( 'is-active' ) ;
this . model . set ( 'activeTray' , $tray . get ( 0 ) ) ;
}
else {
// There is no active tray.
this . model . set ( 'activeTray' , null ) ;
}
}
else {
// There is no active tray.
this . model . set ( 'activeTray' , null ) ;
localStorage . removeItem ( 'Drupal.toolbar.activeTabID' ) ;
}
} ,
/ * *
* Update the attributes of the toolbar bar element .
* /
updateBarAttributes : function ( ) {
var isOriented = this . model . get ( 'isOriented' ) ;
if ( isOriented ) {
this . $el . find ( '.toolbar-bar' ) . attr ( 'data-offset-top' , '' ) ;
}
else {
this . $el . find ( '.toolbar-bar' ) . removeAttr ( 'data-offset-top' ) ;
}
// Toggle between a basic vertical view and a more sophisticated
// horizontal and vertical display of the toolbar bar and trays.
this . $el . toggleClass ( 'toolbar-oriented' , isOriented ) ;
} ,
/ * *
* Updates the orientation of the active tray if necessary .
* /
updateTrayOrientation : function ( ) {
var orientation = this . model . get ( 'orientation' ) ;
// The antiOrientation is used to render the view of action buttons like
// the tray orientation toggle.
var antiOrientation = ( orientation === 'vertical' ) ? 'horizontal' : 'vertical' ;
// Update the orientation of the trays.
var $trays = this . $el . find ( '.toolbar-tray' )
. removeClass ( 'toolbar-tray-horizontal toolbar-tray-vertical' )
. addClass ( 'toolbar-tray-' + orientation ) ;
// Update the tray orientation toggle button.
var iconClass = 'toolbar-icon-toggle-' + orientation ;
var iconAntiClass = 'toolbar-icon-toggle-' + antiOrientation ;
var $orientationToggle = this . $el . find ( '.toolbar-toggle-orientation' )
. toggle ( this . model . get ( 'isTrayToggleVisible' ) ) ;
$orientationToggle . find ( 'button' )
. val ( antiOrientation )
. attr ( 'title' , this . strings [ antiOrientation ] )
. text ( this . strings [ antiOrientation ] )
. removeClass ( iconClass )
. addClass ( iconAntiClass ) ;
// Update data offset attributes for the trays.
var dir = document . documentElement . dir ;
var edge = ( dir === 'rtl' ) ? 'right' : 'left' ;
// Remove data-offset attributes from the trays so they can be refreshed.
$trays . removeAttr ( 'data-offset-left data-offset-right data-offset-top' ) ;
// If an active vertical tray exists, mark it as an offset element.
$trays . filter ( '.toolbar-tray-vertical.is-active' ) . attr ( 'data-offset-' + edge , '' ) ;
// If an active horizontal tray exists, mark it as an offset element.
$trays . filter ( '.toolbar-tray-horizontal.is-active' ) . attr ( 'data-offset-top' , '' ) ;
} ,
/ * *
* Sets the tops of the trays so that they align with the bottom of the bar .
* /
adjustPlacement : function ( ) {
var $trays = this . $el . find ( '.toolbar-tray' ) ;
if ( ! this . model . get ( 'isOriented' ) ) {
2015-09-04 13:20:09 -07:00
$trays . css ( 'margin-top' , 0 ) ;
2015-08-17 17:00:26 -07:00
$trays . removeClass ( 'toolbar-tray-horizontal' ) . addClass ( 'toolbar-tray-vertical' ) ;
}
else {
// The toolbar container is invisible. Its placement is used to
// determine the container for the trays.
2015-09-04 13:20:09 -07:00
$trays . css ( 'margin-top' , this . $el . find ( '.toolbar-bar' ) . outerHeight ( ) ) ;
2015-08-17 17:00:26 -07:00
}
} ,
/ * *
2015-08-27 12:03:05 -07:00
* Calls the endpoint URI that builds an AJAX command with the rendered
* subtrees .
2015-08-17 17:00:26 -07:00
*
* The rendered admin menu subtrees HTML is cached on the client in
* localStorage until the cache of the admin menu subtrees on the server -
* side is invalidated . The subtreesHash is stored in localStorage as well
* and compared to the subtreesHash in drupalSettings to determine when the
* admin menu subtrees cache has been invalidated .
* /
loadSubtrees : function ( ) {
var $activeTab = $ ( this . model . get ( 'activeTab' ) ) ;
var orientation = this . model . get ( 'orientation' ) ;
// Only load and render the admin menu subtrees if:
// (1) They have not been loaded yet.
// (2) The active tab is the administration menu tab, indicated by the
// presence of the data-drupal-subtrees attribute.
// (3) The orientation of the tray is vertical.
if ( ! this . model . get ( 'areSubtreesLoaded' ) && typeof $activeTab . data ( 'drupal-subtrees' ) !== 'undefined' && orientation === 'vertical' ) {
var subtreesHash = drupalSettings . toolbar . subtreesHash ;
2015-08-27 12:03:05 -07:00
var theme = drupalSettings . ajaxPageState . theme ;
var endpoint = Drupal . url ( 'toolbar/subtrees/' + subtreesHash ) ;
var cachedSubtreesHash = localStorage . getItem ( 'Drupal.toolbar.subtreesHash.' + theme ) ;
var cachedSubtrees = JSON . parse ( localStorage . getItem ( 'Drupal.toolbar.subtrees.' + theme ) ) ;
2015-08-17 17:00:26 -07:00
var isVertical = this . model . get ( 'orientation' ) === 'vertical' ;
// If we have the subtrees in localStorage and the subtree hash has not
// changed, then use the cached data.
if ( isVertical && subtreesHash === cachedSubtreesHash && cachedSubtrees ) {
Drupal . toolbar . setSubtrees . resolve ( cachedSubtrees ) ;
}
// Only make the call to get the subtrees if the orientation of the
// toolbar is vertical.
else if ( isVertical ) {
// Remove the cached menu information.
2015-08-27 12:03:05 -07:00
localStorage . removeItem ( 'Drupal.toolbar.subtreesHash.' + theme ) ;
localStorage . removeItem ( 'Drupal.toolbar.subtrees.' + theme ) ;
// The AJAX response's command will trigger the resolve method of the
2015-08-17 17:00:26 -07:00
// Drupal.toolbar.setSubtrees Promise.
2015-08-27 12:03:05 -07:00
Drupal . ajax ( { url : endpoint } ) . execute ( ) ;
2015-08-17 17:00:26 -07:00
// Cache the hash for the subtrees locally.
2015-08-27 12:03:05 -07:00
localStorage . setItem ( 'Drupal.toolbar.subtreesHash.' + theme , subtreesHash ) ;
2015-08-17 17:00:26 -07:00
}
}
}
} ) ;
} ( jQuery , Drupal , drupalSettings , Backbone ) ) ;