Merge branch 'master' into release

pull/20/merge
Paal Joergensen 2017-08-03 10:55:04 +02:00
commit 9bd173ebe0
14 changed files with 549 additions and 134 deletions

86
card.js
View File

@ -8,8 +8,9 @@
* @param {Object} image * @param {Object} image
* @param {number} id * @param {number} id
* @param {string} [description] * @param {string} [description]
* @param {Object} [styles]
*/ */
MemoryGame.Card = function (image, id, description) { MemoryGame.Card = function (image, id, description, styles) {
/** @alias H5P.MemoryGame.Card# */ /** @alias H5P.MemoryGame.Card# */
var self = this; var self = this;
@ -88,8 +89,8 @@
self.appendTo = function ($container) { self.appendTo = function ($container) {
// TODO: Translate alt attr // TODO: Translate alt attr
$card = $('<li class="h5p-memory-wrap"><div class="h5p-memory-card" role="button" tabindex="1">' + $card = $('<li class="h5p-memory-wrap"><div class="h5p-memory-card" role="button" tabindex="1">' +
'<div class="h5p-front"></div>' + '<div class="h5p-front"' + (styles && styles.front ? styles.front : '') + '>' + (styles && styles.backImage ? '' : '<span></span>') + '</div>' +
'<div class="h5p-back">' + '<div class="h5p-back"' + (styles && styles.back ? styles.back : '') + '>' +
'<img src="' + path + '" alt="Memory Card" style="width:' + width + ';height:' + height + '"/>' + '<img src="' + path + '" alt="Memory Card" style="width:' + width + ';height:' + height + '"/>' +
'</div>' + '</div>' +
'</div></li>') '</div></li>')
@ -141,4 +142,83 @@
params.match.path !== undefined); 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); })(H5P.MemoryGame, H5P.EventDispatcher, H5P.jQuery);

3
crowdin.yml Normal file
View File

@ -0,0 +1,3 @@
files:
- source: /language/.en.json
translation: /language/%two_letters_code%.json

87
language/.en.json Normal file
View File

@ -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"
}
]
}
]
}

View File

@ -10,9 +10,7 @@
"label": "الصورة" "label": "الصورة"
}, },
{ {
"englishLabel": "Matching Image",
"label": "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." "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", "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.", "description": "These options will let you control how the game behaves.",
"fields": [ "fields": [
{ {
"englishLabel": "Position the cards in a square",
"label": "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." "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", "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." "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": "Add button for retrying when the game is over"
} }
] ]
}, },
{
"fields": [
{},
{}
]
},
{ {
"label": "الأقلمة", "label": "الأقلمة",
"fields": [ "fields": [
@ -58,13 +55,9 @@
{ {
"label": "نص الملاحظات" "label": "نص الملاحظات"
}, },
{ {},
"englishLabel": "Try again button text", {}
"label": "Try again button text",
"englishDefault": "Try again?",
"default": "Try again?"
}
] ]
} }
] ]
} }

69
language/bs.json Normal file
View File

@ -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?"
},
{}
]
}
]
}

View File

@ -10,9 +10,7 @@
"label": "Bild" "label": "Bild"
}, },
{ {
"englishLabel": "Matching Image",
"label": "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." "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", "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.", "description": "These options will let you control how the game behaves.",
"fields": [ "fields": [
{ {
"englishLabel": "Position the cards in a square",
"label": "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." "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", "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." "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": "Add button for retrying when the game is over"
} }
] ]
}, },
{
"fields": [
{},
{}
]
},
{ {
"label": "Übersetzung", "label": "Übersetzung",
"fields": [ "fields": [
@ -61,13 +58,9 @@
"label": "Text als Rückmeldung", "label": "Text als Rückmeldung",
"default": "Gute Arbeit!" "default": "Gute Arbeit!"
}, },
{ {},
"englishLabel": "Try again button text", {}
"label": "Try again button text",
"englishDefault": "Try again?",
"default": "Try again?"
}
] ]
} }
] ]
} }

View File

@ -10,9 +10,7 @@
"label": "Image" "label": "Image"
}, },
{ {
"englishLabel": "Matching Image",
"label": "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." "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", "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.", "description": "These options will let you control how the game behaves.",
"fields": [ "fields": [
{ {
"englishLabel": "Position the cards in a square",
"label": "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." "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", "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." "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": "Add button for retrying when the game is over"
} }
] ]
}, },
{
"fields": [
{},
{}
]
},
{ {
"label": "Interface", "label": "Interface",
"fields": [ "fields": [
@ -61,13 +58,9 @@
"label": "Texte de l'appréciation finale", "label": "Texte de l'appréciation finale",
"default": "Bien joué !" "default": "Bien joué !"
}, },
{ {},
"englishLabel": "Try again button text", {}
"label": "Try again button text",
"englishDefault": "Try again?",
"default": "Try again?"
}
] ]
} }
] ]
} }

View File

@ -10,9 +10,7 @@
"label": "Immagine" "label": "Immagine"
}, },
{ {
"englishLabel": "Matching Image",
"label": "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." "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", "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.", "description": "These options will let you control how the game behaves.",
"fields": [ "fields": [
{ {
"englishLabel": "Position the cards in a square",
"label": "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." "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", "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." "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": "Add button for retrying when the game is over"
} }
] ]
}, },
{
"fields": [
{},
{}
]
},
{ {
"label": "Localizzazione", "label": "Localizzazione",
"fields": [ "fields": [
@ -58,13 +55,9 @@
{ {
"label": "Testo Feedback" "label": "Testo Feedback"
}, },
{ {},
"englishLabel": "Try again button text", {}
"label": "Try again button text",
"englishDefault": "Try again?",
"default": "Try again?"
}
] ]
} }
] ]
} }

View File

@ -1,86 +1,80 @@
{ {
"semantics": [ "semantics": [
{ {
"englishLabel": "Cards",
"label": "Kort", "label": "Kort",
"englishEntity": "card",
"entity": "kort", "entity": "kort",
"field": { "field": {
"englishLabel": "Card",
"label": "Kort", "label": "Kort",
"fields": [ "fields": [
{ {
"englishLabel": "Image",
"label": "Bilde" "label": "Bilde"
}, },
{ {
"englishLabel": "Matching Image",
"label": "Tilhørende bilde", "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." "description": "Et valgfritt bilde som brukes av kort nummer to istedenfor å bruke to kort med samme bilde."
}, },
{ {
"englishLabel": "Description",
"label": "Beskrivelse", "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." "description": "En valgfri kort tekst som spretter opp når kort-paret er funnet."
} }
] ]
} }
}, },
{ {
"englishLabel": "Behavioural settings",
"label": "Innstillinger for oppførsel", "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.", "description": "Disse instillingene lar deg bestemme hvordan spillet skal oppføre seg.",
"fields": [ "fields": [
{ {
"englishLabel": "Position the cards in a square",
"label": "Plasser kortene i en firkant", "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." "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", "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." "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" "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", "label": "Oversettelser",
"fields": [ "fields": [
{ {
"englishLabel": "Card turns text",
"label": "Etikett for antall vendte kort", "label": "Etikett for antall vendte kort",
"englishDefault": "Card turns",
"default": "Kort vendt" "default": "Kort vendt"
}, },
{ {
"englishLabel": "Time spent text",
"label": "Etikett for tid brukt", "label": "Etikett for tid brukt",
"englishDefault": "Time spent",
"default": "Tid brukt" "default": "Tid brukt"
}, },
{ {
"englishLabel": "Feedback text",
"label": "Tilbakemeldingstekst", "label": "Tilbakemeldingstekst",
"englishDefault": "Good work!",
"default": "Godt jobbet!" "default": "Godt jobbet!"
}, },
{ {
"englishLabel": "Try again button text",
"label": "Prøv på nytt-tekst", "label": "Prøv på nytt-tekst",
"englishDefault": "Try again?",
"default": "Prøv på nytt?" "default": "Prøv på nytt?"
},
{
"label": "Lukk knapp-merkelapp",
"default": "Lukk"
} }
] ]
} }
] ]
} }

View File

@ -3,7 +3,7 @@
"description": "See how many cards you can remember!", "description": "See how many cards you can remember!",
"majorVersion": 1, "majorVersion": 1,
"minorVersion": 2, "minorVersion": 2,
"patchVersion": 2, "patchVersion": 3,
"runnable": 1, "runnable": 1,
"author": "Joubel", "author": "Joubel",
"license": "MIT", "license": "MIT",
@ -35,6 +35,23 @@
"machineName": "H5P.Timer", "machineName": "H5P.Timer",
"majorVersion": 0, "majorVersion": 0,
"minorVersion": 4 "minorVersion": 4
},
{
"machineName": "FontAwesome",
"majorVersion": 4,
"minorVersion": 5
}
],
"editorDependencies": [
{
"machineName": "H5PEditor.ColorSelector",
"majorVersion": 1,
"minorVersion": 2
},
{
"machineName": "H5PEditor.VerticalTabs",
"majorVersion": 1,
"minorVersion": 3
} }
] ]
} }

View File

@ -25,6 +25,7 @@
display: inline-block !important; display: inline-block !important;
margin: auto !important; margin: auto !important;
vertical-align: middle; vertical-align: middle;
position: relative;
} }
.h5p-memory-game .h5p-memory-wrap { .h5p-memory-game .h5p-memory-wrap {
float: left; float: left;
@ -55,6 +56,7 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
background: #cfcfcf; background: #cfcfcf;
background-size: cover;
border: 2px solid #d0d0d0; border: 2px solid #d0d0d0;
box-sizing: border-box; box-sizing: border-box;
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
@ -75,14 +77,32 @@
.h5p-memory-game .h5p-memory-card .h5p-front { .h5p-memory-game .h5p-memory-card .h5p-front {
cursor: pointer; cursor: pointer;
text-align: center; text-align: center;
color: #909090;
} }
.h5p-memory-game .h5p-memory-card .h5p-front:hover { .h5p-memory-game .h5p-memory-card .h5p-front:before,
background: #dfdfdf; .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: "?"; content: "?";
font-size: 3.75em; font-size: 3.75em;
color: #909090;
line-height: 1.67em; line-height: 1.67em;
} }
.h5p-memory-game .h5p-memory-card .h5p-front:after { .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%); 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 { .h5p-memory-game .h5p-memory-card .h5p-back {
line-height: 5.9em; line-height: 5.83em;
text-align: center; text-align: center;
background: #f0f0f0; background-color: #f0f0f0;
-webkit-transform: rotateY(-180deg); -webkit-transform: rotateY(-180deg);
-moz-transform: rotateY(-180deg); -moz-transform: rotateY(-180deg);
transform: rotateY(-180deg); transform: rotateY(-180deg);
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
-ms-transform: scale(0,1.1); -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 { .h5p-memory-game .h5p-memory-card.h5p-flipped .h5p-back {
-webkit-transform: rotateY(0deg); -webkit-transform: rotateY(0deg);
-moz-transform: rotateY(0deg); -moz-transform: rotateY(0deg);
@ -124,6 +148,8 @@
} }
.h5p-memory-game .h5p-memory-card.h5p-matched { .h5p-memory-game .h5p-memory-card.h5p-matched {
opacity: 0.3; opacity: 0.3;
}
.h5p-memory-game .h5p-memory-card.h5p-matched img {
filter: grayscale(100%); filter: grayscale(100%);
} }
@ -170,19 +196,32 @@
.h5p-memory-game .h5p-memory-pop { .h5p-memory-game .h5p-memory-pop {
display: none; display: none;
background: #fff; background: #fff;
padding: 1em; padding: 0.25em;
width: 20em; width: 24em;
max-width: 90%; max-width: 90%;
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
box-shadow: 0 0 1em #666; box-shadow: 0 0 2em #666;
border-radius: 0.25em;
-webkit-transform: translate(-50%,-50%); -webkit-transform: translate(-50%,-50%);
-moz-transform: translate(-50%,-50%); -moz-transform: translate(-50%,-50%);
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 { .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; border: 2px solid #d0d0d0;
box-sizing: border-box; box-sizing: border-box;
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
@ -191,24 +230,79 @@
width: 6.25em; width: 6.25em;
height: 6.25em; height: 6.25em;
text-align: center; 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 { .h5p-memory-game .h5p-row-break {
clear: left; clear: left;
} }
.h5p-memory-game .h5p-memory-desc { .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 { .h5p-memory-reset {
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%,-50%); transform: translate(-50%,-50%) scale(1) rotate(0);
cursor: pointer; cursor: pointer;
font-style: italic; line-height: 1.2;
text-shadow: 0 0 0.5em white; padding: 0.5em 1.25em;
padding: 0.125em 0.25em; border-radius: 2em;
line-height: 1; 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 { .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;
} }

View File

@ -60,14 +60,14 @@ H5P.MemoryGame = (function (EventDispatcher, $) {
var isFinished = (removed === cards.length); var isFinished = (removed === cards.length);
var desc = card.getDescription(); var desc = card.getDescription();
if (isFinished) {
self.triggerXAPIScored(1, 1, 'completed');
}
if (desc !== undefined) { if (desc !== undefined) {
// Pause timer and show desciption. // Pause timer and show desciption.
timer.pause(); 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) { if (isFinished) {
// Game done // Game done
finished(); finished();
@ -91,19 +91,34 @@ H5P.MemoryGame = (function (EventDispatcher, $) {
var finished = function () { var finished = function () {
timer.stop(); timer.stop();
$feedback.addClass('h5p-show'); $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) { if (parameters.behaviour && parameters.behaviour.allowRetry) {
// Create retry button // 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) // 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 resetGame();
$wrapper[0].removeChild(this);
}); });
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 // 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 $wrapper[0].appendChild(retryButton); // Add to DOM
} }
@ -247,21 +262,31 @@ H5P.MemoryGame = (function (EventDispatcher, $) {
return cardsToUse; 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. // Initialize cards.
var cardsToUse = getCardsToUse(); var cardsToUse = getCardsToUse();
for (var i = 0; i < cardsToUse.length; i++) { for (var i = 0; i < cardsToUse.length; i++) {
var cardParams = cardsToUse[i]; var cardParams = cardsToUse[i];
if (MemoryGame.Card.isValid(cardParams)) { if (MemoryGame.Card.isValid(cardParams)) {
// Create first card // 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)) { if (MemoryGame.Card.hasTwoImages(cardParams)) {
// Use matching image for card two // 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 { else {
// Add two cards with the same image // 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 // Add cards to card list for shuffeling
@ -280,6 +305,9 @@ H5P.MemoryGame = (function (EventDispatcher, $) {
this.triggerXAPI('attempted'); this.triggerXAPI('attempted');
// TODO: Only create on first attach! // TODO: Only create on first attach!
$wrapper = $container.addClass('h5p-memory-game').html(''); $wrapper = $container.addClass('h5p-memory-game').html('');
if (invertShades === -1) {
$container.addClass('h5p-invert-shades');
}
// Add cards to list // Add cards to list
var $list = $('<ul/>'); var $list = $('<ul/>');
@ -302,7 +330,7 @@ H5P.MemoryGame = (function (EventDispatcher, $) {
timer = new MemoryGame.Timer($status.find('.h5p-time-spent')[0]); timer = new MemoryGame.Timer($status.find('.h5p-time-spent')[0]);
counter = new MemoryGame.Counter($status.find('.h5p-card-turns')); counter = new MemoryGame.Counter($status.find('.h5p-card-turns'));
popup = new MemoryGame.Popup($container); popup = new MemoryGame.Popup($container, parameters.l10n);
$container.click(function () { $container.click(function () {
popup.close(); popup.close();
@ -380,5 +408,18 @@ H5P.MemoryGame = (function (EventDispatcher, $) {
MemoryGame.prototype = Object.create(EventDispatcher.prototype); MemoryGame.prototype = Object.create(EventDispatcher.prototype);
MemoryGame.prototype.constructor = MemoryGame; 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; return MemoryGame;
})(H5P.EventDispatcher, H5P.jQuery); })(H5P.EventDispatcher, H5P.jQuery);

View File

@ -5,27 +5,41 @@
* *
* @class H5P.MemoryGame.Popup * @class H5P.MemoryGame.Popup
* @param {H5P.jQuery} $container * @param {H5P.jQuery} $container
* @param {Object.<string, string>} l10n
*/ */
MemoryGame.Popup = function ($container) { MemoryGame.Popup = function ($container, l10n) {
/** @alias H5P.MemoryGame.Popup# */ /** @alias H5P.MemoryGame.Popup# */
var self = this; var self = this;
var closed; var closed;
var $popup = $('<div class="h5p-memory-pop"><div class="h5p-memory-image"></div><div class="h5p-memory-desc"></div></div>').appendTo($container); var $popup = $('<div class="h5p-memory-pop"><div class="h5p-memory-top"></div><div class="h5p-memory-desc"></div><div class="h5p-memory-close" role="button" tabindex="0" title="' + (l10n.closeLabel || 'Close') + '"></div></div>').appendTo($container);
var $desc = $popup.find('.h5p-memory-desc'); 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. * Show the popup.
* *
* @param {string} desc * @param {string} desc
* @param {H5P.jQuery} $img * @param {H5P.jQuery[]} imgs
* @param {function} done * @param {function} done
*/ */
self.show = function (desc, $img, done) { self.show = function (desc, imgs, styles, done) {
$desc.html(desc); $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++) {
$('<div class="h5p-memory-image"' + (styles ? styles : '') + '></div>').append(imgs[i]).appendTo($top);
}
$popup.show(); $popup.show();
closed = done; closed = done;
}; };
@ -47,15 +61,14 @@
*/ */
self.setSize = function (fontSize) { self.setSize = function (fontSize) {
// Set image size // Set image size
$image[0].style.fontSize = fontSize + 'px'; $top[0].style.fontSize = fontSize + 'px';
// Determine card size // Determine card size
var cardSize = fontSize * 6.25; // From CSS var cardSize = fontSize * 6.25; // From CSS
// Set popup size // Set popup size
$popup[0].style.minWidth = (cardSize * 2) + 'px'; $popup[0].style.minWidth = (cardSize * 2.5) + 'px';
$popup[0].style.minHeight = cardSize + 'px'; $popup[0].style.minHeight = cardSize + 'px';
$desc[0].style.marginLeft = (cardSize + 20) + 'px';
}; };
}; };

View File

@ -2,6 +2,12 @@
{ {
"name": "cards", "name": "cards",
"type": "list", "type": "list",
"widgets": [
{
"name": "VerticalTabs",
"label": "Default"
}
],
"label": "Cards", "label": "Cards",
"importance": "high", "importance": "high",
"entity": "card", "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", "label": "Localization",
"importance": "low", "importance": "low",
@ -109,6 +147,13 @@
"name": "tryAgain", "name": "tryAgain",
"type": "text", "type": "text",
"default": "Try again?" "default": "Try again?"
},
{
"label": "Close button label",
"importance": "low",
"name": "closeLabel",
"type": "text",
"default": "Close"
} }
] ]
} }