diff --git a/card.js b/card.js index dca1639..58bfcfb 100644 --- a/card.js +++ b/card.js @@ -4,33 +4,31 @@ * Controls all the operations for each card. * * @class H5P.MemoryGame.Card - * @param {Object} parameters - * @param {Number} id + * @param {Object} image + * @param {number} id + * @param {string} [description] */ - MemoryGame.Card = function (parameters, id) { + MemoryGame.Card = function (image, id, description) { var self = this; // Initialize event inheritance EventDispatcher.call(self); - var path = H5P.getPath(parameters.image.path, id); + var path = H5P.getPath(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'; + if (image.width !== undefined && image.height !== undefined) { + if (image.width > image.height) { + width = '100%'; + height = 'auto'; } else { - height = a; - width = parameters.image.width * (height / parameters.image.height); - margin = '0 0 0 ' + ((a - width) / 2) + 'px'; + height = '100%'; + width = 'auto'; } } else { - width = height = a; + width = height = '100%'; } /** @@ -61,7 +59,7 @@ * @returns {string} */ self.getDescription = function () { - return parameters.description; + return description; }; /** @@ -80,18 +78,19 @@ */ self.appendTo = function ($container) { // TODO: Translate alt attr - $card = $('
  • ' + + $card = $('
  • ' + '
    ' + '
    ' + - 'Memory Card' + + 'Memory Card' + '
    ' + - '
  • ') + '') .appendTo($container) - .children('.h5p-front') - .click(function () { - self.flip(); - }) - .end(); + .children('.h5p-memory-card') + .children('.h5p-front') + .click(function () { + self.flip(); + }) + .end(); }; }; @@ -112,4 +111,17 @@ params.image.path !== undefined); }; + /** + * Checks to see if the card parameters should create cards with different + * images. + * + * @param {object} params + * @returns {boolean} + */ + MemoryGame.Card.hasTwoImages = function (params) { + return (params !== undefined && + params.match !== undefined && + params.match.path !== undefined); + }; + })(H5P.MemoryGame, H5P.EventDispatcher, H5P.jQuery); diff --git a/language/ar.json b/language/ar.json index 6ae0267..6b999ef 100644 --- a/language/ar.json +++ b/language/ar.json @@ -9,6 +9,12 @@ { "label": "الصورة" }, + { + "englishLabel": "Matching Image", + "label": "Matching Image", + "englishDescription": "An optional image to match against instead of using two cards with the same image.", + "description": "An optional image to match against instead of using two cards with the same image." + }, { "label": "الوصف", "description": "نص قصير يتم عرضه مرة واحدة علي اثنين من البطاقات متساوية" @@ -16,6 +22,28 @@ ] } }, + { + "englishLabel": "Behavioural settings", + "label": "Behavioural settings", + "englishDescription": "These options will let you control how the game behaves.", + "description": "These options will let you control how the game behaves.", + "fields": [ + { + "englishLabel": "Put the cards in a grid layout", + "label": "Put the cards in a grid layout" + }, + { + "englishLabel": "Number of cards to use", + "label": "Number of cards to use", + "englishDescription": "Setting this to a number greater than 2 will make the game pick random cards from the list of cards.", + "description": "Setting this to a number greater than 2 will make the game pick random cards from the list of cards." + }, + { + "englishLabel": "Add button for retrying when the game is over", + "label": "Add button for retrying when the game is over" + } + ] + }, { "label": "الأقلمة", "fields": [ diff --git a/language/de.json b/language/de.json index 95c034e..e84760f 100644 --- a/language/de.json +++ b/language/de.json @@ -9,6 +9,12 @@ { "label": "Bild" }, + { + "englishLabel": "Matching Image", + "label": "Matching Image", + "englishDescription": "An optional image to match against instead of using two cards with the same image.", + "description": "An optional image to match against instead of using two cards with the same image." + }, { "label": "Beschreibung", "description": "Ein kurzer Text, der angezeigt wird, sobald zwei identische Karten gefunden werden." @@ -16,6 +22,28 @@ ] } }, + { + "englishLabel": "Behavioural settings", + "label": "Behavioural settings", + "englishDescription": "These options will let you control how the game behaves.", + "description": "These options will let you control how the game behaves.", + "fields": [ + { + "englishLabel": "Put the cards in a grid layout", + "label": "Put the cards in a grid layout" + }, + { + "englishLabel": "Number of cards to use", + "label": "Number of cards to use", + "englishDescription": "Setting this to a number greater than 2 will make the game pick random cards from the list of cards.", + "description": "Setting this to a number greater than 2 will make the game pick random cards from the list of cards." + }, + { + "englishLabel": "Add button for retrying when the game is over", + "label": "Add button for retrying when the game is over" + } + ] + }, { "label": "Übersetzung", "fields": [ @@ -34,4 +62,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/language/fr.json b/language/fr.json index e00969e..aa3b53b 100644 --- a/language/fr.json +++ b/language/fr.json @@ -9,6 +9,12 @@ { "label": "Image" }, + { + "englishLabel": "Matching Image", + "label": "Matching Image", + "englishDescription": "An optional image to match against instead of using two cards with the same image.", + "description": "An optional image to match against instead of using two cards with the same image." + }, { "label": "Description", "description": "Petit texte affiché quand deux cartes identiques sont trouvées." @@ -16,6 +22,28 @@ ] } }, + { + "englishLabel": "Behavioural settings", + "label": "Behavioural settings", + "englishDescription": "These options will let you control how the game behaves.", + "description": "These options will let you control how the game behaves.", + "fields": [ + { + "englishLabel": "Put the cards in a grid layout", + "label": "Put the cards in a grid layout" + }, + { + "englishLabel": "Number of cards to use", + "label": "Number of cards to use", + "englishDescription": "Setting this to a number greater than 2 will make the game pick random cards from the list of cards.", + "description": "Setting this to a number greater than 2 will make the game pick random cards from the list of cards." + }, + { + "englishLabel": "Add button for retrying when the game is over", + "label": "Add button for retrying when the game is over" + } + ] + }, { "label": "Interface", "fields": [ diff --git a/language/it.json b/language/it.json index 4adb03f..5b58c96 100644 --- a/language/it.json +++ b/language/it.json @@ -9,6 +9,12 @@ { "label": "Immagine" }, + { + "englishLabel": "Matching Image", + "label": "Matching Image", + "englishDescription": "An optional image to match against instead of using two cards with the same image.", + "description": "An optional image to match against instead of using two cards with the same image." + }, { "label": "Descrizione", "description": "Breve testo visualizzato quando due carte uguali vengono trovate." @@ -16,6 +22,28 @@ ] } }, + { + "englishLabel": "Behavioural settings", + "label": "Behavioural settings", + "englishDescription": "These options will let you control how the game behaves.", + "description": "These options will let you control how the game behaves.", + "fields": [ + { + "englishLabel": "Put the cards in a grid layout", + "label": "Put the cards in a grid layout" + }, + { + "englishLabel": "Number of cards to use", + "label": "Number of cards to use", + "englishDescription": "Setting this to a number greater than 2 will make the game pick random cards from the list of cards.", + "description": "Setting this to a number greater than 2 will make the game pick random cards from the list of cards." + }, + { + "englishLabel": "Add button for retrying when the game is over", + "label": "Add button for retrying when the game is over" + } + ] + }, { "label": "Localizzazione", "fields": [ @@ -31,4 +59,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/language/nb.json b/language/nb.json index b56939a..b90dd68 100644 --- a/language/nb.json +++ b/language/nb.json @@ -13,15 +13,43 @@ "englishLabel": "Image", "label": "Bilde" }, + { + "englishLabel": "Matching Image", + "label": "Tilhørende bilde", + "englishDescription": "An optional image to match against instead of using two cards with the same image.", + "description": "Et valgfritt bilde som brukes av kort nummer to istedenfor å bruke to kort med samme bilde." + }, { "englishLabel": "Description", "label": "Beskrivelse", - "englishDescription": "A short text that is displayed once the two equal cards are found.", - "description": "En kort tekst som vises hver gang et kort-par er funnet." + "englishDescription": "An optional short text that will pop up once the two matching cards are found.", + "description": "En valgfri kort tekst som spretter opp når kort-paret er funnet." } ] } }, + { + "englishLabel": "Behavioural settings", + "label": "Innstillinger for oppførsel", + "englishDescription": "These options will let you control how the game behaves.", + "description": "Disse instillingene lar deg bestemme hvordan spillet skal oppføre seg.", + "fields": [ + { + "englishLabel": "Put the cards in a grid layout", + "label": "Putt kortene i et rutenett" + }, + { + "englishLabel": "Number of cards to use", + "label": "Antall kort som skal brukes", + "englishDescription": "Setting this to a number greater than 2 will make the game pick random cards from the list of cards.", + "description": "Ved å sette antallet høyere enn 2 vil spillet plukke tilfeldige kort fra listen over kort." + }, + { + "englishLabel": "Add button for retrying when the game is over", + "label": "Legg til knapp for å prøve på nytt når spillet er over" + } + ] + }, { "englishLabel": "Localization", "label": "Oversettelser", diff --git a/library.json b/library.json index 894d655..411aab1 100644 --- a/library.json +++ b/library.json @@ -2,8 +2,8 @@ "title": "Memory Game", "description": "See how many cards you can remember!", "majorVersion": 1, - "minorVersion": 1, - "patchVersion": 11, + "minorVersion": 2, + "patchVersion": 1, "runnable": 1, "author": "Amendor AS", "license": "MIT", @@ -20,14 +20,18 @@ { "path": "card.js" }, - { - "path": "timer.js" - }, { "path": "counter.js" }, { "path": "popup.js" } + ], + "preloadedDependencies": [ + { + "machineName": "H5P.Timer", + "majorVersion": 0, + "minorVersion": 4 + } ] } \ No newline at end of file diff --git a/memory-game.css b/memory-game.css index 157ab81..f491184 100644 --- a/memory-game.css +++ b/memory-game.css @@ -1,17 +1,20 @@ .h5p-memory-game { overflow: hidden; } -html .h5p-memory-game > ul { - list-style: none; - padding: 0; - margin: 0; - overflow: hidden; +.h5p-memory-game > ul { + list-style: none !important; + padding: 0.25em 0.5em !important; + margin: 0 !important; + overflow: hidden !important; + font-size: 16px; + box-sizing: border-box; + -moz-box-sizing: border-box; } .h5p-memory-game .h5p-memory-card, .h5p-memory-game .h5p-memory-card .h5p-back, .h5p-memory-game .h5p-memory-card .h5p-front { - width: 100px; - height: 100px; + width: 6.25em; + height: 6.25em; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; @@ -19,23 +22,30 @@ html .h5p-memory-game > ul { } .h5p-memory-game img { -webkit-user-drag: none; + display: inline-block !important; + margin: auto !important; + vertical-align: middle; +} +.h5p-memory-game .h5p-memory-wrap { + float: left; + text-align: center; + background-image: none !important; + margin: 0 !important; + padding: 0 !important; } .h5p-memory-game .h5p-memory-card { - float: left; + display: inline-block; outline: none; position: relative; - margin: 1em; + margin: 0.75em 0.5em; padding: 0; background: transparent; - -webkit-perspective: 400px; - -moz-perspective: 400px; - perspective: 400px; - -webkit-transform-style: preserve-3d; - -moz-transform-style: preserve-3d; - transform-style: preserve-3d; - -webkit-transition: opacity 0.4s; - -moz-transition: opacity 0.4s; - transition: opacity 0.4s; + -webkit-perspective: 25em; + -moz-perspective: 25em; + perspective: 25em; + -webkit-transition: opacity 0.4s, filter 0.4s; + -moz-transition: opacity 0.4s, filter 0.4s; + transition: opacity 0.4s, filter 0.4s; } .h5p-memory-game .h5p-memory-card .h5p-back, .h5p-memory-game .h5p-memory-card .h5p-front { @@ -64,17 +74,16 @@ html .h5p-memory-game > ul { } .h5p-memory-game .h5p-memory-card .h5p-front { cursor: pointer; + text-align: center; } .h5p-memory-game .h5p-memory-card .h5p-front:hover { background: #dfdfdf; } .h5p-memory-game .h5p-memory-card .h5p-front:before { - position: absolute; content: "?"; - font-size: 60px; + font-size: 3.75em; color: #909090; - line-height: 100px; - left: 35px; + line-height: 1.67em; } .h5p-memory-game .h5p-memory-card .h5p-front:after { content: ""; @@ -91,6 +100,8 @@ html .h5p-memory-game > ul { background-image: radial-gradient(ellipse closest-side, rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 0) 100%); } .h5p-memory-game .h5p-memory-card .h5p-back { + line-height: 5.9em; + text-align: center; background: #f0f0f0; -webkit-transform: rotateY(-180deg); -moz-transform: rotateY(-180deg); @@ -112,7 +123,8 @@ html .h5p-memory-game > ul { -ms-transform: scale(0,1.1); } .h5p-memory-game .h5p-memory-card.h5p-matched { - opacity: 0.25; + opacity: 0.3; + filter: grayscale(100%); } .h5p-memory-game .h5p-feedback { @@ -161,13 +173,12 @@ html .h5p-memory-game > ul { padding: 1em; width: 20em; position: absolute; - top: 2em; + top: 50%; left: 50%; - margin-left: -10em; box-shadow: 0 0 1em #666; - -webkit-transform: translateZ(24px); - -moz-transform: translateZ(24px); - transform: translateZ(24px); + -webkit-transform: translate(-50%,-50%); + -moz-transform: translate(-50%,-50%); + transform: translate(-50%,-50%); } .h5p-memory-game .h5p-memory-image { float: left; @@ -177,6 +188,9 @@ html .h5p-memory-game > ul { -moz-box-sizing: border-box; border-radius: 4px; background: #f0f0f0; - width: 100px; - height: 100px; + width: 6.25em; + height: 6.25em; +} +.h5p-memory-game .h5p-row-break { + clear: left; } diff --git a/memory-game.js b/memory-game.js index 845fe3b..5165c1c 100644 --- a/memory-game.js +++ b/memory-game.js @@ -1,5 +1,11 @@ H5P.MemoryGame = (function (EventDispatcher, $) { + // We don't want to go smaller than 100px per card(including the required margin) + var CARD_MIN_SIZE = 100; // PX + var CARD_STD_SIZE = 116; // PX + var STD_FONT_SIZE = 16; // PX + var LIST_PADDING = 1; // EMs + /** * Memory Game Constructor * @@ -13,7 +19,7 @@ H5P.MemoryGame = (function (EventDispatcher, $) { // Initialize event inheritance EventDispatcher.call(self); - var flipped, timer, counter, popup, $feedback; + var flipped, timer, counter, popup, $feedback, $wrapper, maxWidth, numCols; var cards = []; var removed = 0; @@ -42,15 +48,16 @@ H5P.MemoryGame = (function (EventDispatcher, $) { if (desc !== undefined) { // Pause timer and show desciption. - timer.stop(); + timer.pause(); popup.show(desc, card.getImage(), function () { if (finished) { // Game has finished $feedback.addClass('h5p-show'); + if (parameters.behaviour && parameters.behaviour.allowRetry) { /* TODO */ } } else { // Popup is closed, continue. - timer.start(); + timer.play(); } }); } @@ -58,6 +65,7 @@ H5P.MemoryGame = (function (EventDispatcher, $) { // Game has finished timer.stop(); $feedback.addClass('h5p-show'); + if (parameters.behaviour && parameters.behaviour.allowRetry) { /* TODO */ } } } else { @@ -78,7 +86,7 @@ H5P.MemoryGame = (function (EventDispatcher, $) { card.on('flip', function () { self.triggerXAPI('interacted'); // Keep track of time spent - timer.start(); + timer.play(); if (flipped !== undefined) { var matie = flipped; @@ -101,12 +109,53 @@ H5P.MemoryGame = (function (EventDispatcher, $) { cards.push(card); }; + /** + * @private + */ + var getCardsToUse = function () { + var numCardsToUse = (parameters.behaviour && parameters.behaviour.numCardsToUse ? parseInt(parameters.behaviour.numCardsToUse) : 0); + if (numCardsToUse <= 2 || numCardsToUse >= parameters.cards.length) { + // Use all cards + return parameters.cards; + } + + // Pick random cards from pool + var cardsToUse = []; + var pickedCardsMap = {}; + + var numPicket = 0; + while (numPicket < numCardsToUse) { + var pickIndex = Math.floor(Math.random() * parameters.cards.length); + if (pickedCardsMap[pickIndex]) { + continue; // Already picked, try again! + } + + cardsToUse.push(parameters.cards[pickIndex]); + pickedCardsMap[pickIndex] = true; + numPicket++; + } + + return cardsToUse; + }; + // Initialize cards. - for (var i = 0; i < parameters.cards.length; i++) { - if (MemoryGame.Card.isValid(parameters.cards[i])) { - // Add two of each card - var cardOne = new MemoryGame.Card(parameters.cards[i], id); - var cardTwo = new MemoryGame.Card(parameters.cards[i], id); + var cardsToUse = getCardsToUse(); + for (var i = 0; i < cardsToUse.length; i++) { + var cardParams = cardsToUse[i]; + if (MemoryGame.Card.isValid(cardParams)) { + // Create first card + var cardTwo, cardOne = new MemoryGame.Card(cardParams.image, id, cardParams.description); + + if (MemoryGame.Card.hasTwoImages(cardParams)) { + // Use matching image for card two + cardTwo = new MemoryGame.Card(cardParams.match, id, cardParams.description); + } + else { + // Add two cards with the same image + cardTwo = new MemoryGame.Card(cardParams.image, id, cardParams.description); + } + + // Add cards to card list for shuffeling addCard(cardOne, cardTwo); addCard(cardTwo, cardOne); } @@ -120,8 +169,8 @@ H5P.MemoryGame = (function (EventDispatcher, $) { */ self.attach = function ($container) { this.triggerXAPI('attempted'); - // TODO: Only create on first! - $container.addClass('h5p-memory-game').html(''); + // TODO: Only create on first attach! + $wrapper = $container.addClass('h5p-memory-game').html(''); // Add cards to list var $list = $('