Mercurial > hg > isophonics-drupal-site
diff core/modules/tour/js/tour.es6.js @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 1fec387a4317 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/modules/tour/js/tour.es6.js Wed Nov 29 16:09:58 2017 +0000 @@ -0,0 +1,268 @@ +/** + * @file + * Attaches behaviors for the Tour module's toolbar tab. + */ + +(function ($, Backbone, Drupal, document) { + const queryString = decodeURI(window.location.search); + + /** + * Attaches the tour's toolbar tab behavior. + * + * It uses the query string for: + * - 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. + * + * @example + * http://example.com/foo?tour=1&tips=bar + * + * @type {Drupal~behavior} + * + * @prop {Drupal~behaviorAttach} attach + * Attach tour functionality on `tour` events. + */ + Drupal.behaviors.tour = { + attach(context) { + $('body').once('tour').each(() => { + const model = new Drupal.tour.models.StateModel(); + new Drupal.tour.views.ToggleTourView({ + el: $(context).find('#toolbar-tab-tour'), + model, + }); + + model + // Allow other scripts to respond to tour events. + .on('change:isActive', (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} + */ + events: { click: 'onClick' }, + + /** + * Handles edit mode toggle interactions. + * + * @constructs + * + * @augments Backbone.View + */ + initialize() { + this.listenTo(this.model, 'change:tour change:isActive', this.render); + this.listenTo(this.model, 'change:isActive', this.toggleTour); + }, + + /** + * @inheritdoc + * + * @return {Drupal.tour.views.ToggleTourView} + * The `ToggleTourView` view. + */ + render() { + // Render the visibility. + this.$el.toggleClass('hidden', this._getTour().length === 0); + // Render the state. + const 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() { + if (this.model.get('isActive')) { + const $tour = this._getTour(); + this._removeIrrelevantTourItems($tour, this._getDocument()); + const that = this; + if ($tour.find('li').length) { + $tour.joyride({ + autoStart: true, + postRideCallback() { + 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 + * The click event. + */ + onClick(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() { + 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() { + 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.</caption> + * 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($tour, $document) { + let removals = false; + const tips = /tips=([^&]+)/.exec(queryString); + $tour + .find('li') + .each(function () { + const $this = $(this); + const itemId = $this.attr('data-id'); + const 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) { + const total = $tour.find('li').length; + if (!total) { + this.model.set({ tour: [] }); + } + + $tour + .find('li') + // Rebuild the progress data. + .each(function (index) { + const 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));