' +
- '
' +
- '
' +
+ '
' + (styles && 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/crowdin.yml b/crowdin.yml
new file mode 100644
index 0000000..48edea1
--- /dev/null
+++ b/crowdin.yml
@@ -0,0 +1,3 @@
+files:
+ - source: /language/.en.json
+ translation: /language/%two_letters_code%.json
diff --git a/language/.en.json b/language/.en.json
new file mode 100644
index 0000000..8f501f2
--- /dev/null
+++ b/language/.en.json
@@ -0,0 +1,87 @@
+{
+ "semantics": [
+ {
+ "widgets": [
+ {
+ "label": "Default"
+ }
+ ],
+ "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": "Look and feel",
+ "description": "Control the visuals of the game.",
+ "fields": [
+ {
+ "label": "Theme Color",
+ "description": "Choose a color to create a theme for your card game.",
+ "default": "#909090",
+ "spectrum": {}
+ },
+ {
+ "label": "Card Back",
+ "description": "Use a custom back for your cards."
+ }
+ ]
+ },
+ {
+ "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?"
+ },
+ {
+ "label": "Close button label",
+ "default": "Close"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/language/ar.json b/language/ar.json
index 3ef63b3..a61cb4e 100644
--- a/language/ar.json
+++ b/language/ar.json
@@ -10,9 +10,7 @@
"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."
},
{
@@ -23,29 +21,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": "Position the cards in a square",
"label": "Position the cards in a square",
- "englishDescription": "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.",
"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."
},
{
- "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"
}
]
},
+ {
+ "fields": [
+ {},
+ {}
+ ]
+ },
{
"label": "الأقلمة",
"fields": [
@@ -58,13 +55,9 @@
{
"label": "نص الملاحظات"
},
- {
- "englishLabel": "Try again button text",
- "label": "Try again button text",
- "englishDefault": "Try again?",
- "default": "Try again?"
- }
+ {},
+ {}
]
}
]
-}
+}
\ No newline at end of file
diff --git a/language/bs.json b/language/bs.json
new file mode 100644
index 0000000..9a6c016
--- /dev/null
+++ b/language/bs.json
@@ -0,0 +1,69 @@
+{
+ "semantics": [
+ {
+ "label": "Karte",
+ "entity": "karte",
+ "field": {
+ "label": "Karte",
+ "fields": [
+ {
+ "label": "Slika"
+ },
+ {
+ "label": "Ista slika",
+ "description": "Opcionalna slika koja se koristi umjestodvije iste slike."
+ },
+ {
+ "label": "Opis",
+ "description": "Kratak tekst koji će biti prikazan čim se pronađu dvije iste karte."
+ }
+ ]
+ }
+ },
+ {
+ "label": "Podešavanje ponašanja",
+ "description": "These options will let you control how the game behaves.",
+ "fields": [
+ {
+ "label": "Poredaj karte u redove ",
+ "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": "Broj karata za upotrebu",
+ "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"
+ }
+ ]
+ },
+ {
+ "fields": [
+ {},
+ {}
+ ]
+ },
+ {
+ "label": "Prijevod",
+ "fields": [
+ {
+ "label": "Tekst kad se okrene karta ",
+ "default": "Okrenuta karta"
+ },
+ {
+ "label": "Tekst za provedeno vrijeme",
+ "default": "Provedeno vrijeme"
+ },
+ {
+ "label": "Feedback tekst",
+ "default": "BRAVO!"
+ },
+ {
+ "label": "Tekst na dugmetu pokušaj ponovo",
+ "default": "Pokušaj ponovo?"
+ },
+ {}
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/language/de.json b/language/de.json
index d776e7c..533d1d3 100644
--- a/language/de.json
+++ b/language/de.json
@@ -10,9 +10,7 @@
"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."
},
{
@@ -23,29 +21,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": "Position the cards in a square",
"label": "Position the cards in a square",
- "englishDescription": "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.",
"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."
},
{
- "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"
}
]
},
+ {
+ "fields": [
+ {},
+ {}
+ ]
+ },
{
"label": "Übersetzung",
"fields": [
@@ -61,13 +58,9 @@
"label": "Text als Rückmeldung",
"default": "Gute Arbeit!"
},
- {
- "englishLabel": "Try again button text",
- "label": "Try again button text",
- "englishDefault": "Try again?",
- "default": "Try again?"
- }
+ {},
+ {}
]
}
]
-}
+}
\ No newline at end of file
diff --git a/language/fr.json b/language/fr.json
index 41f9217..22313fc 100644
--- a/language/fr.json
+++ b/language/fr.json
@@ -10,9 +10,7 @@
"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."
},
{
@@ -23,29 +21,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": "Position the cards in a square",
"label": "Position the cards in a square",
- "englishDescription": "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.",
"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."
},
{
- "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"
}
]
},
+ {
+ "fields": [
+ {},
+ {}
+ ]
+ },
{
"label": "Interface",
"fields": [
@@ -61,13 +58,9 @@
"label": "Texte de l'appréciation finale",
"default": "Bien joué !"
},
- {
- "englishLabel": "Try again button text",
- "label": "Try again button text",
- "englishDefault": "Try again?",
- "default": "Try again?"
- }
+ {},
+ {}
]
}
]
-}
+}
\ No newline at end of file
diff --git a/language/it.json b/language/it.json
index 697442b..1484e25 100644
--- a/language/it.json
+++ b/language/it.json
@@ -10,9 +10,7 @@
"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."
},
{
@@ -23,29 +21,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": "Position the cards in a square",
"label": "Position the cards in a square",
- "englishDescription": "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.",
"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."
},
{
- "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"
}
]
},
+ {
+ "fields": [
+ {},
+ {}
+ ]
+ },
{
"label": "Localizzazione",
"fields": [
@@ -58,13 +55,9 @@
{
"label": "Testo Feedback"
},
- {
- "englishLabel": "Try again button text",
- "label": "Try again button text",
- "englishDefault": "Try again?",
- "default": "Try again?"
- }
+ {},
+ {}
]
}
]
-}
+}
\ No newline at end of file
diff --git a/language/nb.json b/language/nb.json
index 5633b0d..5905bb6 100644
--- a/language/nb.json
+++ b/language/nb.json
@@ -1,86 +1,80 @@
{
"semantics": [
{
- "englishLabel": "Cards",
"label": "Kort",
- "englishEntity": "card",
"entity": "kort",
"field": {
- "englishLabel": "Card",
"label": "Kort",
"fields": [
{
- "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": "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": "Position the cards in a square",
"label": "Plasser kortene i en firkant",
- "englishDescription": "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.",
"description": "Vil forsøk å samsvare antall kolonner og rader når kortene legges ut. Etterpå vil kortene bli skalert til å passe beholderen."
},
{
- "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": "Tilpass utseende",
+ "description": "Kontroller de visuelle aspektene ved spillet.",
+ "fields": [
+ {
+ "label": "Temafarge",
+ "description": "Velg en farge for å skape et tema over kortspillet ditt."
+ },
+ {
+ "label": "Kortbaksiden",
+ "description": "Bruk en tilpasset kortbakside for kortene dine."
+ }
+ ]
+ },
+ {
"label": "Oversettelser",
"fields": [
{
- "englishLabel": "Card turns text",
"label": "Etikett for antall vendte kort",
- "englishDefault": "Card turns",
"default": "Kort vendt"
},
{
- "englishLabel": "Time spent text",
"label": "Etikett for tid brukt",
- "englishDefault": "Time spent",
"default": "Tid brukt"
},
{
- "englishLabel": "Feedback text",
"label": "Tilbakemeldingstekst",
- "englishDefault": "Good work!",
"default": "Godt jobbet!"
},
{
- "englishLabel": "Try again button text",
"label": "Prøv på nytt-tekst",
- "englishDefault": "Try again?",
"default": "Prøv på nytt?"
+ },
+ {
+ "label": "Lukk knapp-merkelapp",
+ "default": "Lukk"
}
]
}
]
-}
+}
\ No newline at end of file
diff --git a/library.json b/library.json
index bac360c..a396e92 100644
--- a/library.json
+++ b/library.json
@@ -3,7 +3,7 @@
"description": "See how many cards you can remember!",
"majorVersion": 1,
"minorVersion": 2,
- "patchVersion": 2,
+ "patchVersion": 3,
"runnable": 1,
"author": "Joubel",
"license": "MIT",
@@ -35,6 +35,23 @@
"machineName": "H5P.Timer",
"majorVersion": 0,
"minorVersion": 4
+ },
+ {
+ "machineName": "FontAwesome",
+ "majorVersion": 4,
+ "minorVersion": 5
+ }
+ ],
+ "editorDependencies": [
+ {
+ "machineName": "H5PEditor.ColorSelector",
+ "majorVersion": 1,
+ "minorVersion": 2
+ },
+ {
+ "machineName": "H5PEditor.VerticalTabs",
+ "majorVersion": 1,
+ "minorVersion": 3
}
]
-}
\ No newline at end of file
+}
diff --git a/memory-game.css b/memory-game.css
index cb3f9ef..199a07b 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,32 @@
.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,
+.h5p-memory-game .h5p-memory-image: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,
+.h5p-memory-game.h5p-invert-shades .h5p-memory-image: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 {
@@ -100,15 +120,19 @@
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;
+ line-height: 5.83em;
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,
+.h5p-memory-game .h5p-memory-image:before {
+ opacity: 0.5;
+}
.h5p-memory-game .h5p-memory-card.h5p-flipped .h5p-back {
-webkit-transform: rotateY(0deg);
-moz-transform: rotateY(0deg);
@@ -124,6 +148,8 @@
}
.h5p-memory-game .h5p-memory-card.h5p-matched {
opacity: 0.3;
+}
+.h5p-memory-game .h5p-memory-card.h5p-matched img {
filter: grayscale(100%);
}
@@ -170,19 +196,32 @@
.h5p-memory-game .h5p-memory-pop {
display: none;
background: #fff;
- padding: 1em;
- width: 20em;
+ padding: 0.25em;
+ width: 24em;
max-width: 90%;
position: absolute;
top: 50%;
left: 50%;
- box-shadow: 0 0 1em #666;
+ box-shadow: 0 0 2em #666;
+ border-radius: 0.25em;
-webkit-transform: translate(-50%,-50%);
-moz-transform: translate(-50%,-50%);
transform: translate(-50%,-50%);
}
+.h5p-memory-game .h5p-memory-top {
+ padding: 0em 1em;
+ background-color: #f0f0f0;
+ background-size: cover;
+ text-align: center;
+ margin-bottom: 1.75em;
+ border-bottom: 1px solid #d0d0d0;
+}
.h5p-memory-game .h5p-memory-image {
- float: left;
+ display: inline-block;
+ line-height: 5.83em;
+ position: relative;
+ top: 1.5em;
+ left: -0.5em;
border: 2px solid #d0d0d0;
box-sizing: border-box;
-moz-box-sizing: border-box;
@@ -191,24 +230,79 @@
width: 6.25em;
height: 6.25em;
text-align: center;
+ overflow: hidden;
+ box-shadow: 0 0 1em rgba(125,125,125,0.5);
+ background-size: cover;
+}
+.h5p-memory-game .h5p-memory-image:first-child {
+ top: 1em;
+ left: 0;
+}
+.h5p-memory-game .h5p-memory-two-images .h5p-memory-image:first-child {
+ left: 0.5em;
}
.h5p-memory-game .h5p-row-break {
clear: left;
}
.h5p-memory-game .h5p-memory-desc {
- margin-left: 7em;
+ padding: 1em;
+ margin-bottom: 0.5em;
+ text-align: center;
+}
+.h5p-memory-game .h5p-memory-close {
+ cursor: pointer;
+ position: absolute;
+ top: 0.5em;
+ right: 0.5em;
+ font-size: 2em;
+ width: 1em;
+ height: 1em;
+ text-align: center;
+ color: #888;
+}
+.h5p-memory-game .h5p-memory-close:before {
+ content: "\00D7"
+}
+.h5p-memory-game .h5p-memory-close:hover {
+ color: #666;
+}
+.h5p-memory-game .h5p-memory-close:focus {
+ outline: 2px dashed pink;
}
.h5p-memory-reset {
position: absolute;
top: 50%;
left: 50%;
- transform: translate(-50%,-50%);
+ transform: translate(-50%,-50%) scale(1) rotate(0);
cursor: pointer;
- font-style: italic;
- text-shadow: 0 0 0.5em white;
- padding: 0.125em 0.25em;
- line-height: 1;
+ line-height: 1.2;
+ padding: 0.5em 1.25em;
+ border-radius: 2em;
+ background: #1a73d9;
+ color: #ffffff;
+ box-shadow: 0 0.5em 1em #999;
+ opacity: 1;
+ transition: box-shadow 200ms linear, margin 200ms linear, transform 300ms ease-out, opacity 300ms ease-out;
+}
+.h5p-memory-reset:before {
+ font-family: 'H5PFontAwesome4';
+ content: "\f01e";
+ margin-right: 0.5em;
+}
+.h5p-memory-reset:hover,
+.h5p-memory-reset:focus {
+ background: #1356a3;
+ box-shadow: 0 1em 1.5em #999;
+ margin-top: -0.2em;
}
.h5p-memory-reset:focus {
- outline: dashed pink;
+ outline: 2px dashed pink;
+}
+.h5p-memory-transin {
+ transform: translate(-50%,-50%) scale(0) rotate(180deg);
+ opacity: 0;
+}
+.h5p-memory-transout {
+ transform: translate(-50%,-450%) scale(0) rotate(360deg);
+ opacity: 0;
}
diff --git a/memory-game.js b/memory-game.js
index 2e2cd2f..a0029f6 100644
--- a/memory-game.js
+++ b/memory-game.js
@@ -60,14 +60,14 @@ H5P.MemoryGame = (function (EventDispatcher, $) {
var isFinished = (removed === cards.length);
var desc = card.getDescription();
- if (isFinished) {
- self.triggerXAPIScored(1, 1, 'completed');
- }
-
if (desc !== undefined) {
// Pause timer and show desciption.
timer.pause();
- popup.show(desc, card.getImage(), function () {
+ var imgs = [card.getImage()];
+ if (card.hasTwoImages) {
+ imgs.push(mate.getImage());
+ }
+ popup.show(desc, imgs, cardStyles ? cardStyles.back : undefined, function () {
if (isFinished) {
// Game done
finished();
@@ -91,19 +91,34 @@ H5P.MemoryGame = (function (EventDispatcher, $) {
var finished = function () {
timer.stop();
$feedback.addClass('h5p-show');
+
+ // Create and trigger xAPI event 'completed'
+ var completedEvent = self.createXAPIEventTemplate('completed');
+ completedEvent.setScoredResult(1, 1, self, true, true);
+ completedEvent.data.statement.result.duration = 'PT' + (Math.round(timer.getTime() / 10) / 100) + 'S';
+ self.trigger(completedEvent);
+
if (parameters.behaviour && parameters.behaviour.allowRetry) {
// Create retry button
- var retryButton = createButton('reset', parameters.l10n.tryAgain || 'Try again?', function () {
+ var retryButton = createButton('reset', parameters.l10n.tryAgain || 'Reset', function () {
// Trigger handler (action)
- resetGame();
+ retryButton.classList.add('h5p-memory-transout');
+ setTimeout(function () {
+ // Remove button on nextTick to get transition effect
+ $wrapper[0].removeChild(retryButton);
+ }, 300);
- // Remove button from DOM
- $wrapper[0].removeChild(this);
+ resetGame();
});
+ retryButton.classList.add('h5p-memory-transin');
+ setTimeout(function () {
+ // Remove class on nextTick to get transition effect
+ retryButton.classList.remove('h5p-memory-transin');
+ }, 0);
// Same size as cards
- retryButton.style.fontSize = $wrapper.children('ul')[0].style.fontSize;
+ retryButton.style.fontSize = (parseFloat($wrapper.children('ul')[0].style.fontSize) * 0.75) + 'px';
$wrapper[0].appendChild(retryButton); // Add to DOM
}
@@ -247,21 +262,31 @@ 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);
+ cardOne.hasTwoImages = cardTwo.hasTwoImages = true;
}
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 +305,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 = $('
');
@@ -302,7 +330,7 @@ H5P.MemoryGame = (function (EventDispatcher, $) {
timer = new MemoryGame.Timer($status.find('.h5p-time-spent')[0]);
counter = new MemoryGame.Counter($status.find('.h5p-card-turns'));
- popup = new MemoryGame.Popup($container);
+ popup = new MemoryGame.Popup($container, parameters.l10n);
$container.click(function () {
popup.close();
@@ -380,5 +408,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/popup.js b/popup.js
index bee3ceb..24b2b46 100644
--- a/popup.js
+++ b/popup.js
@@ -5,27 +5,41 @@
*
* @class H5P.MemoryGame.Popup
* @param {H5P.jQuery} $container
+ * @param {Object.
} l10n
*/
- MemoryGame.Popup = function ($container) {
+ MemoryGame.Popup = function ($container, l10n) {
/** @alias H5P.MemoryGame.Popup# */
var self = this;
var closed;
- var $popup = $('').appendTo($container);
+ var $popup = $('').appendTo($container);
var $desc = $popup.find('.h5p-memory-desc');
- var $image = $popup.find('.h5p-memory-image');
+ var $top = $popup.find('.h5p-memory-top');
+
+ // Hook up the close button
+ $popup.find('.h5p-memory-close').on('click', function () {
+ self.close();
+ }).on('keypress', function (event) {
+ if (event.which === 13 || event.which === 32) {
+ self.close();
+ event.preventDefault();
+ }
+ });
/**
* Show the popup.
*
* @param {string} desc
- * @param {H5P.jQuery} $img
+ * @param {H5P.jQuery[]} imgs
* @param {function} done
*/
- self.show = function (desc, $img, done) {
+ self.show = function (desc, imgs, styles, done) {
$desc.html(desc);
- $img.appendTo($image.html(''));
+ $top.html('').toggleClass('h5p-memory-two-images', imgs.length > 1);
+ for (var i = 0; i < imgs.length; i++) {
+ $('').append(imgs[i]).appendTo($top);
+ }
$popup.show();
closed = done;
};
@@ -47,15 +61,14 @@
*/
self.setSize = function (fontSize) {
// Set image size
- $image[0].style.fontSize = fontSize + 'px';
+ $top[0].style.fontSize = fontSize + 'px';
// Determine card size
var cardSize = fontSize * 6.25; // From CSS
// Set popup size
- $popup[0].style.minWidth = (cardSize * 2) + 'px';
+ $popup[0].style.minWidth = (cardSize * 2.5) + 'px';
$popup[0].style.minHeight = cardSize + 'px';
- $desc[0].style.marginLeft = (cardSize + 20) + 'px';
};
};
diff --git a/semantics.json b/semantics.json
index b25e38c..fd274e2 100644
--- a/semantics.json
+++ b/semantics.json
@@ -2,6 +2,12 @@
{
"name": "cards",
"type": "list",
+ "widgets": [
+ {
+ "name": "VerticalTabs",
+ "label": "Default"
+ }
+ ],
"label": "Cards",
"importance": "high",
"entity": "card",
@@ -75,6 +81,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",
@@ -109,6 +147,13 @@
"name": "tryAgain",
"type": "text",
"default": "Try again?"
+ },
+ {
+ "label": "Close button label",
+ "importance": "low",
+ "name": "closeLabel",
+ "type": "text",
+ "default": "Close"
}
]
}