6f10f2e92da654e63b167de3aa780cbee4084d2f
[moodle.git] / mod / quiz / yui / src / util / js / slot.js
1 /**
2  * A collection of utility classes for use with slots.
3  *
4  * @module moodle-mod_quiz-util
5  * @submodule moodle-mod_quiz-util-slot
6  */
8 Y.namespace('Moodle.mod_quiz.util.slot');
10 /**
11  * A collection of utility classes for use with slots.
12  *
13  * @class Moodle.mod_quiz.util.slot
14  * @static
15  */
16 Y.Moodle.mod_quiz.util.slot = {
17     CSS: {
18         SLOT : 'slot',
19         QUESTIONTYPEDESCRIPTION : 'qtype_description'
20     },
21     CONSTANTS: {
22         SLOTIDPREFIX : 'slot-',
23         QUESTION : M.util.get_string('question', 'moodle')
24     },
25     SELECTORS: {
26         SLOT: 'li.slot',
27         INSTANCENAME: '.instancename',
28         NUMBER: 'span.slotnumber',
29         PAGECONTENT : 'div#page-content',
30         PAGEBREAK : 'span.page_split_join_wrapper',
31         ICON : 'img.smallicon',
32         QUESTIONTYPEDESCRIPTION : '.qtype_description',
33         SECTIONUL : 'ul.section'
34     },
36     /**
37      * Retrieve the slot item from one of it's child Nodes.
38      *
39      * @method getSlotFromComponent
40      * @param slotcomponent {Node} The component Node.
41      * @return {Node|null} The Slot Node.
42      */
43     getSlotFromComponent: function(slotcomponent) {
44         return Y.one(slotcomponent).ancestor(this.SELECTORS.SLOT, true);
45     },
47     /**
48      * Determines the slot ID for the provided slot.
49      *
50      * @method getId
51      * @param slot {Node} The slot to find an ID for.
52      * @return {Number|false} The ID of the slot in question or false if no ID was found.
53      */
54     getId: function(slot) {
55         // We perform a simple substitution operation to get the ID.
56         var id = slot.get('id').replace(
57                 this.CONSTANTS.SLOTIDPREFIX, '');
59         // Attempt to validate the ID.
60         id = parseInt(id, 10);
61         if (typeof id === 'number' && isFinite(id)) {
62             return id;
63         }
64         return false;
65     },
67     /**
68      * Determines the slot name for the provided slot.
69      *
70      * @method getName
71      * @param slot {Node} The slot to find a name for.
72      * @return {string|false} The name of the slot in question or false if no ID was found.
73      */
74     getName: function(slot) {
75         var instance = slot.one(this.SELECTORS.INSTANCENAME);
76         if (instance) {
77             return instance.get('firstChild').get('data');
78         }
79         return null;
80     },
82     /**
83      * Determines the slot number for the provided slot.
84      *
85      * @method getNumber
86      * @param slot {Node} The slot to find the number for.
87      * @return {Number|false} The number of the slot in question or false if no number was found.
88      */
89     getNumber: function(slot) {
90         if (!slot) {
91             return false;
92         }
93         // We perform a simple substitution operation to get the number.
94         var number = slot.one(this.SELECTORS.NUMBER).get('text').replace(
95                         this.CONSTANTS.QUESTION, '');
96         // Attempt to validate the ID.
97         number = parseInt(number, 10);
98         if (typeof number === 'number' && isFinite(number)) {
99             return number;
100         }
101         return false;
102     },
104     /**
105      * Updates the slot number for the provided slot.
106      *
107      * @method setNumber
108      * @param slot {Node} The slot to update the number for.
109      * @return void
110      */
111     setNumber: function(slot, number) {
112         var numbernode = slot.one(this.SELECTORS.NUMBER);
113         numbernode.setHTML('<span class="accesshide">' + this.CONSTANTS.QUESTION + '</span> ' + number);
114     },
116     /**
117      * Returns a list of all slot elements on the page.
118      *
119      * @method getSlots
120      * @return {node[]} An array containing slot nodes.
121      */
122     getSlots: function() {
123         return Y.all(this.SELECTORS.PAGECONTENT + ' ' + this.SELECTORS.SECTIONUL + ' ' + this.SELECTORS.SLOT);
124     },
126     /**
127      * Returns a list of all slot elements on the page that have numbers. Excudes description questions.
128      *
129      * @method getSlots
130      * @return {node[]} An array containing slot nodes.
131      */
132     getNumberedSlots: function() {
133         var selector = this.SELECTORS.PAGECONTENT + ' ' + this.SELECTORS.SECTIONUL;
134             selector += ' ' + this.SELECTORS.SLOT + ':not(' + this.SELECTORS.QUESTIONTYPEDESCRIPTION + ')';
135         return Y.all(selector);
136     },
138     /**
139      * Returns the previous slot to the given slot.
140      *
141      * @method getPrevious
142      * @param slot Slot node
143      * @return {node|false} The previous slot node or false.
144      */
145     getPrevious: function(slot) {
146         return slot.previous(this.SELECTORS.SLOT);
147     },
149     /**
150      * Returns the previous numbered slot to the given slot.
151      *
152      * Ignores slots containing description question types.
153      *
154      * @method getPrevious
155      * @param slot Slot node
156      * @return {node|false} The previous slot node or false.
157      */
158     getPreviousNumbered: function(slot) {
159         return slot.previous(this.SELECTORS.SLOT + ':not(' + this.SELECTORS.QUESTIONTYPEDESCRIPTION + ')');
160     },
162     /**
163      * Reset the order of the numbers given to each slot.
164      *
165      * @method reorderSlots
166      * @return void
167      */
168     reorderSlots: function() {
169         // Get list of slot nodes.
170         var slots = this.getSlots();
171         // Loop through slots incrementing the number each time.
172         slots.each(function(slot) {
174             if (!Y.Moodle.mod_quiz.util.page.getPageFromSlot(slot)) {
175                 // Move the next page to the front.
176                 var nextpage = slot.next(Y.Moodle.mod_quiz.util.page.SELECTORS.PAGE);
177                 slot.swap(nextpage);
178             }
180             var previousSlot = this.getPreviousNumbered(slot);
181             previousslotnumber = 0;
182             if (slot.hasClass(this.CSS.QUESTIONTYPEDESCRIPTION)) {
183                 return;
184             }
186             if (previousSlot) {
187                 previousslotnumber = this.getNumber(previousSlot);
188             }
190             // Set slot number.
191             this.setNumber(slot, previousslotnumber + 1);
192         }, this);
193     },
195     /**
196      * Remove a slot and related elements from the list of slots.
197      *
198      * @method remove
199      * @param slot Slot node
200      * @return void
201      */
202     remove: function(slot) {
203         var page = Y.Moodle.mod_quiz.util.page.getPageFromSlot(slot);
204         slot.remove();
205         // Is the page empty.
206         if (!Y.Moodle.mod_quiz.util.page.isEmpty(page)) {
207             return;
208         }
209         // If so remove it. Including add menu and page break.
210         Y.Moodle.mod_quiz.util.page.remove(page);
211     },
213     /**
214      * Returns a list of all page break elements on the page.
215      *
216      * @method getPageBreaks
217      * @return {node[]} An array containing page break nodes.
218      */
219     getPageBreaks: function() {
220         var selector = this.SELECTORS.PAGECONTENT + ' ' + this.SELECTORS.SECTIONUL;
221             selector += ' ' + this.SELECTORS.SLOT + this.SELECTORS.PAGEBREAK;
222         return Y.all(selector);
223     },
225     /**
226      * Retrieve the page break element item from the given slot.
227      *
228      * @method getPageBreak
229      * @param slot Slot node
230      * @return {Node|null} The Page Break Node.
231      */
232     getPageBreak: function(slot) {
233         return Y.one(slot).one(this.SELECTORS.PAGEBREAK);
234     },
236     /**
237      * Add a page break and related elements to the list of slots.
238      *
239      * @method addPageBreak
240      * @param beforenode Int | Node | HTMLElement | String to add
241      * @return pagebreak PageBreak node
242      */
243     addPageBreak: function(slot) {
244         var nodetext = M.mod_quiz.resource_toolbox.get('config').addpageiconhtml;
245         nodetext = nodetext.replace('%%SLOT%%', this.getNumber(slot));
246         var pagebreak = Y.Node.create(nodetext);
247         slot.one('div').insert(pagebreak, 'after');
248         return pagebreak;
249     },
251     /**
252      * Remove a pagebreak from the given slot.
253      *
254      * @method removePageBreak
255      * @param slot Slot node
256      * @return boolean
257      */
258     removePageBreak: function(slot) {
259         var pagebreak = this.getPageBreak(slot);
260         if (!pagebreak) {
261             return false;
262         }
263         pagebreak.remove();
264         return true;
265     },
267     /**
268      * Reorder each pagebreak by iterating through each related slot.
269      *
270      * @method reorderPageBreaks
271      * @return void
272      */
273     reorderPageBreaks: function() {
274         // Get list of slot nodes.
275         var slots = this.getSlots(), slotnumber = 0;
276         // Loop through slots incrementing the number each time.
277         slots.each (function(slot, key) {
278             slotnumber++;
279             var pagebreak = this.getPageBreak(slot);
280             // Last slot won't have a page break.
281             if (!pagebreak && key === slots.size() - 1) {
282                 return;
283             }
285             // No pagebreak and not last slot. Add one.
286             if (!pagebreak && key !== slots.size() - 1) {
287                 pagebreak = this.addPageBreak(slot);
288             }
290             // Remove last page break if there is one.
291             if (pagebreak && key === slots.size() - 1) {
292                 this.removePageBreak(slot);
293             }
295             // Get page break anchor element.
296             var pagebreaklink = pagebreak.get('childNodes').item(0);
298             // Get the correct title.
299             var nextactivity = slot.next('li.activity');
301             var titlename = '', action = '', uri = M.cfg.wwwroot;
302             var iconsrc = uri + '/theme/image.php?theme=clean&component=core';
303             // IE8 can't handle svg images.
304             if (Y.one('body.ie8')) {
305                 iconsrc += '&svg=e%2F0';
306             }
307             if (Y.Moodle.mod_quiz.util.page.isPage(nextactivity)) {
308                 action = titlename = 'removepagebreak';
309                 iconsrc += '&image=e%2Fremove_page_break';
310             } else {
311                 action = titlename = 'addpagebreak';
312                 iconsrc += '&image=e%2Finsert_page_break';
313             }
314             var title = M.util.get_string(titlename, 'quiz');
316             // Update the link and image titles
317             pagebreaklink.set('title', title);
318             pagebreaklink.setData('action', action);
319             // Update the image title.
320             var icon = pagebreaklink.one(this.SELECTORS.ICON);
321             icon.set('title', title);
323             // Update the image src.
324             icon.set('src', iconsrc);
326             // Get anchor url parameters as an associative array.
327             var params = Y.QueryString.parse(pagebreaklink.get('href'));
328             // Update slot number.
329             params.slot = slotnumber;
330             // Create the new url.
331             var newurl = '';
332             for (var index in params) {
333                 if (newurl.length) {
334                     newurl += "&";
335                 }
336                 newurl += index + "=" + params[index];
337             }
338             // Update the anchor.
339             pagebreaklink.set('href', newurl);
340         }, this);
341     }
342 };