Merge branch 'master' into release

pull/8/head
Frode Petterson 2016-02-03 11:28:39 +01:00
commit d647de6fbe
7 changed files with 377 additions and 271 deletions

102
card.js Normal file
View File

@ -0,0 +1,102 @@
(function (MemoryGame, EventDispatcher, $) {
/**
* Controls all the operations for each card.
*
* @class H5P.MemoryGame.Card
* @param {Object} parameters
* @param {Number} id
*/
MemoryGame.Card = function (parameters, id) {
var self = this;
// Initialize event inheritance
EventDispatcher.call(self);
var path = H5P.getPath(parameters.image.path, id);
var width, height, margin, $card;
var a = 96;
if (parameters.image.width !== undefined && parameters.image.height !== undefined) {
if (parameters.image.width > parameters.image.height) {
width = a;
height = parameters.image.height * (width / parameters.image.width);
margin = '' + ((a - height) / 2) + 'px 0 0 0';
}
else {
height = a;
width = parameters.image.width * (height / parameters.image.height);
margin = '0 0 0 ' + ((a - width) / 2) + 'px';
}
}
else {
width = height = a;
}
/**
* Flip card.
*/
self.flip = function () {
$card.addClass('h5p-flipped');
self.trigger('flip');
};
/**
* Flip card back.
*/
self.flipBack = function () {
$card.removeClass('h5p-flipped');
};
/**
* Remove.
*/
self.remove = function () {
$card.addClass('h5p-matched');
};
/**
* Get card description.
*
* @returns {string}
*/
self.getDescription = function () {
return parameters.description;
};
/**
* Get image clone.
*
* @returns {H5P.jQuery}
*/
self.getImage = function () {
return $card.find('img').clone();
};
/**
* Append card to the given container.
*
* @param {H5P.jQuery} $container
*/
self.appendTo = function ($container) {
// TODO: Translate alt attr
$card = $('<li class="h5p-memory-card" role="button" tabindex="1">' +
'<div class="h5p-front"></div>' +
'<div class="h5p-back">' +
'<img src="' + path + '" alt="Memory Card" width="' + width + '" height="' + height + '"' + (margin === undefined ? '' : ' style="margin:' + margin + '"') + '/>' +
'</div>' +
'</li>')
.appendTo($container)
.children('.h5p-front')
.click(function () {
self.flip();
})
.end();
};
};
// Extends the event dispatcher
MemoryGame.Card.prototype = Object.create(EventDispatcher.prototype);
MemoryGame.Card.prototype.constructor = MemoryGame.Card;
})(H5P.MemoryGame, H5P.EventDispatcher, H5P.jQuery);

23
counter.js Normal file
View File

@ -0,0 +1,23 @@
(function (MemoryGame) {
/**
* Keeps track of the number of cards that has been turned
*
* @class H5P.MemoryGame.Counter
* @param {H5P.jQuery} $container
*/
MemoryGame.Counter = function ($container) {
var self = this;
var current = 0;
/**
* Increment the counter.
*/
self.increment = function () {
current++;
$container.text(current);
};
};
})(H5P.MemoryGame);

37
language/fr.json Normal file
View File

@ -0,0 +1,37 @@
{
"semantics": [
{
"label": "Cartes",
"entity": "une carte",
"field": {
"label": "Carte",
"fields": [
{
"label": "Image"
},
{
"label": "Description",
"description": "Petit texte affiché quand deux cartes identiques sont trouvées."
}
]
}
},
{
"label": "Localisation",
"fields": [
{
"label": "Texte pour une carte à retourner",
"default": "Retourner"
},
{
"label": "Texte pour le temps passé",
"default": "Durée écoulée"
},
{
"label": "Texte de feedback",
"default": "Bon travail!"
}
]
}
]
}

View File

@ -16,6 +16,18 @@
"preloadedJs": [ "preloadedJs": [
{ {
"path": "memory-game.js" "path": "memory-game.js"
},
{
"path": "card.js"
},
{
"path": "timer.js"
},
{
"path": "counter.js"
},
{
"path": "popup.js"
} }
] ]
} }

View File

@ -1,245 +1,29 @@
var H5P = H5P || {}; H5P.MemoryGame = (function (EventDispatcher, $) {
/**
* H5P Memory Game Module.
*/
H5P.MemoryGame = (function ($) {
/**
* Memory Card Constructor
*
* @param {Object} parameters
* @param {Number} id
*/
function MemoryCard(parameters, id) {
var self = this;
var path = H5P.getPath(parameters.image.path, id);
var width, height, margin, $card;
var a = 96;
if (parameters.image.width !== undefined && parameters.image.height !== undefined) {
if (parameters.image.width > parameters.image.height) {
width = a;
height = parameters.image.height * (width / parameters.image.width);
margin = '' + ((a - height) / 2) + 'px 0 0 0';
}
else {
height = a;
width = parameters.image.width * (height / parameters.image.height);
margin = '0 0 0 ' + ((a - width) / 2) + 'px';
}
}
else {
width = height = a;
}
// Public jQuery wrapper.
this.$ = $(this);
/**
* Public. Flip card.
*/
this.flip = function () {
$card.addClass('h5p-flipped');
};
/**
* Public. Flip card back.
*/
this.flipBack = function () {
$card.removeClass('h5p-flipped');
};
/**
* Public. Remove.
*/
this.remove = function () {
$card.addClass('h5p-matched');
};
/**
* Public. Get card description.
*/
this.getDescription = function () {
return parameters.description;
};
/**
* Public. Get image clone.
*/
this.getImage = function () {
return $card.find('img').clone();
};
/**
* Public. Append card to the given container.
*
* @param {jQuery} $container
*/
this.appendTo = function ($container) {
$card = $('<li class="h5p-memory-card" role="button" tabindex="1">\
<div class="h5p-front"></div>\
<div class="h5p-back">\
<img src="' + path + '" alt="Memory Card" width="' + width + '" height="' + height + '"' + (margin === undefined ? '' : ' style="margin:' + margin + '"') + '/>\
</div>\
</li>')
.appendTo($container)
.children('.h5p-front')
.click(function () {
self.$.trigger('flip');
})
.end();
};
};
function MemoryTimer($container) {
var interval, started, totalTime = 0;
/**
* Private. Make timer more readable for humans.
*
* @param {Number} seconds
* @returns {String}
*/
var humanizeTime = function (seconds) {
var minutes = Math.floor(seconds / 60);
var hours = Math.floor(minutes / 60);
minutes = minutes % 60;
seconds = Math.floor(seconds % 60);
var time = '';
if (hours !== 0) {
time += hours + ':';
if (minutes < 10) {
time += '0';
}
}
time += minutes + ':';
if (seconds < 10) {
time += '0';
}
time += seconds;
return time;
};
/**
* Private. Update the timer element.
*/
var update = function (last) {
var currentTime = (new Date().getTime() - started);
$container.text(humanizeTime(Math.floor((totalTime + currentTime) / 1000)));
if (last === true) {
// This is the last update, stop timing interval.
clearTimeout(interval);
}
else {
// Use setTimeout since setInterval isn't safe.
interval = setTimeout(function () {
update();
}, 1000);
}
return currentTime;
};
/**
* Public. Starts the counter.
*/
this.start = function () {
if (started === undefined) {
started = new Date();
update();
}
};
/**
* Public. Stops the counter.
*/
this.stop = function () {
if (started !== undefined) {
totalTime += update(true);
started = undefined;
}
};
};
/**
* Memory Counter Constructor
*
* @param {jQuery} $container
*/
function MemoryCounter($container) {
var current = 0;
this.increment = function () {
current++;
$container.text(current);
};
}
/**
* Memory Popup Dialog Constructor
*
* @param {jQuery} $container
*/
function MemoryPop($container) {
var closed;
var $popup = $('<div class="h5p-memory-pop"><div class="h5p-memory-image"></div><div class="h5p-memory-desc"></div></div>').appendTo($container);
var $desc = $popup.find('.h5p-memory-desc');
var $image = $popup.find('.h5p-memory-image');
/**
* Public. Show the popup.
*
* @param {String} desc
* @param {jQuery} $img
* @param {Function} done
* @returns {undefined}
*/
this.show = function (desc, $img, done) {
$desc.text(desc);
$img.appendTo($image.html(''));
$popup.show();
closed = done;
};
/**
* Public. Close the popup.
* @returns {undefined}
*/
this.close = function () {
if (closed !== undefined) {
$popup.hide();
closed();
closed = undefined;
}
};
}
/** /**
* Memory Game Constructor * Memory Game Constructor
* *
* @class
* @param {Object} parameters * @param {Object} parameters
* @param {Number} id * @param {Number} id
*/ */
function MemoryGame(parameters, id) { function MemoryGame(parameters, id) {
var flipped, timer, counter, popup, $feedback, cards = [], removed = 0; var self = this;
// Initialize event inheritance
EventDispatcher.call(self);
var flipped, timer, counter, popup, $feedback;
var cards = [];
var removed = 0;
/** /**
* Private. Check if these two cards belongs together. * Check if these two cards belongs together.
* *
* @param {MemoryCard} card * @private
* @param {MemoryCard} mate * @param {H5P.MemoryGame.Card} card
* @param {MemoryCard} correct * @param {H5P.MemoryGame.Card} mate
* @param {H5P.MemoryGame.Card} correct
*/ */
var check = function (card, mate, correct) { var check = function (card, mate, correct) {
if (mate === correct) { if (mate === correct) {
@ -255,8 +39,9 @@ H5P.MemoryGame = (function ($) {
if (desc !== undefined) { if (desc !== undefined) {
// Pause timer and show desciption. // Pause timer and show desciption.
timer.stop(); timer.stop();
popup.show(desc, card.getImage(), function () { popup.show(desc, card.getImage(), function () {
if (finished) { if (finished) {
self.triggerXAPIScored(1, 1, 'completed');
// Game has finished // Game has finished
$feedback.addClass('h5p-show'); $feedback.addClass('h5p-show');
} }
@ -267,6 +52,7 @@ H5P.MemoryGame = (function ($) {
}); });
} }
else if (finished) { else if (finished) {
self.triggerXAPIScored(1, 1, 'completed');
// Game has finished // Game has finished
timer.stop(); timer.stop();
$feedback.addClass('h5p-show'); $feedback.addClass('h5p-show');
@ -280,13 +66,15 @@ H5P.MemoryGame = (function ($) {
}; };
/** /**
* Private. Adds card to card list and set up a flip listener. * Adds card to card list and set up a flip listener.
* *
* @param {MemoryCard} card * @private
* @param {MemoryCard} mate * @param {H5P.MemoryGame.Card} card
* @param {H5P.MemoryGame.Card} mate
*/ */
var addCard = function (card, mate) { var addCard = function (card, mate) {
card.$.on('flip', function () { card.on('flip', function () {
self.triggerXAPI('interacted');
// Keep track of time spent // Keep track of time spent
timer.start(); timer.start();
@ -314,19 +102,21 @@ H5P.MemoryGame = (function ($) {
// Initialize cards. // Initialize cards.
for (var i = 0; i < parameters.cards.length; i++) { for (var i = 0; i < parameters.cards.length; i++) {
// Add two of each card // Add two of each card
var cardOne = new MemoryCard(parameters.cards[i], id); var cardOne = new MemoryGame.Card(parameters.cards[i], id);
var cardTwo = new MemoryCard(parameters.cards[i], id); var cardTwo = new MemoryGame.Card(parameters.cards[i], id);
addCard(cardOne, cardTwo); addCard(cardOne, cardTwo);
addCard(cardTwo, cardOne); addCard(cardTwo, cardOne);
} }
H5P.shuffleArray(cards); H5P.shuffleArray(cards);
/** /**
* Public. Attach this game's html to the given container. * Attach this game's html to the given container.
* *
* @param {jQuery} $container * @param {H5P.jQuery} $container
*/ */
this.attach = function ($container) { self.attach = function ($container) {
this.triggerXAPI('attempted');
// TODO: Only create on first!
$container.addClass('h5p-memory-game').html(''); $container.addClass('h5p-memory-game').html('');
// Add cards to list // Add cards to list
@ -341,23 +131,27 @@ H5P.MemoryGame = (function ($) {
$feedback = $('<div class="h5p-feedback">' + parameters.l10n.feedback + '</div>').appendTo($container); $feedback = $('<div class="h5p-feedback">' + parameters.l10n.feedback + '</div>').appendTo($container);
// Add status bar // Add status bar
var $status = $('<dl class="h5p-status">\ var $status = $('<dl class="h5p-status">' +
<dt>' + parameters.l10n.timeSpent + '</dt>\ '<dt>' + parameters.l10n.timeSpent + '</dt>' +
<dd class="h5p-time-spent">0:00</dd>\ '<dd class="h5p-time-spent">0:00</dd>' +
<dt>' + parameters.l10n.cardTurns + '</dt>\ '<dt>' + parameters.l10n.cardTurns + '</dt>' +
<dd class="h5p-card-turns">0</dd>\ '<dd class="h5p-card-turns">0</dd>' +
</dl>').appendTo($container); '</dl>').appendTo($container);
timer = new MemoryTimer($status.find('.h5p-time-spent')); timer = new MemoryGame.Timer($status.find('.h5p-time-spent'));
counter = new MemoryCounter($status.find('.h5p-card-turns')); counter = new MemoryGame.Counter($status.find('.h5p-card-turns'));
popup = new MemoryPop($container); popup = new MemoryGame.Popup($container);
$container.click(function () { $container.click(function () {
popup.close(); popup.close();
}); });
} }
}; };
}; }
// Extends the event dispatcher
MemoryGame.prototype = Object.create(EventDispatcher.prototype);
MemoryGame.prototype.constructor = MemoryGame;
return MemoryGame; return MemoryGame;
})(H5P.jQuery); })(H5P.EventDispatcher, H5P.jQuery);

44
popup.js Normal file
View File

@ -0,0 +1,44 @@
(function (MemoryGame, $) {
/**
* A dialog for reading the description of a card.
*
* @class H5P.MemoryGame.Popup
* @param {H5P.jQuery} $container
*/
MemoryGame.Popup = function ($container) {
var self = this;
var closed;
var $popup = $('<div class="h5p-memory-pop"><div class="h5p-memory-image"></div><div class="h5p-memory-desc"></div></div>').appendTo($container);
var $desc = $popup.find('.h5p-memory-desc');
var $image = $popup.find('.h5p-memory-image');
/**
* Show the popup.
*
* @param {string} desc
* @param {H5P.jQuery} $img
* @param {function} done
*/
self.show = function (desc, $img, done) {
$desc.text(desc);
$img.appendTo($image.html(''));
$popup.show();
closed = done;
};
/**
* Close the popup.
*/
self.close = function () {
if (closed !== undefined) {
$popup.hide();
closed();
closed = undefined;
}
};
};
})(H5P.MemoryGame, H5P.jQuery);

94
timer.js Normal file
View File

@ -0,0 +1,94 @@
(function (MemoryGame) {
/**
* Keeps track of the time spent.
*
* @class H5P.MemoryGame.Timer
* @param {H5P.jQuery} $container
*/
MemoryGame.Timer = function ($container) {
var self = this;
var interval, started, totalTime = 0;
/**
* Make timer more readable for humans.
* @private
* @param {Number} seconds
* @returns {String}
*/
var humanizeTime = function (seconds) {
var minutes = Math.floor(seconds / 60);
var hours = Math.floor(minutes / 60);
minutes = minutes % 60;
seconds = Math.floor(seconds % 60);
var time = '';
if (hours !== 0) {
time += hours + ':';
if (minutes < 10) {
time += '0';
}
}
time += minutes + ':';
if (seconds < 10) {
time += '0';
}
time += seconds;
return time;
};
/**
* Update the timer element.
*
* @private
* @param {boolean} last
* @returns {number}
*/
var update = function (last) {
var currentTime = (new Date().getTime() - started);
$container.text(humanizeTime(Math.floor((totalTime + currentTime) / 1000)));
if (last === true) {
// This is the last update, stop timing interval.
clearTimeout(interval);
}
else {
// Use setTimeout since setInterval isn't safe.
interval = setTimeout(function () {
update();
}, 1000);
}
return currentTime;
};
/**
* Starts the counter.
*/
self.start = function () {
if (started === undefined) {
started = new Date();
update();
}
};
/**
* Stops the counter.
*/
self.stop = function () {
if (started !== undefined) {
totalTime += update(true);
started = undefined;
}
};
};
})(H5P.MemoryGame);