| 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 |
|