Mercurial > hg > isophonics-drupal-site
comparison 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 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 /** | |
2 * @file | |
3 * Attaches behaviors for the Tour module's toolbar tab. | |
4 */ | |
5 | |
6 (function ($, Backbone, Drupal, document) { | |
7 const queryString = decodeURI(window.location.search); | |
8 | |
9 /** | |
10 * Attaches the tour's toolbar tab behavior. | |
11 * | |
12 * It uses the query string for: | |
13 * - tour: When ?tour=1 is present, the tour will start automatically after | |
14 * the page has loaded. | |
15 * - tips: Pass ?tips=class in the url to filter the available tips to the | |
16 * subset which match the given class. | |
17 * | |
18 * @example | |
19 * http://example.com/foo?tour=1&tips=bar | |
20 * | |
21 * @type {Drupal~behavior} | |
22 * | |
23 * @prop {Drupal~behaviorAttach} attach | |
24 * Attach tour functionality on `tour` events. | |
25 */ | |
26 Drupal.behaviors.tour = { | |
27 attach(context) { | |
28 $('body').once('tour').each(() => { | |
29 const model = new Drupal.tour.models.StateModel(); | |
30 new Drupal.tour.views.ToggleTourView({ | |
31 el: $(context).find('#toolbar-tab-tour'), | |
32 model, | |
33 }); | |
34 | |
35 model | |
36 // Allow other scripts to respond to tour events. | |
37 .on('change:isActive', (model, isActive) => { | |
38 $(document).trigger((isActive) ? 'drupalTourStarted' : 'drupalTourStopped'); | |
39 }) | |
40 // Initialization: check whether a tour is available on the current | |
41 // page. | |
42 .set('tour', $(context).find('ol#tour')); | |
43 | |
44 // Start the tour immediately if toggled via query string. | |
45 if (/tour=?/i.test(queryString)) { | |
46 model.set('isActive', true); | |
47 } | |
48 }); | |
49 }, | |
50 }; | |
51 | |
52 /** | |
53 * @namespace | |
54 */ | |
55 Drupal.tour = Drupal.tour || { | |
56 | |
57 /** | |
58 * @namespace Drupal.tour.models | |
59 */ | |
60 models: {}, | |
61 | |
62 /** | |
63 * @namespace Drupal.tour.views | |
64 */ | |
65 views: {}, | |
66 }; | |
67 | |
68 /** | |
69 * Backbone Model for tours. | |
70 * | |
71 * @constructor | |
72 * | |
73 * @augments Backbone.Model | |
74 */ | |
75 Drupal.tour.models.StateModel = Backbone.Model.extend(/** @lends Drupal.tour.models.StateModel# */{ | |
76 | |
77 /** | |
78 * @type {object} | |
79 */ | |
80 defaults: /** @lends Drupal.tour.models.StateModel# */{ | |
81 | |
82 /** | |
83 * Indicates whether the Drupal root window has a tour. | |
84 * | |
85 * @type {Array} | |
86 */ | |
87 tour: [], | |
88 | |
89 /** | |
90 * Indicates whether the tour is currently running. | |
91 * | |
92 * @type {bool} | |
93 */ | |
94 isActive: false, | |
95 | |
96 /** | |
97 * Indicates which tour is the active one (necessary to cleanly stop). | |
98 * | |
99 * @type {Array} | |
100 */ | |
101 activeTour: [], | |
102 }, | |
103 }); | |
104 | |
105 Drupal.tour.views.ToggleTourView = Backbone.View.extend(/** @lends Drupal.tour.views.ToggleTourView# */{ | |
106 | |
107 /** | |
108 * @type {object} | |
109 */ | |
110 events: { click: 'onClick' }, | |
111 | |
112 /** | |
113 * Handles edit mode toggle interactions. | |
114 * | |
115 * @constructs | |
116 * | |
117 * @augments Backbone.View | |
118 */ | |
119 initialize() { | |
120 this.listenTo(this.model, 'change:tour change:isActive', this.render); | |
121 this.listenTo(this.model, 'change:isActive', this.toggleTour); | |
122 }, | |
123 | |
124 /** | |
125 * @inheritdoc | |
126 * | |
127 * @return {Drupal.tour.views.ToggleTourView} | |
128 * The `ToggleTourView` view. | |
129 */ | |
130 render() { | |
131 // Render the visibility. | |
132 this.$el.toggleClass('hidden', this._getTour().length === 0); | |
133 // Render the state. | |
134 const isActive = this.model.get('isActive'); | |
135 this.$el.find('button') | |
136 .toggleClass('is-active', isActive) | |
137 .prop('aria-pressed', isActive); | |
138 return this; | |
139 }, | |
140 | |
141 /** | |
142 * Model change handler; starts or stops the tour. | |
143 */ | |
144 toggleTour() { | |
145 if (this.model.get('isActive')) { | |
146 const $tour = this._getTour(); | |
147 this._removeIrrelevantTourItems($tour, this._getDocument()); | |
148 const that = this; | |
149 if ($tour.find('li').length) { | |
150 $tour.joyride({ | |
151 autoStart: true, | |
152 postRideCallback() { | |
153 that.model.set('isActive', false); | |
154 }, | |
155 // HTML segments for tip layout. | |
156 template: { | |
157 link: '<a href=\"#close\" class=\"joyride-close-tip\">×</a>', | |
158 button: '<a href=\"#\" class=\"button button--primary joyride-next-tip\"></a>', | |
159 }, | |
160 }); | |
161 this.model.set({ isActive: true, activeTour: $tour }); | |
162 } | |
163 } | |
164 else { | |
165 this.model.get('activeTour').joyride('destroy'); | |
166 this.model.set({ isActive: false, activeTour: [] }); | |
167 } | |
168 }, | |
169 | |
170 /** | |
171 * Toolbar tab click event handler; toggles isActive. | |
172 * | |
173 * @param {jQuery.Event} event | |
174 * The click event. | |
175 */ | |
176 onClick(event) { | |
177 this.model.set('isActive', !this.model.get('isActive')); | |
178 event.preventDefault(); | |
179 event.stopPropagation(); | |
180 }, | |
181 | |
182 /** | |
183 * Gets the tour. | |
184 * | |
185 * @return {jQuery} | |
186 * A jQuery element pointing to a `<ol>` containing tour items. | |
187 */ | |
188 _getTour() { | |
189 return this.model.get('tour'); | |
190 }, | |
191 | |
192 /** | |
193 * Gets the relevant document as a jQuery element. | |
194 * | |
195 * @return {jQuery} | |
196 * A jQuery element pointing to the document within which a tour would be | |
197 * started given the current state. | |
198 */ | |
199 _getDocument() { | |
200 return $(document); | |
201 }, | |
202 | |
203 /** | |
204 * Removes tour items for elements that don't have matching page elements. | |
205 * | |
206 * Or that are explicitly filtered out via the 'tips' query string. | |
207 * | |
208 * @example | |
209 * <caption>This will filter out tips that do not have a matching | |
210 * page element or don't have the "bar" class.</caption> | |
211 * http://example.com/foo?tips=bar | |
212 * | |
213 * @param {jQuery} $tour | |
214 * A jQuery element pointing to a `<ol>` containing tour items. | |
215 * @param {jQuery} $document | |
216 * A jQuery element pointing to the document within which the elements | |
217 * should be sought. | |
218 * | |
219 * @see Drupal.tour.views.ToggleTourView#_getDocument | |
220 */ | |
221 _removeIrrelevantTourItems($tour, $document) { | |
222 let removals = false; | |
223 const tips = /tips=([^&]+)/.exec(queryString); | |
224 $tour | |
225 .find('li') | |
226 .each(function () { | |
227 const $this = $(this); | |
228 const itemId = $this.attr('data-id'); | |
229 const itemClass = $this.attr('data-class'); | |
230 // If the query parameter 'tips' is set, remove all tips that don't | |
231 // have the matching class. | |
232 if (tips && !$(this).hasClass(tips[1])) { | |
233 removals = true; | |
234 $this.remove(); | |
235 return; | |
236 } | |
237 // Remove tip from the DOM if there is no corresponding page element. | |
238 if ((!itemId && !itemClass) || | |
239 (itemId && $document.find(`#${itemId}`).length) || | |
240 (itemClass && $document.find(`.${itemClass}`).length)) { | |
241 return; | |
242 } | |
243 removals = true; | |
244 $this.remove(); | |
245 }); | |
246 | |
247 // If there were removals, we'll have to do some clean-up. | |
248 if (removals) { | |
249 const total = $tour.find('li').length; | |
250 if (!total) { | |
251 this.model.set({ tour: [] }); | |
252 } | |
253 | |
254 $tour | |
255 .find('li') | |
256 // Rebuild the progress data. | |
257 .each(function (index) { | |
258 const progress = Drupal.t('!tour_item of !total', { '!tour_item': index + 1, '!total': total }); | |
259 $(this).find('.tour-progress').text(progress); | |
260 }) | |
261 // Update the last item to have "End tour" as the button. | |
262 .eq(-1) | |
263 .attr('data-text', Drupal.t('End tour')); | |
264 } | |
265 }, | |
266 | |
267 }); | |
268 }(jQuery, Backbone, Drupal, document)); |