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 / index.js

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

208 lines 15.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ( function () {
2 var el = wp.element.createElement;
3 var useState = wp.element.useState;
4 var Fragment = wp.element.Fragment;
5 var registerBlockType = wp.blocks.registerBlockType;
6 var __ = wp.i18n.__;
7 var InspectorControls = wp.blockEditor.InspectorControls;
8 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
9 var useBlockProps = wp.blockEditor.useBlockProps;
10 var PanelBody = wp.components.PanelBody;
11 var RangeControl = wp.components.RangeControl;
12 var TextControl = wp.components.TextControl;
13 var ToggleControl = wp.components.ToggleControl;
14
15 var _tc; function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
16 var _tv; function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
17
18 // ── Readability algorithms ──────────────────────────────────────────────
19 function countSyllables(word) {
20 word = word.toLowerCase().replace(/[^a-z]/g,'');
21 if (!word.length) return 0;
22 if (word.length <= 3) return 1;
23 word = word.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, '');
24 word = word.replace(/^y/, '');
25 var m = word.match(/[aeiouy]{1,2}/g);
26 return m ? m.length : 1;
27 }
28 function countComplexWords(words) {
29 return words.filter(function(w){ return countSyllables(w) >= 3; }).length;
30 }
31 function analyze(text) {
32 if (!text || !text.trim()) return null;
33 var sentences = text.trim().split(/[.!?]+/).filter(function(s){ return s.trim().length > 0; }).length || 1;
34 var wordList = text.trim().split(/\s+/).filter(function(w){ return w.replace(/[^a-zA-Z]/g,'').length > 0; });
35 var words = wordList.length || 1;
36 var syllables = wordList.reduce(function(n,w){ return n + countSyllables(w); }, 0) || 1;
37 var complex = countComplexWords(wordList);
38
39 var fleschEase = Math.round(206.835 - 1.015*(words/sentences) - 84.6*(syllables/words));
40 fleschEase = Math.min(100, Math.max(0, fleschEase));
41 var gradeLevel = Math.max(0, Math.round((0.39*(words/sentences) + 11.8*(syllables/words) - 15.59) * 10) / 10);
42 var gunningFog = Math.max(0, Math.round(0.4*((words/sentences) + 100*(complex/words)) * 10) / 10);
43 var readingTime = Math.ceil(words / 200); // 200 wpm average
44
45 return { fleschEase:fleschEase, gradeLevel:gradeLevel, gunningFog:gunningFog,
46 words:words, sentences:sentences, syllables:syllables,
47 avgWordsPerSentence:Math.round((words/sentences)*10)/10,
48 avgSyllablesPerWord:Math.round((syllables/words)*10)/10,
49 readingTime:readingTime };
50 }
51 function gaugeInfo(score) {
52 if (score >= 90) return { label:'Very Easy', color:'#10b981', grade:'5th grade' };
53 if (score >= 70) return { label:'Easy', color:'#22c55e', grade:'6th grade' };
54 if (score >= 60) return { label:'Fairly Easy', color:'#84cc16', grade:'7th grade' };
55 if (score >= 50) return { label:'Standard', color:'#f59e0b', grade:'8-9th grade' };
56 if (score >= 30) return { label:'Fairly Difficult',color:'#f97316', grade:'10-12th grade' };
57 if (score >= 10) return { label:'Difficult', color:'#ef4444', grade:'College' };
58 return { label:'Very Confusing', color:'#dc2626', grade:'Professional' };
59 }
60
61 function ReadabilityPreview(props) {
62 var a = props.attributes;
63 var accent = a.accentColor || '#6c3fb5';
64
65 var _text = useState(''); var text = _text[0]; var setText = _text[1];
66 var result = analyze(text);
67 var info = result ? gaugeInfo(result.fleschEase) : null;
68
69 var inputStyle = {padding:'10px 12px',borderRadius:'6px',border:'1.5px solid '+(a.textareaBorder||'#e5e7eb'),fontSize:'14px',fontFamily:'inherit',outline:'none',background:a.textareaBg||'#f9fafb',resize:'vertical',transition:'border-color .2s'};
70 var labelStyle = {fontSize:'12px',fontWeight:600,color:a.labelColor||'#374151',textTransform:'uppercase',letterSpacing:'.05em'};
71
72 return el('div', {style:{paddingTop:(a.paddingTop||60)+'px',paddingBottom:(a.paddingBottom||60)+'px',background:a.sectionBg||undefined}},
73 el('div', {style:{background:a.cardBg,borderRadius:(a.cardRadius||16)+'px',padding:'36px 32px',maxWidth:(a.maxWidth||620)+'px',margin:'0 auto',boxShadow:'0 4px 24px rgba(0,0,0,.09)'}},
74
75 (a.showTitle||a.showSubtitle) && el('div', {style:{marginBottom:'22px'}},
76 a.showTitle && el('div', {className:'bkras-title',style:{color:a.titleColor,marginBottom:'6px'}}, a.title),
77 a.showSubtitle && el('div', {className:'bkras-subtitle',style:{color:a.subtitleColor,opacity:.75}}, a.subtitle)
78 ),
79
80 // Textarea
81 el('div', {style:{marginBottom:'20px'}},
82 el('label', {style:{display:'block',marginBottom:'6px',...labelStyle}}, 'Your Text'),
83 el('textarea', {rows:6,value:text,placeholder:a.placeholder||'Paste or type your text here...', style:{...inputStyle,width:'100%',display:'block'}, onChange:function(e){setText(e.target.value);}})
84 ),
85
86 !result && el('div', {style:{padding:'20px',textAlign:'center',color:'#9ca3af',fontSize:'15px'}}, 'Start typing or paste text to see the analysis'),
87
88 result && el(Fragment, null,
89
90 // Stats grid
91 a.showStats && el('div', {style:{display:'grid',gridTemplateColumns:'repeat(auto-fit,minmax(100px,1fr))',gap:'10px',marginBottom:'20px'}},
92 [
93 {label:'Words', val:result.words},
94 {label:'Sentences', val:result.sentences},
95 {label:'Syllables', val:result.syllables},
96 {label:'Avg Words/Sent', val:result.avgWordsPerSentence},
97 {label:'Avg Syll/Word', val:result.avgSyllablesPerWord},
98 ...(a.showReadingTime ? [{label:'Read Time',val:result.readingTime+'m'}] : [])
99 ].map(function(s){
100 return el('div', {key:s.label, style:{background:a.statBg||'#f3f4f6',border:'1px solid '+(a.statBorder||'#e5e7eb'),borderRadius:'8px',padding:'12px 10px',textAlign:'center'}},
101 el('div', {style:{fontSize:'24px',fontWeight:700,color:a.statValueColor||'#111827',lineHeight:1.1}}, s.val),
102 el('div', {style:{fontSize:'11px',color:a.statLabelColor||'#6b7280',marginTop:'4px'}}, s.label)
103 );
104 })
105 ),
106
107 // Gauge
108 a.showGauge && a.showFleschEase && el('div', {style:{marginBottom:'20px'}},
109 el('div', {style:{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:'8px'}},
110 el('span', {style:labelStyle}, 'Flesch Reading Ease'),
111 el('span', {style:{fontWeight:700,color:info.color,fontSize:'18px'}}, result.fleschEase+''+info.label)
112 ),
113 el('div', {style:{background:a.gaugeTrackColor||'#e5e7eb',borderRadius:'20px',height:'12px',overflow:'hidden'}},
114 el('div', {style:{width:result.fleschEase+'%',height:'100%',background:info.color,borderRadius:'20px',transition:'width .4s ease'}})
115 ),
116 el('div', {style:{display:'flex',justifyContent:'space-between',marginTop:'5px',fontSize:'11px',color:'#9ca3af'}},
117 el('span', null, '0 – Very Confusing'),
118 el('span', null, '100 – Very Easy')
119 )
120 ),
121
122 // Score cards row
123 el('div', {style:{display:'grid',gridTemplateColumns:'1fr 1fr',gap:'10px'}},
124 a.showGradeLevel && el('div', {style:{background:a.scoreBg||'#f9fafb',border:'1px solid '+(a.statBorder||'#e5e7eb'),borderRadius:'10px',padding:'14px 16px'}},
125 el('div', {style:{fontSize:'12px',color:a.statLabelColor||'#6b7280',textTransform:'uppercase',letterSpacing:'.05em',marginBottom:'4px'}}, 'Flesch-Kincaid Grade'),
126 el('div', {style:{fontSize:'26px',fontWeight:700,color:a.statValueColor||'#111827'}}, result.gradeLevel),
127 el('div', {style:{fontSize:'12px',color:'#9ca3af',marginTop:'4px'}}, info ? info.grade : '')
128 ),
129 a.showGunningFog && el('div', {style:{background:a.scoreBg||'#f9fafb',border:'1px solid '+(a.statBorder||'#e5e7eb'),borderRadius:'10px',padding:'14px 16px'}},
130 el('div', {style:{fontSize:'12px',color:a.statLabelColor||'#6b7280',textTransform:'uppercase',letterSpacing:'.05em',marginBottom:'4px'}}, 'Gunning Fog Index'),
131 el('div', {style:{fontSize:'26px',fontWeight:700,color:a.statValueColor||'#111827'}}, result.gunningFog),
132 el('div', {style:{fontSize:'12px',color:'#9ca3af',marginTop:'4px'}}, 'Years of education needed')
133 )
134 )
135 )
136 )
137 );
138 }
139
140 registerBlockType('blockenberg/readability-score', {
141 edit: function(props) {
142 var a = props.attributes; var set = props.setAttributes;
143 var TC = getTypoControl();
144 var blockProps = useBlockProps((function() {
145 var _tvFn = getTypoCssVars();
146 var s = {};
147 if (_tvFn) {
148 Object.assign(s, _tvFn(a.titleTypo || {}, '--bkras-tt-'));
149 Object.assign(s, _tvFn(a.subtitleTypo || {}, '--bkras-st-'));
150 }
151 return { style: s };
152 })());
153 var colorSettings = [
154 { value:a.accentColor, onChange:function(v){set({accentColor:v});}, label:'Accent Color' },
155 { value:a.cardBg, onChange:function(v){set({cardBg:v});}, label:'Card Background' },
156 { value:a.textareaBg, onChange:function(v){set({textareaBg:v});}, label:'Textarea Background' },
157 { value:a.textareaBorder, onChange:function(v){set({textareaBorder:v});}, label:'Textarea Border' },
158 { value:a.statBg, onChange:function(v){set({statBg:v});}, label:'Stat Card Background' },
159 { value:a.statBorder, onChange:function(v){set({statBorder:v});}, label:'Stat Card Border' },
160 { value:a.statValueColor, onChange:function(v){set({statValueColor:v});}, label:'Stat Value Color' },
161 { value:a.statLabelColor, onChange:function(v){set({statLabelColor:v});}, label:'Stat Label Color' },
162 { value:a.gaugeTrackColor,onChange:function(v){set({gaugeTrackColor:v});}, label:'Gauge Track Color' },
163 { value:a.scoreBg, onChange:function(v){set({scoreBg:v});}, label:'Score Card Background' },
164 { value:a.labelColor, onChange:function(v){set({labelColor:v});}, label:'Label Color' },
165 { value:a.titleColor, onChange:function(v){set({titleColor:v});}, label:'Title Color' },
166 { value:a.subtitleColor, onChange:function(v){set({subtitleColor:v});}, label:'Subtitle Color' },
167 { value:a.sectionBg, onChange:function(v){set({sectionBg:v});}, label:'Section Background' }
168 ];
169 return el(Fragment, null,
170 el(InspectorControls, null,
171 el(PanelBody,{title:'Header',initialOpen:false},
172 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Title', checked:a.showTitle, onChange:function(v){set({showTitle:v});}}),
173 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Subtitle',checked:a.showSubtitle,onChange:function(v){set({showSubtitle:v});}}),
174 el(TextControl,{label:'Title', value:a.title, onChange:function(v){set({title:v});}}),
175 el(TextControl,{label:'Subtitle',value:a.subtitle, onChange:function(v){set({subtitle:v});}}),
176 el(TextControl,{label:'Textarea Placeholder',value:a.placeholder,onChange:function(v){set({placeholder:v});}})
177 ),
178 el(PanelBody,{title:'Metrics to Show',initialOpen:true},
179 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Gauge', checked:a.showGauge, onChange:function(v){set({showGauge:v});}}),
180 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Flesch Reading Ease',checked:a.showFleschEase, onChange:function(v){set({showFleschEase:v});}}),
181 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show FK Grade Level', checked:a.showGradeLevel, onChange:function(v){set({showGradeLevel:v});}}),
182 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Gunning Fog', checked:a.showGunningFog, onChange:function(v){set({showGunningFog:v});}}),
183 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Stats Grid', checked:a.showStats, onChange:function(v){set({showStats:v});}}),
184 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Reading Time', checked:a.showReadingTime, onChange:function(v){set({showReadingTime:v});}})
185 ),
186
187 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
188 TC && el(TC, { label: __('Title', 'blockenberg'), value: a.titleTypo || {}, onChange: function(v) { set({ titleTypo: v }); } }),
189 TC && el(TC, { label: __('Subtitle', 'blockenberg'), value: a.subtitleTypo || {}, onChange: function(v) { set({ subtitleTypo: v }); } })
190 ),
191 el(PanelColorSettings,{title:'Colors',initialOpen:false,colorSettings:colorSettings}),
192 el(PanelBody,{title:'Sizing & Layout',initialOpen:false},
193 el(RangeControl,{label:'Card Border Radius',value:a.cardRadius, min:0, max:40, step:1, onChange:function(v){set({cardRadius:v});}}),
194 el(RangeControl,{label:'Max Width (px)', value:a.maxWidth, min:340,max:960,step:10,onChange:function(v){set({maxWidth:v});}}),
195 el(RangeControl,{label:'Padding Top (px)', value:a.paddingTop, min:0, max:160,step:4, onChange:function(v){set({paddingTop:v});}}),
196 el(RangeControl,{label:'Padding Bottom (px)',value:a.paddingBottom,min:0,max:160,step:4,onChange:function(v){set({paddingBottom:v});}})
197 )
198 ),
199 el('div', blockProps, el(ReadabilityPreview, {attributes:a}))
200 );
201 },
202 save: function(props) {
203 var a = props.attributes;
204 return el('div', wp.blockEditor.useBlockProps.save(), el('div', {className:'bkbg-rs-app','data-opts':JSON.stringify(a)}));
205 }
206 });
207 }() );
208