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 / readability-score / frontend.js

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

283 lines 14.2 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 // ── Readability algorithms ──────────────────────────────────────────────
5 function countSyllables(word) {
6 word = word.toLowerCase().replace(/[^a-z]/g, '');
7 if (!word.length) return 0;
8 if (word.length <= 3) return 1;
9 word = word.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, '');
10 word = word.replace(/^y/, '');
11 var m = word.match(/[aeiouy]{1,2}/g);
12 return m ? m.length : 1;
13 }
14
15 function analyze(text) {
16 if (!text || !text.trim()) return null;
17 var sentences = text.trim().split(/[.!?]+/).filter(function(s){ return s.trim().length > 0; }).length || 1;
18 var wordList = text.trim().split(/\s+/).filter(function(w){ return w.replace(/[^a-zA-Z]/g,'').length > 0; });
19 var words = wordList.length || 1;
20 var syllables = wordList.reduce(function(n,w){ return n + countSyllables(w); }, 0) || 1;
21 var complex = wordList.filter(function(w){ return countSyllables(w) >= 3; }).length;
22
23 var fe = 206.835 - 1.015*(words/sentences) - 84.6*(syllables/words);
24 var fleschEase = Math.min(100, Math.max(0, Math.round(fe)));
25 var gradeLevel = Math.max(0, Math.round((0.39*(words/sentences) + 11.8*(syllables/words) - 15.59)*10)/10);
26 var gunningFog = Math.max(0, Math.round(0.4*((words/sentences) + 100*(complex/words))*10)/10);
27 var readingTime = Math.max(1, Math.ceil(words/200));
28
29 return {
30 fleschEase: fleschEase,
31 gradeLevel: gradeLevel,
32 gunningFog: gunningFog,
33 words: words,
34 sentences: sentences,
35 syllables: syllables,
36 avgWordsPerSentence:Math.round((words/sentences)*10)/10,
37 avgSyllablesPerWord:Math.round((syllables/words)*10)/10,
38 readingTime: readingTime
39 };
40 }
41
42 function gaugeInfo(score) {
43 if (score >= 90) return { label:'Very Easy', color:'#10b981', grade:'5th grade' };
44 if (score >= 70) return { label:'Easy', color:'#22c55e', grade:'6th grade' };
45 if (score >= 60) return { label:'Fairly Easy', color:'#84cc16', grade:'7th grade' };
46 if (score >= 50) return { label:'Standard', color:'#f59e0b', grade:'8–9th grade' };
47 if (score >= 30) return { label:'Fairly Difficult', color:'#f97316', grade:'10–12th grade' };
48 if (score >= 10) return { label:'Difficult', color:'#ef4444', grade:'College' };
49 return { label:'Very Confusing', color:'#dc2626', grade:'Professional' };
50 }
51
52 var _typoKeys = {
53 family:'font-family', weight:'font-weight', style:'font-style',
54 decoration:'text-decoration', transform:'text-transform',
55 sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m',
56 lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m',
57 letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m',
58 wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m'
59 };
60 function typoCssVarsForEl(el, obj, prefix) {
61 if (!obj || typeof obj !== 'object') return;
62 Object.keys(_typoKeys).forEach(function (k) {
63 var v = obj[k];
64 if (v === undefined || v === '' || v === null) return;
65 if (k === 'sizeDesktop' || k === 'sizeTablet' || k === 'sizeMobile') v = v + (obj.sizeUnit || 'px');
66 else if (k === 'lineHeightDesktop' || k === 'lineHeightTablet' || k === 'lineHeightMobile') v = v + (obj.lineHeightUnit || '');
67 else if (k === 'letterSpacingDesktop' || k === 'letterSpacingTablet' || k === 'letterSpacingMobile') v = v + (obj.letterSpacingUnit || 'px');
68 else if (k === 'wordSpacingDesktop' || k === 'wordSpacingTablet' || k === 'wordSpacingMobile') v = v + (obj.wordSpacingUnit || 'px');
69 el.style.setProperty(prefix + _typoKeys[k], String(v));
70 });
71 }
72
73 function initReadabilityScore(app) {
74 var opts;
75 try { opts = JSON.parse(app.getAttribute('data-opts') || '{}'); } catch(e) { opts = {}; }
76
77 var accent = opts.accentColor || '#6c3fb5';
78 var cardBg = opts.cardBg || '#ffffff';
79 var textareaBg = opts.textareaBg || '#f9fafb';
80 var textareaBorder= opts.textareaBorder || '#e5e7eb';
81 var statBg = opts.statBg || '#f3f4f6';
82 var statBorder = opts.statBorder || '#e5e7eb';
83 var statValueColor= opts.statValueColor || '#111827';
84 var statLabelColor= opts.statLabelColor || '#6b7280';
85 var gaugeTrack = opts.gaugeTrackColor || '#e5e7eb';
86 var scoreBg = opts.scoreBg || '#f9fafb';
87 var labelColor = opts.labelColor || '#374151';
88 var titleColor = opts.titleColor || '';
89 var subtitleColor = opts.subtitleColor || '';
90 var sectionBg = opts.sectionBg || '';
91
92 var maxWidth = (opts.maxWidth || 620) + 'px';
93 var cardRadius = (opts.cardRadius || 16) + 'px';
94 var paddingTop = (opts.paddingTop != null ? opts.paddingTop : 60) + 'px';
95 var paddingBottom = (opts.paddingBottom != null ? opts.paddingBottom : 60) + 'px';
96
97 var showFleschEase = opts.showFleschEase !== false;
98 var showGradeLevel = opts.showGradeLevel !== false;
99 var showGunningFog = opts.showGunningFog !== false;
100 var showStats = opts.showStats !== false;
101 var showGauge = opts.showGauge !== false;
102 var showReadingTime = opts.showReadingTime !== false;
103
104 // Set CSS custom property for focus ring
105 app.style.setProperty('--bkbg-rs-accent', accent);
106 typoCssVarsForEl(app, opts.titleTypo || {}, '--bkras-tt-');
107 typoCssVarsForEl(app, opts.subtitleTypo || {}, '--bkras-st-');
108 app.style.paddingTop = paddingTop;
109 app.style.paddingBottom = paddingBottom;
110 if (sectionBg) app.style.background = sectionBg;
111
112 // Wrapper
113 var wrap = document.createElement('div');
114 wrap.className = 'bkbg-rs-wrap';
115 wrap.style.cssText = 'background:' + cardBg + ';border-radius:' + cardRadius + ';max-width:' + maxWidth;
116 app.appendChild(wrap);
117
118 // Header
119 var showTitle = opts.showTitle && opts.title;
120 var showSubtitle = opts.showSubtitle && opts.subtitle;
121 if (showTitle || showSubtitle) {
122 var header = document.createElement('div');
123 header.className = 'bkbg-rs-header';
124 if (showTitle) {
125 var h = document.createElement('div');
126 h.className = 'bkbg-rs-title bkras-title';
127 if (titleColor) h.style.color = titleColor;
128 h.textContent = opts.title;
129 header.appendChild(h);
130 }
131 if (showSubtitle) {
132 var sub = document.createElement('div');
133 sub.className = 'bkbg-rs-subtitle bkras-subtitle';
134 if (subtitleColor) sub.style.color = subtitleColor;
135 sub.textContent = opts.subtitle;
136 header.appendChild(sub);
137 }
138 wrap.appendChild(header);
139 }
140
141 // Textarea section
142 var labelEl = document.createElement('label');
143 labelEl.className = 'bkbg-rs-label';
144 labelEl.style.color = labelColor;
145 labelEl.textContent = 'Your Text';
146
147 var textarea = document.createElement('textarea');
148 textarea.className = 'bkbg-rs-textarea';
149 textarea.rows = 6;
150 textarea.placeholder = opts.placeholder || 'Paste or type your text here...';
151 textarea.style.cssText = 'background:' + textareaBg + ';border:1.5px solid ' + textareaBorder;
152
153 var textareaWrap = document.createElement('div');
154 textareaWrap.style.marginBottom = '20px';
155 textareaWrap.appendChild(labelEl);
156 textareaWrap.appendChild(textarea);
157 wrap.appendChild(textareaWrap);
158
159 // Results container
160 var results = document.createElement('div');
161 wrap.appendChild(results);
162
163 function renderEmpty() {
164 results.innerHTML = '<div class="bkbg-rs-empty">Start typing or paste text to see the analysis…</div>';
165 }
166
167 function renderResults(data) {
168 results.innerHTML = '';
169 var info = gaugeInfo(data.fleschEase);
170
171 // Stats grid
172 if (showStats) {
173 var statsGrid = document.createElement('div');
174 statsGrid.className = 'bkbg-rs-stats-grid';
175 var statsData = [
176 { label:'Words', val:data.words },
177 { label:'Sentences', val:data.sentences },
178 { label:'Syllables', val:data.syllables },
179 { label:'Avg Words/Sent', val:data.avgWordsPerSentence },
180 { label:'Avg Syll/Word', val:data.avgSyllablesPerWord }
181 ];
182 if (showReadingTime) statsData.push({ label:'Read Time', val:data.readingTime+'m' });
183 statsData.forEach(function(s) {
184 var card = document.createElement('div');
185 card.className = 'bkbg-rs-stat';
186 card.style.cssText = 'background:' + statBg + ';border:1px solid ' + statBorder;
187 var valEl = document.createElement('div');
188 valEl.className = 'bkbg-rs-stat-val';
189 valEl.style.color = statValueColor;
190 valEl.textContent = s.val;
191 var lblEl = document.createElement('div');
192 lblEl.className = 'bkbg-rs-stat-lbl';
193 lblEl.style.color = statLabelColor;
194 lblEl.textContent = s.label;
195 card.appendChild(valEl);
196 card.appendChild(lblEl);
197 statsGrid.appendChild(card);
198 });
199 results.appendChild(statsGrid);
200 }
201
202 // Gauge
203 if (showGauge && showFleschEase) {
204 var gaugeSection = document.createElement('div');
205 gaugeSection.className = 'bkbg-rs-gauge-section';
206
207 var gHeader = document.createElement('div');
208 gHeader.className = 'bkbg-rs-gauge-header';
209 var gLabel = document.createElement('span');
210 gLabel.className = 'bkbg-rs-gauge-label';
211 gLabel.style.color = labelColor;
212 gLabel.textContent = 'Flesch Reading Ease';
213 var gScore = document.createElement('span');
214 gScore.className = 'bkbg-rs-gauge-score';
215 gScore.style.color = info.color;
216 gScore.textContent = data.fleschEase + '' + info.label;
217 gHeader.appendChild(gLabel);
218 gHeader.appendChild(gScore);
219
220 var gTrack = document.createElement('div');
221 gTrack.className = 'bkbg-rs-gauge-track';
222 gTrack.style.background = gaugeTrack;
223 var gFill = document.createElement('div');
224 gFill.className = 'bkbg-rs-gauge-fill';
225 gFill.style.cssText = 'width:' + data.fleschEase + '%;background:' + info.color;
226 gTrack.appendChild(gFill);
227
228 var gRange = document.createElement('div');
229 gRange.className = 'bkbg-rs-gauge-range';
230 var gLeft = document.createElement('span'); gLeft.textContent = '0 – Very Confusing';
231 var gRight = document.createElement('span'); gRight.textContent = '100 – Very Easy';
232 gRange.appendChild(gLeft);
233 gRange.appendChild(gRight);
234
235 gaugeSection.appendChild(gHeader);
236 gaugeSection.appendChild(gTrack);
237 gaugeSection.appendChild(gRange);
238 results.appendChild(gaugeSection);
239 }
240
241 // Score cards
242 if (showGradeLevel || showGunningFog) {
243 var scoresRow = document.createElement('div');
244 scoresRow.className = 'bkbg-rs-scores-row';
245
246 if (showGradeLevel) {
247 var glCard = document.createElement('div');
248 glCard.className = 'bkbg-rs-score-card';
249 glCard.style.cssText = 'background:' + scoreBg + ';border:1px solid ' + statBorder;
250 glCard.innerHTML = '<div class="bkbg-rs-score-name" style="color:' + statLabelColor + '">Flesch-Kincaid Grade</div>' +
251 '<div class="bkbg-rs-score-val" style="color:' + statValueColor + '">' + data.gradeLevel + '</div>' +
252 '<div class="bkbg-rs-score-desc">' + info.grade + '</div>';
253 scoresRow.appendChild(glCard);
254 }
255 if (showGunningFog) {
256 var gfCard = document.createElement('div');
257 gfCard.className = 'bkbg-rs-score-card';
258 gfCard.style.cssText = 'background:' + scoreBg + ';border:1px solid ' + statBorder;
259 gfCard.innerHTML = '<div class="bkbg-rs-score-name" style="color:' + statLabelColor + '">Gunning Fog Index</div>' +
260 '<div class="bkbg-rs-score-val" style="color:' + statValueColor + '">' + data.gunningFog + '</div>' +
261 '<div class="bkbg-rs-score-desc">Years of education needed</div>';
262 scoresRow.appendChild(gfCard);
263 }
264 results.appendChild(scoresRow);
265 }
266 }
267
268 renderEmpty();
269
270 var debounceTimer;
271 textarea.addEventListener('input', function() {
272 clearTimeout(debounceTimer);
273 debounceTimer = setTimeout(function() {
274 var data = analyze(textarea.value);
275 if (data) { renderResults(data); }
276 else { renderEmpty(); }
277 }, 250);
278 });
279 }
280
281 document.querySelectorAll('.bkbg-rs-app').forEach(initReadabilityScore);
282 })();
283