2015-08-17 17:00:26 -07:00
/ * *
* @ file
* Attaches behaviors for the Tour module ' s toolbar tab .
* /
( function ( $ , Backbone , Drupal , document ) {
"use strict" ;
var queryString = decodeURI ( window . location . search ) ;
/ * *
* Attaches the tour ' s toolbar tab behavior .
*
* It uses the query string for :
2015-09-04 13:20:09 -07:00
* - tour : When ? tour = 1 is present , the tour will start automatically after
* the page has loaded .
* - tips : Pass ? tips = class in the url to filter the available tips to the
* subset which match the given class .
2015-08-17 17:00:26 -07:00
*
* @ example
* http : //example.com/foo?tour=1&tips=bar
*
* @ type { Drupal ~ behavior }
2015-09-04 13:20:09 -07:00
*
* @ prop { Drupal ~ behaviorAttach } attach
* Attach tour functionality on ` tour ` events .
2015-08-17 17:00:26 -07:00
* /
Drupal . behaviors . tour = {
attach : function ( context ) {
$ ( 'body' ) . once ( 'tour' ) . each ( function ( ) {
var model = new Drupal . tour . models . StateModel ( ) ;
new Drupal . tour . views . ToggleTourView ( {
el : $ ( context ) . find ( '#toolbar-tab-tour' ) ,
model : model
} ) ;
model
// Allow other scripts to respond to tour events.
. on ( 'change:isActive' , function ( model , isActive ) {
$ ( document ) . trigger ( ( isActive ) ? 'drupalTourStarted' : 'drupalTourStopped' ) ;
} )
// Initialization: check whether a tour is available on the current
// page.
. set ( 'tour' , $ ( context ) . find ( 'ol#tour' ) ) ;
// Start the tour immediately if toggled via query string.
if ( /tour=?/i . test ( queryString ) ) {
model . set ( 'isActive' , true ) ;
}
} ) ;
}
} ;
/ * *
* @ namespace
* /
Drupal . tour = Drupal . tour || {
/ * *
* @ namespace Drupal . tour . models
* /
models : { } ,
/ * *
* @ namespace Drupal . tour . views
* /
views : { }
} ;
/ * *
* Backbone Model for tours .
*
* @ constructor
*
* @ augments Backbone . Model
* /
Drupal . tour . models . StateModel = Backbone . Model . extend ( /** @lends Drupal.tour.models.StateModel# */ {
/ * *
* @ type { object }
* /
defaults : /** @lends Drupal.tour.models.StateModel# */ {
/ * *
* Indicates whether the Drupal root window has a tour .
*
* @ type { Array }
* /
tour : [ ] ,
/ * *
* Indicates whether the tour is currently running .
*
* @ type { bool }
* /
isActive : false ,
/ * *
* Indicates which tour is the active one ( necessary to cleanly stop ) .
*
* @ type { Array }
* /
activeTour : [ ]
}
} ) ;
Drupal . tour . views . ToggleTourView = Backbone . View . extend ( /** @lends Drupal.tour.views.ToggleTourView# */ {
/ * *
* @ type { object }
* /
2015-09-04 13:20:09 -07:00
events : { click : 'onClick' } ,
2015-08-17 17:00:26 -07:00
/ * *
* Handles edit mode toggle interactions .
*
* @ constructs
*
* @ augments Backbone . View
* /
initialize : function ( ) {
this . listenTo ( this . model , 'change:tour change:isActive' , this . render ) ;
this . listenTo ( this . model , 'change:isActive' , this . toggleTour ) ;
} ,
/ * *
* @ inheritdoc
*
* @ return { Drupal . tour . views . ToggleTourView }
2015-09-04 13:20:09 -07:00
* The ` ToggleTourView ` view .
2015-08-17 17:00:26 -07:00
* /
render : function ( ) {
// Render the visibility.
this . $el . toggleClass ( 'hidden' , this . _getTour ( ) . length === 0 ) ;
// Render the state.
var isActive = this . model . get ( 'isActive' ) ;
this . $el . find ( 'button' )
. toggleClass ( 'is-active' , isActive )
. prop ( 'aria-pressed' , isActive ) ;
return this ;
} ,
/ * *
* Model change handler ; starts or stops the tour .
* /
toggleTour : function ( ) {
if ( this . model . get ( 'isActive' ) ) {
var $tour = this . _getTour ( ) ;
this . _removeIrrelevantTourItems ( $tour , this . _getDocument ( ) ) ;
var that = this ;
if ( $tour . find ( 'li' ) . length ) {
$tour . joyride ( {
autoStart : true ,
postRideCallback : function ( ) { that . model . set ( 'isActive' , false ) ; } ,
// HTML segments for tip layout.
template : {
link : '<a href=\"#close\" class=\"joyride-close-tip\">×</a>' ,
button : '<a href=\"#\" class=\"button button--primary joyride-next-tip\"></a>'
}
} ) ;
this . model . set ( { isActive : true , activeTour : $tour } ) ;
}
}
else {
this . model . get ( 'activeTour' ) . joyride ( 'destroy' ) ;
this . model . set ( { isActive : false , activeTour : [ ] } ) ;
}
} ,
/ * *
* Toolbar tab click event handler ; toggles isActive .
*
* @ param { jQuery . Event } event
2015-09-04 13:20:09 -07:00
* The click event .
2015-08-17 17:00:26 -07:00
* /
onClick : function ( event ) {
this . model . set ( 'isActive' , ! this . model . get ( 'isActive' ) ) ;
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
} ,
/ * *
* Gets the tour .
*
* @ return { jQuery }
* A jQuery element pointing to a ` <ol> ` containing tour items .
* /
_getTour : function ( ) {
return this . model . get ( 'tour' ) ;
} ,
/ * *
* Gets the relevant document as a jQuery element .
*
* @ return { jQuery }
* A jQuery element pointing to the document within which a tour would be
* started given the current state .
* /
_getDocument : function ( ) {
return $ ( document ) ;
} ,
/ * *
* Removes tour items for elements that don ' t have matching page elements .
*
* Or that are explicitly filtered out via the 'tips' query string .
*
* @ example
* < caption > This will filter out tips that do not have a matching
* page element or don ' t have the "bar" class . < / c a p t i o n >
* http : //example.com/foo?tips=bar
*
* @ param { jQuery } $tour
* A jQuery element pointing to a ` <ol> ` containing tour items .
* @ param { jQuery } $document
* A jQuery element pointing to the document within which the elements
* should be sought .
*
* @ see Drupal . tour . views . ToggleTourView # _getDocument
* /
_removeIrrelevantTourItems : function ( $tour , $document ) {
var removals = false ;
var tips = /tips=([^&]+)/ . exec ( queryString ) ;
$tour
. find ( 'li' )
. each ( function ( ) {
var $this = $ ( this ) ;
var itemId = $this . attr ( 'data-id' ) ;
var itemClass = $this . attr ( 'data-class' ) ;
// If the query parameter 'tips' is set, remove all tips that don't
// have the matching class.
if ( tips && ! $ ( this ) . hasClass ( tips [ 1 ] ) ) {
removals = true ;
$this . remove ( ) ;
return ;
}
// Remove tip from the DOM if there is no corresponding page element.
if ( ( ! itemId && ! itemClass ) ||
( itemId && $document . find ( '#' + itemId ) . length ) ||
( itemClass && $document . find ( '.' + itemClass ) . length ) ) {
return ;
}
removals = true ;
$this . remove ( ) ;
} ) ;
// If there were removals, we'll have to do some clean-up.
if ( removals ) {
var total = $tour . find ( 'li' ) . length ;
if ( ! total ) {
this . model . set ( { tour : [ ] } ) ;
}
$tour
. find ( 'li' )
// Rebuild the progress data.
. each ( function ( index ) {
var progress = Drupal . t ( '!tour_item of !total' , { '!tour_item' : index + 1 , '!total' : total } ) ;
$ ( this ) . find ( '.tour-progress' ) . text ( progress ) ;
} )
// Update the last item to have "End tour" as the button.
. eq ( - 1 )
. attr ( 'data-text' , Drupal . t ( 'End tour' ) ) ;
}
}
} ) ;
} ) ( jQuery , Backbone , Drupal , document ) ;