remember/sims/leitner/leitner.js

544 lines
11 KiB
JavaScript
Raw Normal View History

2018-09-18 17:17:42 +00:00
/*******************************
// Step 1: a bunch of numbered, colored boxes (array) @done
// Step 2: a calendar @done
// Step 3: "cards" go up/down on STEP/day @done
// Step 4: ...with 5% failing @done
Next steps:
- Per DAY, now. @done
- labels for steps @done
- labels for day @done
Animation
- cards number @done
- box bounce @done
- multiple times... @done
- arrows for movement of cards
- + for adding cards
// Arrowheads @done
// TOTAL CARDS say...
CALCULATOR... nah.
********************************/
window.onload = function(){
2018-09-26 19:59:45 +00:00
2018-09-18 17:17:42 +00:00
BOXES[0] += NEW_CARDS;
_newStep();
update();
2018-09-26 19:59:45 +00:00
// Get Mode
window.MODE = parseInt( _getQueryVariable("mode") );
if(MODE==2){
$("#MODE2_time").style.display = "inline-block";
//$("#MODE2_sliders").style.display = "block";
}
2018-10-17 15:37:43 +00:00
// POINTY
_addPointy(MODE==2);
2018-09-18 17:17:42 +00:00
};
//////////////////////////////////////////
//////////////////////////////////////////
/***************
Each day...
a. Review levels from top to bottom
(if succeed, next level)
(if fail, back to Level 1)
b. All cards in Level 1 go to Level 2
c. Add new cards to Level 1
****************/
var NEW_CARDS = 10;
2018-09-26 19:59:45 +00:00
var CARDS_WRONG = 0.05;
2018-09-18 17:17:42 +00:00
var _STAGE = 0;
// 0 - new day
// 1 - reviewing
// 2 - adding
2018-09-26 19:59:45 +00:00
// Button Labels
$("#next_step").innerHTML = _getLabel("leitner_button_next_step");
$("#next_day").innerHTML = _getLabel("leitner_button_next_day");
$("#next_week").innerHTML = _getLabel("leitner_button_next_week");
$("#next_month").innerHTML = _getLabel("leitner_button_next_month");
$("#reset").innerHTML = _getLabel("leitner_reset");
// RESET ALL
$("#reset").onclick = function(){
BOXES = [
0,0,0,0,
0,0,0,0,
];
DAY = 0;
//ANIM_CARDS = BOXES.concat(); // clone
//ANIM_BOXES = BOXES.concat(); // clone
CURRENTLY_REVIEWED = -1;
QUEUE = [];
BOXES[0] += NEW_CARDS;
_STAGE = 0;
_newStep();
//update();
2018-10-17 20:09:50 +00:00
//_killPointy();
if(window.pointy) window.pointy.kill();
playSound("reset");
2018-10-17 15:37:43 +00:00
2018-09-26 19:59:45 +00:00
};
// UI Sliders
var slider_new = $("#slider_new");
slider_new.oninput = function(){
NEW_CARDS = parseInt(slider_new.value);
$("#slider_new_label").innerHTML = _getLabel("leitner_slider_new").replace("[N]",NEW_CARDS);
};
slider_new.oninput();
var slider_wrong = $("#slider_wrong");
slider_wrong.oninput = function(){
CARDS_WRONG = parseFloat(slider_wrong.value);
$("#slider_wrong_label").innerHTML = _getLabel("leitner_slider_wrong").replace("[N]",Math.round(CARDS_WRONG*100));
};
slider_wrong.oninput();
2018-10-17 20:09:50 +00:00
// Sliders have SOUNDS
slider_new.onmousedown = slider_new.ontouchstart = function(){
playSound("slider_down");
};
slider_new.onmouseup = slider_new.ontouchend = function(){
playSound("slider_up");
};
slider_wrong.onmousedown = slider_wrong.ontouchstart = function(){
playSound("slider_down");
};
slider_wrong.onmouseup = slider_wrong.ontouchend = function(){
playSound("slider_up");
};
var boop = -1;
function alternateBoops(){
boop++;
if(boop%2==0){
playSound("button_down");
}else{
playSound("button_up");
}
}
2018-09-26 19:59:45 +00:00
2018-09-18 17:17:42 +00:00
function _updateLabels(){
// Step
var html;
switch(_STAGE){
case 2:
html = _getLabel("leitner_step_new").replace("[N]",NEW_CARDS);
break;
case 0:
html = _getLabel("leitner_step_to_review") + QUEUE.toString();
break;
case 1:
html = _getLabel("leitner_step_reviewing").replace("[N]",CURRENTLY_REVIEWED+1);
break;
}
$("#label_step").innerHTML = html;
// Day
$("#label_day").innerHTML = _getLabel("leitner_day").replace("[N]",DAY);
// Stats
var sum = 0;
var vlt = 0;
BOXES.forEach(function(n, i){
sum += n;
if(i>=7) vlt += n; // Level 6 and up
});
html = _getLabel("leitner_step_stats").replace("[N]", sum);
if(vlt>0){
html += _getLabel("leitner_step_stats_2").replace("[N]", vlt);
}
$("#label_stats").innerHTML = html;
}
$("#next_step").onclick = function(){
_newStep();
2018-10-17 15:37:43 +00:00
_killPointy();
2018-09-18 17:17:42 +00:00
};
function _newStep(skipLabels){
// If queue's empty, start a new day!
if(QUEUE.length==0){
if(_STAGE==1){
_STAGE = 2; // add new cards
BOXES[0] += NEW_CARDS;
// Annotate
_clearAnnotations();
_annotateAdd(NEW_CARDS);
}else{
_newDay();
_STAGE = 0; // new day!
_clearAnnotations();
}
CURRENTLY_REVIEWED = -1;
}else{
// review
_STAGE = 1;
// But if not, pop off the queue.
var reviewedLevel = QUEUE.shift();
var rIndex = reviewedLevel-1;
CURRENTLY_REVIEWED = rIndex;
// Is this Level 1?
var total, passed, failed;
total = BOXES[rIndex];
if(reviewedLevel==1){
// ALL goes to Level 2
passed = total;
failed = 0;
}else{
// 95% goes to next level
// the rest goes to ONE
2018-09-26 19:59:45 +00:00
passed = Math.round(total*(1-CARDS_WRONG));
2018-09-18 17:17:42 +00:00
failed = total-passed;
}
BOXES[rIndex+1] += passed;
BOXES[0] += failed;
BOXES[rIndex] = 0;
// Annotations
_clearAnnotations();
_annotatePass(rIndex, passed);
_annotateFail(rIndex, failed);
}
// Label
if(!skipLabels) _updateLabels();
window.REDRAW = 60;
};
$("#next_day").onclick = function(){
_newDay();
_updateLabels();
2018-10-17 15:37:43 +00:00
_killPointy();
2018-09-18 17:17:42 +00:00
};
function _newDay(skipLabels){
// Any previous stuff in queue? Finish it!
if(QUEUE.length>0){
while(QUEUE.length>0 || _STAGE==1){
_newStep(true); // until queue's done AND past "adding new cards"
}
}
// Increase day, add stuff to queue
DAY++;
var d = (DAY-1)%CALENDAR.length; // -1 for offset, also loop around.
QUEUE = QUEUE.concat(CALENDAR[d]); // to clone it.
// Redraw
window.REDRAW = 60;
// Label
_STAGE = 0; // new day!
_clearAnnotations();
if(!skipLabels) _updateLabels();
};
function _reviewMultipleDays(days){
for(var i=0;i<days;i++) _newDay();
_updateLabels()
}
$("#next_week").onclick = function(){
_reviewMultipleDays(7);
2018-10-17 15:37:43 +00:00
_killPointy();
2018-09-18 17:17:42 +00:00
};
$("#next_month").onclick = function(){
_reviewMultipleDays(30);
2018-10-17 15:37:43 +00:00
_killPointy();
2018-09-18 17:17:42 +00:00
};
//////////////////////////////////////////
//////////////////////////////////////////
var canvas = document.getElementById("sim");
var ctx = canvas.getContext('2d');
var COLORS = [
"#ee4035", // red
"#f37736", // orange
"#ffdb13", // yellow
"#7bc043", // green
"#0392cf", // blue
"#673888", // indigo-ish
"#ef4f91", // violet-ish
2018-09-26 16:39:24 +00:00
"#e0e0e0" // white. VALHALLA.
2018-09-18 17:17:42 +00:00
];
var BOXES = [
0,0,0,0,
0,0,0,0,
];
var ANIM_CARDS = BOXES.concat(); // clone
var ANIM_BOXES = BOXES.concat(); // clone
var CURRENTLY_REVIEWED = -1;
var ANIM_EASE = 0.8;
var DAY = 0;
var QUEUE = [];
var CALENDAR = [
[2,1], [3,1], [2,1], [4,1], [2,1], [3,1], [2,1], [1],
[2,1], [3,1], [2,1], [5,1], [4,2,1], [3,1], [2,1], [1],
[2,1], [3,1], [2,1], [4,1], [2,1], [3,1], [2,1], [6,1],
[2,1], [3,1], [2,1], [5,1], [4,2,1], [3,1], [2,1], [1],
[2,1], [3,1], [2,1], [4,1], [2,1], [3,1], [2,1], [1],
[2,1], [3,1], [2,1], [5,1], [4,2,1], [3,1], [2,1], [1],
[2,1], [3,1], [2,1], [4,1], [2,1], [3,1], [2,1], [7,1],
[2,1], [3,1], [6,2,1],[5,1], [4,2,1], [3,1], [2,1], [1],
];
function _calculateCardLabelPosition(boxN){
var x = 0;
var y = 0;
x += boxN*62.5;
y += canvas.height/2 - 60;
y += -ANIM_BOXES[boxN];
y += -ANIM_CARDS[boxN]/3;
x += 60/2;
y -= 13;
if(y<50) y=50;
return {
x:x,
y:y
};
}
var annPass = null;
var annFail = null;
var annAdd = null;
function _clearAnnotations(){
annPass = null;
annFail = null;
annAdd = null;
};
function _annotatePass(from, N){
if(N>0) annPass = {from:from, N:N};
}
function _annotateFail(from, N){
if(N>0) annFail = {from:from, N:N};
}
function _annotateAdd(N){
annAdd = {N:N};
}
window.REDRAW = 0;
function update(){
// Don't re-draw unnecessarily!
if(window.REDRAW>0){
window.REDRAW -= 1;
// Clear & Retina
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
ctx.save();
ctx.scale(2,2);
// Draw boxes, 1 to 7
for(var i=0;i<BOXES.length;i++){
var w = 60;
var h = 60;
// ANIMATED!
ANIM_CARDS[i] = ANIM_CARDS[i]*ANIM_EASE + BOXES[i]*(1-ANIM_EASE);
// Card labels...
ctx.fillStyle = "#bbb";
ctx.font = "20px PatrickHand";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
var pos = _calculateCardLabelPosition(i);
ctx.fillText(Math.round(ANIM_CARDS[i]), pos.x, pos.y);
// Transform...
ctx.save();
ctx.translate(i*62.5, canvas.height/2-h);
// Bounce! ANIMATED!
var elevation = (CURRENTLY_REVIEWED==i) ? 10 : 0;
ANIM_BOXES[i] = ANIM_BOXES[i]*ANIM_EASE + elevation*(1-ANIM_EASE);
ctx.translate(0, -ANIM_BOXES[i]);
// The cards inside me
var cardsHeight = ANIM_CARDS[i]/3;
ctx.save();
ctx.translate(0, -cardsHeight);
ctx.fillStyle = "rgba(0,0,0,0.2)";
ctx.fillRect(5, 0, w-10, cardsHeight);
ctx.restore();
// Am I active today?
var activeToday = (QUEUE.indexOf(i+1)>=0); // +1 coz offset
if(CURRENTLY_REVIEWED==i) activeToday=true; // also if CURRENTLY reviewed...
var color = COLORS[i];
// Fill
ctx.fillStyle = activeToday ? color : "#fff";
ctx.fillRect(0, 0, 60, 60);
// Stroke
if(!activeToday){
ctx.strokeStyle = color;
var lw = 2;
ctx.lineWidth = lw;
ctx.beginPath();
ctx.rect(lw/2, lw/2, w-lw, h-lw);
ctx.stroke();
}
// Number
ctx.font = "50px PatrickHand";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = activeToday ? "#fff": color;
2018-10-25 20:20:30 +00:00
var num = i<7 ? (i+1) : "👼"; // 8 = RETIRED.
ctx.fillText(num, w/2, h/2);
2018-09-18 17:17:42 +00:00
ctx.restore();
}
// Draw Annotations!
if(annFail){
_drawArrow(true);
}
if(annPass){
_drawArrow();
}
if(annAdd){
// The number...
ctx.fillStyle = "#000";
ctx.font = "18px PatrickHand";
ctx.textAlign = "center";
ctx.textBaseline = "bottom";
var label = "+" + annAdd.N;
var pos = _calculateCardLabelPosition(0);
ctx.fillText(label, pos.x, pos.y-15);
}
// And, again...
ctx.restore();
}
requestAnimationFrame(update);
}
function _drawArrow(fail){
var ann = fail ? annFail : annPass;
// Points
var pos1 = _calculateCardLabelPosition(ann.from);
var pos2 = _calculateCardLabelPosition(fail ? 0 : ann.from+1);
var xOffset = fail ? -5 : 5;
pos1.x += xOffset;
pos1.y -= 15;
pos2.x -= xOffset;
pos2.y -= 15;
var cp = {
x: (pos1.x+pos2.x)/2,
y: Math.min(pos1.y,pos2.y)-30
};
// Color!
var color = fail ? COLORS[0] : COLORS[3]; // red/green
// Arrow
ctx.beginPath();
ctx.moveTo(pos1.x, pos1.y);
ctx.quadraticCurveTo(cp.x, cp.y, pos2.x, pos2.y);
ctx.strokeStyle = color;
var w = ann.N/10;
if(w<1) w=1;
if(w>7) w=7;
ctx.lineWidth = w;
ctx.stroke();
// ArrowHEAD
ctx.save();
ctx.translate(pos2.x, pos2.y);
var dy = pos2.y - cp.y;
var dx = pos2.x - cp.x;
var rotation = Math.atan2(dy,dx);
ctx.rotate(rotation-Math.PI/2);
ctx.beginPath();
var arrSize = w*1.5;
if(arrSize<5) arrSize=5;
ctx.moveTo(-arrSize,-arrSize);
ctx.lineTo(0,0);
ctx.lineTo(arrSize,-arrSize);
ctx.stroke();
ctx.restore();
// The number...
ctx.fillStyle = color;
ctx.font = "18px PatrickHand";
ctx.textAlign = "center";
ctx.textBaseline = "bottom";
var label = (fail ? "-" : "+") + ann.N;
ctx.fillText(label, cp.x, cp.y+15);
}
2018-10-17 15:37:43 +00:00
//////////////////////////////////////////
//////////////////////////////////////////
function _addPointy(mode2){
window.pointy = new createAnimatedUIHelper({
x: mode2 ? 380 : 120,
y: 50,
width: 100,
height: 100,
img: "../../pics/ui_point.png"
});
}
function _killPointy(){
if(window.pointy) window.pointy.kill();
2018-10-17 20:09:50 +00:00
alternateBoops();
2018-10-17 15:37:43 +00:00
}