After being inspired by some MVC style design patterns, I have been trying to separate data from views in my code and move toward a more sensibly organized object based approach. (please, don't bother telling me that my code doesn't follow strict MVC pattern correctly, that is not my concern at this point!) All the data for a single tutorial is now handled by a data model object (currentTutorial) while currentvideo holds the state and relevant data (such as auto-pause times) for the videos but I have hit a bit of a wall with trying to couple the currentvideo and currentTutorial objects.
I want the currentvideo object to be a property of the currentTutorial object, it makes no sense for it to exist separately as there is exactly one corresponding video for each tutorial and I think it makes my code looser, more confusing and fragile as a result.
Unfortunately every time I try to move the video object into the tutorial object I run into countless scoping problems that just kinda make my brain hurt and I end up reverting back to this point. I know it's a pretty big ask but I would be very grateful to any OOP guru who could untangle this mess for me! :)
P.S. I will of course consider suggestions relating to other issues but this is my main concern right now..
var conjugationsets = [
["작다", "놀다", "닦다"],
["웃다", "울다", "멀다"]
];
var section_number = 0;
var tutorial_number = 0;
//firstpause, tryagain, tryagainpause, firstcongrats, firstcongratspause, secondcongrats, secondcongratspause, thirdcongrats
var videos = [{
url: "conjugation tut.mp4",
times: [159, 160.5, 163.8, 164.15, 166.8, 167.1, 170, 171.7]
}, {
url: "play.mp4",
times: [159, 160.5, 163.8, 164.15, 166.8, 167.1, 170, 171.7]
}];
var questionnum = 0;
var currentvideo;
var currentset = {};
var currentTutorial;
function handler() {
tutorial_number++;
currentTutorial = new Buildtutorial;
currentvideo = new Buildvideo(videos[tutorial_number]);
currentvideo.videoobject.play(0);
}
function Buildtutorial(num) {
if (num){tutorial_number = num};
this.sets = [
{name: "Conjugation",tutorials: ["ㅗ and ㅏ regular", "ㅜ, ㅓ and ㅣ regular", "ㅏ and ㅓ reductive", "ㅗ and ㅜ reductive", "ㅣ reductive"]},
{name: "Sentence Building",tutorials: ["Particles", "Word Order"]}
];
this.tutorial_name = this.sets[section_number].tutorials[tutorial_number];
this.section_name = this.sets[section_number].name;
this.tutorial_number = tutorial_number;
this.section_number = section_number;
update_menu(this);
}
function Buildvideo(x) {
if (x === undefined) {
alert("no new videos");
return;
}
this.firstpause = x.times[0];
this.url = x.url;
this.tryagain = x.times[1];
this.tryagainpause = x.times[2];
this.firstcongrats = x.times[3];
this.firstcongratspause = x.times[4];
this.secondcongrats = x.times[5];
this.time = $("#video").get(0).currentTime;
this.secondcongratspause = x.times[6];
this.thirdcongrats = x.times[7];
this.videoobject = $("#video").get(0);
$(this.videoobject).bind('ended', handler);
this.load = function() {
$("#video").html("<source src='" + currentvideo.url + "' type='video/ogg'>");
this.videoobject.load();
};
this.start = function(time) {
this.videoobject.currentTime = time;
time = time || 0;
this.videoobject.play();
this.pause(this.firstpause);
};
this.pause = function(time) {
this.videoobject.addEventListener('timeupdate', function() {
if (this.currentTime >= time && this.currentTime < (time + 0.3)) {
console.log("pause at " + this.currentTime);
this.pause();
}
}, true);
};
}
function getnewset() {
currentset = {
conjugations: {},
baseverb: conjugationsets[tutorial_number][questionnum]
};
}
var random = function(r) {
r.children().sort(function() {
return (Math.round(Math.random()) - 0.5);
}).appendTo(r);
};
var vowels = "0ㅏㅐㅑㅒㅓㅔㅕㅖㅗㅘㅙㅚㅛㅜㅝㅞㅟㅠㅡㅢㅣ".split(""),
leads = "0ㄱㄲㄴㄷㄸㄹㅁㅂㅃㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎ".split(""),
tails = "0ㄱㄲㄳㄴㄵㄶㄷㄹㄺㄻㄼㄽㄾㄿㅀㅁㅂㅄㅅㅆㅇㅈ".split("");
function render(x) {
$("#wrong0").text(x.conjugations.incorrect[0]);
$("#wrong1").text(x.conjugations.incorrect[1]);
$("#wrong2").text(x.conjugations.incorrect[2]);
$("#correct").text(x.conjugations.correct);
random($(".answers"));
$(".answer").css("border-radius", "0px");
$('.answer').first().css('border-radius', '10px 0px 0px 10px');
$('.answer').last().css('border-radius', '0px 10px 10px 0px');
$(".answer").css("background-color", "#B7BECC");
$("#base").text(x.baseverb);
}
function update_menu(y){
$("#nav,#nav *").css("background-color", "white");
var query = "#" + y.section_name + ",#" + y.section_name + "" + y.tutorial_number;
$(query).css("background-color", "rgb(215,215,215)");
}
function render_menu(x) {
var nav = $("#nav");
$.each(x.sets, function(ind, val) {
var text = "";
$.each(val.tutorials, function(i, v) {
text += "<li id='" + val.name + i + "'class='tutorial_title'>" + v + "</li>"
});
nav.append($("<li id='" + x.sets[ind].name + "' class='section_title'>" + val.name + "</li><ul id='tutorial_titles'>" + text + "</ul>"));
});
update_menu(x);
$(".tutorial_title").click(function(){
var id = $(this).attr('id');
var num = id.substring(id.length - 1);
currentTutorial = new Buildtutorial(num);
});
}
$(function() {
//set mouseover colors
$(".answer").each(function() {
$(this).mouseover(function() {
var bg = $(this).css("background-color");
if (bg === "rgb(183, 190, 204)" || bg === "#b7becc") {
$(this).css("background-color", "#E5E8EE");
}
}).mouseout(function() {
var bg = $(this).css("background-color");
if (bg === "rgb(229, 232, 238)" || bg === "#e5e8ee") {
$(this).css("background-color", "#B7BECC");
}
});
});
function seperate(x) {
x = x.charCodeAt();
var y = {},
z = {};
y.tail = (x - 44032) % 28;
y.vowel = 1 + ((x - 44032 - y.tail) % 588 / 28);
y.lead = 1 + (parseInt((x - 44032) / 588, 10));
z.vowel = vowels[y.vowel];
z.tail = tails[y.tail];
z.lead = leads[y.lead];
return z;
}
function buildhangeul(x) {
var tail = tails.indexOf(x.tail),
vowel = vowels.indexOf(x.vowel),
lead = leads.indexOf(x.lead),
codepoint = tail + (vowel - 1) * 28 + (lead - 1) * 588 + 44032;
return String.fromCharCode(codepoint);
}
function conjugate(base) {
var output = {},
each = base.split("");
output.incorrect = [];
if (each[each.length - 1] === "다") {
var stemlast = each[each.length - 2],
lastjamo = seperate(stemlast);
//ㅂ irregular
if (lastjamo.tail === "ㅂ") {
lastjamo.tail = "0";
stemlast = buildhangeul(lastjamo);
output.correct = each.slice(0, -2).join("") + stemlast + "워";
output.incorrect[0] = each.slice(0, -1).join("") + buildhangeul({
lead: "ㅇ",
vowel: lastjamo.vowel,
tail: "0"
});
output.incorrect[1] = each.slice(0, -1).join("") + "워";
output.incorrect[2] = each.slice(0, -2).join("") + stemlast + "와";
} else if (lastjamo.vowel === "ㅏ" && lastjamo.tail !== "0" || lastjamo.vowel === "ㅗ" && lastjamo.tail !== "0") {
//ㅏ and ㅗ regular
output.correct = stemlast + "아";
output.incorrect[0] = buildhangeul({
lead: lastjamo.lead,
vowel: lastjamo.vowel,
tail: "0"
}) + buildhangeul({
lead: lastjamo.tail,
vowel: "ㅏ",
tail: "0"
});
output.incorrect[1] = stemlast + "ㅏ";
output.incorrect[2] = stemlast;
} else if (lastjamo.vowel === "ㅓ" && lastjamo.tail !== "0" || lastjamo.vowel === "ㅜ" && lastjamo.tail !== "0" || lastjamo.vowel === "ㅣ" && lastjamo.tail !== "0") {
//ㅓ, ㅜ, and ㅣ regular
output.correct = stemlast + "어";
output.incorrect[0] = buildhangeul({
lead: lastjamo.lead,
vowel: lastjamo.vowel,
tail: "0"
}) + buildhangeul({
lead: lastjamo.tail,
vowel: "ㅓ",
tail: "0"
});
output.incorrect[1] = stemlast + "ㅓ";
output.incorrect[2] = stemlast;
}
//ㅏ and ㅓ reductive
else if (lastjamo.vowel === "ㅏ" && lastjamo.tail === "0" || lastjamo.vowel === "ㅓ" && lastjamo.tail === "0") {
output.correct = stemlast;
output.incorrect[0] = stemlast + buildhangeul({
lead: "ㅇ",
vowel: lastjamo.vowel,
tail: "0"
});
output.incorrect[1] = buildhangeul({
lead: lastjamo.lead,
vowel: "ㅐ",
tail: "0"
});
output.incorrect[2] = base;
}
//ㅗ and ㅜ reductive
else if (lastjamo.vowel === "ㅗ" && lastjamo.tail === "0") {
lastjamo.vowel = "ㅘ";
output.correct = buildhangeul(lastjamo);
output.incorrect[0] = stemlast + "와";
output.incorrect[1] = stemlast + "아";
output.incorrect[2] = buildhangeul({
lead: lastjamo.lead,
vowel: "ㅝ",
tail: "0"
});
} else if (lastjamo.vowel === "ㅜ" && lastjamo.tail === "0") {
lastjamo.vowel = "ㅝ";
output.correct = buildhangeul(lastjamo);
output.incorrect[0] = stemlast + buildhangeul({
lead: "ㅇ",
vowel: "ㅝ",
tail: "0"
});
output.incorrect[1] = buildhangeul({
lead: lastjamo.lead,
vowel: "ㅜ",
tail: "0"
}) + buildhangeul({
lead: "ㅇ",
vowel: "ㅓ",
tail: "0"
});
output.incorrect[2] = buildhangeul({
lead: lastjamo.lead,
vowel: "ㅘ",
tail: "0"
});
}
//ㅣreductive
else if (lastjamo.vowel === "ㅣ" && lastjamo.tail === "0") {
lastjamo.vowel = "ㅕ";
output.correct = each.slice(0, -2).join("") + buildhangeul(lastjamo);
}
//르 irregular
else if (stemlast === "르") {
var secondlastjamo = seperate(each[each.length - 3]);
secondlastjamo.tail = "ㄹ";
if (secondlastjamo.vowel === "ㅗ") {
stemlast = "라";
} else if (secondlastjamo.vowel === "ㅜ") {
stemlast = "러";
}
var secondlast = buildhangeul(secondlastjamo);
output.correct = each.slice(0, -3).join("") + secondlast + stemlast;
}
} else {
alert("Only Korean verbs in dictionary form please ;)");
}
return output;
}
$('.answer').click(function() { //click answer
//wait for tutorial to end
if (currentvideo.videoobject.currentTime <= currentvideo.firstpause) {
$("#warning").text("Not yet!").fadeIn(300).delay(1400).fadeOut(300);
return
}
if ($(this).text() === currentset.conjugations.correct) {
//if correct
//skip to congratulations
$(this).css("background-color", "#62F05F");
questionnum++;
if (questionnum === 1) {
currentvideo.start(currentvideo.firstcongrats);
currentvideo.pause(currentvideo.firstcongratspause);
} else if (questionnum === 2) {
currentvideo.start(currentvideo.secondcongrats);
currentvideo.pause(currentvideo.secondcongratspause);
} else if (questionnum === 3) {
currentvideo.start(currentvideo.thirdcongrats);
questionnum = 0;
}
getnewset();
currentset.conjugations = conjugate(currentset.baseverb);
setTimeout(function() {
render(currentset);
}, 2000);
//if incorrect
} else {
//skip to try again msg
$(this).css("background-color", "red");
currentvideo.start(currentvideo.tryagain);
currentvideo.pause(currentvideo.tryagainpause);
}
});
$("#reset").click(function() {
currentvideo.videoobject.currentTime = 0;
currentvideo.start();
});
currentTutorial = new Buildtutorial(tutorial_number);
render_menu(currentTutorial);
currentvideo = new Buildvideo(videos[tutorial_number]);
getnewset();
currentvideo.load();
currentvideo.videoobject.play();
currentvideo.pause(currentvideo.firstpause);
currentset.conjugations = conjugate(currentset.baseverb);
render(currentset);
});