aboutsummaryrefslogtreecommitdiff
path: root/public/js/luoparsetrainer.js
diff options
context:
space:
mode:
Diffstat (limited to 'public/js/luoparsetrainer.js')
-rw-r--r--public/js/luoparsetrainer.js436
1 files changed, 436 insertions, 0 deletions
diff --git a/public/js/luoparsetrainer.js b/public/js/luoparsetrainer.js
new file mode 100644
index 0000000..11394e4
--- /dev/null
+++ b/public/js/luoparsetrainer.js
@@ -0,0 +1,436 @@
+/**
+ * Luo Parse Trainer - practice Ancient Greek verb forms
+ * Copyright (C) 2015-present Camil Staps <info@camilstaps.nl>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+$(document).ready(function(){
+ // http://stackoverflow.com/a/4399433/1544337
+ jQuery.fn.shake = function(intShakes, intDistance, intDuration) {
+ this.each(function() {
+ $(this).css("position","relative");
+ for (var x=1; x<=intShakes; x++) {
+ $(this).animate({left:(intDistance*-1)}, (intDuration/intShakes)/4)
+ .animate({left:intDistance}, (intDuration/intShakes)/2)
+ .animate({left:0}, (intDuration/intShakes)/4);
+ }
+ });
+ return this;
+ };
+
+ var audio_positive = new Audio('public/audio/positive.wav');
+ var audio_negative = new Audio('public/audio/negative.wav');
+
+ var correct_answers;
+ var input_count = 0;
+ var checked = false;
+
+ function stepFancyInput(step, val) {
+ $('#trainer-input-fancy').html('');
+ var buts = {};
+ switch (step) {
+ case 0:
+ buts = { 'pr ': 'Praesens'
+ , 'impf ': 'Imperfectum'
+ , 'aor ': 'Aoristus'
+ , 'fut ': 'Futurum'
+ , 'pf ': 'Perfectum'
+ , 'pqpf ': 'Plusquamperfectum'
+ }; break;
+ case 1:
+ buts = { 'ind ': 'Indicativus'
+ , 'conj ': 'Conjunctivus'
+ , 'opt ': 'Optativus'
+ , 'imp! ': 'Imperativus'
+ , 'ptc ': 'Participium'
+ , 'inf ': 'Infinitivus'
+ }; break;
+ case 2:
+ buts = { 'A ': 'Activum'
+ , 'M ': 'Medium'
+ , 'P ': 'Passivum'
+ , 'MP ': 'Medio-passivum'
+ }; break;
+ case 3:
+ if (val.match(/ inf /)) {
+ /* no buttons */
+ } else if (val.match(/ ptc /)) {
+ buts = {
+ 'M nom ev': 'M nom ev',
+ 'F nom ev': 'F nom ev',
+ 'N nom ev': 'N nom ev',
+ 'M gen ev': 'M gen ev',
+ 'F gen ev': 'F gen ev',
+ 'N gen ev': 'N gen ev',
+ 'M dat ev': 'M dat ev',
+ 'F dat ev': 'F dat ev',
+ 'N dat ev': 'N dat ev',
+ 'M acc ev': 'M acc ev',
+ 'N acc ev': 'N acc ev',
+ 'F acc ev': 'F acc ev',
+ 'M nom mv': 'M nom mv',
+ 'F nom mv': 'F nom mv',
+ 'N nom mv': 'N nom mv',
+ 'M gen mv': 'M gen mv',
+ 'F gen mv': 'F gen mv',
+ 'N gen mv': 'N gen mv',
+ 'M dat mv': 'M dat mv',
+ 'F dat mv': 'F dat mv',
+ 'N dat mv': 'N dat mv',
+ 'M acc mv': 'M acc mv',
+ 'F acc mv': 'F acc mv',
+ 'N acc mv': 'N acc mv'
+ };
+ } else {
+ buts = {
+ '1 ev': '1 ev',
+ '2 ev': '2 ev',
+ '3 ev': '3 ev',
+ '1 mv': '1 mv',
+ '2 mv': '2 mv',
+ '3 mv': '3 mv',
+ };
+ }
+ break;
+ }
+
+ var input_field = $('#trainer-input-fancy');
+
+ var i = 0;
+ for (k in buts) {
+ var but = $('<button></button>');
+ but.addClass('btn btn-default').attr('role', 'button');
+ but.text(buts[k]).val(k);
+ but.click(function(){
+ var ip = $('#trainer-input-'+input_count);
+ ip.val(ip.val() + $(this).val()).focus();
+ if (step < 3) {
+ stepFancyInput(step + 1, ip.val());
+ } else {
+ var done = checkInput(true);
+ if ($('#trainer-input-'+input_count).parent().hasClass('has-error')) {
+ var next = $('<button></button>');
+ next.addClass('btn btn-warning').attr('role', 'button');
+ next.click(reloadVerb);
+ if (done) {
+ next.text('Next');
+ } else {
+ next.text('Skip');
+ }
+ $('#trainer-input-fancy').html(next);
+ }
+ }
+ });
+
+ input_field.append(but).append('&nbsp;');
+
+ if (Object.keys(buts).length > 6 && !(++i % 3))
+ input_field.append($('<br>'));
+ }
+ }
+
+ function addInput() {
+ input_count++;
+ var html = "<div class='row trainer-input'>\
+ <div class='col-md-8'>\
+ <div class='form-group'>\
+ <label for='trainer-input-"+input_count+"'>Parse:</label>\
+ <input type='text' class='form-control' id='trainer-input-"+input_count+"' placeholder='pr ind A 1 ev' spellcheck='false'/>\
+ </div>\
+ </div>\
+ <div class='col-md-4'>\
+ <div class='form-group'>\
+ <label for='trainer-parsed-"+input_count+"'>Interpreted as:</label>\
+ <input type='text' class='form-control' id='trainer-parsed-"+input_count+"' readonly='readonly'/>\
+ </div>\
+ </div>\
+ </div>";
+ $('#trainer-input-container').append(html);
+
+ $('#trainer-input-'+input_count).keyup(function(e){
+ if (e.keyCode == 13) {
+ if (!checked) {
+ checked = checkInput(false);
+ } else {
+ reloadVerb();
+ $(this)
+ .val('')
+ .css({backgroundColor:'transparent'})
+ .parent().removeClass('has-error has-success');
+ checked = false;
+ }
+ } else {
+ $(this)
+ .css({backgroundColor:'transparent'})
+ .parent().removeClass('has-error has-success');
+ checked = false;
+ processInput();
+ }
+ }).focus();
+
+ stepFancyInput(0);
+ }
+
+ function removeInputs() {
+ $('.trainer-input').remove();
+ input_count = 0;
+ }
+
+ function reloadVerb() {
+ $('#trainer-404').hide();
+ $('#trainer-verb').css({color: 'gray'});
+ $('#trainer-answer').text('');
+ removeInputs();
+
+ var modes = $('input[name="mode"]:checked').map(function(){return this.value;});
+ var tenses = $('input[name="tense"]:checked').map(function(){return this.value;});
+
+ $.ajax('form/random/', {
+ method: 'POST',
+ data: {
+ _token: $('#csrf').val(),
+ mode: $.makeArray(modes).join(),
+ tense: $.makeArray(tenses).join()
+ },
+ dataType: 'json',
+ error: function(jqxhr, status, error) {
+ $('#trainer-404').fadeIn();
+ },
+ success: function(data, status, jqxhr) {
+ $('#trainer-verb').text(data.form.form).css({color: 'black'});
+
+ correct_answers = [];
+ for (var i in data.answers) {
+ var answer = {
+ tense: data.answers[i].tense,
+ mode: data.answers[i].mode,
+ voice: data.answers[i].voice,
+ person: data.answers[i].person,
+ gender: data.answers[i].gender,
+ number: data.answers[i].number,
+ case_: data.answers[i]['case']
+ };
+ correct_answers.push(answer);
+ }
+
+ addInput();
+ }
+ });
+ }
+
+ var modes = [];
+ var tenses = [];
+ var tenses_abbr = [];
+
+ function findTense(tense) {
+ switch (tense) {
+ case 'pr': return 'praesens';
+ case 'impf': return 'imperfectum';
+ case 'aor': return 'aoristus';
+ case 'fut': return 'futurum';
+ case 'pf': return 'perfectum';
+ case 'pqpf': return 'plusquamperfectum';
+ default: return undefined;
+ }
+ }
+
+ function findMode(mode) {
+ switch (mode) {
+ case 'ind': return 'indicativus';
+ case 'conj': return 'conjunctivus';
+ case 'opt': return 'optativus';
+ case 'imp!': return 'imperativus';
+ default: return undefined;
+ }
+ }
+
+ function parseAnswer(parsing) {
+ var match = parsing.match(/^\s*(pr|impf|aor|fut|pf|pqpf)\s+(ind|conj|opt|imp!)\s+(A|M|P|MP)\s+([123])\s*([em]v)\s*$/);
+ if (match) {
+ var tense = findTense(match[1]);
+ var mode = findMode(match[2]);
+
+ if (typeof tense=='undefined' || typeof mode=='undefined')
+ return false;
+
+ return {
+ tense: tense,
+ mode: mode,
+ voice: match[3],
+ person: match[4],
+ gender: null,
+ number: match[5] == 'ev' ? 'sg' : 'pl',
+ case_: null
+ };
+ }
+
+ match = parsing.match(/^\s*(pr|impf|aor|fut|pf|pqpf)\s+ptc\s+(A|M|P|MP)\s+([MFN])\s+(nom|gen|dat|acc)\s+([em]v)\s*$/);
+ if (match) {
+ var tense = findTense(match[1]);
+
+ if (typeof tense=='undefined')
+ return false;
+
+ return {
+ tense: tense,
+ mode: 'participium',
+ voice: match[2],
+ person: null,
+ gender: match[3],
+ number: match[5] == 'ev' ? 'sg' : 'pl',
+ case_: match[4]
+ };
+ }
+
+ match = parsing.match(/^\s*(pr|impf|aor|fut|pf|pqpf)\s+inf\s+(A|M|P|MP)\s*$/);
+ if (match) {
+ var tense = findTense(match[1]);
+
+ if (typeof tense=='undefined')
+ return false;
+
+ return {
+ tense: tense,
+ mode: 'infinitivus',
+ voice: match[2],
+ person: null,
+ gender: null,
+ number: null,
+ case_: null
+ };
+ }
+
+ return false;
+ }
+
+ function abbreviate(term) {
+ switch (term) {
+ case 'praesens': return 'pr';
+ case 'imperfectum': return 'impf';
+ case 'aoristus': return 'aor';
+ case 'futurum': return 'fut';
+ case 'perfectum': return 'pf';
+ case 'plusquamperfectum': return 'pqpf';
+ case 'indicativus': return 'ind';
+ case 'conjunctivus': return 'conj';
+ case 'optativus': return 'opt';
+ case 'imperativus': return 'imp!';
+ case 'participium': return 'ptc';
+ case 'infinitivus': return 'inf';
+ default: return term;
+ }
+ }
+
+ function parsingToString(parsing, abbr) {
+ var tense = abbr === true ? abbreviate(parsing.tense) : parsing.tense;
+ var mode = abbr === true ? abbreviate(parsing.mode) : parsing.mode;
+ var number = parsing.number == 'sg' ? 'ev' : 'mv';
+ var tense_mode_voice = [tense, mode, parsing.voice].join(' ');
+
+ switch (parsing.mode) {
+ case 'infinitivus':
+ return tense_mode_voice;
+ case 'participium':
+ return [tense_mode_voice, parsing.gender, parsing.case_, number].join(' ');
+ default:
+ return [tense_mode_voice, parsing.person, number].join(' ');
+ }
+ }
+
+ function processInput() {
+ var input = $('#trainer-input-' + input_count);
+ var answer = parseAnswer(input.val());
+ if (answer === false) {
+ input.parent().addClass('has-error');
+ $('#trainer-parsed-' + input_count).val(
+ input.val().length < 10 ? 'Vul een volledige parsering in...' : 'Incorrecte invoer');
+ } else {
+ input.parent().removeClass('has-error');
+ $('#trainer-parsed-' + input_count).val(parsingToString(answer, true));
+ }
+ return answer;
+ }
+
+ function checkInput(reload) {
+ var answer = processInput();
+ if (!answer && $('#trainer-input-'+input_count).val() != '') {
+ $('#trainer-input-'+input_count).shake(2, 12, 300);
+ return false;
+ }
+
+ for (var i in correct_answers) {
+ var correct_answer = correct_answers[i];
+ var json = JSON.stringify(correct_answer);
+ if (JSON.stringify(answer) == json) {
+ $('#trainer-input-'+input_count)
+ .css({backgroundColor: '#dff0d8'})
+ .parent().addClass('has-success');
+ if ($('#settings-audio').prop('checked')) audio_positive.play();
+
+ correct_answers.splice(i,1);
+ if (correct_answers.length > 0) {
+ addInput();
+ return false;
+ } else {
+ if (reload === true) {
+ window.setTimeout(reloadVerb, 600);
+ }
+ return true;
+ }
+ }
+ }
+
+ $('#trainer-input-'+input_count)
+ .css({backgroundColor: '#f2dede'})
+ .parent().addClass('has-error');
+ if ($('#settings-audio').prop('checked')) audio_negative.play();
+ $('#trainer-answer').text(' - ' + correct_answers.map(parsingToString).join(', '));
+
+ return true;
+ }
+
+ function init() {
+ $.ajax('mode/', {
+ dataType: 'json',
+ success: function(data, status, jqxhr) {
+ modes = data.map(function(d){return d.name;});
+ }
+ });
+
+ $.ajax('tense/', {
+ dataType: 'json',
+ success: function(data, status, jqxhr) {
+ tenses = data.map(function(d){return d.name;});
+ tenses_abbr = data.map(function(d){return d.abbreviation;});
+ }
+ });
+
+ if (typeof reload_on_load != 'undefined' && reload_on_load)
+ reloadVerb();
+ }
+
+ $('#luoparsetrainer-settings .reload-verb').change(function(){
+ reloadVerb();
+ });
+
+ var help_shown = false;
+ $('#show-hide-help').click(function(){
+ help_shown = !help_shown;
+ $('#trainer-input-help').slideToggle();
+ $(this).text((help_shown ? 'Hide' : 'Show') + ' help');
+ $('#trainer-input-'+input_count).focus();
+ });
+
+ init();
+});