Angular app for learning single-digit numbers addition. Numbers for the exercise are randomly generated. First is in range [6,9], numbers sum is in range [11,14]. User can see a scale, which visualizes the task. User can input digits. If first number is correct, its input field is blocked, second input is shown to user. If second number is correct, its input field is blocked, result input is unlocked. If result is correct, user is congratulated and offered to try again. Upon wrong number input its color turns red, and correct number is highlighted in equation on the top of the page.
Last active
April 22, 2017 15:48
-
-
Save rfprod/1a157737b8cbed2b4326e9a79ec46f16 to your computer and use it in GitHub Desktop.
Learn single-digit numbers addition
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html > | |
<body ng-app="learningApp"> | |
<a id="gist" class="btn" href="#" target=_blank><span class="glyphicon glyphicon-download-alt" ></span> GIST</a> | |
<div ng-controller="exercise1Ctrl" class="wide" load-data> | |
<h1 class="container">{{title}}</h1> | |
<div id="task-details" class="col-xs-12 col-sm-12 col-md-12 col-lg-12"></div> | |
<span us-spinner spinner-key="spinner-1"></span> | |
</div> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var app = angular.module('learningApp',['angularSpinner']); | |
app.controller('exercise1Ctrl', ($scope, $window, usSpinnerService) => { | |
$scope.title = 'Learn single-digit numbers addition'; | |
$scope.scale = 20; | |
$scope.a = Math.floor(Math.random()*(9-6+1) + 6); | |
$scope.b = Math.floor(Math.random()*((14-$scope.a)-(11-$scope.a)+1) + (11-$scope.a)); | |
$scope.xCoord = []; | |
$scope.yModifier = 100; | |
$scope.textCoord = []; | |
$scope.yCoordRegular = [20,30]; | |
$scope.yCoordBold = [15,35]; | |
$scope.arc1InputCoord = 0; | |
$scope.arc2InputCoord = 0; | |
$scope.svgDefArrow = '<defs><marker id="triangle" viewBox="0 0 10 10" refX="1" refY="5" markerWidth="4" markerHeight="6" orient="auto"><path d="M2,2 L2,11 L10,6 L2,2" style="fill: red;" /></marker></defs>'; | |
$scope.refreshPage = () => { | |
window.location.reload(false); | |
}; | |
$scope.resized = false; | |
$scope.windowResized = () => { | |
$scope.$apply(() => {$scope.resized = true;}); | |
usSpinnerService.spin('spinner-1'); | |
setTimeout(() => {$scope.initResize();},750); | |
}; | |
$scope.initResize = () => { | |
$scope.$apply(() => { | |
$scope.resized = false; | |
}); | |
//usSpinnerService.stop('spinner-1'); | |
$scope.refreshPage(); | |
}; | |
}); | |
app.directive("loadData", ($compile) => { | |
return (scope, el, attrs) => { | |
$('#task-details').html($compile('<div id="equation" class="well well-sm"><span id="dialog"></span><h4><span id="first-item">'+scope.a+'</span> + <span id="second-item">'+scope.b+'</span> = <input type="text" id="result-input" class="form-control" placeholder="?" readonly check-result/></h4></div>')(scope)); | |
var rulerMarks = '', rulerText = ''; | |
for (var i=0; i<=scope.scale; i++){ | |
scope.xCoord.push(10+(i*$('#task-details').width()/21)); | |
if (i==0 || i==5 || i==10 || i==15 || i==20) rulerMarks += '<line class="ruler-mark" x1="'+scope.xCoord[i]+'" y1="'+(scope.yCoordBold[0]+scope.yModifier)+'" x2="'+scope.xCoord[i]+'" y2="'+(scope.yCoordBold[1]+scope.yModifier)+'" />'; | |
else rulerMarks += '<line class="ruler-mark" x1="'+scope.xCoord[i]+'" y1="'+(scope.yCoordRegular[0]+scope.yModifier)+'" x2="'+scope.xCoord[i]+'" y2="'+(scope.yCoordRegular[1]+scope.yModifier)+'" />'; | |
if (i<10) scope.textCoord.push(scope.xCoord[i]-3); | |
else scope.textCoord.push(scope.xCoord[i]-7); | |
if (i==0 || i==5 || i==10 || i==15 || i==20) rulerText += '<text x="'+scope.textCoord[i]+'" y="'+(47+scope.yModifier)+'" fill="black" class="bold-text">'+i+'</text>'; | |
else rulerText += '<text x="'+scope.textCoord[i]+'" y="'+(47+scope.yModifier)+'" fill="black" class="common-text">'+i+'</text>'; | |
} | |
// arc 1 | |
var arc1 = '<path id="arc1" d="M 10,'+(10+scope.yModifier)+' q '+((scope.textCoord[scope.a]-7)/2)+' -150 '+(scope.textCoord[scope.a]-12.5)+' 0" stroke="blue" stroke-width="3" fill="none" marker-end="url(#triangle)" />'; | |
scope.arc1InputCoord = scope.textCoord[Math.floor(scope.a/2)]-7; | |
if (scope.a%2 == 0) scope.arc1InputCoord = scope.arc1InputCoord-13; | |
else if ($(window).width() > 1000) scope.arc1InputCoord = scope.arc1InputCoord+($(window).width()/77); | |
else if ($(window).width() < 1000) scope.arc1InputCoord = scope.arc1InputCoord+($(window).width()/130); | |
var arc1Input = '<foreignObject x="'+scope.arc1InputCoord+'" y="0" width="50" height="50"><input class="form-control arc-input" id="arc1-input" type="text" check-first-value/></foreignObject>'; | |
// arc 2 | |
var arc2 = '<path id="arc2" d="M '+(scope.textCoord[scope.a]+2)+','+(10+scope.yModifier)+' q '+((scope.textCoord[scope.b])/2)+' -150 '+(scope.textCoord[scope.b]-9.5)+' 0" stroke="transparent" stroke-width="3" fill="none" />'; | |
scope.arc2InputCoord = scope.textCoord[Math.floor(scope.b/2)]-15+scope.textCoord[scope.a]; | |
if (scope.b%2 == 0) scope.arc2InputCoord = scope.arc2InputCoord-18; | |
else if ($(window).width() > 1000) scope.arc2InputCoord = scope.arc2InputCoord+($(window).width()/77); | |
else if ($(window).width() < 1000) scope.arc2InputCoord = scope.arc2InputCoord+($(window).width()/130); | |
var arc2Input = '<foreignObject x="'+scope.arc2InputCoord+'" y="0" width="50" height="50"><input class="form-control arc-input" id="arc2-input" type="text" check-second-value/></foreignObject>'; | |
// svg output | |
$('#task-details').append($compile('<svg id="scale">'+scope.svgDefArrow+'<rect width="100%" y="'+scope.yModifier+'" height="50" class="ruler"/><line class="axis" x1="0" y1="'+(25+scope.yModifier)+'" x2="100%" y2="'+(25+scope.yModifier)+'"/>'+rulerMarks+rulerText+arc1Input+arc1+arc2Input+arc2+'</svg>')(scope)); | |
$('#arc1-input').focus(); | |
scope.resized = false; | |
$(window).on('resize',() => { | |
scope.windowResized(); | |
}); | |
} | |
}); | |
app.directive('checkFirstValue', ($compile) => { | |
return (scope, el, attrs) => { | |
el.bind('input', () => { | |
if (scope.a == el.val()) { | |
el.css('color','green'); | |
el.attr('readonly','readonly'); | |
$('#arc2').css('stroke','blue'); | |
$('#arc2').css("marker-end","url(#triangle)"); | |
$('#arc2-input').css('display','block'); | |
$('#arc2-input').focus(); | |
if ($('#first-item').hasClass('highlighted')) $('#first-item').removeClass('highlighted'); | |
}else{ | |
el.css('color','rgba(255,0,0,1)'); | |
$('#first-item').addClass('highlighted'); | |
} | |
}); | |
} | |
}); | |
app.directive('checkSecondValue', ($compile) => { | |
return (scope, el, attrs) => { | |
el.bind('input',() => { | |
if (scope.b == el.val()) { | |
el.css('color','green'); | |
el.attr('readonly','readonly'); | |
$('#result-input').removeAttr('readonly'); | |
$('#result-input').attr('placeholder',''); | |
$('#result-input').focus(); | |
if ($('#second-item').hasClass('highlighted')) $('#second-item').removeClass('highlighted'); | |
}else { | |
el.css('color','rgba(255,0,0,1)'); | |
$('#second-item').addClass('highlighted'); | |
} | |
}); | |
} | |
}); | |
app.directive('checkResult',($compile) => { | |
return (scope, el, attrs) => { | |
el.bind('input', () => { | |
if ((scope.a + scope.b) == el.val()) { | |
el.css('color','green'); | |
el.attr('readonly','readonly'); | |
el.blur(); | |
$('#dialog').html($compile('<span class="glyphicon glyphicon-thumbs-up"></span> Congratulations! Your answer is correct. <button type="button" class="btn btn-info" ng-click="refreshPage()">Try Again</button>')(scope)); | |
}else el.css('color','rgba(255,0,0,1)'); | |
}); | |
} | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script> | |
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.2/angular.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/spin.js/2.3.2/spin.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-spinner/0.8.1/angular-spinner.min.js"></script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body | |
#gist | |
position: absolute | |
top: 0 | |
right: 0 | |
h1 | |
text-align: center | |
.wide | |
width: 100% | |
a:hover | |
text-decoration: none | |
#task-details | |
#equation | |
text-align: center | |
#dialog | |
font-weight: bold | |
#result-input | |
display: inline-block | |
width: 50px | |
text-align: center | |
font-weight: bold | |
.highlighted | |
background-color: rgba(240,183,0,0.8) | |
#scale | |
width: 100% | |
height: 50vh | |
.ruler | |
fill: rgba(240,230,140,0.8) | |
.axis | |
stroke: rgba(254,0,0,1) | |
stroke-width: 1 | |
.ruler-mark | |
stroke: rgba(254,0,0,1) | |
stroke-width: 1 | |
.bold-text | |
font-weight: bold | |
.common-text | |
font-size: 0.85em | |
.arc-input | |
width: 100% | |
text-align: center | |
font-weight: bold | |
#arc2-input | |
display: none |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" /> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment