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 / grade-calculator / frontend.js

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

358 lines 15.8 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_SCALE = [
5 {letter:'A+', min:97, dflt:'#059669'},
6 {letter:'A', min:93, dflt:'#10b981'},
7 {letter:'A−', min:90, dflt:'#34d399'},
8 {letter:'B+', min:87, dflt:'#2563eb'},
9 {letter:'B', min:83, dflt:'#3b82f6'},
10 {letter:'B−', min:80, dflt:'#60a5fa'},
11 {letter:'C+', min:77, dflt:'#d97706'},
12 {letter:'C', min:73, dflt:'#f59e0b'},
13 {letter:'C−', min:70, dflt:'#fbbf24'},
14 {letter:'D+', min:67, dflt:'#dc2626'},
15 {letter:'D', min:63, dflt:'#ef4444'},
16 {letter:'D−', min:60, dflt:'#f87171'},
17 {letter:'F', min:0, dflt:'#6b7280'}
18 ];
19
20 var GRADE_COLOR_KEYS = {
21 'A+':'gradeAColor','A':'gradeAColor','A−':'gradeAColor',
22 'B+':'gradeBColor','B':'gradeBColor','B−':'gradeBColor',
23 'C+':'gradeCColor','C':'gradeCColor','C−':'gradeCColor',
24 'D+':'gradeDColor','D':'gradeDColor','D−':'gradeDColor',
25 'F':'gradeFColor'
26 };
27
28 function getLetter(pct, opts) {
29 for (var i = 0; i < GRADE_SCALE.length; i++) {
30 if (pct >= GRADE_SCALE[i].min) {
31 var key = GRADE_COLOR_KEYS[GRADE_SCALE[i].letter];
32 return {letter: GRADE_SCALE[i].letter, color: (opts[key] || GRADE_SCALE[i].dflt), dflt: GRADE_SCALE[i].dflt};
33 }
34 }
35 return {letter: 'F', color: opts.gradeFColor || '#6b7280', dflt: '#6b7280'};
36 }
37
38 function calcAvg(subjects, mode) {
39 if (!subjects.length) return 0;
40 if (mode === 'simple') {
41 var sum = 0, total = 0;
42 subjects.forEach(function(s) {
43 sum += parseFloat(s.score) || 0;
44 total += parseFloat(s.max) || 100;
45 });
46 return total > 0 ? (sum / total) * 100 : 0;
47 }
48 var totalWeight = 0, weightedSum = 0;
49 subjects.forEach(function(s) {
50 var sc = parseFloat(s.score) || 0;
51 var mx = parseFloat(s.max) || 100;
52 var wt = parseFloat(s.weight) || 0;
53 var pct = mx > 0 ? (sc / mx) * 100 : 0;
54 weightedSum += pct * wt;
55 totalWeight += wt;
56 });
57 return totalWeight > 0 ? weightedSum / totalWeight : 0;
58 }
59
60 function setStyle(el, styles) {
61 Object.keys(styles).forEach(function(k) { el.style[k] = styles[k]; });
62 }
63
64 function typoCssVarsForEl(typo, prefix, el) {
65 if (!typo || typeof typo !== 'object') return;
66 var map = {
67 family:'font-family', weight:'font-weight', style:'font-style',
68 decoration:'text-decoration', transform:'text-transform',
69 sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m',
70 lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m',
71 letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m',
72 wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m'
73 };
74 Object.keys(map).forEach(function(k) {
75 if (typo[k] !== undefined && typo[k] !== '') {
76 var v = typo[k];
77 if (['sizeDesktop','sizeTablet','sizeMobile'].indexOf(k) !== -1) v = v + (typo.sizeUnit || 'px');
78 else if (['lineHeightDesktop','lineHeightTablet','lineHeightMobile'].indexOf(k) !== -1) v = v + (typo.lineHeightUnit || '');
79 else if (['letterSpacingDesktop','letterSpacingTablet','letterSpacingMobile'].indexOf(k) !== -1) v = v + (typo.letterSpacingUnit || 'px');
80 else if (['wordSpacingDesktop','wordSpacingTablet','wordSpacingMobile'].indexOf(k) !== -1) v = v + (typo.wordSpacingUnit || 'px');
81 el.style.setProperty(prefix + map[k], String(v));
82 }
83 });
84 }
85
86 function makeEl(tag, attrs, children) {
87 var el = document.createElement(tag);
88 if (attrs) {
89 Object.keys(attrs).forEach(function(k) {
90 if (k === 'className') { el.className = attrs[k]; }
91 else if (k === 'textContent') { el.textContent = attrs[k]; }
92 else if (k === 'style' && typeof attrs[k] === 'object') { setStyle(el, attrs[k]); }
93 else { el.setAttribute(k, attrs[k]); }
94 });
95 }
96 if (children) {
97 (Array.isArray(children) ? children : [children]).forEach(function(c) {
98 if (c == null) return;
99 if (typeof c === 'string') { el.appendChild(document.createTextNode(c)); }
100 else { el.appendChild(c); }
101 });
102 }
103 return el;
104 }
105
106 function input(type, value, placeholder, onChange) {
107 var el = document.createElement('input');
108 el.type = type;
109 el.value = value;
110 el.className = 'bkbg-grc-input';
111 if (placeholder) el.placeholder = placeholder;
112 el.addEventListener('input', function() { onChange(el.value); });
113 return el;
114 }
115
116 function initApp(app) {
117 var opts;
118 try { opts = JSON.parse(app.getAttribute('data-opts') || '{}'); } catch(e) { return; }
119
120 var accent = opts.accentColor || '#6c3fb5';
121 var cardBg = opts.cardBg || '#fff';
122 var resultBg = opts.resultBg || accent;
123 var resultClr = opts.resultColor || '#fff';
124 var cardR = (opts.cardRadius || 16) + 'px';
125 var inpR = (opts.inputRadius || 8) + 'px';
126 var maxW = (opts.maxWidth || 640) + 'px';
127 var ptop = (opts.paddingTop || 60) + 'px';
128 var pbot = (opts.paddingBottom || 60) + 'px';
129
130 var subjects = [
131 {id:1, name:'Math', score:90, max:100, weight:30},
132 {id:2, name:'English', score:85, max:100, weight:30},
133 {id:3, name:'Science', score:78, max:100, weight:20},
134 {id:4, name:'History', score:92, max:100, weight:20}
135 ];
136 var nextId = 10;
137
138 app.style.fontFamily = '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif';
139 app.style.paddingTop = ptop;
140 app.style.paddingBottom = pbot;
141 if (opts.sectionBg) app.style.background = opts.sectionBg;
142
143 typoCssVarsForEl(opts.typoTitle, '--bkbg-grc-tt-', app);
144 typoCssVarsForEl(opts.typoResult, '--bkbg-grc-rs-', app);
145 typoCssVarsForEl(opts.typoLetter, '--bkbg-grc-lt-', app);
146
147 /* ── Card ── */
148 var card = makeEl('div', {className:'bkbg-grc-card', style:{
149 background: cardBg,
150 borderRadius: cardR,
151 padding: '32px',
152 maxWidth: maxW,
153 margin: '0 auto',
154 boxShadow: '0 4px 24px rgba(0,0,0,.09)'
155 }});
156
157 /* ── Header ── */
158 if (opts.showTitle && opts.title) {
159 var titleEl = makeEl('div', {className:'bkbg-grc-title', style:{
160 color: opts.titleColor || '#1e1b4b'
161 }, textContent: opts.title});
162 card.appendChild(titleEl);
163 }
164 if (opts.showSubtitle && opts.subtitle) {
165 var subEl = makeEl('div', {className:'bkbg-grc-subtitle', style:{
166 color: opts.subtitleColor || '#6b7280'
167 }, textContent: opts.subtitle});
168 card.appendChild(subEl);
169 }
170
171 /* ── Result card ── */
172 var resultCard = makeEl('div', {className:'bkbg-grc-result-card', style:{
173 background: resultBg,
174 color: resultClr,
175 borderRadius: '12px',
176 padding: '20px 24px',
177 display: 'flex',
178 alignItems: 'center',
179 justifyContent: 'center',
180 gap: '24px',
181 marginBottom: '20px',
182 flexWrap: 'wrap'
183 }});
184
185 var pctEl = makeEl('div', {className:'bkbg-grc-result-pct'});
186 var modeEl = makeEl('div', {className:'bkbg-grc-result-label', style:{fontSize:'14px',opacity:'0.8',marginTop:'4px'}});
187 modeEl.textContent = opts.gradeMode === 'simple' ? 'Simple Average' : 'Weighted Average';
188 var avgBlock = makeEl('div', {className:'bkbg-grc-result-avg'}, [pctEl, modeEl]);
189 resultCard.appendChild(avgBlock);
190
191 var letterEl;
192 if (opts.showLetterGrade !== false) {
193 var divider = makeEl('div', {className:'bkbg-grc-result-divider', style:{width:'1px',height:'60px',background:'rgba(255,255,255,.25)',flexShrink:'0'}});
194 resultCard.appendChild(divider);
195 letterEl = makeEl('div', {className:'bkbg-grc-result-letter'});
196 var letterLabel = makeEl('div',{className:'bkbg-grc-result-letter-label',style:{fontSize:'13px',opacity:'0.8',marginTop:'4px'},textContent:'Letter Grade'});
197 var letterBlock = makeEl('div',{className:'bkbg-grc-result-letter-block',style:{textAlign:'center'}},[letterEl,letterLabel]);
198 resultCard.appendChild(letterBlock);
199 }
200 card.appendChild(resultCard);
201
202 /* ── Subjects table ── */
203 var tableWrap = makeEl('div', {className:'bkbg-grc-breakdown'});
204 if (opts.showBreakdown !== false) {
205 var tHead = makeEl('div',{className:'bkbg-grc-table-header',style:{
206 display:'grid',
207 gridTemplateColumns:'2fr 1fr 1fr 1fr 80px',
208 gap:'6px',
209 padding:'6px 10px',
210 marginBottom:'4px'
211 }});
212 ['Subject','Score','Max','Weight %',''].forEach(function(txt){
213 tHead.appendChild(makeEl('div',{className:'bkbg-grc-table-header-cell',style:{fontSize:'12px',fontWeight:'600',color:opts.labelColor||'#374151'},textContent:txt}));
214 });
215 tableWrap.appendChild(tHead);
216 card.appendChild(tableWrap);
217 }
218
219 /* ── Add Subject button ── */
220 var addBtn = makeEl('button',{className:'bkbg-grc-add-btn',style:{
221 marginTop:'8px',
222 padding:'9px 18px',
223 background:accent,
224 color:'#fff',
225 border:'none',
226 borderRadius:inpR,
227 fontWeight:'700',
228 cursor:'pointer',
229 fontSize:'14px'
230 },textContent:'+ Add Subject'});
231
232 /* ── Grade scale ── */
233 var scaleSection = makeEl('div',{style:{display: opts.showGradeScale !== false ? 'block' : 'none'}});
234
235 /* ── Render ── */
236 function renderSubjects() {
237 // Remove old rows (all except header)
238 var rows = tableWrap.querySelectorAll('.bkbg-grc-subject-row');
239 rows.forEach(function(r) { r.parentNode.removeChild(r); });
240
241 subjects.forEach(function(s) {
242 var spct = (parseFloat(s.max)||100) > 0 ? ((parseFloat(s.score)||0)/(parseFloat(s.max)||100))*100 : 0;
243 var sg = getLetter(spct, opts);
244
245 var row = makeEl('div',{className:'bkbg-grc-subject-row', style:{
246 display:'grid',
247 gridTemplateColumns:'2fr 1fr 1fr 1fr 80px',
248 gap:'6px',
249 alignItems:'center',
250 background:opts.rowBg||'#f9fafb',
251 border:'1px solid '+(opts.rowBorder||'#e5e7eb'),
252 borderRadius:'8px',
253 padding:'8px 10px',
254 marginBottom:'6px'
255 }});
256
257 var nameInp = input('text', s.name, 'Subject', function(v){ s.name=v; updateResult(); });
258 nameInp.style.borderRadius = inpR;
259 nameInp.style.borderColor = opts.inputBorder || '#e5e7eb';
260 var scoreInp = input('number', s.score, '', function(v){ s.score=parseFloat(v)||0; renderSubjects(); updateResult(); });
261 scoreInp.style.borderRadius = inpR;
262 scoreInp.style.borderColor = opts.inputBorder || '#e5e7eb';
263 var maxInp = input('number', s.max, '', function(v){ s.max=parseFloat(v)||100; renderSubjects(); updateResult(); });
264 maxInp.style.borderRadius = inpR;
265 maxInp.style.borderColor = opts.inputBorder || '#e5e7eb';
266 var wInp = input('number', s.weight, '', function(v){ s.weight=parseFloat(v)||0; updateResult(); });
267 wInp.style.borderRadius = inpR;
268 wInp.style.borderColor = opts.inputBorder || '#e5e7eb';
269
270 var badge = makeEl('span',{className:'bkbg-grc-grade-badge',style:{fontWeight:'700',fontSize:'13px',color:sg.color,minWidth:'28px',textAlign:'center'},textContent:sg.letter});
271 var removeBtn = makeEl('button',{className:'bkbg-grc-remove-btn',textContent:'×'});
272 removeBtn.addEventListener('click', function(){
273 subjects = subjects.filter(function(x){ return x.id !== s.id; });
274 renderSubjects();
275 updateResult();
276 });
277
278 var actions = makeEl('div',{style:{display:'flex',alignItems:'center',gap:'6px',justifyContent:'flex-end'}},[badge,removeBtn]);
279 row.appendChild(nameInp);
280 row.appendChild(scoreInp);
281 row.appendChild(maxInp);
282 row.appendChild(wInp);
283 row.appendChild(actions);
284 tableWrap.appendChild(row);
285 });
286 }
287
288 function updateResult() {
289 var avg = calcAvg(subjects, opts.gradeMode || 'weighted');
290 pctEl.textContent = avg.toFixed(1) + '%';
291 if (letterEl) {
292 var info = getLetter(avg, opts);
293 letterEl.textContent = info.letter;
294 }
295 renderScale();
296 }
297
298 /* ── Grade scale rendering ── */
299 function renderScale() {
300 scaleSection.innerHTML = '';
301 if (opts.showGradeScale === false) return;
302 var avg = calcAvg(subjects, opts.gradeMode || 'weighted');
303
304 scaleSection.appendChild(makeEl('div',{className:'bkbg-grc-scale-title',style:{
305 fontSize:'12px',fontWeight:'700',color:opts.labelColor||'#374151',
306 textTransform:'uppercase',letterSpacing:'.05em',marginBottom:'10px'
307 },textContent:'Grade Scale'}));
308
309 var grid = makeEl('div',{style:{display:'flex',flexWrap:'wrap',gap:'6px'}});
310 GRADE_SCALE.forEach(function(gs) {
311 var key = GRADE_COLOR_KEYS[gs.letter];
312 var clr = opts[key] || gs.dflt;
313 var active = avg >= gs.min && (gs.min === 0 || avg < (GRADE_SCALE[GRADE_SCALE.indexOf(gs)-1]||{min:101}).min);
314 var item = makeEl('div',{style:{
315 background: active ? clr+'22' : (opts.scaleBg||'#f3f4f6'),
316 border: '2px solid ' + clr,
317 borderRadius:'6px',
318 padding:'5px 10px',
319 textAlign:'center',
320 minWidth:'52px',
321 transition:'background .2s'
322 }});
323 item.appendChild(makeEl('div',{style:{fontWeight:'800',fontSize:'14px',color:clr},textContent:gs.letter}));
324 item.appendChild(makeEl('div',{style:{fontSize:'11px',color:'#6b7280'},textContent:gs.min+'%+'}));
325 grid.appendChild(item);
326 });
327 scaleSection.appendChild(grid);
328 }
329
330 addBtn.addEventListener('click', function(){
331 nextId++;
332 subjects.push({id:nextId, name:'', score:0, max:100, weight:0});
333 renderSubjects();
334 updateResult();
335 });
336
337 var scaleWrap = makeEl('div',{style:{
338 marginTop:'20px',
339 background: opts.scaleBg||'#f3f4f6',
340 border:'1px solid '+(opts.scaleBorder||'#e5e7eb'),
341 borderRadius:'10px',
342 padding:'14px 16px'
343 }});
344 scaleWrap.appendChild(scaleSection);
345
346 if (opts.showBreakdown !== false) {
347 tableWrap.appendChild(addBtn);
348 }
349 card.appendChild(scaleWrap);
350 app.appendChild(card);
351
352 renderSubjects();
353 updateResult();
354 }
355
356 document.querySelectorAll('.bkbg-grc-app').forEach(initApp);
357 })();
358