diff --git a/card.js b/card.js
new file mode 100644
index 0000000..0c0a608
--- /dev/null
+++ b/card.js
@@ -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 = $('
' +
+ '' +
+ '' +
+ '
' +
+ '
' +
+ '')
+ .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);
diff --git a/counter.js b/counter.js
new file mode 100644
index 0000000..39343c8
--- /dev/null
+++ b/counter.js
@@ -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);
diff --git a/language/fr.json b/language/fr.json
new file mode 100644
index 0000000..96828bb
--- /dev/null
+++ b/language/fr.json
@@ -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!"
+ }
+ ]
+ }
+ ]
+}
diff --git a/library.json b/library.json
index 2d0dd69..a9a2c3b 100644
--- a/library.json
+++ b/library.json
@@ -16,6 +16,18 @@
"preloadedJs": [
{
"path": "memory-game.js"
+ },
+ {
+ "path": "card.js"
+ },
+ {
+ "path": "timer.js"
+ },
+ {
+ "path": "counter.js"
+ },
+ {
+ "path": "popup.js"
}
]
-}
\ No newline at end of file
+}
diff --git a/memory-game.js b/memory-game.js
index 7f38263..98166b8 100644
--- a/memory-game.js
+++ b/memory-game.js
@@ -1,262 +1,47 @@
-var H5P = H5P || {};
-
-/**
- * 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 = $('\
- \
- \
-
\
-
\
- ')
- .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 = $('').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;
- }
- };
- }
+H5P.MemoryGame = (function (EventDispatcher, $) {
/**
* Memory Game Constructor
*
+ * @class
* @param {Object} parameters
* @param {Number} 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
- * @param {MemoryCard} mate
- * @param {MemoryCard} correct
+ * @private
+ * @param {H5P.MemoryGame.Card} card
+ * @param {H5P.MemoryGame.Card} mate
+ * @param {H5P.MemoryGame.Card} correct
*/
var check = function (card, mate, correct) {
if (mate === correct) {
// Remove them from the game.
card.remove();
mate.remove();
-
+
removed += 2;
-
+
var finished = (removed === cards.length);
var desc = card.getDescription();
-
+
if (desc !== undefined) {
// Pause timer and show desciption.
timer.stop();
- popup.show(desc, card.getImage(), function () {
+ popup.show(desc, card.getImage(), function () {
if (finished) {
+ self.triggerXAPIScored(1, 1, 'completed');
// Game has finished
$feedback.addClass('h5p-show');
}
@@ -267,6 +52,7 @@ H5P.MemoryGame = (function ($) {
});
}
else if (finished) {
+ self.triggerXAPIScored(1, 1, 'completed');
// Game has finished
timer.stop();
$feedback.addClass('h5p-show');
@@ -278,23 +64,25 @@ H5P.MemoryGame = (function ($) {
mate.flipBack();
}
};
-
+
/**
- * 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
- * @param {MemoryCard} mate
+ * @private
+ * @param {H5P.MemoryGame.Card} card
+ * @param {H5P.MemoryGame.Card} mate
*/
var addCard = function (card, mate) {
- card.$.on('flip', function () {
+ card.on('flip', function () {
+ self.triggerXAPI('interacted');
// Keep track of time spent
timer.start();
-
+
if (flipped !== undefined) {
var matie = flipped;
// Reset the flipped card.
flipped = undefined;
-
+
setTimeout(function () {
check(card, matie, mate);
}, 800);
@@ -303,61 +91,67 @@ H5P.MemoryGame = (function ($) {
// Keep track of the flipped card.
flipped = card;
}
-
+
// Count number of cards turned
counter.increment();
});
-
+
cards.push(card);
};
-
+
// Initialize cards.
for (var i = 0; i < parameters.cards.length; i++) {
// Add two of each card
- var cardOne = new MemoryCard(parameters.cards[i], id);
- var cardTwo = new MemoryCard(parameters.cards[i], id);
+ var cardOne = new MemoryGame.Card(parameters.cards[i], id);
+ var cardTwo = new MemoryGame.Card(parameters.cards[i], id);
addCard(cardOne, cardTwo);
addCard(cardTwo, cardOne);
}
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('');
-
+
// Add cards to list
var $list = $('');
for (var i = 0; i < cards.length; i++) {
cards[i].appendTo($list);
}
-
+
if ($list.children().length) {
$list.appendTo($container);
-
+
$feedback = $('' + parameters.l10n.feedback + '
').appendTo($container);
-
+
// Add status bar
- var $status = $('\
- - ' + parameters.l10n.timeSpent + '
\
- - 0:00
\
- - ' + parameters.l10n.cardTurns + '
\
- - 0
\
-
').appendTo($container);
-
- timer = new MemoryTimer($status.find('.h5p-time-spent'));
- counter = new MemoryCounter($status.find('.h5p-card-turns'));
- popup = new MemoryPop($container);
-
+ var $status = $('' +
+ '- ' + parameters.l10n.timeSpent + '
' +
+ '- 0:00
' +
+ '- ' + parameters.l10n.cardTurns + '
' +
+ '- 0
' +
+ '
').appendTo($container);
+
+ timer = new MemoryGame.Timer($status.find('.h5p-time-spent'));
+ counter = new MemoryGame.Counter($status.find('.h5p-card-turns'));
+ popup = new MemoryGame.Popup($container);
+
$container.click(function () {
popup.close();
});
}
};
- };
-
+ }
+
+ // Extends the event dispatcher
+ MemoryGame.prototype = Object.create(EventDispatcher.prototype);
+ MemoryGame.prototype.constructor = MemoryGame;
+
return MemoryGame;
-})(H5P.jQuery);
+})(H5P.EventDispatcher, H5P.jQuery);
diff --git a/popup.js b/popup.js
new file mode 100644
index 0000000..2973974
--- /dev/null
+++ b/popup.js
@@ -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 = $('').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);
diff --git a/timer.js b/timer.js
new file mode 100644
index 0000000..39ca1f4
--- /dev/null
+++ b/timer.js
@@ -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);