MDL-80544 core_h5p: Add core lib changes after upgrading it
[moodle.git] / h5p / h5plib / v124 / joubel / core / js / h5p-utils.js
blobb5aa3334e0e9659e3c154d6a14e86c11b7d7dbf0
1 /* global H5PAdminIntegration*/
2 var H5PUtils = H5PUtils || {};
4 (function ($) {
5   /**
6    * Generic function for creating a table including the headers
7    *
8    * @param {array} headers List of headers
9    */
10   H5PUtils.createTable = function (headers) {
11     var $table = $('<table class="h5p-admin-table' + (H5PAdminIntegration.extraTableClasses !== undefined ? ' ' + H5PAdminIntegration.extraTableClasses : '') + '"></table>');
13     if (headers) {
14       var $thead = $('<thead></thead>');
15       var $tr = $('<tr></tr>');
17       $.each(headers, function (index, value) {
18         if (!(value instanceof Object)) {
19           value = {
20             html: value
21           };
22         }
24         $('<th/>', value).appendTo($tr);
25       });
27       $table.append($thead.append($tr));
28     }
30     return $table;
31   };
33   /**
34    * Generic function for creating a table row
35    *
36    * @param {array} rows Value list. Object name is used as class name in <TD>
37    */
38   H5PUtils.createTableRow = function (rows) {
39     var $tr = $('<tr></tr>');
41     $.each(rows, function (index, value) {
42       if (!(value instanceof Object)) {
43         value = {
44           html: value
45         };
46       }
48       $('<td/>', value).appendTo($tr);
49     });
51     return $tr;
52   };
54   /**
55    * Generic function for creating a field containing label and value
56    *
57    * @param {string} label The label displayed in front of the value
58    * @param {string} value The value
59    */
60   H5PUtils.createLabeledField = function (label, value) {
61     var $field = $('<div class="h5p-labeled-field"></div>');
63     $field.append('<div class="h5p-label">' + label + '</div>');
64     $field.append('<div class="h5p-value">' + value + '</div>');
66     return $field;
67   };
69   /**
70    * Replaces placeholder fields in translation strings
71    *
72    * @param {string} template The translation template string in the following format: "$name is a $sex"
73    * @param {array} replacors An js object with key and values. Eg: {'$name': 'Frode', '$sex': 'male'}
74    */
75   H5PUtils.translateReplace = function (template, replacors) {
76     $.each(replacors, function (key, value) {
77       template = template.replace(new RegExp('\\'+key, 'g'), value);
78     });
79     return template;
80   };
82   /**
83    * Get throbber with given text.
84    *
85    * @param {String} text
86    * @returns {$}
87    */
88   H5PUtils.throbber = function (text) {
89     return $('<div/>', {
90       class: 'h5p-throbber',
91       text: text
92     });
93   };
95   /**
96    * Makes it possbile to rebuild all content caches from admin UI.
97    * @param {Object} notCached
98    * @returns {$}
99    */
100   H5PUtils.getRebuildCache = function (notCached) {
101     var $container = $('<div class="h5p-admin-rebuild-cache"><p class="message">' + notCached.message + '</p><p class="progress">' + notCached.progress + '</p></div>');
102     var $button = $('<button>' + notCached.button + '</button>').appendTo($container).click(function () {
103       var $spinner = $('<div/>', {class: 'h5p-spinner'}).replaceAll($button);
104       var parts = ['|', '/', '-', '\\'];
105       var current = 0;
106       var spinning = setInterval(function () {
107         $spinner.text(parts[current]);
108         current++;
109         if (current === parts.length) current = 0;
110       }, 100);
112       var $counter = $container.find('.progress');
113       var build = function () {
114         $.post(notCached.url, function (left) {
115           if (left === '0') {
116             clearInterval(spinning);
117             $container.remove();
118             location.reload();
119           }
120           else {
121             var counter = $counter.text().split(' ');
122             counter[0] = left;
123             $counter.text(counter.join(' '));
124             build();
125           }
126         });
127       };
128       build();
129     });
131     return $container;
132   };
134   /**
135    * Generic table class with useful helpers.
136    *
137    * @class
138    * @param {Object} classes
139    *   Custom html classes to use on elements.
140    *   e.g. {tableClass: 'fixed'}.
141    */
142   H5PUtils.Table = function (classes) {
143     var numCols;
144     var sortByCol;
145     var $sortCol;
146     var sortCol;
147     var sortDir;
149     // Create basic table
150     var tableOptions = {};
151     if (classes.table !== undefined) {
152       tableOptions['class'] = classes.table;
153     }
154     var $table = $('<table/>', tableOptions);
155     var $thead = $('<thead/>').appendTo($table);
156     var $tfoot = $('<tfoot/>').appendTo($table);
157     var $tbody = $('<tbody/>').appendTo($table);
159     /**
160      * Add columns to given table row.
161      *
162      * @private
163      * @param {jQuery} $tr Table row
164      * @param {(String|Object)} col Column properties
165      * @param {Number} id Used to seperate the columns
166      */
167     var addCol = function ($tr, col, id) {
168       var options = {
169         on: {}
170       };
172       if (!(col instanceof Object)) {
173         options.text = col;
174       }
175       else {
176         if (col.text !== undefined) {
177           options.text = col.text;
178         }
179         if (col.class !== undefined) {
180           options.class = col.class;
181         }
183         if (sortByCol !== undefined && col.sortable === true) {
184           // Make sortable
185           options.role = 'button';
186           options.tabIndex = 0;
188           // This is the first sortable column, use as default sort
189           if (sortCol === undefined) {
190             sortCol = id;
191             sortDir = 0;
192           }
194           // This is the sort column
195           if (sortCol === id) {
196             options['class'] = 'h5p-sort';
197             if (sortDir === 1) {
198               options['class'] += ' h5p-reverse';
199             }
200           }
202           options.on.click = function () {
203             sort($th, id);
204           };
205           options.on.keypress = function (event) {
206             if ((event.charCode || event.keyCode) === 32) { // Space
207               sort($th, id);
208             }
209           };
210         }
211       }
213       // Append
214       var $th = $('<th>', options).appendTo($tr);
215       if (sortCol === id) {
216         $sortCol = $th; // Default sort column
217       }
218     };
220     /**
221      * Updates the UI when a column header has been clicked.
222      * Triggers sorting callback.
223      *
224      * @private
225      * @param {jQuery} $th Table header
226      * @param {Number} id Used to seperate the columns
227      */
228     var sort = function ($th, id) {
229       if (id === sortCol) {
230         // Change sorting direction
231         if (sortDir === 0) {
232           sortDir = 1;
233           $th.addClass('h5p-reverse');
234         }
235         else {
236           sortDir = 0;
237           $th.removeClass('h5p-reverse');
238         }
239       }
240       else {
241         // Change sorting column
242         $sortCol.removeClass('h5p-sort').removeClass('h5p-reverse');
243         $sortCol = $th.addClass('h5p-sort');
244         sortCol = id;
245         sortDir = 0;
246       }
248       sortByCol({
249         by: sortCol,
250         dir: sortDir
251       });
252     };
254     /**
255      * Set table headers.
256      *
257      * @public
258      * @param {Array} cols
259      *   Table header data. Can be strings or objects with options like
260      *   "text" and "sortable". E.g.
261      *   [{text: 'Col 1', sortable: true}, 'Col 2', 'Col 3']
262      * @param {Function} sort Callback which is runned when sorting changes
263      * @param {Object} [order]
264      */
265     this.setHeaders = function (cols, sort, order) {
266       numCols = cols.length;
267       sortByCol = sort;
269       if (order) {
270         sortCol = order.by;
271         sortDir = order.dir;
272       }
274       // Create new head
275       var $newThead = $('<thead/>');
276       var $tr = $('<tr/>').appendTo($newThead);
277       for (var i = 0; i < cols.length; i++) {
278         addCol($tr, cols[i], i);
279       }
281       // Update DOM
282       $thead.replaceWith($newThead);
283       $thead = $newThead;
284     };
286     /**
287      * Set table rows.
288      *
289      * @public
290      * @param {Array} rows Table rows with cols: [[1,'hello',3],[2,'asd',6]]
291      */
292     this.setRows = function (rows) {
293       var $newTbody = $('<tbody/>');
295       for (var i = 0; i < rows.length; i++) {
296         var $tr = $('<tr/>').appendTo($newTbody);
298         for (var j = 0; j < rows[i].length; j++) {
299           $('<td>', {
300             html: rows[i][j]
301           }).appendTo($tr);
302         }
303       }
305       $tbody.replaceWith($newTbody);
306       $tbody = $newTbody;
308       return $tbody;
309     };
311     /**
312      * Set custom table body content. This can be a message or a throbber.
313      * Will cover all table columns.
314      *
315      * @public
316      * @param {jQuery} $content Custom content
317      */
318     this.setBody = function ($content) {
319       var $newTbody = $('<tbody/>');
320       var $tr = $('<tr/>').appendTo($newTbody);
321       $('<td>', {
322         colspan: numCols
323       }).append($content).appendTo($tr);
324       $tbody.replaceWith($newTbody);
325       $tbody = $newTbody;
326     };
328     /**
329      * Set custom table foot content. This can be a pagination widget.
330      * Will cover all table columns.
331      *
332      * @public
333      * @param {jQuery} $content Custom content
334      */
335     this.setFoot = function ($content) {
336       var $newTfoot = $('<tfoot/>');
337       var $tr = $('<tr/>').appendTo($newTfoot);
338       $('<td>', {
339         colspan: numCols
340       }).append($content).appendTo($tr);
341       $tfoot.replaceWith($newTfoot);
342     };
345     /**
346      * Appends the table to the given container.
347      *
348      * @public
349      * @param {jQuery} $container
350      */
351     this.appendTo = function ($container) {
352       $table.appendTo($container);
353     };
354   };
356   /**
357    * Generic pagination class. Creates a useful pagination widget.
358    *
359    * @class
360    * @param {Number} num Total number of items to pagiate.
361    * @param {Number} limit Number of items to dispaly per page.
362    * @param {Function} goneTo
363    *   Callback which is fired when the user wants to go to another page.
364    * @param {Object} l10n
365    *   Localization / translations. e.g.
366    *   {
367    *     currentPage: 'Page $current of $total',
368    *     nextPage: 'Next page',
369    *     previousPage: 'Previous page'
370    *   }
371    */
372   H5PUtils.Pagination = function (num, limit, goneTo, l10n) {
373     var current = 0;
374     var pages = Math.ceil(num / limit);
376     // Create components
378     // Previous button
379     var $left = $('<button/>', {
380       html: '&lt;',
381       'class': 'button',
382       title: l10n.previousPage
383     }).click(function () {
384       goTo(current - 1);
385     });
387     // Current page text
388     var $text = $('<span/>').click(function () {
389       $input.width($text.width()).show().val(current + 1).focus();
390       $text.hide();
391     });
393     // Jump to page input
394     var $input = $('<input/>', {
395       type: 'number',
396       min : 1,
397       max: pages,
398       on: {
399         'blur': function () {
400           gotInput();
401         },
402         'keyup': function (event) {
403           if (event.keyCode === 13) {
404             gotInput();
405             return false;
406           }
407         }
408       }
409     }).hide();
411     // Next button
412     var $right = $('<button/>', {
413       html: '&gt;',
414       'class': 'button',
415       title: l10n.nextPage
416     }).click(function () {
417       goTo(current + 1);
418     });
420     /**
421      * Check what page the user has typed in and jump to it.
422      *
423      * @private
424      */
425     var gotInput = function () {
426       var page = parseInt($input.hide().val());
427       if (!isNaN(page)) {
428         goTo(page - 1);
429       }
430       $text.show();
431     };
433     /**
434      * Update UI elements.
435      *
436      * @private
437      */
438     var updateUI = function () {
439       var next = current + 1;
441       // Disable or enable buttons
442       $left.attr('disabled', current === 0);
443       $right.attr('disabled', next === pages);
445       // Update counter
446       $text.html(l10n.currentPage.replace('$current', next).replace('$total', pages));
447     };
449     /**
450      * Try to go to the requested page.
451      *
452      * @private
453      * @param {Number} page
454      */
455     var goTo = function (page) {
456       if (page === current || page < 0 || page >= pages) {
457         return; // Invalid page number
458       }
459       current = page;
461       updateUI();
463       // Fire callback
464       goneTo(page * limit);
465     };
467     /**
468      * Update number of items and limit.
469      *
470      * @public
471      * @param {Number} newNum Total number of items to pagiate.
472      * @param {Number} newLimit Number of items to dispaly per page.
473      */
474     this.update = function (newNum, newLimit) {
475       if (newNum !== num || newLimit !== limit) {
476         // Update num and limit
477         num = newNum;
478         limit = newLimit;
479         pages = Math.ceil(num / limit);
480         $input.attr('max', pages);
482         if (current >= pages) {
483           // Content is gone, move to last page.
484           goTo(pages - 1);
485           return;
486         }
488         updateUI();
489       }
490     };
492     /**
493      * Append the pagination widget to the given container.
494      *
495      * @public
496      * @param {jQuery} $container
497      */
498     this.appendTo = function ($container) {
499       $left.add($text).add($input).add($right).appendTo($container);
500     };
502     // Update UI
503     updateUI();
504   };
506 })(H5P.jQuery);