From 53fb80023697dac7091733ca69c56ea68b2589da Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 2 Jun 2021 21:57:01 -0400 Subject: [PATCH] Init www --- CNAME | 1 + index.html | 89 ++++++++++ pages.json | 6 + pages/grade-calc/README.md | 23 +++ pages/grade-calc/config/eee3307c.json | 25 +++ pages/grade-calc/config/eel4742c.json | 29 ++++ pages/grade-calc/config/eel4781.json | 35 ++++ pages/grade-calc/config/map2302.json | 41 +++++ pages/grade-calc/gc_client.js | 203 ++++++++++++++++++++++ pages/grade-calc/index.html | 127 ++++++++++++++ pages/index.html | 20 +++ pages/recommended.html | 97 +++++++++++ pages/resources.html | 41 +++++ scripts/fuzzynav.js | 77 +++++++++ stylesheets/persia.css | 237 ++++++++++++++++++++++++++ 15 files changed, 1051 insertions(+) create mode 100644 CNAME create mode 100644 index.html create mode 100644 pages.json create mode 100644 pages/grade-calc/README.md create mode 100644 pages/grade-calc/config/eee3307c.json create mode 100644 pages/grade-calc/config/eel4742c.json create mode 100644 pages/grade-calc/config/eel4781.json create mode 100644 pages/grade-calc/config/map2302.json create mode 100644 pages/grade-calc/gc_client.js create mode 100644 pages/grade-calc/index.html create mode 100644 pages/index.html create mode 100644 pages/recommended.html create mode 100644 pages/resources.html create mode 100644 scripts/fuzzynav.js create mode 100644 stylesheets/persia.css diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..5e9e1a5 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +paulw.xyz diff --git a/index.html b/index.html new file mode 100644 index 0000000..9b8b06a --- /dev/null +++ b/index.html @@ -0,0 +1,89 @@ + + + + + + + PaulW.XYZ / + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pages.json b/pages.json new file mode 100644 index 0000000..95930a8 --- /dev/null +++ b/pages.json @@ -0,0 +1,6 @@ +[ + ["Pages", "/pages/"], + ["Resources", "/pages/resources"], + ["Recommended", "/pages/recommended"], + ["Grade Calculator", "/pages/grade-calc/"] +] \ No newline at end of file diff --git a/pages/grade-calc/README.md b/pages/grade-calc/README.md new file mode 100644 index 0000000..3a99b3b --- /dev/null +++ b/pages/grade-calc/README.md @@ -0,0 +1,23 @@ +# Grade Calculator + +Some professors do not properly configure the Canvas grade percentages based on their syllabus. Instead, they opt to use Excel to calculate the final grades after everything. It can be hard to estimate how much effort I should allocate for those classes without making an Excel file. + +So I wrote this to quickly configure a calculator to see what I will end up getting in a class based on how much effort I put in. + +## Short Summary of the JSON structure + +The JSON is expected to have an array of section descriptions. + +`name : string` is used as the class name. They have to be unique for each section to work as expected. + +`title : string` is used as the heading for the sections. + +`percentage : number` is used to weigh the section for the final grade. + +`output : boolean` when true, it calculates the section score. + +`points : [number]` used for each assignment in the section. + +`bothMethods : boolean` when true, the weighted and unweighted scores are calculated. + +`info : string` used for section description. \ No newline at end of file diff --git a/pages/grade-calc/config/eee3307c.json b/pages/grade-calc/config/eee3307c.json new file mode 100644 index 0000000..09e6e3c --- /dev/null +++ b/pages/grade-calc/config/eee3307c.json @@ -0,0 +1,25 @@ +[ + { + "name": "final", + "title": "Final", + "percentage": 40, + "info": "" + }, + { + "name": "midterm", + "title": "Midterm", + "percentage": 30 + }, + { + "name": "homework", + "title": "Homework", + "percentage": 10, + "points": [100,100,100,100,100,100] + }, + { + "name": "lab", + "title": "Lab", + "percentage": 20, + "info": "Lab score is accurate and does not need any sub-calculation." + } +] \ No newline at end of file diff --git a/pages/grade-calc/config/eel4742c.json b/pages/grade-calc/config/eel4742c.json new file mode 100644 index 0000000..a7c2457 --- /dev/null +++ b/pages/grade-calc/config/eel4742c.json @@ -0,0 +1,29 @@ +[ + { + "name": "exams", + "percentage": 70, + "points": [ + 100, + 100, + 100 + ], + "title": "Exams" + }, + { + "name": "homework", + "percentage": 15, + "points": [ + 90, + 50, + 110, + 90 + ], + "title": "Homework" + }, + { + "info": "Lab score is accurate and does not need any sub-calculation.", + "name": "lab", + "percentage": 15, + "title": "Lab" + } +] \ No newline at end of file diff --git a/pages/grade-calc/config/eel4781.json b/pages/grade-calc/config/eel4781.json new file mode 100644 index 0000000..f8cd9da --- /dev/null +++ b/pages/grade-calc/config/eel4781.json @@ -0,0 +1,35 @@ +[ + { + "name": "midterm", + "percentage": 25, + "info":"http://www.ece.ucf.edu/~yuksem/teaching/EEL-4781-Spring-2021.html" + }, + { + "name": "final", + "percentage": 25 + }, + { + "name": "project", + "percentage": 15, + "info":"Webserver project" + }, + { + "name": "homework", + "percentage": 20, + "points": [ + 100, + 100, + 100, + 100, + 100 + ] + }, + { + "name": "lab", + "percentage": 15, + "points": [ + 100, + 100 + ] + } +] \ No newline at end of file diff --git a/pages/grade-calc/config/map2302.json b/pages/grade-calc/config/map2302.json new file mode 100644 index 0000000..abddd19 --- /dev/null +++ b/pages/grade-calc/config/map2302.json @@ -0,0 +1,41 @@ +[ + { + "name": "final", + "title": "Final", + "percentage": 25, + "info": "" + }, + { + "name": "midterm", + "title": "Midterm", + "percentage": 50, + "points": [ + 20, + 20, + 20 + ], + "output": true + }, + { + "name": "quiz", + "title": "Quiz", + "percentage": 10, + "points": [ + 30, + 10, + 15 + ], + "output": true, + "bothMethods": true + }, + { + "name": "homework", + "title": "Homework", + "percentage": 10 + }, + { + "name": "attendance", + "title": "Attendance", + "percentage": 5 + } +] \ No newline at end of file diff --git a/pages/grade-calc/gc_client.js b/pages/grade-calc/gc_client.js new file mode 100644 index 0000000..fb64dbc --- /dev/null +++ b/pages/grade-calc/gc_client.js @@ -0,0 +1,203 @@ +class GradeCalc { + maxscore = 0; + sections = []; + inputSection = []; + outputSection = []; + fields = []; + grades = []; + ugrades = []; + both = false; + totalOutput = null; + + constructor(config, outCallback) { + this.totalOutput = document.createElement("div"); + let dConfig = JSON.parse(JSON.stringify(config)); // dirty clone + let sanConfig = []; + for (let conf of dConfig) { + if (conf.percentage === undefined || conf.name === undefined) + continue; + if (conf.title === undefined) + conf.title = conf.name[0].toUpperCase() + conf.name.slice(1); + sanConfig.push(conf); + } + this.config = sanConfig; + for (let [i, conf] of this.config.entries()) { + this.maxscore += conf.percentage; + + this.inputSection[i] = []; + this.outputSection[i] = document.createElement("div"); + + if (conf.bothMethods) { + this.both = true; + } + + this.sections[i] = (this.createSection(i)); + } + + for (let [k, v] of this.fields.entries()) { + for (let field of v) { + this.addInputEventListener(k, field); + } + } + + outCallback(this.sections); + } + + createSection(id) { + let conf = this.config[id]; + + var section = document.createElement("div"); + section.classList.add(conf.name); + + var heading = document.createElement("h2"); + heading.innerHTML = `${conf.title} (${conf.percentage}%)`; + + section.appendChild(heading); + + if (conf.info !== undefined) + section.appendChild(document.createTextNode(conf.info)); + + this.fields[id] = []; + if (conf.points !== undefined) { + for (var i = 0; i < conf.points.length; i++) { + section.appendChild(this.createInputSection(id, i)); + } + } + else { + section.appendChild(this.createInputSection(id, 0, true)); + } + + + section.appendChild(this.outputSection[id]); + return section; + } + + createInputSection(sectId, inputId, soleInput = false) { + let conf = this.config[sectId]; + let inputSection = document.createElement("div"); + inputSection.classList.add("input-section"); + + let label = document.createElement("label"); + if (soleInput) + label.innerHTML = `${conf.title} Score: `; + else + label.innerHTML = `${conf.title} ${inputId + 1} Score: `; + + let field = document.createElement("input"); + field.classList.add(`${conf.name}-score`); + this.fields[sectId][inputId] = field; + + let suffix = (soleInput) ? "%" : ` / ${conf.points[inputId]} pts`; + + inputSection.appendChild(label); + inputSection.appendChild(field); + inputSection.appendChild(document.createTextNode(suffix)); + + this.inputSection[sectId][inputId] = inputSection; + return inputSection; + } + + addInputEventListener(id, field, event = "keyup") { + let conf = this.config[id]; + field.addEventListener(event, () => { + if (conf.output !== undefined && conf.output) + this.showSectionGrade(id); + this.showTotalGrade(); + }); + } + + calculateSectionGrade(id, unweighted = false) { + let conf = this.config[id]; + let fields = this.fields[id]; + if (fields === undefined) + return; + if (conf.points === undefined) { + return parseFloat(fields[0].value); + } + + let total = 0; + + if (unweighted) { + let counter = 0; + for (let [i, field] of fields.entries()) { + let val = parseFloat(field.value); + if (isNaN(val)) + continue; + total += val / conf.points[i]; + counter++; + } + + return (total / counter * 100); + } + + total = fields.reduce((acc, cur) => { + let c = parseFloat(cur.value); + if (isNaN(c)) + return acc; + return acc + parseFloat(c); + }, 0); + + let max_total = 0; + for (let [i, field] of conf.points.entries()) { + if (isNaN(parseFloat(fields[i].value))) + continue; + max_total += field; + } + + return (total / max_total * 100); + } + + showSectionGrade(id) { + let conf = this.config[id]; + let grade = this.calculateSectionGrade(id); + let ugrade = this.calculateSectionGrade(id, true); + + + this.grades[id] = grade * parseFloat(conf.percentage) / 100; + this.ugrades[id] = ugrade * parseFloat(conf.percentage) / 100; + + grade = !isNaN(grade) ? grade.toFixed(2) : "..."; + ugrade = !isNaN(ugrade) ? ugrade.toFixed(2) : "..."; + if (conf.bothMethods) { + this.outputSection[id].innerHTML + = `Score (weighted): ${grade}%
Score (unweighted): ${ugrade}%`; + return; + } + + this.outputSection[id].innerHTML = `Score: ${grade}`; + } + + showTotalGrade() { + for (let [k, conf] of this.config.entries()) { + if (!conf.output) { + this.grades[k] = this.calculateSectionGrade(k) * parseFloat(conf.percentage) / 100; + this.ugrades[k] = this.calculateSectionGrade(k, true) * parseFloat(conf.percentage) / 100; + } + } + + let grade = this.grades.reduce((a, c) => { + if (isNaN(c)) + return a; + return a + c + }, 0); + let ugrade = this.ugrades.reduce((a, c) => { + if (isNaN(c)) + return a; + return a + c + }, 0); + + grade = !isNaN(grade) ? grade.toFixed(2) : "..."; + ugrade = !isNaN(ugrade) ? ugrade.toFixed(2) : "..."; + if (this.both) { + this.totalOutput.innerHTML + = `Total Score (weighted): ${grade}%
Total Score (unweighted): ${ugrade}%`; + return; + } + + this.totalOutput.innerHTML = `Total Score: ${grade}%`; + } + + get elemTotal() { + return this.totalOutput; + } +} \ No newline at end of file diff --git a/pages/grade-calc/index.html b/pages/grade-calc/index.html new file mode 100644 index 0000000..a37a313 --- /dev/null +++ b/pages/grade-calc/index.html @@ -0,0 +1,127 @@ + + + + + + + + Grade Calc + + + + +
+

PaulW.XYZ / Pages / Grade Calculator

+
+

About

+ Check out the README.md file + to learn more about the configuration structure. +

Usage

+
    +
  1. Either configure the calculator using the text box below or load one from the existing JSON files to + generate one.
  2. +
  3. Click Generate.
  4. +
  5. Enter the input values.
  6. +
+
+
+

Configuration

+

Load config from file

+ +
+ +
+ + +
+
+
+ + + + + \ No newline at end of file diff --git a/pages/index.html b/pages/index.html new file mode 100644 index 0000000..2ca9e31 --- /dev/null +++ b/pages/index.html @@ -0,0 +1,20 @@ + + + + + PaulW.XYZ / Pages + + + +

PaulW.XYZ / Pages

+ +
+ + + + \ No newline at end of file diff --git a/pages/recommended.html b/pages/recommended.html new file mode 100644 index 0000000..09cafba --- /dev/null +++ b/pages/recommended.html @@ -0,0 +1,97 @@ + + + + + Pages / Recommended + + + + +

PaulW.XYZ / Pages / Recommended

+

If the one you're looking for is not on this list, it is most likely I haven't had the chance to read it yet or I may have put it on the Resources page, if it is freely available.

+
+

Books

+

Technology

+

C programming

+ +

Operating Systems

+ + + + +

Computer Engineering

+ + + + +

Compilers

+ + +

Other

+ + +

Classics

+

Only the English ones for now.

+ + +

Languages

+ +
+ +

Movies

+ +
+ +

Music

+ +
+ +

Video Games

+ + + \ No newline at end of file diff --git a/pages/resources.html b/pages/resources.html new file mode 100644 index 0000000..a93ce51 --- /dev/null +++ b/pages/resources.html @@ -0,0 +1,41 @@ + + + + + Pages / Resources + + + +

PaulW.XYZ / Pages / Resources

+
+

Programming

+ +

Posts

+ +

Talks

+ +

Electrical

+ +

Other Topics

+ + + \ No newline at end of file diff --git a/scripts/fuzzynav.js b/scripts/fuzzynav.js new file mode 100644 index 0000000..201a6b8 --- /dev/null +++ b/scripts/fuzzynav.js @@ -0,0 +1,77 @@ +(function () { + var client = new XMLHttpRequest(); + client.open("GET", "pages.json"); + client.onreadystatechange = function () { + initFuzzy(client.responseText); + } + client.send(); +})(); + +function initFuzzy(text) { + if (text == "") + return; + + var pages; + try { + pages = JSON.parse(text); + } catch (e) { + return; + } + + pages.sort(); + + let output = ""; + + for (const [name, rlink] of pages) { + output += `
${name}
`; + } + + document.querySelector("#results").innerHTML = output; + + document.querySelector("#search") + .addEventListener("keyup", (e) => { + + let results = []; + for (const [i, [title, page]] of pages.entries()) { + ret = hotFuzz(title, document.querySelector("#search").value); + if (ret == false) + continue; + results.push([ret, page]); + } + + results.sort((x, y) => {return x[0].second - y[0].second}); + + let output = ""; + for (const [hfRet, rlink] of results) { + output += `
${hfRet.first}
`; + } + + if (output == "") + output = "No matches found." + + document.querySelector("#results").innerHTML = output; + } + ); +} + +function hotFuzz(list, input) { + let search = input.replace(/\s/g, ""); + search = search.toLowerCase(); + let tokens = list.split(''); + let pc = 0; + let score = 0; + + for (const [i, ch] of tokens.entries()) { + if (ch.toLowerCase() == search[pc]) { + score += i - pc; + tokens[i] = `${ch}`; + pc++; + if (search.length < pc) + return false; + } + } + if (search.length != pc) + return false; + + return {first: tokens.join(''), second: (score / search.length)}; +} \ No newline at end of file diff --git a/stylesheets/persia.css b/stylesheets/persia.css new file mode 100644 index 0000000..7bd632e --- /dev/null +++ b/stylesheets/persia.css @@ -0,0 +1,237 @@ +* { + box-sizing: border-box; +} + +html { + font-family: sans-serif; + line-height: 1.15; + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + background: linear-gradient(to bottom right, #64a95a, #388c73) no-repeat + center center fixed; + background-size: cover; + height: 100%; +} + +article, +aside, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section { + display: block; +} + +body { + margin: 0; + font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; + font-size: 16px; + font-weight: 400; + line-height: 1.5; + color: #fff; + text-align: left; + height: 100%; + background-color: transparent; +} + +[tabindex="-1"]:focus { + outline: 0 !important; +} + +hr { + box-sizing: content-box; + height: 0; + overflow: visible; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + margin-top: 0; + margin-bottom: 0.5rem; +} + +h1, +.h1 { + font-size: 2.5rem; +} + +h2, +.h2 { + font-size: 2rem; +} + +h3, +.h3 { + font-size: 1.75rem; +} + +h4, +.h4 { + font-size: 1.5rem; +} + +h5, +.h5 { + font-size: 1.25rem; +} + +h6, +.h6 { + font-size: 1rem; +} + +p { + margin-top: 0; + margin-bottom: 1rem; +} + +a { + color: #b0e1ff; + text-decoration: underline; + background-color: transparent; + outline: none; +} +a:hover { + text-decoration: underline; +} +a:focus { + text-decoration: underline dotted; +} + +.button { + display: inline-block; + font-weight: 400; + text-align: center; + user-select: none; + background-color: transparent; + border: 1px solid transparent; + text-decoration: none; + padding: 0.25rem 1rem; + font-size: 1rem; + line-height: 1.5; + border: none; + border-radius: 0.25rem; + transition: filter 250ms ease; + color: #fff; + background: linear-gradient(#90c541, #388c73); + margin: 0.25rem; + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05), 0 2px 3px 0 rgba(0, 0, 0, 0.1); +} + +.button:hover { + filter: brightness(115%); + color: #fff; + text-decoration: none; +} + +.button:focus { + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05), 0 2px 3px 0 rgba(0, 0, 0, 0.1), + 0 0 0 0.2rem rgb(89, 149, 0); + border: none; + outline: none; +} + +.title { + display: none; + + text-align: center; + font-size: 3rem; +} + +.fuzzynav { + margin: 2rem; +} + +.fuzzynav input.search { + display: block; + width: 100%; + font-size: 2rem; + background: linear-gradient(rgba(116, 116, 116, 0.2),rgba(65, 65, 65, 0.2)); + box-shadow: inset 0 4px 8px 0 rgba(0,0,0,0.2); + padding: 0.5rem; + color: rgb(255, 255, 255); + border: none; + outline: none; +} + +.fuzzynav input.search::placeholder { + color: rgb(214, 214, 214); + /* font-style: italic; */ +} + +.fuzzynav input.search::after { + content: ""; + /* padding: 0.5rem; + display: block; + position: absolute; + top:0; + left:0; + background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgaWQ9InN2ZzgiCiAgIHZlcnNpb249IjEuMSIKICAgdmlld0JveD0iMCAwIDMyIDMyIgogICA+CiAgPGRlZnMKICAgICBpZD0iZGVmczIiIC8+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNSI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6dGl0bGU+PC9kYzp0aXRsZT4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPHBhdGgKICAgICBpZD0icGF0aDQ3NjIiCiAgICAgZD0iTSAyMCAwIEEgMTIgMTIgMCAwIDAgOCAxMiBBIDEyIDEyIDAgMCAwIDEwLjg0NTcwMyAxOS43NDAyMzQgTCAwIDMwLjU4NTkzOCBMIDEuNDE0MDYyNSAzMiBMIDEyLjI1MTk1MyAyMS4xNjIxMDkgQSAxMiAxMiAwIDAgMCAyMCAyNCBBIDEyIDEyIDAgMCAwIDMyIDEyIEEgMTIgMTIgMCAwIDAgMjAgMCB6IE0gMjAgMiBBIDEwIDEwIDAgMCAxIDMwIDEyIEEgMTAgMTAgMCAwIDEgMjAgMjIgQSAxMCAxMCAwIDAgMSAxMCAxMiBBIDEwIDEwIDAgMCAxIDIwIDIgeiAiCiAgICAgc3R5bGU9Im9wYWNpdHk6MTtmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7IiAvPgo8L3N2Zz4K); */ +} + +.fuzzynav div.results { + margin: 1rem; + padding-bottom: 2rem; +} + + +.fuzzynav div.results .hyperlink { + color: #009dff; + background-color: rgba(34, 34, 34, 0.616); + display: block; + box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); + padding: 0.5rem; + width: 100%; + margin: 0.5rem auto; + font-size: 1.4rem; + transition: 300ms cubic-bezier(0.075, 0.82, 0.165, 1); + text-decoration: none; +} + + +.fuzzynav div.results .hyperlink:hover { + text-decoration: none; + transform: scale(1.01); +} + +.fuzzynav div.results .hyperlink:focus { + box-shadow: 0 0 1px 1px rgb(255, 255, 255); +} + +.fuzzynav div.results .hyperlink .name { + color: #ffffff; +} + +.fuzzynav div.results .hyperlink .link { + text-align: right; + font-size: 1.1rem; +} + +.fuzzynav div.results .hyperlink .highlight { + color: rgb(175, 175, 175) +} + +footer { + position: fixed; + bottom: 0; + right: 0; + color: #fff; + background: linear-gradient(#90c541, #388c73); + padding: 0.25rem; + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05), 0 2px 3px 0 rgba(0, 0, 0, 0.1); +} + +.m-icon { + position: fixed; + bottom: 0; + left: 0; + z-index: -1; +} \ No newline at end of file