MDL-49813 quiz: add sections in the right place after page changes
[moodle.git] / mod / quiz / yui / src / util / js / page.js
bloba7469fc89586e6a18ee3c21f1fda1df1903b9f8a
1 /**
2  * A collection of utility classes for use with pages.
3  *
4  * @module moodle-mod_quiz-util
5  * @submodule moodle-mod_quiz-util-page
6  */
8 Y.namespace('Moodle.mod_quiz.util.page');
10 /**
11  * A collection of utility classes for use with pages.
12  *
13  * @class Moodle.mod_quiz.util.page
14  * @static
15  */
16 Y.Moodle.mod_quiz.util.page = {
17     CSS: {
18         PAGE : 'page'
19     },
20     CONSTANTS: {
21         ACTIONMENUIDPREFIX: 'action-menu-',
22         ACTIONMENUBARIDSUFFIX: '-menubar',
23         ACTIONMENUMENUIDSUFFIX: '-menu',
24         PAGEIDPREFIX: 'page-',
25         PAGENUMBERPREFIX: M.util.get_string('page', 'moodle') + ' '
26     },
27     SELECTORS: {
28         ACTIONMENU: 'div.moodle-actionmenu',
29         ACTIONMENUBAR: 'ul.menubar',
30         ACTIONMENUMENU: 'ul.menu',
31         PAGE: 'li.page',
32         INSTANCENAME: '.instancename',
33         NUMBER: 'h4'
34     },
36     /**
37      * Retrieve the page item from one of it's child Nodes.
38      *
39      * @method getPageFromComponent
40      * @param pagecomponent {Node} The component Node.
41      * @return {Node|null} The Page Node.
42      */
43     getPageFromComponent: function(pagecomponent) {
44         return Y.one(pagecomponent).ancestor(this.SELECTORS.PAGE, true);
45     },
47     /**
48      * Retrieve the page item from one of it's previous siblings.
49      *
50      * @method getPageFromSlot
51      * @param pagecomponent {Node} The component Node.
52      * @return {Node|null} The Page Node.
53      */
54     getPageFromSlot: function(slot) {
55         return Y.one(slot).previous(this.SELECTORS.PAGE);
56     },
58     /**
59      * Returns the page ID for the provided page.
60      *
61      * @method getId
62      * @param page {Node} The page to find an ID for.
63      * @return {Number|false} The ID of the page in question or false if no ID was found.
64      */
65     getId: function(page) {
66         // We perform a simple substitution operation to get the ID.
67         var id = page.get('id').replace(
68                 this.CONSTANTS.PAGEIDPREFIX, '');
70         // Attempt to validate the ID.
71         id = parseInt(id, 10);
72         if (typeof id === 'number' && isFinite(id)) {
73             return id;
74         }
75         return false;
76     },
78     /**
79      * Updates the page id for the provided page.
80      *
81      * @method setId
82      * @param page {Node} The page to update the number for.
83      * @param id int The id value.
84      * @return void
85      */
86     setId: function(page, id) {
87         page.set('id', this.CONSTANTS.PAGEIDPREFIX + id);
88     },
90     /**
91      * Determines the page name for the provided page.
92      *
93      * @method getName
94      * @param page {Node} The page to find a name for.
95      * @return {string|false} The name of the page in question or false if no ID was found.
96      */
97     getName: function(page) {
98         var instance = page.one(this.SELECTORS.INSTANCENAME);
99         if (instance) {
100             return instance.get('firstChild').get('data');
101         }
102         return null;
103     },
105     /**
106      * Determines the page number for the provided page.
107      *
108      * @method getNumber
109      * @param page {Node} The page to find a number for.
110      * @return {Number|false} The number of the page in question or false if no number was found.
111      */
112     getNumber: function(page) {
113         // We perform a simple substitution operation to get the number.
114         var number = page.one(this.SELECTORS.NUMBER).get('text').replace(
115                 this.CONSTANTS.PAGENUMBERPREFIX, '');
117         // Attempt to validate the ID.
118         number = parseInt(number, 10);
119         if (typeof number === 'number' && isFinite(number)) {
120             return number;
121         }
122         return false;
123     },
125     /**
126      * Updates the page number for the provided page.
127      *
128      * @method setNumber
129      * @param page {Node} The page to update the number for.
130      * @return void
131      */
132     setNumber: function(page, number) {
133         page.one(this.SELECTORS.NUMBER).set('text', this.CONSTANTS.PAGENUMBERPREFIX + number);
134     },
136     /**
137      * Returns a list of all page elements.
138      *
139      * @method getPages
140      * @return {node[]} An array containing page nodes.
141      */
142     getPages: function() {
143         return Y.all(Y.Moodle.mod_quiz.util.slot.SELECTORS.PAGECONTENT + ' ' +
144                      Y.Moodle.mod_quiz.util.slot.SELECTORS.SECTIONUL + ' ' +
145                     this.SELECTORS.PAGE);
146     },
148     /**
149      * Is the given element a page element?
150      *
151      * @method isPage
152      * @param page Page node
153      * @return boolean
154      */
155     isPage: function(page) {
156         if (!page) {
157             return false;
158         }
159         return page.hasClass(this.CSS.PAGE);
160     },
162     /**
163      * Does the page have atleast one slot?
164      *
165      * @method isEmpty
166      * @param page Page node
167      * @return boolean
168      */
169     isEmpty: function(page) {
170         var activity = page.next('li.activity');
171         if (!activity) {
172             return true;
173         }
174         return !activity.hasClass('slot');
175     },
177     /**
178      * Add a page and related elements to the list of slots.
179      *
180      * @method add
181      * @param beforenode Int | Node | HTMLElement | String to add
182      * @return page Page node
183      */
184     add: function(beforenode) {
185         var pagenumber = this.getNumber(this.getPageFromSlot(beforenode)) + 1;
186         var pagehtml = M.mod_quiz.resource_toolbox.get('config').pagehtml;
188         // Normalise the page number.
189         pagehtml = pagehtml.replace(/%%PAGENUMBER%%/g, pagenumber);
191         // Create the page node.
192         var page = Y.Node.create(pagehtml);
194         // Assign is as a drop target.
195         YUI().use('dd-drop', function(Y) {
196             var drop = new Y.DD.Drop({
197                 node: page,
198                 groups: M.mod_quiz.dragres.groups
199             });
200             page.drop = drop;
201         });
203         // Insert in the correct place.
204         beforenode.insert(page, 'after');
206         // Enhance the add menu to make if fully visible and clickable.
207         M.core.actionmenu.newDOMNode(page);
208         return page;
209     },
211     /**
212      * Remove a page and related elements from the list of slots.
213      *
214      * @method remove
215      * @param page Page node
216      * @return void
217      */
218     remove: function(page, keeppagebreak) {
219         // Remove page break from previous slot.
220         var previousslot = page.previous(Y.Moodle.mod_quiz.util.slot.SELECTORS.SLOT);
221         if (!keeppagebreak && previousslot) {
222             Y.Moodle.mod_quiz.util.slot.removePageBreak(previousslot);
223         }
224         page.remove();
225     },
227     /**
228      * Reset the order of the numbers given to each page.
229      *
230      * @method reorderPages
231      * @return void
232      */
233     reorderPages: function() {
234         // Get list of page nodes.
235         var pages = this.getPages(), currentpagenumber = 0;
236         // Loop through pages incrementing the number each time.
237         pages.each(function(page) {
238             // Is the page empty?
239             if (this.isEmpty(page)) {
240                 var keeppagebreak = page.next('li.slot') ? true : false;
241                 this.remove(page, keeppagebreak);
242                 return;
243             }
245             currentpagenumber++;
246             // Set page number.
247             this.setNumber(page, currentpagenumber);
248             this.setId(page, currentpagenumber);
249         }, this);
251         // Reorder action menus
252         this.reorderActionMenus();
253     },
255     /**
256      * Reset the order of the numbers given to each action menu.
257      *
258      * @method reorderActionMenus
259      * @return void
260      */
261     reorderActionMenus: function() {
262         // Get list of action menu nodes.
263         var actionmenus = this.getActionMenus();
264         // Loop through pages incrementing the number each time.
265         actionmenus.each(function(actionmenu, key) {
266             var previousActionMenu = actionmenus.item(key - 1),
267                 previousActionMenunumber = 0;
268             if (previousActionMenu) {
269                 previousActionMenunumber = this.getActionMenuId(previousActionMenu);
270             }
271             var id = previousActionMenunumber + 1;
273             // Set menu id.
274             this.setActionMenuId(actionmenu, id);
276             // Update action-menu-1-menubar
277             var menubar = actionmenu.one(this.SELECTORS.ACTIONMENUBAR);
278             menubar.set('id', this.CONSTANTS.ACTIONMENUIDPREFIX + id + this.CONSTANTS.ACTIONMENUBARIDSUFFIX);
280             // Update action-menu-1-menu
281             var menumenu = actionmenu.one(this.SELECTORS.ACTIONMENUMENU);
282             menumenu.set('id', this.CONSTANTS.ACTIONMENUIDPREFIX + id + this.CONSTANTS.ACTIONMENUMENUIDSUFFIX);
284             // Update the URL of the add-section action.
285             menumenu.one('a.addasection').set('href',
286                     menumenu.one('a.addasection').get('href').replace(/\baddsectionatpage=\d/, 'addsectionatpage=' + id));
288         }, this);
289     },
291     /**
292      * Returns a list of all page elements.
293      *
294      * @method getActionMenus
295      * @return {node[]} An array containing page nodes.
296      */
297     getActionMenus: function() {
298         return Y.all(Y.Moodle.mod_quiz.util.slot.SELECTORS.PAGECONTENT + ' ' +
299                      Y.Moodle.mod_quiz.util.slot.SELECTORS.SECTIONUL + ' ' +
300                      this.SELECTORS.ACTIONMENU);
301     },
303     /**
304      * Returns the ID for the provided action menu.
305      *
306      * @method getId
307      * @param actionmenu {Node} The actionmenu to find an ID for.
308      * @return {Number|false} The ID of the actionmenu in question or false if no ID was found.
309      */
310     getActionMenuId: function(actionmenu) {
311         // We perform a simple substitution operation to get the ID.
312         var id = actionmenu.get('id').replace(
313                 this.CONSTANTS.ACTIONMENUIDPREFIX, '');
315         // Attempt to validate the ID.
316         id = parseInt(id, 10);
317         if (typeof id === 'number' && isFinite(id)) {
318             return id;
319         }
320         return false;
321     },
323     /**
324      * Updates the page id for the provided page.
325      *
326      * @method setId
327      * @param page {Node} The page to update the number for.
328      * @param id int The id value.
329      * @return void
330      */
331     setActionMenuId: function(actionmenu, id) {
332         actionmenu.set('id', this.CONSTANTS.ACTIONMENUIDPREFIX + id);
333     }