PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.7
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.7
2.0.13 2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
blockenberg / blocks / gpa-calculator / frontend.js

frontend.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.7, at blocks/gpa-calculator/frontend.js

232 lines 10.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () {
2 'use strict';
3
4 var GRADE_POINTS = {
5 'A+': 4.0, 'A': 4.0, 'A-': 3.7,
6 'B+': 3.3, 'B': 3.0, 'B-': 2.7,
7 'C+': 2.3, 'C': 2.0, 'C-': 1.7,
8 'D+': 1.3, 'D': 1.0, 'D-': 0.7,
9 'F': 0.0
10 };
11 var GRADE_KEYS = Object.keys(GRADE_POINTS);
12
13 function calcGPA(courses) {
14 var totalCredits = 0, totalPoints = 0;
15 courses.forEach(function (c) {
16 var pts = GRADE_POINTS[c.grade];
17 var cr = parseFloat(c.credits) || 0;
18 if (pts !== undefined && cr > 0) {
19 totalCredits += cr;
20 totalPoints += pts * cr;
21 }
22 });
23 return {
24 gpa: totalCredits > 0 ? totalPoints / totalCredits : 0,
25 totalCredits: totalCredits,
26 totalPoints: totalPoints
27 };
28 }
29
30 function gradeClass(gpa) {
31 if (gpa >= 3.9) return 'Summa Cum Laude';
32 if (gpa >= 3.7) return 'Magna Cum Laude';
33 if (gpa >= 3.5) return 'Cum Laude';
34 if (gpa >= 3.0) return 'Good Standing';
35 if (gpa >= 2.0) return 'Satisfactory';
36 if (gpa > 0) return 'Probation Risk';
37 return '';
38 }
39
40 function gradeClassColor(gpa, accent) {
41 if (gpa >= 3.5) return '#15803d';
42 if (gpa >= 3.0) return accent;
43 if (gpa >= 2.0) return '#d97706';
44 return '#dc2626';
45 }
46
47 function esc(s) {
48 return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
49 }
50
51 function typoCssVarsForEl(typo, prefix, el) {
52 if (!typo || typeof typo !== 'object') return;
53 var map = {
54 family: 'font-family', weight: 'font-weight', style: 'font-style',
55 decoration: 'text-decoration', transform: 'text-transform',
56 sizeDesktop: 'font-size-d', sizeTablet: 'font-size-t', sizeMobile: 'font-size-m',
57 lineHeightDesktop: 'line-height-d', lineHeightTablet: 'line-height-t', lineHeightMobile: 'line-height-m',
58 letterSpacingDesktop: 'letter-spacing-d', letterSpacingTablet: 'letter-spacing-t', letterSpacingMobile: 'letter-spacing-m',
59 wordSpacingDesktop: 'word-spacing-d', wordSpacingTablet: 'word-spacing-t', wordSpacingMobile: 'word-spacing-m'
60 };
61 Object.keys(map).forEach(function (k) {
62 if (typo[k] !== undefined && typo[k] !== '') {
63 var v = typo[k];
64 if (['sizeDesktop','sizeTablet','sizeMobile'].indexOf(k) !== -1) {
65 v = v + (typo.sizeUnit || 'px');
66 } else if (['lineHeightDesktop','lineHeightTablet','lineHeightMobile'].indexOf(k) !== -1) {
67 v = v + (typo.lineHeightUnit || '');
68 } else if (['letterSpacingDesktop','letterSpacingTablet','letterSpacingMobile'].indexOf(k) !== -1) {
69 v = v + (typo.letterSpacingUnit || 'px');
70 } else if (['wordSpacingDesktop','wordSpacingTablet','wordSpacingMobile'].indexOf(k) !== -1) {
71 v = v + (typo.wordSpacingUnit || 'px');
72 }
73 el.style.setProperty(prefix + map[k], String(v));
74 }
75 });
76 }
77
78 function buildApp(app) {
79 var opts;
80 try { opts = JSON.parse(app.getAttribute('data-opts') || '{}'); } catch (e) { return; }
81 var o = opts;
82 var accent = o.accentColor || '#6c3fb5';
83
84 /* State */
85 var courses = (o.defaultCourses || []).map(function (c) {
86 return { name: c.name, grade: c.grade, credits: c.credits };
87 });
88
89 app.style.paddingTop = (o.paddingTop || 60) + 'px';
90 app.style.paddingBottom = (o.paddingBottom || 60) + 'px';
91 if (o.sectionBg) app.style.background = o.sectionBg;
92
93 typoCssVarsForEl(o.typoTitle, '--bkbg-gpa-tt-', app);
94 typoCssVarsForEl(o.typoGpa, '--bkbg-gpa-gp-', app);
95
96 /* Header */
97 var maxW = (o.maxWidth || 680) + 'px';
98 if (o.showTitle || o.showSubtitle) {
99 var hdr = document.createElement('div');
100 hdr.style.cssText = 'text-align:center;margin-bottom:32px;max-width:' + maxW + ';margin-left:auto;margin-right:auto';
101 if (o.showTitle && o.title) {
102 var ht = document.createElement('h3');
103 ht.className = 'bkbg-gpa-title';
104 ht.textContent = o.title;
105 ht.style.cssText = 'color:' + (o.titleColor || '#1e1b4b') + ';margin:0 0 8px';
106 hdr.appendChild(ht);
107 }
108 if (o.showSubtitle && o.subtitle) {
109 var hs = document.createElement('p');
110 hs.textContent = o.subtitle;
111 hs.style.cssText = 'color:' + (o.subtitleColor || '#6b7280') + ';margin:0';
112 hdr.appendChild(hs);
113 }
114 app.appendChild(hdr);
115 }
116
117 /* Card */
118 var card = document.createElement('div');
119 card.className = 'bkbg-gpa-card';
120 card.style.cssText = 'background:' + (o.cardBg || '#fff') + ';border-radius:' + (o.cardRadius || 16) + 'px;padding:32px;max-width:' + maxW + ';margin:0 auto;box-shadow:0 4px 24px rgba(0,0,0,0.06)';
121 app.appendChild(card);
122
123 /* Result area */
124 var resultArea = document.createElement('div');
125 resultArea.className = 'bkbg-gpa-result';
126 resultArea.style.cssText = 'background:' + (o.resultBg || '#f5f3ff') + ';border:2px solid ' + (o.resultBorder || '#ede9fe') + ';border-radius:' + (o.cardRadius || 16) + 'px;padding:24px 32px;text-align:center;margin-bottom:24px';
127 card.appendChild(resultArea);
128
129 /* Table header */
130 var tHead = document.createElement('div');
131 tHead.style.cssText = 'display:grid;grid-template-columns:1fr 100px 80px 40px;background:' + (o.tableHeaderBg || '#f9fafb') + ';border-radius:8px 8px 0 0;padding:8px 12px;font-weight:600;font-size:13px;color:' + (o.subtitleColor || '#6b7280') + ';gap:8px';
132 tHead.innerHTML = '<span>Course</span><span style="text-align:center">Grade</span><span style="text-align:center">Credits</span><span></span>';
133 card.appendChild(tHead);
134
135 /* Rows container */
136 var tbody = document.createElement('div');
137 tbody.className = 'bkbg-gpa-tbody';
138 card.appendChild(tbody);
139
140 /* Add button */
141 var addBtn = document.createElement('button');
142 addBtn.className = 'bkbg-gpa-add-btn';
143 addBtn.textContent = '+ Add Course';
144 addBtn.style.cssText = 'background:' + accent + ';color:#fff;margin-top:12px;border:none;border-radius:' + (o.inputRadius || 8) + 'px;padding:8px 18px;cursor:pointer;font-size:14px;font-weight:600';
145 addBtn.addEventListener('click', function () {
146 courses.push({ name: 'New Course', grade: 'B', credits: 3 });
147 render();
148 });
149 card.appendChild(addBtn);
150
151 /* Grade scale help */
152 if (o.showHelp !== false) {
153 var help = document.createElement('div');
154 help.className = 'bkbg-gpa-scale-help';
155 ['A/A+ = 4.0', 'A- = 3.7', 'B+ = 3.3', 'B = 3.0', 'B- = 2.7', 'C+ = 2.3', 'C = 2.0', 'D = 1.0'].forEach(function (t) {
156 var sp = document.createElement('span');
157 sp.textContent = t;
158 help.appendChild(sp);
159 });
160 card.appendChild(help);
161 }
162
163 function renderResult() {
164 var res = calcGPA(courses);
165 var gpa = res.gpa.toFixed(2);
166 var html = '<div class="bkbg-gpa-number" style="color:' + (o.gpaColor || accent) + '">' + esc(gpa) + '</div>';
167 html += '<div style="color:' + (o.subtitleColor || '#6b7280') + ';margin-top:4px">GPA (4.0 scale)</div>';
168 if (o.showGradeClass !== false) {
169 html += '<div class="bkbg-gpa-classification" style="color:' + gradeClassColor(res.gpa, accent) + '">' + esc(gradeClass(res.gpa)) + '</div>';
170 }
171 if (o.showTotals !== false) {
172 html += '<div style="display:flex;justify-content:center;gap:32px;margin-top:16px">';
173 html += '<div style="text-align:center"><div style="font-size:22px;font-weight:700;color:' + (o.labelColor || '#374151') + '">' + res.totalCredits + '</div><div style="font-size:12px;color:' + (o.subtitleColor || '#6b7280') + '">Credits</div></div>';
174 html += '<div style="text-align:center"><div style="font-size:22px;font-weight:700;color:' + (o.labelColor || '#374151') + '">' + res.totalPoints.toFixed(1) + '</div><div style="font-size:12px;color:' + (o.subtitleColor || '#6b7280') + '">Grade Points</div></div>';
175 html += '</div>';
176 }
177 resultArea.innerHTML = html;
178 }
179
180 function render() {
181 tbody.innerHTML = '';
182 courses.forEach(function (c, i) {
183 var row = document.createElement('div');
184 row.className = 'bkbg-gpa-row';
185 row.style.background = i % 2 === 0 ? '#fff' : '#fafafa';
186
187 /* Name input */
188 var nameIn = document.createElement('input');
189 nameIn.type = 'text'; nameIn.value = c.name; nameIn.placeholder = 'Course name';
190 nameIn.style.borderRadius = (o.inputRadius || 8) + 'px';
191 nameIn.addEventListener('input', function () { courses[i].name = nameIn.value; });
192
193 /* Grade select */
194 var gradeEl = document.createElement('select');
195 gradeEl.style.borderRadius = (o.inputRadius || 8) + 'px';
196 gradeEl.style.textAlign = 'center';
197 GRADE_KEYS.forEach(function (g) {
198 var opt = document.createElement('option');
199 opt.value = g; opt.textContent = g;
200 if (g === c.grade) opt.selected = true;
201 gradeEl.appendChild(opt);
202 });
203 gradeEl.addEventListener('change', function () { courses[i].grade = gradeEl.value; renderResult(); });
204
205 /* Credits input */
206 var credIn = document.createElement('input');
207 credIn.type = 'number'; credIn.min = '0'; credIn.step = '0.5'; credIn.value = c.credits;
208 credIn.style.borderRadius = (o.inputRadius || 8) + 'px';
209 credIn.style.textAlign = 'center';
210 credIn.addEventListener('input', function () { courses[i].credits = parseFloat(credIn.value) || 0; renderResult(); });
211
212 /* Remove btn */
213 var rmBtn = document.createElement('button');
214 rmBtn.className = 'bkbg-gpa-remove-btn';
215 rmBtn.textContent = '';
216 rmBtn.addEventListener('click', function () { courses.splice(i, 1); render(); });
217
218 row.appendChild(nameIn);
219 row.appendChild(gradeEl);
220 row.appendChild(credIn);
221 row.appendChild(rmBtn);
222 tbody.appendChild(row);
223 });
224 renderResult();
225 }
226
227 render();
228 }
229
230 document.querySelectorAll('.bkbg-gpa-app').forEach(buildApp);
231 })();
232