diff --git a/README.md b/README.md
index 84b6820..15d3f43 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Kana Quiz 2
+# Kana Quiz
Kana Quiz made with React.js.
See live at https://kana.pro/
@@ -8,4 +8,4 @@ Install deps: `npm install`
Development: `npm start`
-Production: `npm run build`
\ No newline at end of file
+Production: `npm run build`
diff --git a/package.json b/package.json
index 7a42d96..39bc666 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,7 @@
},
"homepage": "https://github.com/anzzstuff/kanaquiz#readme",
"browserslist": [
- "> 0.25%",
+ ">0.25%",
"not dead"
],
"devDependencies": {
diff --git a/src/components/App/App.jsx b/src/components/App/App.jsx
index fa34a2b..c5a3eec 100644
--- a/src/components/App/App.jsx
+++ b/src/components/App/App.jsx
@@ -7,57 +7,50 @@ import { removeHash } from '../../data/helperFuncs';
const options = {};
class App extends Component {
- constructor(props) {
- super(props);
- this.state = {
- gameState: 'chooseCharacters'
- }
- this.startGame = this.startGame.bind(this);
- this.endGame = this.endGame.bind(this);
- }
+ state = { gameState: 'chooseCharacters' };
- startGame() {
- this.setState({gameState: 'game'});
- }
+ startGame = () => {
+ this.setState({gameState: 'game'});
+ }
- endGame() {
- this.setState({gameState: 'chooseCharacters'});
- }
+ endGame = () => {
+ this.setState({gameState: 'chooseCharacters'});
+ }
- componentWillUpdate(nextProps, nextState) {
- // This is primarily for demo site purposes. Hides #footer when game is on.
- if(document.getElementById('footer')) {
- if(nextState.gameState=='chooseCharacters')
- document.getElementById('footer').style.display = "block";
- else
- document.getElementById('footer').style.display = "none";
- }
+ componentWillUpdate(nextProps, nextState) {
+ // This is primarily for demo site purposes. Hides #footer when game is on.
+ if(document.getElementById('footer')) {
+ if(nextState.gameState=='chooseCharacters')
+ document.getElementById('footer').style.display = "block";
+ else
+ document.getElementById('footer').style.display = "none";
}
+ }
- componentWillMount() {
- if(document.getElementById('footer'))
- document.getElementById('footer').style.display = "block";
- }
+ componentWillMount() {
+ if(document.getElementById('footer'))
+ document.getElementById('footer').style.display = "block";
+ }
- render() {
- return (
-
this.toggleAlternative(whichKana, postfix)}
+ className="choose-row"
+ >
+ {
+ if(status == 'check')
+ this.selectNone(whichKana, postfix == '_a', postfix == '_s');
+ else if(status == 'check half' || status == 'unchecked')
+ this.selectAll(whichKana, postfix == '_a', postfix == '_s');
+ e.stopPropagation();
+ }}
+ >
+ {
+ show ? ▲
+ : ▼
+ }
+ {
+ postfix == '_a' ? 'Alternative characters (ga · ba · kya..)' :
+ 'Look-alike characters'
+ }
+
+ }
+
+ showGroupRows(whichKana, showAlternatives, showSimilars = false) {
+ const thisKana = kanaDictionary[whichKana];
+ let rows = [];
+ Object.keys(thisKana).forEach((groupName, idx) => {
+ if(groupName == "h_group11_a" || groupName == "k_group13_a")
+ rows.push(this.alternativeToggleRow(whichKana, "_a", showAlternatives));
+ if(groupName == "k_group11_s")
+ rows.push(this.alternativeToggleRow(whichKana, "_s", showSimilars));
+
+ if((!groupName.endsWith("a") || showAlternatives) &&
+ (!groupName.endsWith("s") || showSimilars)) {
+ rows.push( this.toggleAlternative(whichKana, postfix)}
- className="choose-row"
- >
- {
- if(status == 'check')
- this.selectNone(whichKana, postfix == '_a', postfix == '_s');
- else if(status == 'check half' || status == 'unchecked')
- this.selectAll(whichKana, postfix == '_a', postfix == '_s');
- e.stopPropagation();
- }}
- >
- {
- show ? ▲
- : ▼
- }
- {
- postfix == '_a' ? 'Alternative characters (ga · ba · kya..)' :
- 'Look-alike characters'
- }
-
- }
-
- showGroupRows(whichKana, showAlternatives, showSimilars = false) {
- const thisKana = kanaDictionary[whichKana];
- let rows = [];
- Object.keys(thisKana).forEach((groupName, idx) => {
- if(groupName == "h_group11_a" || groupName == "k_group13_a")
- rows.push(this.alternativeToggleRow(whichKana, "_a", showAlternatives));
- if(groupName == "k_group11_s")
- rows.push(this.alternativeToggleRow(whichKana, "_s", showSimilars));
-
- if((!groupName.endsWith("a") || showAlternatives) &&
- (!groupName.endsWith("s") || showSimilars)) {
- rows.push(
-
-
-
-
-
Welcome to Kana Quiz!
-
Please choose the groups of characters that you'd like to be studying.
-
-
-
-
-
-
-
-
Hiragana · ひらがな
-
- {this.showGroupRows('hiragana', this.state.showAlternatives.indexOf('hiragana') >= 0)}
-
-
-
-
-
-
-
Katakana · カタカナ
-
- {this.showGroupRows('katakana', this.state.showAlternatives.indexOf('katakana') >= 0, this.state.showSimilars.indexOf('katakana') >= 0)}
-
-
-
-
-
- Lock to stage
- {
- this.props.isLocked &&
- this.props.lockStage(e.target.value, true)}
- value={this.props.stage}
- />
- }
- this.props.lockStage(1)} on={this.props.isLocked} />
-
-
- {
- this.state.errMsg != '' &&
-
{this.state.errMsg}
- }
-
-
-
this.scrollToStart(e)}
- >
- Start
-
-
+ render() {
+ return (
+
+
+
+
+
+
Welcome to Kana Pro!
+
Please choose the groups of characters that you'd like to be studying.
+
- );
- }
+
+
+
+
+
+
Hiragana · ひらがな
+
+ {this.showGroupRows('hiragana', this.state.showAlternatives.indexOf('hiragana') >= 0)}
+
+
+
+
+
+
+
Katakana · カタカナ
+
+ {this.showGroupRows('katakana', this.state.showAlternatives.indexOf('katakana') >= 0, this.state.showSimilars.indexOf('katakana') >= 0)}
+
+
+
+
+
+ Lock to stage
+ {
+ this.props.isLocked &&
+ this.props.lockStage(e.target.value, true)}
+ value={this.props.stage}
+ />
+ }
+ this.props.lockStage(1)} on={this.props.isLocked} />
+
+
+ {
+ this.state.errMsg != '' &&
+
{this.state.errMsg}
+ }
+
+
+
this.scrollToStart(e)}
+ >
+ Start
+
+
+
+ );
+ }
}
export default ChooseCharacters;
diff --git a/src/components/ChooseCharacters/ChooseCharacters.scss b/src/components/ChooseCharacters/ChooseCharacters.scss
index f6b0a64..cff1d26 100644
--- a/src/components/ChooseCharacters/ChooseCharacters.scss
+++ b/src/components/ChooseCharacters/ChooseCharacters.scss
@@ -1,143 +1,143 @@
.welcome {
- p:last-child {
- margin-bottom: 0;
- }
- h4 {
- margin-top: 0;
- }
+ p:last-child {
+ margin-bottom: 0;
+ }
+ h4 {
+ margin-top: 0;
+ }
}
.choose-characters
{
- .panel-heading {
- font-weight: bold;
- }
- .panel-heading span {
- color: #aaa;
- }
- .panel-footer a {
- text-decoration: none;
- color: #337ab7;
- }
- .choose-row {
- font-size: 1em;
- padding: 5px;
- }
- .choose-row:not(:last-child) {
- border-bottom: 1px #eee solid;
- }
- .alt-row {
- padding-left: 20px;
- background-color: #fafafa;
- }
- .toggle-caret {
- margin: 0 4px;
- }
- @media (min-width: 768px) {
- .choose-row:hover {
- background-color: #f4f4f4;
- }
- }
+ .panel-heading {
+ font-weight: bold;
+ }
+ .panel-heading span {
+ color: #aaa;
+ }
+ .panel-footer a {
+ text-decoration: none;
+ color: #337ab7;
+ }
+ .choose-row {
+ font-size: 1em;
+ padding: 5px;
+ }
+ .choose-row:not(:last-child) {
+ border-bottom: 1px #eee solid;
+ }
+ .alt-row {
+ padding-left: 20px;
+ background-color: #fafafa;
+ }
+ .toggle-caret {
+ margin: 0 4px;
+ }
+ @media (min-width: 768px) {
.choose-row:hover {
- cursor: pointer;
- }
- .glyphicon {
- font-size: 0.9em;
- }
- .glyphicon-check {
- color: green;
- }
- .glyphicon-check.half {
- color: #ccc;
- }
- .glyphicon-unchecked {
- color: #ccc;
- }
- .selection-areas {
- padding: 7px;
- }
- .success-percent {
- color: #ccc;
+ background-color: #f4f4f4;
}
+ }
+ .choose-row:hover {
+ cursor: pointer;
+ }
+ .glyphicon {
+ font-size: 0.9em;
+ }
+ .glyphicon-check {
+ color: green;
+ }
+ .glyphicon-check.half {
+ color: #ccc;
+ }
+ .glyphicon-unchecked {
+ color: #ccc;
+ }
+ .selection-areas {
+ padding: 7px;
+ }
+ .success-percent {
+ color: #ccc;
+ }
}
.error-message {
- color: #d9534f;
- padding-bottom: 10px;
+ color: #d9534f;
+ padding-bottom: 10px;
}
.lock {
- color: #888;
- margin-bottom: 10px;
+ color: #888;
+ margin-bottom: 10px;
}
.stage-choice {
- border: 1px solid #999;
- padding-left: 5px;
- border-radius: 5px;
- margin-right: 5px;
- outline: none;
+ border: 1px solid #999;
+ padding-left: 5px;
+ border-radius: 5px;
+ margin-right: 5px;
+ outline: none;
}
.switch {
- border: 1px solid #ccc;
- background: #e5e5e5;
- width: 44px;
- height: 19px;
- border-radius: 13px;
- cursor: pointer;
- display: block;
- float: right;
- margin-top: 1px;
+ border: 1px solid #ccc;
+ background: #e5e5e5;
+ width: 44px;
+ height: 19px;
+ border-radius: 13px;
+ cursor: pointer;
+ display: block;
+ float: right;
+ margin-top: 1px;
}
.switch-toggle {
- border: 1px solid #999;
- box-shadow: 1px 1px 1px #ccc;
- width: 19px;
- height: 17px;
- left: 0;
- border-radius: 12px;
- background: #eee;
- position: relative;
- transition: left .2s ease-in-out;
+ border: 1px solid #999;
+ box-shadow: 1px 1px 1px #ccc;
+ width: 19px;
+ height: 17px;
+ left: 0;
+ border-radius: 12px;
+ background: #eee;
+ position: relative;
+ transition: left .2s ease-in-out;
}
.switch.on {
- border: 1px solid #4da94f;
- background: #5cb85c;
+ border: 1px solid #4da94f;
+ background: #5cb85c;
}
.switch.on .switch-toggle {
- left: 23px;
- border: 1px solid #b8ffb2;
- background: #b8ffb2;
+ left: 23px;
+ border: 1px solid #b8ffb2;
+ background: #b8ffb2;
}
.switch.disabled {
- cursor: not-allowed;
+ cursor: not-allowed;
}
.down-arrow {
- user-select: none;
- cursor: pointer;
- border-radius: 4px 4px 0 0;
- display: block;
- position: fixed;
- bottom: 20px;
- right: 12px;
- color: #fff;
- background: #d9534f;
- padding: 7px 0 2px;
- width: 60px;
- text-align: center;
+ user-select: none;
+ cursor: pointer;
+ border-radius: 4px 4px 0 0;
+ display: block;
+ position: fixed;
+ bottom: 20px;
+ right: 12px;
+ color: #fff;
+ background: #d9534f;
+ padding: 7px 0 2px;
+ width: 60px;
+ text-align: center;
}
.down-arrow:after {
- content: '';
- display: block;
- position: absolute;
- left: 0;
- top: 100%;
- width: 0;
- height: 0;
- border-top: 10px solid #d9534f;
- border-right: 30px solid transparent;
- border-bottom: 0 solid transparent;
- border-left: 30px solid transparent;
+ content: '';
+ display: block;
+ position: absolute;
+ left: 0;
+ top: 100%;
+ width: 0;
+ height: 0;
+ border-top: 10px solid #d9534f;
+ border-right: 30px solid transparent;
+ border-bottom: 0 solid transparent;
+ border-left: 30px solid transparent;
}
diff --git a/src/components/Game/Game.jsx b/src/components/Game/Game.jsx
index 1f413c0..9ce5bc9 100644
--- a/src/components/Game/Game.jsx
+++ b/src/components/Game/Game.jsx
@@ -4,42 +4,40 @@ import ShowStage from './ShowStage';
import Question from './Question';
class Game extends Component {
- constructor(props) {
- super(props);
- this.state = {
- showScreen: ''
+ state = { showScreen: '' }
+
+ componentWillMount() {
+ this.setState({showScreen: 'stage'});
+ }
+
+ stageUp = () => {
+ this.props.stageUp();
+ this.setState({showScreen: 'stage'});
+ }
+
+ lockStage = stage => {
+ this.setState({showScreen: 'question'});
+ this.props.lockStage(stage);
+ }
+
+ showQuestion = () => {
+ this.setState({showScreen: 'question'})
+ }
+
+ render() {
+ return (
+
+ {
+ this.state.showScreen==='stage' &&
+
}
- this.showQuestion = this.showQuestion.bind(this);
- this.stageUp = this.stageUp.bind(this);
- this.lockStage = this.lockStage.bind(this);
- }
-
- stageUp() {
- this.props.stageUp();
- this.setState({showScreen: 'stage'});
- }
-
- lockStage(stage) {
- this.setState({showScreen: 'question'});
- this.props.lockStage(stage);
- }
-
- showQuestion() {
- this.setState({showScreen: 'question'})
- }
-
- componentWillMount() {
- this.setState({showScreen: 'stage'});
- }
-
- render() {
- return (
-
- { this.state.showScreen==='stage' ? : '' }
- { this.state.showScreen==='question' ? : '' }
-
- );
- }
+ {
+ this.state.showScreen==='question' &&
+
+ }
+
+ );
+ }
}
-export default Game;
\ No newline at end of file
+export default Game;
diff --git a/src/components/Game/Question.jsx b/src/components/Game/Question.jsx
index 7264596..3d67aa0 100644
--- a/src/components/Game/Question.jsx
+++ b/src/components/Game/Question.jsx
@@ -5,264 +5,272 @@ import { findRomajisAtKanaKey, removeFromArray, arrayContains, shuffle, cartesia
import './Question.scss';
class Question extends Component {
- constructor(props) {
- super(props);
- this.state = {
- previousQuestion: [],
- previousAnswer: '',
- currentAnswer: '',
- currentQuestion: [],
- answerOptions: [],
- stageProgress: 0
+ state = {
+ previousQuestion: [],
+ previousAnswer: '',
+ currentAnswer: '',
+ currentQuestion: [],
+ answerOptions: [],
+ stageProgress: 0
+ }
+ // this.setNewQuestion = this.setNewQuestion.bind(this);
+ // this.handleAnswer = this.handleAnswer.bind(this);
+ // this.handleAnswerChange = this.handleAnswerChange.bind(this);
+ // this.handleSubmit = this.handleSubmit.bind(this);
+ // }
+
+ getRandomKanas(amount, include, exclude) {
+ let randomizedKanas = this.askableKanaKeys.slice();
+
+ if(exclude && exclude.length > 0) {
+ // we're excluding previous question when deciding a new question
+ randomizedKanas = removeFromArray(exclude, randomizedKanas);
+ }
+
+ if(include && include.length > 0) {
+ // we arrive here when we're deciding answer options (included = currentQuestion)
+
+ // remove included kana
+ randomizedKanas = removeFromArray(include, randomizedKanas);
+ shuffle(randomizedKanas);
+
+ // cut the size to make looping quicker
+ randomizedKanas = randomizedKanas.slice(0,20);
+
+ // let's remove kanas that have the same answer as included
+ let searchFor = findRomajisAtKanaKey(include, kanaDictionary)[0];
+ randomizedKanas = randomizedKanas.filter(character => {
+ return searchFor!=findRomajisAtKanaKey(character, kanaDictionary)[0];
+ });
+
+ // now let's remove "duplicate" kanas (if two kanas have same answers)
+ let tempRandomizedKanas = randomizedKanas.slice();
+ randomizedKanas = randomizedKanas.filter(r => {
+ let dupeFound = false;
+ searchFor = findRomajisAtKanaKey(r, kanaDictionary)[0];
+ tempRandomizedKanas.shift();
+ tempRandomizedKanas.forEach(w => {
+ if(findRomajisAtKanaKey(w, kanaDictionary)[0]==searchFor)
+ dupeFound = true;
+ });
+ return !dupeFound;
+ });
+
+ // alright, let's cut the array and add included to the end
+ randomizedKanas = randomizedKanas.slice(0, amount-1); // -1 so we have room to add included
+ randomizedKanas.push(include);
+ shuffle(randomizedKanas);
+ }
+ else {
+ shuffle(randomizedKanas);
+ randomizedKanas = randomizedKanas.slice(0, amount);
+ }
+ return randomizedKanas;
+ }
+
+ setNewQuestion() {
+ if(this.props.stage!=4)
+ this.currentQuestion = this.getRandomKanas(1, false, this.previousQuestion);
+ else
+ this.currentQuestion = this.getRandomKanas(3, false, this.previousQuestion);
+ this.setState({currentQuestion: this.currentQuestion});
+ this.setAnswerOptions();
+ this.setAllowedAnswers();
+ // console.log(this.currentQuestion);
+ }
+
+ setAnswerOptions() {
+ this.answerOptions = this.getRandomKanas(3, this.currentQuestion[0], false);
+ this.setState({answerOptions: this.answerOptions});
+ // console.log(this.answerOptions);
+ }
+
+ setAllowedAnswers() {
+ // console.log(this.currentQuestion);
+ this.allowedAnswers = [];
+ if(this.props.stage==1 || this.props.stage==3)
+ this.allowedAnswers = findRomajisAtKanaKey(this.currentQuestion, kanaDictionary);
+ else if(this.props.stage==2)
+ this.allowedAnswers = this.currentQuestion;
+ else if(this.props.stage==4) {
+ let tempAllowedAnswers = [];
+
+ this.currentQuestion.forEach(key => {
+ tempAllowedAnswers.push(findRomajisAtKanaKey(key, kanaDictionary));
+ });
+
+ cartesianProduct(tempAllowedAnswers).forEach(answer => {
+ this.allowedAnswers.push(answer.join(''));
+ });
+ }
+ // console.log(this.allowedAnswers);
+ }
+
+ handleAnswer = answer => {
+ if(this.props.stage<=2) document.activeElement.blur(); // reset answer button's :active
+ this.previousQuestion = this.currentQuestion;
+ this.setState({previousQuestion: this.previousQuestion});
+ this.previousAnswer = answer;
+ this.setState({previousAnswer: this.previousAnswer});
+ this.previousAllowedAnswers = this.allowedAnswers;
+ if(this.isInAllowedAnswers(this.previousAnswer))
+ this.stageProgress = this.stageProgress+1;
+ else
+ this.stageProgress = this.stageProgress > 0 ? this.stageProgress - 1 : 0;
+ this.setState({stageProgress: this.stageProgress});
+ if(this.stageProgress >= quizSettings.stageLength[this.props.stage] && !this.props.isLocked) {
+ setTimeout(() => { this.props.handleStageUp() }, 300);
+ }
+ else
+ this.setNewQuestion();
+ }
+
+ initializeCharacters() {
+ this.askableKanas = {};
+ this.askableKanaKeys = [];
+ this.askableRomajis = [];
+ this.previousQuestion = '';
+ this.previousAnswer = '';
+ this.stageProgress = 0;
+ Object.keys(kanaDictionary).forEach(whichKana => {
+ // console.log(whichKana); // 'hiragana' or 'katakana'
+ Object.keys(kanaDictionary[whichKana]).forEach(groupName => {
+ // console.log(groupName); // 'h_group1', ...
+ // do we want to include this group?
+ if(arrayContains(groupName, this.props.decidedGroups)) {
+ // let's merge the group to our askableKanas
+ this.askableKanas = Object.assign(this.askableKanas, kanaDictionary[whichKana][groupName]['characters']);
+ Object.keys(kanaDictionary[whichKana][groupName]['characters']).forEach(key => {
+ // let's add all askable kana keys to array
+ this.askableKanaKeys.push(key);
+ this.askableRomajis.push(kanaDictionary[whichKana][groupName]['characters'][key][0]);
+ });
}
- this.setNewQuestion = this.setNewQuestion.bind(this);
- this.handleAnswer = this.handleAnswer.bind(this);
- this.handleAnswerChange = this.handleAnswerChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- }
+ });
+ });
+ // console.log(this.askableKanas);
+ }
- getRandomKanas(amount, include, exclude) {
- let randomizedKanas = this.askableKanaKeys.slice();
+ getAnswerType() {
+ if(this.props.stage==2) return 'kana';
+ else return 'romaji';
+ }
- if(exclude && exclude.length > 0) {
- // we're excluding previous question when deciding a new question
- randomizedKanas = removeFromArray(exclude, randomizedKanas);
- }
-
- if(include && include.length > 0) {
- // we arrive here when we're deciding answer options (included = currentQuestion)
-
- // remove included kana
- randomizedKanas = removeFromArray(include, randomizedKanas);
- shuffle(randomizedKanas);
+ getShowableQuestion() {
+ if(this.getAnswerType()=='kana')
+ return findRomajisAtKanaKey(this.state.currentQuestion, kanaDictionary)[0];
+ else return this.state.currentQuestion;
+ }
- // cut the size to make looping quicker
- randomizedKanas = randomizedKanas.slice(0,20);
+ getPreviousResult() {
+ let resultString='';
+ // console.log(this.previousAnswer);
+ if(this.previousQuestion=='')
+ resultString =
Let's go! Which character is this?
+ else {
+ let rightAnswer = (
+ this.props.stage==2 ?
+ findRomajisAtKanaKey(this.previousQuestion, kanaDictionary)[0]
+ : this.previousQuestion.join('')
+ )+' = '+ this.previousAllowedAnswers[0];
- // let's remove kanas that have the same answer as included
- let searchFor = findRomajisAtKanaKey(include, kanaDictionary)[0];
- randomizedKanas = randomizedKanas.filter(function(character) {
- return searchFor!=findRomajisAtKanaKey(character, kanaDictionary)[0];
- }, this);
-
- // now let's remove "duplicate" kanas (if two kanas have same answers)
- let tempRandomizedKanas = randomizedKanas.slice();
- randomizedKanas = randomizedKanas.filter(function(r) {
- let dupeFound = false;
- searchFor = findRomajisAtKanaKey(r, kanaDictionary)[0];
- tempRandomizedKanas.shift();
- tempRandomizedKanas.map(function(w) {
- if(findRomajisAtKanaKey(w, kanaDictionary)[0]==searchFor)
- dupeFound = true;
- }, this);
- return !dupeFound;
- }, this);
-
- // alright, let's cut the array and add included to the end
- randomizedKanas = randomizedKanas.slice(0, amount-1); // -1 so we have room to add included
- randomizedKanas.push(include);
- shuffle(randomizedKanas);
- }
- else {
- shuffle(randomizedKanas);
- randomizedKanas = randomizedKanas.slice(0, amount);
- }
- return randomizedKanas;
- }
-
- setNewQuestion() {
- if(this.props.stage!=4)
- this.currentQuestion = this.getRandomKanas(1, false, this.previousQuestion);
- else
- this.currentQuestion = this.getRandomKanas(3, false, this.previousQuestion);
- this.setState({currentQuestion: this.currentQuestion});
- this.setAnswerOptions();
- this.setAllowedAnswers();
- // console.log(this.currentQuestion);
- }
-
- setAnswerOptions() {
- this.answerOptions = this.getRandomKanas(3, this.currentQuestion[0], false);
- this.setState({answerOptions: this.answerOptions});
- // console.log(this.answerOptions);
- }
-
- setAllowedAnswers() {
- // console.log(this.currentQuestion);
- this.allowedAnswers = [];
- if(this.props.stage==1 || this.props.stage==3)
- this.allowedAnswers = findRomajisAtKanaKey(this.currentQuestion, kanaDictionary);
- else if(this.props.stage==2)
- this.allowedAnswers = this.currentQuestion;
- else if(this.props.stage==4) {
- let tempAllowedAnswers = [];
-
- this.currentQuestion.map(function(key, idx) {
- tempAllowedAnswers.push(findRomajisAtKanaKey(key, kanaDictionary));
- }, this);
-
- cartesianProduct(tempAllowedAnswers).map(function(answer) {
- this.allowedAnswers.push(answer.join(''));
- }, this);
- }
- // console.log(this.allowedAnswers);
- }
-
- handleAnswer(answer) {
- if(this.props.stage<=2) document.activeElement.blur(); // reset answer button's :active
- this.previousQuestion = this.currentQuestion;
- this.setState({previousQuestion: this.previousQuestion});
- this.previousAnswer = answer;
- this.setState({previousAnswer: this.previousAnswer});
- this.previousAllowedAnswers = this.allowedAnswers;
- if(this.isInAllowedAnswers(this.previousAnswer))
- this.stageProgress = this.stageProgress+1;
- else
- this.stageProgress = this.stageProgress > 0 ? this.stageProgress - 1 : 0;
- this.setState({stageProgress: this.stageProgress});
- if(this.stageProgress >= quizSettings.stageLength[this.props.stage] &&
- !this.props.isLocked) {
- let that = this;
- setTimeout(function() { that.props.handleStageUp(); }, 300);
- }
- else
- this.setNewQuestion();
- }
-
- initializeCharacters() {
- this.askableKanas = {};
- this.askableKanaKeys = [];
- this.askableRomajis = [];
- this.previousQuestion = '';
- this.previousAnswer = '';
- this.stageProgress = 0;
- Object.keys(kanaDictionary).map(function(whichKana) {
- // console.log(whichKana); // 'hiragana' or 'katakana'
- Object.keys(kanaDictionary[whichKana]).map(function(groupName) {
- // console.log(groupName); // 'h_group1', ...
- // do we want to include this group?
- if(arrayContains(groupName, this.props.decidedGroups)) {
- // let's merge the group to our askableKanas
- this.askableKanas = Object.assign(this.askableKanas, kanaDictionary[whichKana][groupName]['characters']);
- Object.keys(kanaDictionary[whichKana][groupName]['characters']).map(function(key) {
- // let's add all askable kana keys to array
- this.askableKanaKeys.push(key);
- this.askableRomajis.push(kanaDictionary[whichKana][groupName]['characters'][key][0]);
- }, this);
- }
- }, this);
- }, this);
- // console.log(this.askableKanas);
- }
-
- getAnswerType() {
- if(this.props.stage==2) return 'kana';
- else return 'romaji';
- }
-
- getShowableQuestion() {
- if(this.getAnswerType()=='kana')
- return findRomajisAtKanaKey(this.state.currentQuestion, kanaDictionary)[0];
- else return this.state.currentQuestion;
- }
-
- getPreviousResult() {
- let resultString='';
- // console.log(this.previousAnswer);
- if(this.previousQuestion=='')
- resultString =
Let's go! Which character is this?
- else {
- let rightAnswer = (this.props.stage==2?findRomajisAtKanaKey(this.previousQuestion, kanaDictionary)[0]:this.previousQuestion.join(''))+' = '+
- this.previousAllowedAnswers[0];
- if(this.isInAllowedAnswers(this.previousAnswer))
- resultString =
{rightAnswer}
- else
- resultString =
{rightAnswer}
- }
- return resultString;
- }
-
- isInAllowedAnswers(previousAnswer) {
- // console.log(previousAnswer);
- // console.log(this.allowedAnswers);
- if(arrayContains(previousAnswer, this.previousAllowedAnswers))
- return true;
- else return false;
- }
-
- handleAnswerChange(e) {
- this.setState({currentAnswer: e.target.value.replace(/\s+/g, '')});
- }
-
- handleSubmit(e) {
- e.preventDefault();
- if(this.state.currentAnswer!='') {
- this.handleAnswer(this.state.currentAnswer.toLowerCase());
- this.setState({currentAnswer: ''});
- }
- }
-
- componentWillMount() {
- this.initializeCharacters();
- }
-
- componentDidMount() {
- this.setNewQuestion();
- }
-
- render() {
- let btnClass = "btn btn-default answer-button";
- if ('ontouchstart' in window)
- btnClass += " no-hover"; // disables hover effect on touch screens
- let stageProgressPercentage = Math.round((this.state.stageProgress/quizSettings.stageLength[this.props.stage])*100)+'%';
- let stageProgressPercentageStyle = { width: stageProgressPercentage }
- return (
-
- {this.getPreviousResult()}
-
{this.getShowableQuestion()}
-
- {this.props.stage<3?this.state.answerOptions.map(function(answer, idx) {
- return
- }, this):
-
-
-
- }
-
-
-
- Stage {this.props.stage} {this.props.isLocked?' (Locked)':''}
-
-
-
+ if(this.isInAllowedAnswers(this.previousAnswer))
+ resultString = (
+
+ {rightAnswer}
+
+ );
+ else
+ resultString = (
+
+ {rightAnswer}
+
);
}
+ return resultString;
+ }
+
+ isInAllowedAnswers(previousAnswer) {
+ // console.log(previousAnswer);
+ // console.log(this.allowedAnswers);
+ if(arrayContains(previousAnswer, this.previousAllowedAnswers))
+ return true;
+ else return false;
+ }
+
+ handleAnswerChange = e => {
+ this.setState({currentAnswer: e.target.value.replace(/\s+/g, '')});
+ }
+
+ handleSubmit = e => {
+ e.preventDefault();
+ if(this.state.currentAnswer!='') {
+ this.handleAnswer(this.state.currentAnswer.toLowerCase());
+ this.setState({currentAnswer: ''});
+ }
+ }
+
+ componentWillMount() {
+ this.initializeCharacters();
+ }
+
+ componentDidMount() {
+ this.setNewQuestion();
+ }
+
+ render() {
+ let btnClass = "btn btn-default answer-button";
+ if ('ontouchstart' in window)
+ btnClass += " no-hover"; // disables hover effect on touch screens
+ let stageProgressPercentage = Math.round((this.state.stageProgress/quizSettings.stageLength[this.props.stage])*100)+'%';
+ let stageProgressPercentageStyle = { width: stageProgressPercentage }
+ return (
+
+ {this.getPreviousResult()}
+
{this.getShowableQuestion()}
+
+ {this.props.stage<3?this.state.answerOptions.map(function(answer, idx) {
+ return
+ }, this):
+
+
+
+ }
+
+
+
+ Stage {this.props.stage} {this.props.isLocked?' (Locked)':''}
+
+
+
+ );
+ }
}
class AnswerButton extends Component {
- getShowableAnswer() {
- if(this.props.answertype=='romaji')
- return findRomajisAtKanaKey(this.props.answer, kanaDictionary)[0];
- else return this.props.answer;
- }
+ getShowableAnswer() {
+ if(this.props.answertype=='romaji')
+ return findRomajisAtKanaKey(this.props.answer, kanaDictionary)[0];
+ else return this.props.answer;
+ }
- render() {
- return (
-
- );
- }
+ render() {
+ return (
+
+ );
+ }
}
-export default Question;
\ No newline at end of file
+export default Question;
diff --git a/src/components/Game/Question.scss b/src/components/Game/Question.scss
index 825c0f9..8e1ad62 100644
--- a/src/components/Game/Question.scss
+++ b/src/components/Game/Question.scss
@@ -1,87 +1,87 @@
.question {
- .progress {
- position: relative;
- background-color: #ddd;
- height: 30px;
- @media (max-width: 768px) {
- max-width: 360px;
- margin: 0 auto;
- }
+ .progress {
+ position: relative;
+ background-color: #ddd;
+ height: 30px;
+ @media (max-width: 768px) {
+ max-width: 360px;
+ margin: 0 auto;
}
- .progress span {
- position: absolute;
- top: 5px;
- color: #444;
- display: block;
- width: 100%;
+ }
+ .progress span {
+ position: absolute;
+ top: 5px;
+ color: #444;
+ display: block;
+ width: 100%;
+ }
+ .previous-result{
+ max-width: 360px;
+ padding: 8px;
+ margin: 30px auto 28px;
+ border-radius: 3px;
+ @media (max-width: 768px) {
+ margin: 0px auto 0px;
}
- .previous-result{
- max-width: 360px;
- padding: 8px;
- margin: 30px auto 28px;
- border-radius: 3px;
- @media (max-width: 768px) {
- margin: 0px auto 0px;
- }
+ }
+ .size-up {
+ font-size: 1.1em;
+ }
+ .none {
+ background-color: #aaa;
+ color: #f5f5f5;
+ }
+ .correct {
+ color: #f5f5f5;
+ background-color: #5cb85c;
+ }
+ .wrong {
+ color: #f5f5f5;
+ background-color: #d9534f;
+ }
+ .previous-result-none {
+ max-width: 360px;
+ padding: 6px;
+ margin: 30px auto;
+ color: #f5f5f5;
+ }
+ .big-character {
+ font-size: 5em;
+ }
+ .answer-container {
+ max-width: 360px;
+ margin: 0 auto;
+ display: flex;
+ justify-content: space-between;
+ }
+ .answer-form-container {
+ max-width: 100px;
+ margin: 0 auto;
+ }
+ .answer-button {
+ min-width: 90px;
+ font-size: 2em;
+ margin: 30px 0 60px;
+ @media (max-width: 768px) {
+ margin: 0px 0 15px;
}
- .size-up {
- font-size: 1.1em;
+ }
+ .answer-input, .answer-input:focus {
+ outline: none;
+ width: 110px;
+ text-align: center;
+ font-size: 2em;
+ margin: 25px 0 60px;
+ background: none;
+ border: none;
+ border-bottom: solid 1px #aaa;
+ @media (max-width: 768px) {
+ margin: 0px 0 25px;
}
- .none {
- background-color: #aaa;
- color: #f5f5f5;
- }
- .correct {
- color: #f5f5f5;
- background-color: #5cb85c;
- }
- .wrong {
- color: #f5f5f5;
- background-color: #d9534f;
- }
- .previous-result-none {
- max-width: 360px;
- padding: 6px;
- margin: 30px auto;
- color: #f5f5f5;
- }
- .big-character {
- font-size: 5em;
- }
- .answer-container {
- max-width: 360px;
- margin: 0 auto;
- display: flex;
- justify-content: space-between;
- }
- .answer-form-container {
- max-width: 100px;
- margin: 0 auto;
- }
- .answer-button {
- min-width: 90px;
- font-size: 2em;
- margin: 30px 0 60px;
- @media (max-width: 768px) {
- margin: 0px 0 15px;
- }
- }
- .answer-input, .answer-input:focus {
- outline: none;
- width: 110px;
- text-align: center;
- font-size: 2em;
- margin: 25px 0 60px;
- background: none;
- border: none;
- border-bottom: solid 1px #aaa;
- @media (max-width: 768px) {
- margin: 0px 0 25px;
- }
- }
- .no-hover {
- /* disables hover effect on touch screens */
- background-color: #fff;
- border-color: #ccc;
- }
-}
\ No newline at end of file
+ }
+ .no-hover {
+ /* disables hover effect on touch screens */
+ background-color: #fff;
+ border-color: #ccc;
+ }
+}
diff --git a/src/components/GameContainer/GameContainer.jsx b/src/components/GameContainer/GameContainer.jsx
index 779a6c3..fee51a7 100644
--- a/src/components/GameContainer/GameContainer.jsx
+++ b/src/components/GameContainer/GameContainer.jsx
@@ -4,66 +4,63 @@ import ChooseCharacters from '../ChooseCharacters/ChooseCharacters';
import Game from '../Game/Game';
class GameContainer extends Component {
- constructor(props) {
- super(props);
- this.startGame = this.startGame.bind(this);
- this.state = {
- stage:1,
- isLocked: false,
- decidedGroups: ['h_group1']
- }
- this.stageUp = this.stageUp.bind(this);
- this.lockStage = this.lockStage.bind(this);
- }
+ state = {
+ stage:1,
+ isLocked: false,
+ decidedGroups: JSON.parse(localStorage.getItem('decidedGroups') || null) || []
+ }
- startGame(decidedGroups) {
- if(parseInt(this.state.stage)<1 || isNaN(parseInt(this.state.stage)))
- this.setState({stage: 1});
- else if(parseInt(this.state.stage)>4)
- this.setState({stage: 4});
+ componentWillReceiveProps() {
+ if(!this.state.isLocked)
+ this.setState({stage: 1});
+ }
- this.setState({decidedGroups: decidedGroups});
- this.props.handleStartGame();
- }
+ startGame = decidedGroups => {
+ if(parseInt(this.state.stage)<1 || isNaN(parseInt(this.state.stage)))
+ this.setState({stage: 1});
+ else if(parseInt(this.state.stage)>4)
+ this.setState({stage: 4});
- stageUp() {
- this.setState({stage: this.state.stage+1});
- }
+ this.setState({decidedGroups: decidedGroups});
+ localStorage.setItem('decidedGroups', JSON.stringify(decidedGroups));
+ this.props.handleStartGame();
+ }
- lockStage(stage, forceLock) {
- // if(stage<1 || stage>4) stage=1; // don't use this to allow backspace
- if(forceLock)
- this.setState({stage: stage, isLocked: true});
- else
- this.setState({stage: stage, isLocked: !this.state.isLocked});
- }
+ stageUp = () => {
+ this.setState({stage: this.state.stage+1});
+ }
- componentWillReceiveProps() {
- if(!this.state.isLocked)
- this.setState({stage: 1});
- }
+ lockStage = (stage, forceLock) => {
+ // if(stage<1 || stage>4) stage=1; // don't use this to allow backspace
+ if(forceLock)
+ this.setState({stage: stage, isLocked: true});
+ else
+ this.setState({stage: stage, isLocked: !this.state.isLocked});
+ }
- render() {
- return (
-
- { this.props.gameState==='chooseCharacters' ?
- : '' }
- { this.props.gameState==='game' ?
- : '' }
-
- )
- }
+ render() {
+ return (
+
+ { this.props.gameState==='chooseCharacters' &&
+
+ }
+ { this.props.gameState==='game' &&
+
+ }
+
+ )
+ }
}
-export default GameContainer;
\ No newline at end of file
+export default GameContainer;
diff --git a/src/components/Navbar/Navbar.jsx b/src/components/Navbar/Navbar.jsx
index 7e9da98..4dd5905 100644
--- a/src/components/Navbar/Navbar.jsx
+++ b/src/components/Navbar/Navbar.jsx
@@ -2,28 +2,27 @@ import React, { Component } from 'react';
import './Navbar.scss';
class Navbar extends Component {
- render() {
- let leftLink;
- switch(this.props.gameState) {
- case 'chooseCharacters':
- default:
- leftLink =
Kana Quiz 2
- break;
- case 'game':
- leftLink =
Back to menu
- }
- return (
-
- )
- }
+ render() {
+ return (
+
+ )
+ }
}
-export default Navbar;
\ No newline at end of file
+export default Navbar;
diff --git a/src/components/Navbar/Navbar.scss b/src/components/Navbar/Navbar.scss
index 04ef8c6..dd071be 100644
--- a/src/components/Navbar/Navbar.scss
+++ b/src/components/Navbar/Navbar.scss
@@ -1,15 +1,15 @@
#navbar ul li {
- font-size: 1.2em;
- @media (max-width: 768px) {
- font-size: 1.0em;
- }
- .glyphicon {
- font-size: 0.9em;
- color: #d9534f;
- }
+ font-size: 1.2em;
+ @media (max-width: 768px) {
+ font-size: 1.0em;
+ }
+ .glyphicon {
+ font-size: 0.9em;
+ color: #d9534f;
+ }
}
.navbar-inverse {
- background-color: #333;
+ background-color: #333;
}
.hidden-nano {
@media (max-width: 390px) {
@@ -88,7 +88,7 @@
border-width: 0 1px 1px;
border-radius: 0 0 4px 4px;
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
- box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
}
.navbar-default .navbar-nav .open .dropdown-menu > li > a {
color: #333;
@@ -106,4 +106,4 @@
.navbar .navbar-nav .open .dropdown-menu > .disabled > a:focus {
color: #999 !important;
background-color: transparent !important;
-}
\ No newline at end of file
+}
diff --git a/src/index.html b/src/index.html
index fcf6fcd..abc1050 100644
--- a/src/index.html
+++ b/src/index.html
@@ -1,13 +1,13 @@
-
Kana Quiz 2: Learn hiragana & katakana fast and easy
+
Kana Pro: Learn hiragana & katakana fast and easy
-
+
-
\ No newline at end of file
+