Spaces:
Running
Running
File size: 5,694 Bytes
e03836d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | const STOPWORDS = {
en: new Set('the a an and or but if while of to in on for from with without by as is are was were be been being this that these those it its into about over under between through can may should would could will not no than then also'.split(' ')),
es: new Set('el la los las un una unos unas y o pero si de del a en por para con sin como es son fue fueron ser sido este esta estos estas esto que se su sus más menos también'.split(' ')),
pt: new Set('o a os as um uma uns umas e ou mas se de do da dos das em por para com sem como é são foi foram ser sido este esta estes estas isso que seu sua seus suas mais menos também'.split(' ')),
};
const LANG_MARKERS = {
es: [' el ', ' la ', ' de ', ' que ', ' para ', ' investigación ', ' educación ', ' datos ', ' análisis '],
pt: [' o ', ' a ', ' de ', ' que ', ' para ', ' pesquisa ', ' educação ', ' dados ', ' análise '],
en: [' the ', ' and ', ' of ', ' that ', ' for ', ' research ', ' education ', ' data ', ' analysis '],
};
const TOPICS = {
'Artificial Intelligence': ['artificial intelligence','inteligencia artificial','inteligência artificial','machine learning','aprendizaje automático','aprendizado de máquina','generative ai','ia generativa'],
'Research': ['research','investigación','pesquisa','methodology','metodología','metodologia','evidence','evidencia','literature review','revisión bibliográfica'],
'Education': ['education','educación','educação','university','universidad','universidade','student','estudiante','estudante','teaching','docencia','ensino'],
'Business & Management': ['business','management','empresa','gestión','gestão','organization','organización','organização','strategy','estrategia'],
'Economics & Trade': ['economy','economía','economia','trade','comercio','market','mercado','investment','inversión','investimento'],
'Data & Analytics': ['data','datos','dados','analytics','análisis','análise','visualization','visualización','visualização','dashboard'],
'Policy & Governance': ['policy','política','governance','gobernanza','governança','government','gobierno','governo','institution','institución','instituição'],
'Automation & Productivity': ['automation','automatización','automação','productivity','productividad','produtividade','workflow','process','proceso','processo'],
};
function normalize(text){ return text.normalize('NFKC').replace(/\s+/g,' ').trim(); }
function words(text){ return (text.toLowerCase().match(/[\p{L}][\p{L}\p{M}'’-]*/gu)||[]).filter(Boolean); }
function splitSentences(text){ return text.replace(/\n+/g,' ').split(/(?<=[.!?])\s+/).map(s=>s.trim()).filter(s=>s.length>20); }
function paragraphs(text){ return text.split(/\n\s*\n+/).map(p=>p.trim()).filter(Boolean); }
function detectLanguage(text){
const padded = ` ${normalize(text).toLowerCase()} `;
const scores = Object.fromEntries(Object.entries(LANG_MARKERS).map(([lang, markers])=>[lang, markers.reduce((s,m)=>s+(padded.includes(m)?1:0),0)]));
return Object.entries(scores).sort((a,b)=>b[1]-a[1])[0][0];
}
function keywordTable(text, lang, limit=15){
const stop = STOPWORDS[lang] || STOPWORDS.en;
const toks = words(text).filter(w=>w.length>2 && !stop.has(w));
const counts = new Map();
toks.forEach(t=>counts.set(t,(counts.get(t)||0)+1));
const bigrams=[];
for(let i=0;i<toks.length-1;i++){ if(toks[i]!==toks[i+1]) bigrams.push(`${toks[i]} ${toks[i+1]}`); }
bigrams.forEach(b=>counts.set(b,(counts.get(b)||0)+0.65));
return [...counts.entries()].filter(([,v])=>v>=1).sort((a,b)=>b[1]-a[1]).slice(0,limit).map(([term,v])=>({term,frequency:Number(v.toFixed(2))}));
}
function topicSignals(text){
const lower = normalize(text).toLowerCase();
return Object.entries(TOPICS).map(([topic,terms])=>{
const score = terms.reduce((sum,t)=>sum+(lower.split(t).length-1),0);
return {topic,score};
}).filter(x=>x.score>0).sort((a,b)=>b.score-a.score);
}
function summarize(text, lang, maxSentences=4){
const sents = splitSentences(text);
if(sents.length<=maxSentences) return sents.join(' ');
const keys = keywordTable(text,lang,20);
const weights = new Map(keys.map((k,i)=>[k.term,(keys.length-i)/keys.length]));
const scored=sents.map((s,i)=>{
const low=s.toLowerCase(); let score=0;
for(const [k,w] of weights){ if(low.includes(k)) score+=w; }
score += Math.max(0,0.15-(i/sents.length)*0.08);
return {i,s,score:score/Math.max(words(s).length,8)};
});
const chosen=scored.sort((a,b)=>b.score-a.score).slice(0,maxSentences).sort((a,b)=>a.i-b.i);
return chosen.map(x=>x.s).join(' ');
}
function analyzeText(raw){
const text = raw.trim();
if(text.length<40) throw new Error('Please provide at least 40 characters of text.');
const lang=detectLanguage(text); const toks=words(text); const sents=splitSentences(text); const paras=paragraphs(text);
const unique=new Set(toks.map(x=>x.toLowerCase()));
return {
language: lang==='es'?'Spanish':lang==='pt'?'Portuguese':'English',
languageCode: lang,
words:toks.length,
sentences:sents.length,
paragraphs:Math.max(paras.length,1),
readingTime:Math.max(1,Math.round(toks.length/220)),
avgSentence:Number((toks.length/Math.max(sents.length,1)).toFixed(1)),
lexicalDiversity:Number((unique.size/Math.max(toks.length,1)).toFixed(3)),
summary:summarize(text,lang),
keywords:keywordTable(text,lang),
topics:topicSignals(text),
};
}
if(typeof module!=='undefined') module.exports={analyzeText,detectLanguage,keywordTable,topicSignals,summarize};
if(typeof window!=='undefined') window.ResearchLensEngine={analyzeText,detectLanguage,keywordTable,topicSignals,summarize};
|