' +
- '
' +
- '
' +
+ '
' + (styles.backImage ? '' : '') + '
' +
+ '
' +
'
' +
'
' +
'
')
@@ -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 = $('
');
@@ -380,5 +392,18 @@ H5P.MemoryGame = (function (EventDispatcher, $) {
MemoryGame.prototype = Object.create(EventDispatcher.prototype);
MemoryGame.prototype.constructor = MemoryGame;
+ /**
+ * Determine color contrast level compared to white(#fff)
+ *
+ * @private
+ * @param {string} color hex code
+ * @return {number} From 1 to Infinity.
+ */
+ var getContrast = function (color) {
+ return 255 / ((parseInt(color.substr(1, 2), 16) * 299 +
+ parseInt(color.substr(3, 2), 16) * 587 +
+ parseInt(color.substr(5, 2), 16) * 144) / 1000);
+ };
+
return MemoryGame;
})(H5P.EventDispatcher, H5P.jQuery);
diff --git a/semantics.json b/semantics.json
index b25e38c..ea1e290 100644
--- a/semantics.json
+++ b/semantics.json
@@ -75,6 +75,38 @@
}
]
},
+ {
+ "name": "lookNFeel",
+ "type": "group",
+ "label": "Look and feel",
+ "importance": "low",
+ "description": "Control the visuals of the game.",
+ "optional": true,
+ "fields": [
+ {
+ "name": "themeColor",
+ "type": "text",
+ "label": "Theme Color",
+ "importance": "low",
+ "description": "Choose a color to create a theme for your card game.",
+ "optional": true,
+ "default": "#909090",
+ "widget": "colorSelector",
+ "spectrum": {
+ "showInput": true
+ }
+ },
+ {
+ "name": "cardBack",
+ "type": "image",
+ "label": "Card Back",
+ "importance": "low",
+ "optional": true,
+ "description": "Use a custom back for your cards.",
+ "ratio": 1
+ }
+ ]
+ },
{
"label": "Localization",
"importance": "low",