MDL-69520 core_h5p: unit tests for covering example and tutorial
[moodle.git] / admin / tool / usertours / amd / src / usertours.js
1 /**
2  * User tour control library.
3  *
4  * @module     tool_usertours/usertours
5  * @class      usertours
6  * @package    tool_usertours
7  * @copyright  2016 Andrew Nicols <andrew@nicols.co.uk>
8  */
9 define(
10 ['core/ajax', 'tool_usertours/tour', 'jquery', 'core/templates', 'core/str', 'core/log', 'core/notification'],
11 function(ajax, BootstrapTour, $, templates, str, log, notification) {
12     var usertours = {
13         tourId: null,
15         currentTour: null,
17         context: null,
19         /**
20          * Initialise the user tour for the current page.
21          *
22          * @method  init
23          * @param   {Number}    tourId      The ID of the tour to start.
24          * @param   {Bool}      startTour   Attempt to start the tour now.
25          * @param   {Number}    context     The context of the current page.
26          */
27         init: function(tourId, startTour, context) {
28             // Only one tour per page is allowed.
29             usertours.tourId = tourId;
31             usertours.context = context;
33             if (typeof startTour === 'undefined') {
34                 startTour = true;
35             }
37             if (startTour) {
38                 // Fetch the tour configuration.
39                 usertours.fetchTour(tourId);
40             }
42             usertours.addResetLink();
43             // Watch for the reset link.
44             $('body').on('click', '[data-action="tool_usertours/resetpagetour"]', function(e) {
45                 e.preventDefault();
46                 usertours.resetTourState(usertours.tourId);
47             });
48         },
50         /**
51          * Fetch the configuration specified tour, and start the tour when it has been fetched.
52          *
53          * @method  fetchTour
54          * @param   {Number}    tourId      The ID of the tour to start.
55          */
56         fetchTour: function(tourId) {
57             M.util.js_pending('admin_usertour_fetchTour' + tourId);
58             $.when(
59                 ajax.call([
60                     {
61                         methodname: 'tool_usertours_fetch_and_start_tour',
62                         args: {
63                             tourid:     tourId,
64                             context:    usertours.context,
65                             pageurl:    window.location.href,
66                         }
67                     }
68                 ])[0],
69                 templates.render('tool_usertours/tourstep', {})
70             )
71             .then(function(response, template) {
72                 // If we don't have any tour config (because it doesn't need showing for the current user), return early.
73                 if (!response.hasOwnProperty('tourconfig')) {
74                     return;
75                 }
77                 return usertours.startBootstrapTour(tourId, template[0], response.tourconfig);
78             })
79             .always(function() {
80                 M.util.js_complete('admin_usertour_fetchTour' + tourId);
82                 return;
83             })
84             .fail(notification.exception);
85         },
87         /**
88          * Add a reset link to the page.
89          *
90          * @method  addResetLink
91          */
92         addResetLink: function() {
93             var ele;
94             M.util.js_pending('admin_usertour_addResetLink');
96             // Append the link to the most suitable place on the page
97             // with fallback to legacy selectors and finally the body
98             // if there is no better place.
99             if ($('.tool_usertours-resettourcontainer').length) {
100                 ele = $('.tool_usertours-resettourcontainer');
101             } else if ($('.logininfo').length) {
102                 ele = $('.logininfo');
103             } else if ($('footer').length) {
104                 ele = $('footer');
105             } else {
106                 ele = $('body');
107             }
108             templates.render('tool_usertours/resettour', {})
109             .then(function(html, js) {
110                 templates.appendNodeContents(ele, html, js);
112                 return;
113             })
114             .always(function() {
115                 M.util.js_complete('admin_usertour_addResetLink');
117                 return;
118             })
119             .fail();
120         },
122         /**
123          * Start the specified tour.
124          *
125          * @method  startBootstrapTour
126          * @param   {Number}    tourId      The ID of the tour to start.
127          * @param   {String}    template    The template to use.
128          * @param   {Object}    tourConfig  The tour configuration.
129          * @return  {Object}
130          */
131         startBootstrapTour: function(tourId, template, tourConfig) {
132             if (usertours.currentTour) {
133                 // End the current tour, but disable end tour handler.
134                 tourConfig.onEnd = null;
135                 usertours.currentTour.endTour();
136                 delete usertours.currentTour;
137             }
139             // Normalize for the new library.
140             tourConfig.eventHandlers = {
141                 afterEnd: [usertours.markTourComplete],
142                 afterRender: [usertours.markStepShown],
143             };
145             // Sort out the tour name.
146             tourConfig.tourName = tourConfig.name;
147             delete tourConfig.name;
149             // Add the template to the configuration.
150             // This enables translations of the buttons.
151             tourConfig.template = template;
153             tourConfig.steps = tourConfig.steps.map(function(step) {
154                 if (typeof step.element !== 'undefined') {
155                     step.target = step.element;
156                     delete step.element;
157                 }
159                 if (typeof step.reflex !== 'undefined') {
160                     step.moveOnClick = !!step.reflex;
161                     delete step.reflex;
162                 }
164                 if (typeof step.content !== 'undefined') {
165                     step.body = step.content;
166                     delete step.content;
167                 }
169                 return step;
170             });
172             usertours.currentTour = new BootstrapTour(tourConfig);
173             return usertours.currentTour.startTour();
174         },
176         /**
177          * Mark the specified step as being shownd by the user.
178          *
179          * @method  markStepShown
180          */
181         markStepShown: function() {
182             var stepConfig = this.getStepConfig(this.getCurrentStepNumber());
183             $.when(
184                 ajax.call([
185                     {
186                         methodname: 'tool_usertours_step_shown',
187                         args: {
188                             tourid:     usertours.tourId,
189                             context:    usertours.context,
190                             pageurl:    window.location.href,
191                             stepid:     stepConfig.stepid,
192                             stepindex:  this.getCurrentStepNumber(),
193                         }
194                     }
195                 ])[0]
196             ).fail(log.error);
197         },
199         /**
200          * Mark the specified tour as being completed by the user.
201          *
202          * @method  markTourComplete
203          */
204         markTourComplete: function() {
205             var stepConfig = this.getStepConfig(this.getCurrentStepNumber());
206             $.when(
207                 ajax.call([
208                     {
209                         methodname: 'tool_usertours_complete_tour',
210                         args: {
211                             tourid:     usertours.tourId,
212                             context:    usertours.context,
213                             pageurl:    window.location.href,
214                             stepid:     stepConfig.stepid,
215                             stepindex:  this.getCurrentStepNumber(),
216                         }
217                     }
218                 ])[0]
219             ).fail(log.error);
220         },
222         /**
223          * Reset the state, and restart the the tour on the current page.
224          *
225          * @method  resetTourState
226          * @param   {Number}    tourId      The ID of the tour to start.
227          */
228         resetTourState: function(tourId) {
229             $.when(
230                 ajax.call([
231                     {
232                         methodname: 'tool_usertours_reset_tour',
233                         args: {
234                             tourid:     tourId,
235                             context:    usertours.context,
236                             pageurl:    window.location.href,
237                         }
238                     }
239                 ])[0]
240             ).then(function(response) {
241                 if (response.startTour) {
242                     usertours.fetchTour(response.startTour);
243                 }
244                 return;
245             }).fail(notification.exception);
246         }
247     };
249     return /** @alias module:tool_usertours/usertours */ {
250         /**
251          * Initialise the user tour for the current page.
252          *
253          * @method  init
254          * @param   {Number}    tourId      The ID of the tour to start.
255          * @param   {Bool}      startTour   Attempt to start the tour now.
256          */
257         init: usertours.init,
259         /**
260          * Reset the state, and restart the the tour on the current page.
261          *
262          * @method  resetTourState
263          * @param   {Number}    tourId      The ID of the tour to restart.
264          */
265         resetTourState: usertours.resetTourState
266     };
267 });