544 lines
No EOL
11 KiB
JavaScript
544 lines
No EOL
11 KiB
JavaScript
/*******************************
|
|
|
|
// 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(){
|
|
|
|
BOXES[0] += NEW_CARDS;
|
|
_newStep();
|
|
update();
|
|
|
|
// Get Mode
|
|
window.MODE = parseInt( _getQueryVariable("mode") );
|
|
if(MODE==2){
|
|
$("#MODE2_time").style.display = "inline-block";
|
|
//$("#MODE2_sliders").style.display = "block";
|
|
}
|
|
|
|
// POINTY
|
|
_addPointy(MODE==2);
|
|
|
|
};
|
|
|
|
//////////////////////////////////////////
|
|
//////////////////////////////////////////
|
|
|
|
/***************
|
|
|
|
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;
|
|
var CARDS_WRONG = 0.05;
|
|
var _STAGE = 0;
|
|
// 0 - new day
|
|
// 1 - reviewing
|
|
// 2 - adding
|
|
|
|
// 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();
|
|
|
|
//_killPointy();
|
|
if(window.pointy) window.pointy.kill();
|
|
playSound("reset");
|
|
|
|
};
|
|
|
|
// 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();
|
|
|
|
// 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");
|
|
}
|
|
}
|
|
|
|
|
|
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();
|
|
_killPointy();
|
|
};
|
|
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
|
|
passed = Math.round(total*(1-CARDS_WRONG));
|
|
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();
|
|
_killPointy();
|
|
};
|
|
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);
|
|
_killPointy();
|
|
};
|
|
$("#next_month").onclick = function(){
|
|
_reviewMultipleDays(30);
|
|
_killPointy();
|
|
};
|
|
|
|
//////////////////////////////////////////
|
|
//////////////////////////////////////////
|
|
|
|
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
|
|
"#e0e0e0" // white. VALHALLA.
|
|
];
|
|
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;
|
|
var num = i<7 ? (i+1) : "👼"; // 8 = RETIRED.
|
|
ctx.fillText(num, w/2, h/2);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
//////////////////////////////////////////
|
|
//////////////////////////////////////////
|
|
|
|
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();
|
|
alternateBoops();
|
|
} |