Created a new table data view from library management and library details that is more generic.
Added support for finish event from H5Ps. JS now also log to results when the H5P was opened and finished.d6
parent
1479138abb
commit
86c18476cd
|
@ -0,0 +1,126 @@
|
|||
var H5PDataView = (function ($) {
|
||||
|
||||
/**
|
||||
* Initialize a new H5P data view.
|
||||
*
|
||||
* @param {Object} container
|
||||
* @param {String} source URL for data
|
||||
* @param {Array} headers for data
|
||||
* @param {Object} l10n translations
|
||||
*/
|
||||
function H5PDataView(container, source, headers, l10n, classes) {
|
||||
var self = this;
|
||||
|
||||
self.$container = $(container).addClass('h5p-data-view').html('');
|
||||
H5PUtils.throbber(l10n.loading).appendTo(self.$container);
|
||||
|
||||
self.source = source;
|
||||
self.headers = headers;
|
||||
self.l10n = l10n;
|
||||
self.classes = (classes === undefined ? {} : classes);
|
||||
self.limit = 20;
|
||||
|
||||
self.loadData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load data for view.
|
||||
*
|
||||
* @param {Number} offset data collection offset
|
||||
*/
|
||||
H5PDataView.prototype.loadData = function (offset) {
|
||||
var self = this;
|
||||
|
||||
// Create URL
|
||||
var url = self.source;
|
||||
if (offset !== undefined) {
|
||||
url += (url.indexOf('?') === -1 ? '?' : '&') + 'offset=' + offset + '&limit=' + self.limit;
|
||||
}
|
||||
|
||||
// Fire ajax request
|
||||
$.ajax({
|
||||
dataType: 'json',
|
||||
cache: true,
|
||||
url: url
|
||||
}).fail(function () {
|
||||
// Error handling
|
||||
self.setMessage(self.l10n.ajaxFailed);
|
||||
}).done(function (data) {
|
||||
if (!data.rows.length) {
|
||||
self.setMessage(self.l10n.noData);
|
||||
}
|
||||
else {
|
||||
// Update table data
|
||||
self.updateTable(data.rows);
|
||||
}
|
||||
|
||||
// Update pagination widget
|
||||
self.updatePagination(data.num);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Display the given message to the user.
|
||||
*
|
||||
* @param {String} message
|
||||
*/
|
||||
H5PDataView.prototype.setMessage = function (message) {
|
||||
var self = this;
|
||||
|
||||
var $message = $('<p/>', {
|
||||
text: message
|
||||
});
|
||||
if (self.table === undefined) {
|
||||
self.$container.children().replaceWith($message);
|
||||
}
|
||||
else {
|
||||
self.table.setBody($('<p/>', {text: message}));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update table data.
|
||||
*
|
||||
* @param {Array} rows
|
||||
*/
|
||||
H5PDataView.prototype.updateTable = function (rows) {
|
||||
var self = this;
|
||||
|
||||
if (self.table === undefined) {
|
||||
// Create new table
|
||||
self.table = new H5PUtils.Table(self.classes, self.headers);
|
||||
self.table.appendTo(self.$container.html(''));
|
||||
}
|
||||
|
||||
// Add/update rows
|
||||
self.table.setRows(rows);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update pagination widget.
|
||||
*
|
||||
* @param {Number} num size of data collection
|
||||
*/
|
||||
H5PDataView.prototype.updatePagination = function (num) {
|
||||
var self = this;
|
||||
|
||||
if (self.pagination === undefined) {
|
||||
// Create new widget
|
||||
var $pagerContainer = $('<div/>', {'class': 'h5p-pagination'});
|
||||
self.pagination = new H5PUtils.Pagination(num, self.limit, function (offset) {
|
||||
// Handle page changes in pagination widget
|
||||
self.table.setBody(H5PUtils.throbber(self.l10n.loading));
|
||||
self.loadData(offset);
|
||||
}, self.l10n);
|
||||
|
||||
self.pagination.appendTo($pagerContainer);
|
||||
self.table.setFoot($pagerContainer);
|
||||
}
|
||||
else {
|
||||
// Update existing widget
|
||||
self.pagination.update(num, self.limit);
|
||||
}
|
||||
};
|
||||
|
||||
return H5PDataView;
|
||||
})(H5P.jQuery);
|
222
js/h5p-utils.js
222
js/h5p-utils.js
|
@ -64,7 +64,7 @@ var H5PUtils = H5PUtils || {};
|
|||
|
||||
return $field;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Replaces placeholder fields in translation strings
|
||||
*
|
||||
|
@ -130,4 +130,224 @@ var H5PUtils = H5PUtils || {};
|
|||
return $container;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic table class with useful helpers.
|
||||
*
|
||||
* @param {Object} classes to use for styling
|
||||
* @param {Array} cols headers
|
||||
*/
|
||||
H5PUtils.Table = function (classes, cols) {
|
||||
// Create basic table
|
||||
var tableOptions = {};
|
||||
if (classes.table !== undefined) {
|
||||
tableOptions['class'] = classes.table;
|
||||
}
|
||||
var $table = $('<table/>', tableOptions);
|
||||
var $thead = $('<thead/>').appendTo($table);
|
||||
var $tfoot = $('<tfoot/>').appendTo($table);
|
||||
var $tbody = $('<tbody/>').appendTo($table);
|
||||
|
||||
// Set cols - create header
|
||||
var $tr = $('<tr/>').appendTo($thead);
|
||||
for (var i = 0; i < cols.length; i++) {
|
||||
$('<th>', {
|
||||
html: cols[i]
|
||||
}).appendTo($tr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public.
|
||||
*
|
||||
* @param {Array} rows with cols
|
||||
*/
|
||||
this.setRows = function (rows) {
|
||||
var $newTbody = $('<tbody/>');
|
||||
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var $tr = $('<tr/>').appendTo($newTbody);
|
||||
|
||||
for (var j = 0; j < rows[i].length; j++) {
|
||||
$('<td>', {
|
||||
html: rows[i][j]
|
||||
}).appendTo($tr);
|
||||
}
|
||||
}
|
||||
|
||||
$tbody.replaceWith($newTbody);
|
||||
$tbody = $newTbody;
|
||||
};
|
||||
|
||||
/**
|
||||
* Public.
|
||||
*
|
||||
* @param {jQuery} $content custom
|
||||
*/
|
||||
this.setBody = function ($content) {
|
||||
var $newTbody = $('<tbody/>');
|
||||
var $tr = $('<tr/>').appendTo($newTbody);
|
||||
$('<td>', {
|
||||
colspan: cols.length
|
||||
}).append($content).appendTo($tr);
|
||||
$tbody.replaceWith($newTbody);
|
||||
$tbody = $newTbody;
|
||||
};
|
||||
|
||||
/**
|
||||
* Public.
|
||||
*
|
||||
* @param {jQuery} $content custom
|
||||
*/
|
||||
this.setFoot = function ($content) {
|
||||
var $newTfoot = $('<tfoot/>');
|
||||
var $tr = $('<tr/>').appendTo($newTfoot);
|
||||
$('<td>', {
|
||||
colspan: cols.length
|
||||
}).append($content).appendTo($tr);
|
||||
$tfoot.replaceWith($newTfoot);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Public.
|
||||
*
|
||||
* @param {jQuery} $container
|
||||
*/
|
||||
this.appendTo = function ($container) {
|
||||
$table.appendTo($container);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic pagination class.
|
||||
*
|
||||
* @param {Number} num total items
|
||||
* @param {Number} limit items per page
|
||||
* @param {Function} goneTo page callback
|
||||
*/
|
||||
H5PUtils.Pagination = function (num, limit, goneTo, l10n) {
|
||||
var current = 0;
|
||||
var pages = Math.ceil(num / limit);
|
||||
|
||||
// Create components
|
||||
|
||||
// Previous button
|
||||
var $left = $('<button/>', {
|
||||
html: '<',
|
||||
'class': 'button',
|
||||
title: l10n.previousPage
|
||||
}).click(function () {
|
||||
goTo(current - 1);
|
||||
});
|
||||
|
||||
// Current page text
|
||||
var $text = $('<span/>').click(function () {
|
||||
$input.width($text.width()).show().val(current + 1).focus();
|
||||
$text.hide();
|
||||
});
|
||||
|
||||
// Jump to page input
|
||||
var $input = $('<input/>', {
|
||||
type: 'number',
|
||||
min : 1,
|
||||
max: pages,
|
||||
on: {
|
||||
'blur': function () {
|
||||
gotInput();
|
||||
},
|
||||
'keyup': function (event) {
|
||||
if (event.keyCode === 13) {
|
||||
gotInput();
|
||||
}
|
||||
}
|
||||
}
|
||||
}).hide();
|
||||
|
||||
// Next button
|
||||
var $right = $('<button/>', {
|
||||
html: '>',
|
||||
'class': 'button',
|
||||
title: l10n.nextPage
|
||||
}).click(function () {
|
||||
goTo(current + 1);
|
||||
});
|
||||
|
||||
/**
|
||||
* Private. Input box value may have changed.
|
||||
*/
|
||||
var gotInput = function () {
|
||||
var page = parseInt($input.hide().val());
|
||||
if (!isNaN(page)) {
|
||||
goTo(page - 1);
|
||||
}
|
||||
$text.show();
|
||||
};
|
||||
|
||||
/**
|
||||
* Private. Update UI elements.
|
||||
*/
|
||||
var updateUI = function () {
|
||||
var next = current + 1;
|
||||
|
||||
// Disable or enable buttons
|
||||
$left.attr('disabled', current === 0);
|
||||
$right.attr('disabled', next === pages);
|
||||
|
||||
// Update counter
|
||||
$text.html(l10n.currentPage.replace('$current', next).replace('$total', pages));
|
||||
};
|
||||
|
||||
/**
|
||||
* Private. Try to go to the requested page.
|
||||
*
|
||||
* @param {Number} page
|
||||
*/
|
||||
var goTo = function (page) {
|
||||
if (page === current || page < 0 || page >= pages) {
|
||||
return; // Invalid page number
|
||||
}
|
||||
current = page;
|
||||
|
||||
updateUI();
|
||||
|
||||
// Fire callback
|
||||
goneTo(page * limit);
|
||||
};
|
||||
|
||||
/**
|
||||
* Public. Update number of items and limit.
|
||||
*
|
||||
* @param {Number} newNum
|
||||
* @param {Number} newLimit
|
||||
*/
|
||||
this.update = function (newNum, newLimit) {
|
||||
if (newNum !== num || newLimit !== limit) {
|
||||
// Update num and limit
|
||||
num = newNum;
|
||||
limit = newLimit;
|
||||
pages = Math.ceil(num / limit);
|
||||
$input.attr('max', pages);
|
||||
|
||||
if (current >= pages) {
|
||||
// Content is gone, move to last page.
|
||||
goTo(pages - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
updateUI();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Public. Append the pagination widget to the given item.
|
||||
*
|
||||
* @param {jQuery} $container
|
||||
*/
|
||||
this.appendTo = function ($container) {
|
||||
$left.add($text).add($input).add($right).appendTo($container);
|
||||
};
|
||||
|
||||
// Update UI
|
||||
updateUI();
|
||||
};
|
||||
|
||||
})(H5P.jQuery);
|
||||
|
|
45
js/h5p.js
45
js/h5p.js
|
@ -30,6 +30,9 @@ else if (document.documentElement.msRequestFullscreen) {
|
|||
H5P.fullScreenBrowserPrefix = 'ms';
|
||||
}
|
||||
|
||||
// Keep track of when the H5Ps where started
|
||||
H5P.opened = {};
|
||||
|
||||
/**
|
||||
* Initialize H5P content.
|
||||
* Scans for ".h5p-content" in the document and initializes H5P instances where found.
|
||||
|
@ -89,6 +92,16 @@ H5P.init = function () {
|
|||
}
|
||||
$actions.insertAfter($container);
|
||||
|
||||
// Keep track of when we started
|
||||
H5P.opened[contentId] = new Date();
|
||||
|
||||
// Handle events when the user finishes the content. Useful for logging exercise results.
|
||||
instance.$.on('finish', function (event) {
|
||||
if (event.data !== undefined) {
|
||||
H5P.setFinished(contentId, event.data.score, event.data.maxScore, event.data.time);
|
||||
}
|
||||
});
|
||||
|
||||
if (H5P.isFramed) {
|
||||
// Make it possible to resize the iframe when the content changes size. This way we get no scrollbars.
|
||||
var iframe = window.parent.document.getElementById('h5p-iframe-' + contentId);
|
||||
|
@ -965,16 +978,38 @@ H5P.shuffleArray = function (array) {
|
|||
};
|
||||
|
||||
/**
|
||||
* DEPRECATED! Do not use this function directly, trigger the finish event
|
||||
* instead.
|
||||
*
|
||||
* Post finished results for user.
|
||||
* TODO: Should we use events instead? That way the parent can handle the results of the child.
|
||||
*
|
||||
* @param {Number} contentId
|
||||
* @param {Number} points
|
||||
* @param {Number} maxPoints
|
||||
* @param {Number} score achieved
|
||||
* @param {Number} maxScore that can be achieved
|
||||
* @param {Number} time optional reported time usage
|
||||
*/
|
||||
H5P.setFinished = function (contentId, points, maxPoints) {
|
||||
H5P.setFinished = function (contentId, score, maxScore, time) {
|
||||
if (H5P.postUserStatistics === true) {
|
||||
H5P.jQuery.post(H5P.ajaxPath + 'setFinished', {contentId: contentId, points: points, maxPoints: maxPoints});
|
||||
/**
|
||||
* Return unix timestamp for the given JS Date.
|
||||
*
|
||||
* @param {Date} date
|
||||
* @returns {Number}
|
||||
*/
|
||||
var toUnix = function (date) {
|
||||
return Math.round(date.getTime() / 1000);
|
||||
};
|
||||
|
||||
// Post the results
|
||||
// TODO: Should we use a variable with the complete path?
|
||||
H5P.jQuery.post(H5P.ajaxPath + 'setFinished', {
|
||||
contentId: contentId,
|
||||
score: score,
|
||||
maxScore: maxScore,
|
||||
opened: toUnix(H5P.opened[contentId]),
|
||||
finished: toUnix(new Date()),
|
||||
time: time
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
border: none;
|
||||
}
|
||||
|
||||
.h5p-admin-table tr:nth-child(odd) {
|
||||
.h5p-admin-table tr:nth-child(odd),
|
||||
.h5p-data-view tr:nth-child(odd) {
|
||||
background-color: #F9F9F9;
|
||||
}
|
||||
.h5p-admin-table tbody tr:hover {
|
||||
|
@ -30,7 +31,7 @@
|
|||
}
|
||||
|
||||
.h5p-admin-buttons-wrapper {
|
||||
white-space: nowrap;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.h5p-admin-table.libraries button {
|
||||
|
@ -69,13 +70,13 @@
|
|||
}
|
||||
|
||||
.h5p-admin-upgrade-library {
|
||||
color: #339900;
|
||||
color: #339900;
|
||||
}
|
||||
.h5p-admin-view-library {
|
||||
color: #0066cc;
|
||||
color: #0066cc;
|
||||
}
|
||||
.h5p-admin-delete-library {
|
||||
color: #990000;
|
||||
color: #990000;
|
||||
}
|
||||
.h5p-admin-delete-library:disabled,
|
||||
.h5p-admin-upgrade-library:disabled {
|
||||
|
@ -86,9 +87,9 @@
|
|||
.h5p-library-info {
|
||||
padding: 1em 1em;
|
||||
margin: 1em 0;
|
||||
|
||||
|
||||
width: 350px;
|
||||
|
||||
|
||||
border: 1px solid #DDD;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
@ -118,14 +119,14 @@
|
|||
.h5p-content-search {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
||||
|
||||
width: 100%;
|
||||
padding: 5px 0;
|
||||
margin-top: 10px;
|
||||
|
||||
|
||||
border: 1px solid #CCC;
|
||||
border-radius: 3px;
|
||||
box-shadow: 2px 2px 5px #888888;
|
||||
box-shadow: 2px 2px 5px #888888;
|
||||
}
|
||||
.h5p-content-search:before {
|
||||
font-family: 'H5P';
|
||||
|
@ -219,7 +220,7 @@ button.h5p-admin.disabled:hover {
|
|||
.h5p-content-pager > .pager-info:hover {
|
||||
background-color: #555;
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
.h5p-content-pager > .pager-info,
|
||||
.h5p-content-pager > .h5p-pager-goto {
|
||||
margin: 0 10px;
|
||||
|
@ -244,4 +245,10 @@ button.h5p-admin.disabled:hover {
|
|||
padding: 0 0.5em;
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
.h5p-pagination {
|
||||
text-align: center;
|
||||
}
|
||||
.h5p-pagination > span, .h5p-pagination > input {
|
||||
margin: 0 1em;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue