diff --git a/card.js b/card.js index 9b1aa90..0343f60 100644 --- a/card.js +++ b/card.js @@ -8,8 +8,9 @@ * @param {Object} image * @param {number} id * @param {string} [description] + * @param {Object} [styles] */ - MemoryGame.Card = function (image, id, description) { + MemoryGame.Card = function (image, id, description, styles) { /** @alias H5P.MemoryGame.Card# */ var self = this; @@ -88,8 +89,8 @@ self.appendTo = function ($container) { // TODO: Translate alt attr $card = $('
  • ' + - '
    ' + - '
    ' + + '
    ' + (styles.backImage ? '' : '') + '
    ' + + '
    ' + 'Memory Card' + '
    ' + '
  • ') @@ -141,4 +142,83 @@ params.match.path !== undefined); }; + /** + * Determines the theme for how the cards should look + * + * @param {string} color The base color selected + * @param {number} invertShades Factor used to invert shades in case of bad contrast + */ + MemoryGame.Card.determineStyles = function (color, invertShades, backImage) { + var styles = { + front: '', + back: '', + backImage: !!backImage + }; + + // Create color theme + if (color) { + var frontColor = shade(color, 43.75 * invertShades); + var backColor = shade(color, 56.25 * invertShades); + + styles.front += 'color:' + color + ';' + + 'background-color:' + frontColor + ';' + + 'border-color:' + frontColor +';'; + styles.back += 'color:' + color + ';' + + 'background-color:' + backColor + ';' + + 'border-color:' + frontColor +';'; + } + + // Add back image for card + if (backImage) { + var backgroundImage = 'background-image:url(' + backImage + ')'; + + styles.front += backgroundImage; + styles.back += backgroundImage; + } + + // Prep style attribute + if (styles.front) { + styles.front = ' style="' + styles.front + '"'; + } + if (styles.back) { + styles.back = ' style="' + styles.back + '"'; + } + + return styles; + }; + + /** + * Convert hex color into shade depending on given percent + * + * @private + * @param {string} color + * @param {number} percent + * @return {string} new color + */ + var shade = function (color, percent) { + var newColor = '#'; + + // Determine if we should lighten or darken + var max = (percent < 0 ? 0 : 255); + + // Always stay positive + if (percent < 0) { + percent *= -1; + } + percent /= 100; + + for (var i = 1; i < 6; i += 2) { + // Grab channel and convert from hex to dec + var channel = parseInt(color.substr(i, 2), 16); + + // Calculate new shade and convert back to hex + channel = (Math.round((max - channel) * percent) + channel).toString(16); + + // Make sure to always use two digits + newColor += (channel.length < 2 ? '0' + channel : channel); + } + + return newColor; + }; + })(H5P.MemoryGame, H5P.EventDispatcher, H5P.jQuery); diff --git a/language/ar.json b/language/ar.json index 3ef63b3..8e9e0a9 100644 --- a/language/ar.json +++ b/language/ar.json @@ -46,6 +46,20 @@ } ] }, + { + "englishLabel": "Look and feel", + "englishDescription": "Control the visuals of the game.", + "fields": [ + { + "englishLabel": "Theme Color", + "englishDescription": "Choose a color to create a theme for your card game." + }, + { + "englishLabel": "Card Back", + "englishDescription": "Use a custom back for your cards." + } + ] + }, { "label": "الأقلمة", "fields": [ diff --git a/language/de.json b/language/de.json index d776e7c..2338693 100644 --- a/language/de.json +++ b/language/de.json @@ -46,6 +46,20 @@ } ] }, + { + "englishLabel": "Look and feel", + "englishDescription": "Control the visuals of the game.", + "fields": [ + { + "englishLabel": "Theme Color", + "englishDescription": "Choose a color to create a theme for your card game." + }, + { + "englishLabel": "Card Back", + "englishDescription": "Use a custom back for your cards." + } + ] + }, { "label": "Übersetzung", "fields": [ diff --git a/language/fr.json b/language/fr.json index 41f9217..619b64e 100644 --- a/language/fr.json +++ b/language/fr.json @@ -46,6 +46,20 @@ } ] }, + { + "englishLabel": "Look and feel", + "englishDescription": "Control the visuals of the game.", + "fields": [ + { + "englishLabel": "Theme Color", + "englishDescription": "Choose a color to create a theme for your card game." + }, + { + "englishLabel": "Card Back", + "englishDescription": "Use a custom back for your cards." + } + ] + }, { "label": "Interface", "fields": [ diff --git a/language/it.json b/language/it.json index 697442b..b0d1dbb 100644 --- a/language/it.json +++ b/language/it.json @@ -46,6 +46,20 @@ } ] }, + { + "englishLabel": "Look and feel", + "englishDescription": "Control the visuals of the game.", + "fields": [ + { + "englishLabel": "Theme Color", + "englishDescription": "Choose a color to create a theme for your card game." + }, + { + "englishLabel": "Card Back", + "englishDescription": "Use a custom back for your cards." + } + ] + }, { "label": "Localizzazione", "fields": [ diff --git a/language/nb.json b/language/nb.json index 5633b0d..38801d6 100644 --- a/language/nb.json +++ b/language/nb.json @@ -52,6 +52,26 @@ } ] }, + { + "englishLabel": "Look and feel", + "label": "Tilpass utseende", + "englishDescription": "Control the visuals of the game.", + "description": "Kontroller de visuelle aspektene ved spillet.", + "fields": [ + { + "englishLabel": "Theme Color", + "label": "Temafarge", + "englishDescription": "Choose a color to create a theme for your card game.", + "description": "Velg en farge for å skape et tema over kortspillet ditt." + }, + { + "englishLabel": "Card Back", + "label": "Kortbaksiden", + "englishDescription": "Use a custom back for your cards.", + "description": "Bruk en tilpasset kortbakside for kortene dine." + } + ] + }, { "englishLabel": "Localization", "label": "Oversettelser", diff --git a/language/source/en.json b/language/source/en.json deleted file mode 100644 index 823589f..0000000 --- a/language/source/en.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "semantics": [ - { - "label": "Cards", - "entity": "card", - "field": { - "label": "Card", - "fields": [ - { - "label": "Image" - }, - { - "label": "Matching Image", - "description": "An optional image to match against instead of using two cards with the same image." - }, - { - "label": "Description", - "description": "An optional short text that will pop up once the two matching cards are found." - } - ] - } - }, - { - "label": "Behavioural settings", - "description": "These options will let you control how the game behaves.", - "fields": [ - { - "label": "Position the cards in a square", - "description": "Will try to match the number of columns and rows when laying out the cards. Afterward, the cards will be scaled to fit the container." - }, - { - "label": "Number of cards to use", - "description": "Setting this to a number greater than 2 will make the game pick random cards from the list of cards." - }, - { - "label": "Add button for retrying when the game is over" - } - ] - }, - { - "label": "Localization", - "fields": [ - { - "label": "Card turns text", - "default": "Card turns" - }, - { - "label": "Time spent text", - "default": "Time spent" - }, - { - "label": "Feedback text", - "default": "Good work!" - }, - { - "label": "Try again button text", - "default": "Try again?" - } - ] - } - ] -} \ No newline at end of file diff --git a/library.json b/library.json index bac360c..7aac140 100644 --- a/library.json +++ b/library.json @@ -36,5 +36,12 @@ "majorVersion": 0, "minorVersion": 4 } + ], + "editorDependencies": [ + { + "machineName": "H5PEditor.ColorSelector", + "majorVersion": 1, + "minorVersion": 2 + } ] -} \ No newline at end of file +} diff --git a/memory-game.css b/memory-game.css index cb3f9ef..d1ef427 100644 --- a/memory-game.css +++ b/memory-game.css @@ -25,6 +25,7 @@ display: inline-block !important; margin: auto !important; vertical-align: middle; + position: relative; } .h5p-memory-game .h5p-memory-wrap { float: left; @@ -55,6 +56,7 @@ width: 100%; height: 100%; background: #cfcfcf; + background-size: cover; border: 2px solid #d0d0d0; box-sizing: border-box; -moz-box-sizing: border-box; @@ -75,14 +77,30 @@ .h5p-memory-game .h5p-memory-card .h5p-front { cursor: pointer; text-align: center; + color: #909090; } -.h5p-memory-game .h5p-memory-card .h5p-front:hover { - background: #dfdfdf; +.h5p-memory-game .h5p-memory-card .h5p-front:before, +.h5p-memory-game .h5p-memory-card .h5p-back:before { + position: absolute; + display: block; + content: ""; + width: 100%; + height: 100%; + background: #fff; + opacity: 0; } -.h5p-memory-game .h5p-memory-card .h5p-front:before { +.h5p-memory-game.h5p-invert-shades .h5p-memory-card .h5p-front:before, +.h5p-memory-game.h5p-invert-shades .h5p-memory-card .h5p-back:before { + background: #000; +} + +.h5p-memory-game .h5p-memory-card .h5p-front:hover:before { + opacity: 0.4; +} +.h5p-memory-game .h5p-memory-card .h5p-front > span:before { + position: relative; content: "?"; font-size: 3.75em; - color: #909090; line-height: 1.67em; } .h5p-memory-game .h5p-memory-card .h5p-front:after { @@ -102,13 +120,16 @@ .h5p-memory-game .h5p-memory-card .h5p-back { line-height: 5.9em; text-align: center; - background: #f0f0f0; + background-color: #f0f0f0; -webkit-transform: rotateY(-180deg); -moz-transform: rotateY(-180deg); transform: rotateY(-180deg); filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); -ms-transform: scale(0,1.1); } +.h5p-memory-game .h5p-memory-card .h5p-back:before { + opacity: 0.5; +} .h5p-memory-game .h5p-memory-card.h5p-flipped .h5p-back { -webkit-transform: rotateY(0deg); -moz-transform: rotateY(0deg); @@ -124,6 +145,8 @@ } .h5p-memory-game .h5p-memory-card.h5p-matched { opacity: 0.3; +} +.h5p-memory-game .h5p-memory-card.h5p-matched img { filter: grayscale(100%); } diff --git a/memory-game.js b/memory-game.js index 2e2cd2f..a0fd4b9 100644 --- a/memory-game.js +++ b/memory-game.js @@ -247,21 +247,30 @@ H5P.MemoryGame = (function (EventDispatcher, $) { return cardsToUse; }; + var cardStyles, invertShades; + if (parameters.lookNFeel) { + // If the contrast between the chosen color and white is too low we invert the shades to create good contrast + invertShades = (parameters.lookNFeel.themeColor && + getContrast(parameters.lookNFeel.themeColor) < 1.7 ? -1 : 1); + var backImage = (parameters.lookNFeel.cardBack ? H5P.getPath(parameters.lookNFeel.cardBack.path, id) : null); + cardStyles = MemoryGame.Card.determineStyles(parameters.lookNFeel.themeColor, invertShades, backImage); + } + // Initialize cards. 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); + var cardTwo, cardOne = new MemoryGame.Card(cardParams.image, id, cardParams.description, cardStyles); if (MemoryGame.Card.hasTwoImages(cardParams)) { // Use matching image for card two - cardTwo = new MemoryGame.Card(cardParams.match, id, cardParams.description); + cardTwo = new MemoryGame.Card(cardParams.match, id, cardParams.description, cardStyles); } else { // Add two cards with the same image - cardTwo = new MemoryGame.Card(cardParams.image, id, cardParams.description); + cardTwo = new MemoryGame.Card(cardParams.image, id, cardParams.description, cardStyles); } // Add cards to card list for shuffeling @@ -280,6 +289,9 @@ H5P.MemoryGame = (function (EventDispatcher, $) { this.triggerXAPI('attempted'); // TODO: Only create on first attach! $wrapper = $container.addClass('h5p-memory-game').html(''); + if (invertShades === -1) { + $container.addClass('h5p-invert-shades'); + } // Add cards to list var $list = $('