Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ryan778
GitHub Repository: Ryan778/Ryan778.github.io
Path: blob/master/final-grade-calculator/main.js
574 views
1
/* main.js
2
* Functions and scripts specific to FGC page (rendering, handlers, etc)
3
* (C) 2019 Ryan Zhang. All Rights Reserved.
4
* Interested in anything on here? Contact me at https://ryan778.github.io/about-me/ and we can discuss.
5
*/
6
7
let globalData = {// Contains all information inputted, makes life easier
8
gradeType: 0, // 0=weighted, 1=unweighted
9
examType: 0, // 0=final, 1=test
10
unequalTests: 0, // whether tests are weighted equally or not
11
testPolicy: 0, // 0=none, 1=drop lowest, 2=make up difference, 3=make up half of difference
12
calcType: 0, // 0=see what you need, 1=already took test, 2=auto detect
13
currentGrade: -1,
14
currentGradePts: -1, // for unweighted
15
currentGradeTotalPts: -1, // also for unweighted
16
finalWorth: -1, // weight of final (weighted)
17
finalWorthPts: -1, // weight of final (unweighted)
18
targetGrade: -1,
19
finalGrade: -1,
20
equalTestWeight: 1, // 0=no (use totalTestScore), 1=yes (use totalTests)
21
testWorth: -1, // weight of test (or points)
22
testAvg: -1, // average grade across all tests
23
totalTests: -1,
24
testCatPts: -1,
25
testCatTotalPts: -1,
26
lowestTest: -1, // lowest test
27
curvePolicy: 0, // 0=none, 1=to highest test, 2=square root, 3=nth root
28
curveTo: -1, // highest score (assuming scores curve to this score)
29
curveStrength: -1 // for nth root curves
30
};
31
32
let alertMessages = {
33
'weight': `<b>Grade Weighting</b><br>Most colleges and many high school classes use a <b>weighted</b> grading system so that different assignments have different "weights" (e.g. final, tests, and homework). If your grade has any kind of categories, then you have a weighted grading system. <br><br>Some classes give point values to assignments instead, usually with the final being worth a lot more than say, homework. If that's your case, then you have an <b>unweighted</b> grading system.`,
34
'examType': `<b>Exam Type</b><br>If your final has its own category (most common for weighted classes, default on RogerHub), select <b>Final</b>.<br>If you're taking a test/exam, select <b>Test/Exam</b>.<br>If you're taking a final but the final is counted as a test/exam (less common, but some classes do this), also select <b>Test/Exam</b>.<br>The difference between the two is that selecting "final" assumes your "final" exam goes into its own category, whereas "test/exam" assumes there's already other tests/exams in the category.`,
35
'calcTypes': `<b>Grade Calculation Types</b><br>First of all, there's two main categories for weighted calculations - tests and finals. A final is put <b>into its own category</b> for weighing, whereas a test is put <b>with other tests</b>. <br><br>If you already took the test/final, you'll need to select an option that begins with "<b>I took a...</b>". Otherwise, the calculator will tell you how much you need to get on the test/final in order to <b>maintain a specified grade</b>.`,
36
'equalTests': `<b>Equal Test Weights</b><br>If each test is worth the same number of points (e.g., 100 pts per test), then select "yes". If each test is put in as a different number of points (e.g., one test is worth 30 pts, another is 35 pts, etc.) then select "no".<br/><br/>Note that "Test/Exam" does not have to be used on tests; it can be used for any category where you already have tests/assignments. Simply select "no" and enter the point total of the category you want to use grade calculator on.`,
37
'legal': `<b>Legal</b><br/>&copy; 2019 Ryan Zhang.<br/>core.js is written by me and is used for grade calculations. It's licensed under <a href='https://www.gnu.org/licenses/gpl-3.0.en.html' target='_blank'>GPLv3</a>.</br>Everything else unlicensed for the time being (albeit being open source); <a href='https://ryan778.github.io/about-me/'>contact me</a> if interested.`
38
}
39
40
let testAdjInfo = [`Your lowest test grade will be completely removed.`, `If your grade on the final is better than your lowest test, it'll replace your lowest test grade.`, `You'll get half the difference (if applicable) between your lowest test grade and the final.`];
41
42
let testReqInputs = ['finalWorth']; // required inputs tied to test adjustment and/or "test/exam" input requirements
43
44
function validateInput(val, type){
45
//Returns object {st: number, val: number, msg: string}; status 0=success, 1=warn, 2=error
46
if(typeof val !== 'string'){return {st: 2, msg: `An error occured and it's not your fault (report this as a bug!) - Invalid Data Type (${typeof val})`}}
47
switch(type){
48
case 'grd': //Grade (Accepts percents and letters)
49
if(parseFloat(val).toString() === val || parseFloat(val).toString() === val.slice(0, val.length-1) && val.slice(-1) === '0'){
50
if(parseFloat(val) > 150){
51
return {st: 1, val: parseFloat(val), msg: 'This looks rather high. Is there a typo?'};
52
}
53
else if(parseFloat(val) < 15){
54
return {st: 1, val: parseFloat(val), msg: 'This looks rather low. Is there a typo?'};
55
}
56
return {st: 0, val: parseFloat(val)}
57
}
58
else if(convToGrade(val) !== -1){
59
return {st: 0, val: convToGrade(val), msg: `<span class='nt-hidden-ltGrd'></span>Using ${val.toUpperCase()} as ${convToGrade(val)}%`}
60
}
61
return {st: 2, msg: `Invalid input (is it a number/letter grade?)`}
62
case 'weight': //Weight value (ex: 20%)
63
if(parseFloat(val).toString() === val){
64
if(parseFloat(val) > 65){
65
return {st: 1, val: parseFloat(val), msg: 'This looks rather high. Is there a typo?'};
66
}
67
else if(parseFloat(val) < 5){
68
return {st: 1, val: parseFloat(val), msg: 'This looks rather low. Is there a typo?'};
69
}
70
return {st: 0, val: parseFloat(val)}
71
}
72
return {st: 2, msg: `Invalid input (is it a number?)`}
73
case 'num': // Numerical value (ex: 5)
74
if(parseInt(val).toString() === val){
75
if(parseInt(val) >= 50){
76
return {st: 1, val: parseInt(val), msg: 'This looks rather high. Is there a typo?'};
77
}
78
else if(parseFloat(val) < 1){
79
if(parseFloat(val) < 0){
80
return {st: 2, msg: `You need a non-negative integer here!`}
81
}
82
return {st: 1, val: parseInt(val), msg: 'This looks rather low. Is there a typo?'};
83
}
84
return {st: 0, val: parseInt(val)}
85
}
86
return {st: 2, msg: `Invalid input (is it a whole number?)`}
87
case 'pts': // Points
88
case 'ptsSingle': // Points
89
if(val.match(/^\d*\.?\d*$/)){
90
if(parseFloat(val) >= 4000 || (type==='ptsSingle' && parseFloat(val) > 400)){
91
return {st: 1, val: parseFloat(val), msg: 'This looks rather high. Is there a typo?'};
92
}
93
else if(parseFloat(val) <= 10){
94
return {st: 1, val: parseFloat(val), msg: 'This looks rather low. Is there a typo?'};
95
}
96
return {st: 0, val: parseFloat(val)}
97
}
98
return {st: 2, msg: `Invalid input (is it a decimal number?)`}
99
case 'sp:testAvg': //Special: Test Average - Treated as a number, but is weighted depending on the value entered
100
if(parseFloat(val).toString() === val || parseFloat(val).toString() === val.slice(0, val.length-1) && val.slice(-1) === '0'){
101
if(parseFloat(val) > 150){
102
return {st: 1, val: parseFloat(val), msg: 'This looks rather high. Is there a typo?'}
103
}
104
if(globalData.testWorth === -1){
105
$('#inp_testAvg').val('');
106
return {st: 2, msg: 'Please enter the test weight first!'}
107
}
108
if(parseFloat(val) >= 70 || parseFloat(val) > globalData.testWorth * 1.1){
109
//Use grade as is
110
return {st: 0, val: parseFloat(val), msg: `Using ${parseFloat(val)}% as overall test grade (${(parseFloat(val)*globalData.testWorth/100).toFixed(2)}% weighted)`}
111
}
112
else{
113
//Use grade as weighted to test percentage
114
return {st: 0, val: (10000*parseFloat(val)/globalData.testWorth)/100, msg: `Using ${parseFloat(val)}% weighted as ${(100*parseFloat(val)/globalData.testWorth).toFixed(2)}% overall`}
115
}
116
}
117
else if(convToGrade(val) !== -1){
118
return {st: 0, val: convToGrade(val), msg: `Using ${convToGrade(val)}% as overall test grade (${(parseFloat(val)*globalData.testWorth/100).toFixed(2)}% weighted)`}
119
}
120
return {st: 2, msg: `Invalid input (is it a number/letter grade?)`}
121
case 'root': // similar to case "num", but allows decimals - only used in "what is n" question
122
if(val.match(/^\d*\.?\d*$/)){
123
if(parseFloat(val) >= 5){
124
return {st: 1, val: parseFloat(val), msg: 'This looks rather high. Is there a typo?'};
125
}
126
else if(parseFloat(val) <= 1){
127
return {st: 1, val: parseFloat(val), msg: 'This looks rather low. Is there a typo?'};
128
}
129
return {st: 0, val: parseFloat(val)}
130
}
131
return {st: 2, msg: `Invalid input (is it a decimal number?)`}
132
default:
133
return {st: 2, msg: `An error occured and it's not your fault (report this as a bug!) - <type> inputted is invalid (${type})`}
134
}
135
}
136
137
function setBorderClass(ele, color){
138
//Usage: Remove existing border color to an input field and replace it with another class (input "red", "org", "gr", -1, 0, 1, or 2)
139
if(color === 0){color = 'gr'}
140
else if(color === 1){color = 'org'}
141
else if(color === 2){color = 'red'}
142
$(ele).removeClass('red-b org-b gr-b');
143
if(color === -1){return;}
144
$(ele).addClass(color+'-b');
145
}
146
147
/*function showErrorMsg(prop, msg){
148
//Usage: Show an error message
149
150
}*/
151
152
function setStatusText(eleIn, id, msg){
153
//id: -1=clear, 0=green, 1=orange, 2=red
154
//eleIn is the input box, and not the input-status box
155
//msg is optional
156
let ele = $(eleIn).parent().find('.input-status');
157
ele.removeClass('red org gr');
158
ele.children('span').html(msg);
159
if(!msg){ele.children('span').text('');}
160
switch(id){
161
case 2:
162
ele.addClass('red');
163
ele.children('i').text('error');
164
break;
165
case 1:
166
ele.addClass('org');
167
ele.children('i').text('warning');
168
break;
169
case 0:
170
ele.addClass('gr');
171
ele.children('i').text('check');
172
if(msg){$(eleIn).parent().find('.percSign').text('')}
173
else{$(eleIn).parent().find('.percSign').text('%')}
174
break;
175
default:
176
case -1:
177
$(eleIn).parent().find('.percSign').text('%')
178
ele.children('i').text('');
179
break;
180
}
181
}
182
183
function calcTestMod(){
184
// Uses under {globalData}:
185
// equalTestWeight = 0: totalTestScore, testAvg, lowestTestGrade
186
// equalTestWeight = 1: testWorth, totalTests, testAvg, lowestTestGrade
187
}
188
189
function detPlu(n){
190
// Returns "an" for 8X, returns "a" for any other number
191
if(n >= 80 && n < 90){
192
return 'an'
193
}
194
return 'a'
195
}
196
197
function validateInputs(vars){
198
// Checks [array] and makes sure all elements in [array] are valid, returns an error otherwise
199
// Only works with input fields (ex. not select fields)
200
let valid = true;
201
let possibleTypo = false;
202
vars.forEach((ele => {
203
if(globalData[ele] === -1){
204
if(!$('#inp_'+ele).hasClass('red-b')){
205
setBorderClass('#inp_'+ele, 'red')
206
setStatusText('#inp_'+ele, 2, `This can't be empty.`);
207
}
208
valid = false;
209
}
210
else if ($('#inp_'+ele).hasClass('org-b')){
211
possibleTypo = true}
212
}));
213
if(possibleTypo && valid){return 2}
214
return valid;
215
}
216
217
function getSubtitle(grade){
218
if (grade > 115){
219
return 'Unless your teacher gives that much extra credit, you may have to lower your expectations slightly.'}
220
else if (grade > 100){
221
return 'Maybe there\'s extra credit? Or you can lower your expectations slightly?'}
222
else if(grade >= 95){
223
return "Looks like it might be a bit challenging, but you got this!"}
224
else if(grade >= 90){
225
return "Don't worry too much, you'll do great!"}
226
else if(grade >= 80){
227
return "Looks like it shouldn't be too bad!"}
228
else if(grade >= 70){
229
return "Maybe study just a little bit...?"}
230
else if(grade >= 60) {
231
return globalData.testPolicy===2?"Looks like you'll have no trouble!":"Looks like you'll have no trouble reaching your goal!"}
232
else if(grade >= 0){
233
return globalData.testPolicy===2?"Well that seems pretty easy to do.":"Maybe you could raise your expectations a little?"}
234
else{
235
return "Looks like you don't even have to show up!"}
236
}
237
238
function processCalculations(){
239
let postCurveGrade = 0;
240
$('.calcRes').hide();
241
$('.p-res').hide();
242
$('#res_moreInfo').hide();
243
$('#res_warn-grdAdj').hide();
244
$('#res-warn').hide();
245
if($('.nt-hidden-ltGrd').length > 0){ //Letter grade used somewhere
246
$('#res-warn-gr').show()}
247
else{
248
$('#res-warn-gr').hide()}
249
// let t = globalData.targetGrade, w = globalData.finalWorth, wp = globalData.finalWorthPts, c = globalData.currentGrade, cp = globalData.currentGradePts, ct = globalData.currentGradeTotalPts, f = globalData.finalGrade, r, vald;
250
let f = globalData.finalGrade, c = globalData.currentGrade, t = globalData.targetGrade;
251
if(globalData.gradeType){
252
c = 100 * globalData.currentGradePts / globalData.currentGradeTotalPts}
253
/* if(globalData.gradeType === 1){
254
if(!validateInputs(['currentGradePts', 'currentGradeTotalPts', 'targetGrade', 'finalWorthPts'])){
255
$('#calcErr').show();
256
return}
257
let out = calcTargetGrade(t);
258
r = out[0];
259
$('#res-0').show();
260
$('#res-an-0').text(detPlu(r));
261
$('#res-val-0').text(`${r.toFixed(2)}% (${convToLetter(r)})`);
262
postCurveGrade = r;
263
$('#res-an-0b').text(detPlu(t));
264
$('#res-val-0b').text(`${t.toFixed(2)}% (${convToLetter(t)})`);
265
} */
266
if(globalData.testPolicy > 0){
267
if(globalData.testPolicy === 1){
268
$('#resi').show();
269
$('#resi_a').show()}
270
else{
271
$('#resi_b').show()}
272
if(globalData.testPolicy !== 2){
273
$('#res_moreInfo').show()}
274
}
275
switch(globalData.calcType){
276
case 0:
277
//targetGrade = (finalWorth/100)*whatYouNeed + currentGrade*(1-(finalWorth/100))
278
//whatYouNeed = (targetGrade - currentGrade*(1-(finalWorth/100))) / (finalWorth/100)
279
vald = globalData.gradeType?validateInputs(['currentGradePts', 'currentGradeTotalPts', 'targetGrade', 'finalWorthPts']):validateInputs(['targetGrade', 'currentGrade'].concat(testReqInputs));
280
if(!vald){
281
$('#calcErr').show();
282
return}
283
r = calcTargetGrade(t);
284
//r = (t-c*(1-(w/100)))/(w/100); //Algebraic manipulation
285
handleQueryAction(1, Math.round(c*100));
286
$('#res-0').show();
287
$('#res-an-0').text(detPlu(r));
288
$('#res-val-0').text(`${r.toFixed(2)}% (${convToLetter(r)})`);
289
postCurveGrade = r;
290
$('#res-an-0b').text(detPlu(t));
291
$('#res-val-0b').text(`${t.toFixed(2)}% (${convToLetter(t)})`);
292
// $('#res_sub').text(getSubtitle(Math.floor(r)));
293
if(vald === 2){$('#res-warn').show()}
294
break;
295
case 1:
296
//finalGrade = (finalWorth/100)*finalExamGrade + currentGrade*(1-(finalWorth/100))
297
vald = globalData.gradeType?validateInputs(['currentGradePts', 'currentGradeTotalPts', 'finalGrade', 'finalWorthPts']):validateInputs(['finalGrade', 'currentGrade'].concat(testReqInputs));
298
if(!vald){
299
$('#calcErr').show();
300
return}
301
r = calcResultingGrade(f);
302
if(globalData.testPolicy > 0 && !globalData.gradeType){ // test adjustment policies don't exist for unweighted classes
303
let endDiff = 0;
304
switch(globalData.testPolicy){
305
case 1:
306
endDiff = calcTestDrop()[1] - calcTestDrop()[0];
307
break;
308
case 2:
309
endDiff = ((globalData.testWorth/100)*(globalData.finalGrade - globalData.lowestTest)/globalData.totalTests);
310
break;
311
case 3:
312
endDiff = ((globalData.testWorth/100)*0.5*(globalData.finalGrade - globalData.lowestTest)/globalData.totalTests);
313
break;
314
}
315
if (endDiff > 0) {
316
r += endDiff;
317
$('#res-finalGrade-testAdj').find('b').text(endDiff.toFixed(2));
318
$('#res-finalGrade-testAdj').show()}
319
else{
320
$('#res-finalGrade-noTestAdj').show()}
321
};
322
handleQueryAction(2, Math.round(c*100));
323
$('#res-1').show();
324
$('#res-an-1').text(detPlu(r));
325
$('#res-val-1').text(`${r.toFixed(2)}% (${convToLetter(r)})`);
326
break;
327
case 2:
328
//whatYouNeed same as case 0, except used multiple times for various targetGrade to find the best one
329
vald = globalData.gradeType?validateInputs(['currentGradePts', 'currentGradeTotalPts', 'finalWorthPts']):validateInputs(['currentGrade'].concat(testReqInputs));
330
if(!vald){
331
$('#calcErr').show();
332
return}
333
handleQueryAction(3, Math.round(c*100));
334
let upper = Math.ceil(c/10)*10, lower = Math.floor(c/10)*10;
335
let r1 = calcTargetGrade(upper);
336
let r2 = calcTargetGrade(lower);
337
//let r1 = (upper-c*(1-(w/100)))/(w/100); //Upper target
338
//let r2 = (lower-c*(1-(w/100)))/(w/100); //Lower target
339
// let r3 = globalData.gradeType ? ((cp + wp) / (ct + wp) * 100) : ((w) + (c*(1-(w/100)))); //Final grade w/ 100%
340
let r3 = calcResultingGrade(100);
341
if(upper == 100 || r1 > 100){ //Show info to keep current grade (if upper is not achievable or grade is already an A)
342
calcTargetGrade(upper); //Calling this updates [More Info] section if necessary
343
$('#res-0').show();
344
$('#res-an-0').text(detPlu(r2));
345
$('#res-val-0').text(`${r2.toFixed(2)}% (${convToLetter(r2)})`);
346
$('#res-an-0b').text(detPlu(lower));
347
$('#res-val-0b').text(`${lower.toFixed(2)}% (${convToLetter(lower)})`);
348
postCurveGrade = r2;
349
// $('#res_sub').text(getSubtitle(Math.floor(r2)));
350
}
351
if(r1 > 100){ //Upper grade is not achievable
352
$('#res-1s').show();
353
$('#res-an-1s').text(detPlu(r3));
354
$('#res-val-1s').text(`${r3.toFixed(2)}% (${convToLetter(r3)})`);
355
calcTargetGrade(lower);
356
postCurveGrade = r2;
357
if (lower < 90 && (r3 % 10 > 7)) {$('#res_sub').text('Maybe you can get extra credit somewhere to bump it up?')} //Hide message if the lower bound is already an A or if there's a large gap (>3%) until the next letter grade
358
}
359
else{ //Upper grade is achievable
360
calcTargetGrade(upper); //Calling this updates [More Info] section if necessary
361
$('#res-0').show();
362
$('#res-an-0').text(detPlu(r1));
363
$('#res-val-0').text(`${r1.toFixed(2)}% (${convToLetter(r1)})`);
364
$('#res-an-0b').text(detPlu(upper));
365
$('#res-val-0b').text(`${upper.toFixed(2)}% (${convToLetter(upper)})`);
366
$('#res-0s').show();
367
$('#res-an-0s').text(detPlu(r2));
368
$('#res-val-0s').text(`${r2.toFixed(2)}% (${convToLetter(r2)})`);
369
postCurveGrade = r1;
370
// $('#res_sub').text(getSubtitle(Math.floor(r1)));
371
}
372
break;
373
default:
374
alert(`An error occured. \nPlease report this bug!\nError: invalid calcType (${globalData.calcType})`)
375
}
376
if(globalData.curvePolicy){ // curving the output grade if selected
377
$('.res-hasCurve').show();
378
let g = postCurveGrade;
379
if(globalData.curvePolicy >= 2){
380
if(globalData.curvePolicy == 3){
381
if(!validateInputs(['curveStrength'])){
382
$('#calcErr').show();
383
return}
384
}
385
let curveStrength = (globalData.curvePolicy==2)?2:globalData.curveStrength;
386
g = Math.pow(g/100, curveStrength)*100;
387
}
388
else{ // curvePolicy = 1
389
if(!validateInputs(['curveTo'])){
390
$('#calcErr').show();
391
return}
392
g = g * (globalData.curveTo/100);
393
}
394
$('#res-val-c').html(`${detPlu(g)} <b>${g.toFixed(2)}% (${convToLetter(g)})</b>`);
395
if(globalData.gradeType === 1){
396
$('#sp-pts-curve').show();
397
$('#res-val-curvePts').text(`${(g*globalData.finalWorthPts/100).toFixed(1)}/${globalData.finalWorthPts.toFixed(1)} pts`)
398
}
399
$('#res_sub').text(getSubtitle(Math.floor(g)));
400
}
401
else{
402
$('.res-hasCurve').hide();
403
$('#res_sub').text(getSubtitle(Math.floor(postCurveGrade)));
404
}
405
$('#calcRes').show();
406
}
407
408
function registerHandlers(){
409
$('#calc').find('button').each((n, ele) => {
410
if($(ele).hasClass('btn-opt')){
411
$(ele).click(() => {
412
$('.'+ele.classList[1]).prop('disabled', false);
413
globalData[$(ele).data('for')] = $(ele).data('val');
414
$(ele).prop('disabled', true);
415
if($(ele).data('for') === 'examType' || $(ele).data('for') === 'unequalTests'){
416
updateTestInputs()}
417
else if($(ele).data('for') === 'gradeType'){
418
if($(ele).data('val') === 1){
419
$('.btn-opt.btn-opt-1')[0].click();
420
$('#sel_testAdj').val(0);
421
$('#sel_testAdj').change();
422
$('.p_weightedOnly').hide();
423
$('.p_unweightedOnly').show();
424
$('#opt_finalOrTest').hide();
425
$('.opt_testAdj').hide()
426
}
427
else{
428
$('.p_weightedOnly').show();
429
$('.p_unweightedOnly').hide();
430
$('.opt_testAdj').show();
431
$('#opt_finalOrTest').show();
432
}
433
}
434
});
435
}
436
else if(ele.id === 'calcBtn'){
437
$(ele).click(() => {
438
processCalculations();
439
if(window.innerWidth < 720) {
440
zenscroll.to(calcBtn, 500);
441
}
442
});
443
}
444
});
445
$('#calc').find('input').each((n, ele) => {
446
if($(ele).data('vald') === 'grd'){
447
ele.inputMode='decimal';
448
ele.maxLength = 6}
449
else if($(ele).data('vald') === 'weight'){ele.maxLength = 6}
450
else if($(ele).data('vald') === 'root'){ele.maxLength = 5}
451
else if($(ele).data('vald') === 'pts'){$(ele).css('width', '52px')}
452
$(ele).prop('id', 'inp_'+ele.dataset.for);
453
$(ele).change(() => {
454
let val = ele.value;
455
if(val === '' || val === ' '){
456
setBorderClass(ele, -1);
457
setStatusText(ele, -1, '');
458
globalData[$(ele).data('for')] = -1;
459
if(ele.dataset.for === 'testWorth'){
460
if($('#inp_testAvg').val() !== ''){
461
$('#inp_testAvg').change();
462
}
463
}
464
return}
465
let vald = validateInput(val, ele.dataset.vald);
466
setBorderClass(ele, vald.st);
467
setStatusText(ele, vald.st, vald.msg);
468
if(vald.st !== 2){//Input is valid
469
globalData[$(ele).data('for')] = vald.val
470
if(ele.dataset.for === 'testWorth'){
471
if($('#inp_testAvg').val() !== ''){
472
$('#inp_testAvg').change();
473
}
474
}
475
}
476
});
477
$(ele).on('input', () => {
478
if($(ele).hasClass('red-b')){
479
setBorderClass(ele, -1);
480
setStatusText(ele, -1);
481
}
482
})
483
});
484
$('#calc').find('select').each((n, ele) => {
485
$(ele).change(() => {
486
$('#calcRes').hide();
487
let val = ele.value;
488
if(!isNaN(parseInt(val))){val = parseInt(val)}
489
globalData[$(ele).data('for')] = val;
490
if(ele.dataset.for==='calcType'){
491
$('#info_opt2').hide();
492
let fg = (globalData.gradeType === 1?'#opt_finalWorthPts':'#opt_finalWorth');
493
$('#opt_targetGrade').hide();
494
$('#opt_finalGrade').hide();
495
$('.p_curve').show();
496
$(fg).show();
497
switch(ele.value){
498
case '0':
499
default:
500
$('#opt_targetGrade').show();
501
break;
502
case '1':
503
$('#opt_finalGrade').show();
504
$('#sel_curve').val(0);
505
$('#sel_curve').change();
506
$('.p_curve').hide();
507
break;
508
case '2':
509
$('#info_opt2').show()
510
break;
511
}
512
}
513
else if(ele.dataset.for==='testPolicy'){
514
updateTestInputs();
515
}
516
else if(ele.dataset.for='curvePolicy'){
517
$('#opt_curveTo').hide();
518
$('#opt_curveStrength').hide();
519
if(ele.value === '1'){
520
$('#opt_curveTo').show()}
521
else if(ele.value === '3'){
522
$('#opt_curveStrength').show()}
523
}
524
setBorderClass(ele, 'gr');
525
setTimeout(function(){
526
setBorderClass(ele, -1);
527
}, 1000);
528
});
529
});
530
$('html').find('.infoLink').each((n, ele) => {
531
$(ele).click(() => {
532
alertify.alert(alertMessages[ele.dataset.alertmsg])
533
});
534
});
535
$('#res_moreInfo').click(() => {
536
if(!$('#calcRes_moreInfo').is(':visible')){
537
$('#calcRes_moreInfo').show();
538
}
539
else{
540
$('#calcRes_moreInfo').hide();
541
}
542
});
543
}
544
545
function updateTestInputs(){
546
testReqInputs = [];
547
548
if(globalData.examType === 1){
549
$('.opt_testAdj').hide();
550
if(globalData.testPolicy){ // test policies don't work
551
$('#sel_testAdj').val(0);
552
$('#sel_testAdj').change();
553
return;
554
}
555
$('#opt_unequalTests').show()}
556
else{
557
$('.opt_testAdj').show();
558
$('#opt_unequalTests').hide()}
559
560
if(globalData.unequalTests){
561
$('.p_testAdjUnequal').show();
562
$('.p_testAdjEqual').hide();
563
testReqInputs = ['testWorth', 'testCatPts', 'testCatTotalPts', 'testWorthPts'];
564
return;
565
}
566
else{
567
$('.p_testAdjUnequal').hide();
568
$('.p_testAdjEqual').show();
569
}
570
571
if(globalData.examType === 0 && globalData.testPolicy === 0){ // neither "test" type nor test policy
572
$('.p_testAdj').hide();
573
$('#opt_finalWorth').show();
574
testReqInputs.push('finalWorth');
575
return;
576
}
577
$('.p_testAdj').show(); //All fields can be used for everything
578
testReqInputs.push('testWorth');
579
testReqInputs.push('totalTests');
580
if(globalData.testPolicy){ // test policy exists
581
$('#p_testAdjInfo').show();
582
$('#opt_lowestTest').show();
583
$('#sp_testAdjInfo').text(testAdjInfo[parseInt(globalData.testPolicy)-1]);
584
testReqInputs.push('lowestTest');
585
} else{
586
$('#p_testAdjInfo').hide();
587
$('#opt_lowestTest').hide();
588
}
589
if(globalData.examType == 1){
590
$('#opt_finalWorth').hide()}
591
else {
592
testReqInputs.push('finalWorth');
593
$('#opt_finalWorth').show()}
594
if(globalData.testPolicy !== 1 && globalData.examType == 0){
595
$('#p_testAvgInfo').hide();
596
$('#opt_testAvg').hide()}
597
else{
598
testReqInputs.push('testAvg');
599
$('#p_testAvgInfo').show();
600
$('#opt_testAvg').show()}
601
}
602
603
$(document).ready(function () {
604
'use strict';
605
let h = location.hostname;
606
if(['127.0.0.1', '10.10.7.38', 'itsryan.org', 'ryan778.github.io', 'ryan778.herokuapp.com', 'ryan778.azurewebsites.net'].indexOf(h) === -1){
607
$('#calc-load').html(`<i class='material-icons'>error</i> <b>Non-Whitelisted Domain</b><br>It looks like you're opening this page somewhere it's not supposed to be.<br>Make sure that you're on <a href='https://ryan778.github.io/final-grade-calculator/'>the official website</a>, and if this error persists, <a href='https://bit.ly/fgc-feedback'>contact us here</a>.`);
608
return;
609
}
610
registerHandlers();
611
$('.p_testAdj').hide();
612
$('#opt_curveTo').hide();
613
$('#opt_unequalTests').hide();
614
$('.p_unweightedOnly').hide();
615
$('.p_testAdjUnequal').hide();
616
$('#opt_curveStrength').hide();
617
$('#calc-load').hide();
618
$('#calc-inner').show();
619
});
620