Merge branch 'MDL-61218-master' of git://github.com/lameze/moodle
[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                 return usertours.startBootstrapTour(tourId, template[0], response.tourconfig);
73             })
74             .always(function() {
75                 M.util.js_complete('admin_usertour_fetchTour' + tourId);
77                 return;
78             })
79             .fail(notification.exception);
80         },
82         /**
83          * Add a reset link to the page.
84          *
85          * @method  addResetLink
86          */
87         addResetLink: function() {
88             var ele;
89             M.util.js_pending('admin_usertour_addResetLink');
91             // Append the link to the most suitable place on the page
92             // with fallback to legacy selectors and finally the body
93             // if there is no better place.
94             if ($('.tool_usertours-resettourcontainer').length) {
95                 ele = $('.tool_usertours-resettourcontainer');
96             } else if ($('.logininfo').length) {
97                 ele = $('.logininfo');
98             } else if ($('footer').length) {
99                 ele = $('footer');
100             } else {
101                 ele = $('body');
102             }
103             templates.render('tool_usertours/resettour', {})
104             .then(function(html, js) {
105                 templates.appendNodeContents(ele, html, js);
107                 return;
108             })
109             .always(function() {
110                 M.util.js_complete('admin_usertour_addResetLink');
112                 return;
113             })
114             .fail();
115         },
117         /**
118          * Start the specified tour.
119          *
120          * @method  startBootstrapTour
121          * @param   {Number}    tourId      The ID of the tour to start.
122          * @param   {String}    template    The template to use.
123          * @param   {Object}    tourConfig  The tour configuration.
124          * @return  {Object}
125          */
126         startBootstrapTour: function(tourId, template, tourConfig) {
127             if (usertours.currentTour) {
128                 // End the current tour, but disable end tour handler.
129                 tourConfig.onEnd = null;
130                 usertours.currentTour.endTour();
131                 delete usertours.currentTour;
132             }
134             // Normalize for the new library.
135             tourConfig.eventHandlers = {
136                 afterEnd: [usertours.markTourComplete],
137                 afterRender: [usertours.markStepShown],
138             };
140             // Sort out the tour name.
141             tourConfig.tourName = tourConfig.name;
142             delete tourConfig.name;
144             // Add the template to the configuration.
145             // This enables translations of the buttons.
146             tourConfig.template = template;
148             tourConfig.steps = tourConfig.steps.map(function(step) {
149                 if (typeof step.element !== 'undefined') {
150                     step.target = step.element;
151                     delete step.element;
152                 }
154                 if (typeof step.reflex !== 'undefined') {
155                     step.moveOnClick = !!step.reflex;
156                     delete step.reflex;
157                 }
159                 if (typeof step.content !== 'undefined') {
160                     step.body = step.content;
161                     delete step.content;
162                 }
164                 return step;
165             });
167             usertours.currentTour = new BootstrapTour(tourConfig);
168             return usertours.currentTour.startTour();
169         },
171         /**
172          * Mark the specified step as being shownd by the user.
173          *
174          * @method  markStepShown
175          */
176         markStepShown: function() {
177             var stepConfig = this.getStepConfig(this.getCurrentStepNumber());
178             $.when(
179                 ajax.call([
180                     {
181                         methodname: 'tool_usertours_step_shown',
182                         args: {
183                             tourid:     usertours.tourId,
184                             context:    usertours.context,
185                             pageurl:    window.location.href,
186                             stepid:     stepConfig.stepid,
187                             stepindex:  this.getCurrentStepNumber(),
188                         }
189                     }
190                 ])[0]
191             ).fail(log.error);
192         },
194         /**
195          * Mark the specified tour as being completed by the user.
196          *
197          * @method  markTourComplete
198          */
199         markTourComplete: function() {
200             var stepConfig = this.getStepConfig(this.getCurrentStepNumber());
201             $.when(
202                 ajax.call([
203                     {
204                         methodname: 'tool_usertours_complete_tour',
205                         args: {
206                             tourid:     usertours.tourId,
207                             context:    usertours.context,
208                             pageurl:    window.location.href,
209                             stepid:     stepConfig.stepid,
210                             stepindex:  this.getCurrentStepNumber(),
211                         }
212                     }
213                 ])[0]
214             ).fail(log.error);
215         },
217         /**
218          * Reset the state, and restart the the tour on the current page.
219          *
220          * @method  resetTourState
221          * @param   {Number}    tourId      The ID of the tour to start.
222          */
223         resetTourState: function(tourId) {
224             $.when(
225                 ajax.call([
226                     {
227                         methodname: 'tool_usertours_reset_tour',
228                         args: {
229                             tourid:     tourId,
230                             context:    usertours.context,
231                             pageurl:    window.location.href,
232                         }
233                     }
234                 ])[0]
235             ).then(function(response) {
236                 if (response.startTour) {
237                     usertours.fetchTour(response.startTour);
238                 }
239                 return;
240             }).fail(notification.exception);
241         }
242     };
244     return /** @alias module:tool_usertours/usertours */ {
245         /**
246          * Initialise the user tour for the current page.
247          *
248          * @method  init
249          * @param   {Number}    tourId      The ID of the tour to start.
250          * @param   {Bool}      startTour   Attempt to start the tour now.
251          */
252         init: usertours.init,
254         /**
255          * Reset the state, and restart the the tour on the current page.
256          *
257          * @method  resetTourState
258          * @param   {Number}    tourId      The ID of the tour to restart.
259          */
260         resetTourState: usertours.resetTourState
261     };
262 });