Last active
January 7, 2025 15:19
-
-
Save derekmc/19bc63b1c270b8fc3a5afa69a3722747 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<style> | |
/* CSS */ | |
body{ | |
font-family: sans-serif; | |
text-align: center; | |
} | |
input[type=number]{ | |
width: 35px; | |
} | |
#wordchecker{ | |
display: inline-block; | |
font-size: 20px; | |
background: #00a; | |
color: #fff; | |
padding: 20px; | |
width: 250px; | |
text-align: center; | |
} | |
#wordindex{ | |
display: inline-block; | |
font-size: 20px; | |
background: #235; | |
color: #fff; | |
padding: 20px; | |
width: 60px; | |
text-align: center; | |
} | |
button{ | |
background: #000; | |
color: #fff; | |
font-size: 16px; | |
border-radius: 10px; | |
padding: 8px 16px; | |
border: none; | |
font-weight: bold; | |
} | |
#excludelist, #includelist{ | |
font-family: monospace; | |
width: 400px; | |
} | |
/* END */ | |
</style> | |
</head> | |
<body> | |
<div class="markdown-src"> | |
<!-- HTML --> | |
# Dictionary Filter Tool <hr> | |
## Load Dictionary | |
Dictionary URL  | |
<input type="text" id='dicturl_input' | |
value="https://raw.githubusercontent.com/first20hours/google-10000-english/refs/heads/master/20k.txt"> | |
<br><br> | |
<button onclick='loadDict()'>Load</button> | |
<br><br> | |
<hr> | |
## Filter | |
Min Length <input id='minlen' type='number' value='2' onchange='updateWords()'> | |
 Max Length <input id='maxlen' type='number' value='5' onchange='updateWords()'> | |
<br><br> | |
<span id="wordindex">000</span> | |
<span id="wordchecker">Test Word</span> | |
<br><br> | |
<button onclick="prev()" title="Left Arrow">Prev</button> | |
<button onclick="reject()" title="Space">Reject</button> | |
<button onclick="accept()" title="Enter">Next</button> | |
<br> | |
Left     Enter    Right | |
<hr> | |
<h3> Exclude List </h3> | |
<span id='excludelist'> | |
The quick brown fox | |
</span> | |
<br><br> | |
<button onclick='copyExclude()'>Copy Exclude</button> | |
<hr> | |
<h3> Include List </h3> | |
<span id="includelist"></span> | |
<br><br> | |
<button onclick='copyInclude()'>Copy Include</button> | |
<!-- END --> | |
</div> | |
<div style="display: none;"></div> | |
<script> | |
let datakey = "__HTML_NOTES__:notedataid=KsfJOvIznj" | |
let inframe = (window!=window.top) | |
window.Note = {} | |
window.Note.data = | |
/* JSON */ | |
{"exclude":["re","pm","jan","uk","non","rss","faq","feb","sep","et","st","aug","apr","en","jul","ny","eur","usr","nj","em","rw","ne","du","nt","es","gb","pr","fr","aa","var","mt","usd","mg","les","ch","sd","rs","avg","src","pda","dsl","sql","ss","ap","nm","mn","nd","op","acc","tn","ce","der","tm","sp","nh","mysql","pdt","db","ia","pt","ds","und","lg","nw","ff","ky","dont","br","ml","res","cs","sf","ut","ag","edt","pmid","cr","pg","ee","ing","ks","sw","hd","er","gcc","asp","nv","su","exp","nr","mp","def","nl","tr","bb","nz","te","av","nsw","pci","cst","twiki","ec","rm","pdas","cf","vt","urw","nec","gm","ri","rt","cp","dd","pl","crm","rf","ak","td","sb","sm","wv","ns","bs","hrs","img","rpm","ll","cl","ieee","ae","hs","yr","ic","mx","gr","xhtml","ext","ts","ge","pe","tt","dv","val","fy","rr","ef","jp","bt","rfc","sl","ins","hb","tc","oem","zdnet","oclc","plc","msg","cb","fc","fw","gs","bp","std","oo","fs","nn","kde","vb","dl","ls","phpbb","je","cms","sg","nav","ist","lc","sys","icq","scsi","cu","pty","ws","np","tft","jvc","dt","gc","ci","yn","vii","cfr","pmc","nb","rx","ddr","this","dec","ii","ca","oct","nov","jun","mon","fri","cnet","ltd","hp","ip","tue","thu","tx","iii","gmt","fl","il","az","th","pp","au","nc","va","rd","sc","se","ga","sa","wi","ct","eq","jd","oe","wy","pd","uc","ld","mf","mw","nu","dp","ht","za","ve","rh","fg","bk","dx","sk","yu","oc","wp","jm","cn","rn","cg","ix","tb","bm","rj","ka","rp","hc","fp","jj","rl","gl","dh","df","pf","uw","bd","hh","fd","wm","pk","wb","sn","dk","fu","wn","sv","zu","dg","vg","ul","js","pn","wt","mv","rb","mh","ww","bw","lf","wr","rg","bl","wx","bg","kw","lm","hl","zus","cet","ppc","ons","tgp","ati","ict","asn","qld","llp","sbjct","apnic","cdna","qt","dts","pgp","cz","ob","aaa","an","we","us","if","my","do","no","he","up","so","am","me","mediawiki","wto","ccd","aud","fcc","eds","gpl","ac","af","gmc","gtk","loc","oxley","arxiv","ru","lu","tg","pv","vw","dw","jl","mk","wl","sz","bf","sj","ij","xd","wh","xr","bn","wk","jb","jw","xt","fb","ua","kc","kt","tw","gw","ao","tl","lr","nf","pw","wg","lv","hf","wd","lj","ke","lx","bv","tk","gf","vm","jh","uu","kr","jt","dn","hw","xm","fn","ib","gh","gn","yy","gu","ay","jf","kk","bh","kl","pu","px","cy","lh","kd","xv","ji","hj","sx","rk","ux","vu","sy","ik","hx","ov","cx","fh","ej","dy","qb","jg","xf","hn","vv","wf","oa","iu","vf","zz","kj","kv","kx","eo","lw","ju","vo","xy","uf","kh","vn","wj","lq","hy","ih","nk","ud","lk","kp","vx","gk","hv","aq","gx","fj","gj","gz","tz","xa","ue","kn","py","vl","dq","pq","ub","xu","bx","jn","dz","zh","kz","cq","xb","qd","iz","gv","ck","ki","bj","ku","vid","bbw","wal","rrp","ooo","ste","tba","boc","tmp","ala","cio","enb","ata","wma","cir","dsc","wan","sen","dat","ind","qui","slr","rel","ign","att","ffl","ppp","ies","xsl","ctr","vcd","mst","dmx","str","obj","auf","pkg","sch","mfg","blk","bsd","adv","imc","pcr","mmc","rms","ont","hsn","sgh","een","rdf","liu","gta","abt","svn","twp","atx","bst","nsf","dcr","kvm","sts","goa","thr","emi","dst","msa","pcb","crc","chf","clr","dba","cdr","agp","exc","fra","rcw","aps","iis","jsp","dlp","afp","nfs","lac","edi","pid","xii","emc","aix","psc","icc","iec","esa","uml","spi","ppl","hcl","abn","mfr","afb","tcl","qos","dmc","asc","nsu","wks","rbi","ics","apa","phe","ses","bsc","bbb","nws","ngc","kos","psu","ogg","tyr","lsu","uid","umd","gdb","rpc","thb","idg","dds","cpc","alr","cme","ecc","crs","ssi","aac","cbd","rfp","sle","scr","pdb","svc","dcp","pte","ors","cps","fte","stl","gsa","otc","ssa","itu","mpi","mps","nwt","ths","hgh","oss","wmv","mgm","csa","nrc","chr","scm","fno","spd","adc","ubc","csu","fsa","xiv","sgd","ftd","smb","bse","itk","kms","thn","kpx","spp","mfc","nmr","mso","htm","dhs","msu","fsb","cns","omb","cpl","sbs","swf","ipb","tsn","bdd","csc","gln","stp","avr","cts","wmd","cfs","ffi","tcm","prc","afc","srl","thx","xvi","ttl","bmp","dss","ssk","dsm","spc","pcg","snp","adm","snr","pps","ntp","opp","lps","pdp","gpo","cpp","crn","mmf","rtf","bmc","csr","dvb","cdp","mev","sdn","mta","esd","gnd","osu","utp","jpy","ccm","bmg","jbl","pcm","dtd","hpa","pvt","atp","tec","doi","dsp","dll","iss","cmp","ich","sbc","dei","gsm","buf","pts","sst","teh","acct","adsl","cdrw","cdma","dmoz","divx","embl","exif","hklm","incl","msdn","mpls","ngos","staa","uefa","undp","wolverhampton","commentsblog","verzeichnis","viewpicture","christchurch","oscommerce","viewsonic","ret","xtc","addr","attr","ostg","xnxx","xxxx","xvid","xslt","xiii","yyyy","devel","endif","fomit","bizkit","bibtex","fulfil","xenical","zoofilia","gcse","engl","mgmt","odbc","wwii","sublimedirectory","registerregister","starsmerchant","powerseller","websphere","smugmug","xpress","xtreme","xemacs","webmin","zshops","xoops","vioxx","adp","adr","aff","afl","eia","fpo","gst","isd","tdk","astm","auch","caa","hipaa","sgi","mcsg","mcse","sgml","msgid","secsg","msgstr","newsgator"],"wordIndex":799,"minlen":3,"maxlen":17}; | |
/* END */ | |
(function(){ | |
window.Note.save = saveData | |
window.Note.load = loadData | |
window.Note.autoSave = autoSave | |
function getNoteModule(){ | |
if(!window['Note']) window.Note = {} | |
return window.Note | |
} | |
function loadData(){ | |
let note = getNoteModule() | |
if(!inframe){ | |
try{ | |
let savedata = localStorage.getItem(datakey) | |
if(savedata && savedata.length){ | |
note.data = JSON.parse(savedata) | |
} | |
window.addEventListener("message", onMessage) | |
} catch(e){ | |
console.log("No local data") | |
console.log(e) | |
} | |
} else { | |
return note.data | |
} | |
} | |
let firstLogCall = true | |
let originalLogFunc = console.log | |
function log(x, ...rest){ | |
if(firstLogCall) | |
document.body.appendChild(document.createElement("hr")) | |
else | |
document.body.appendChild(document.createElement("br")) | |
document.body.appendChild( | |
document.createTextNode(x + " " + rest.join(" "))) | |
firstLogCall = false | |
originalLogFunc(x, ...rest) | |
} | |
console.log = log | |
let SaveInterval = 500 | |
window.addEventListener("load", ()=>{ | |
convertMarkdown() | |
autoSave() | |
loadData() // dont wait for async | |
}) | |
let saveIntervalRef = null | |
function autoSave(enable){ | |
if(enable === undefined) enable = true | |
clearInterval(saveIntervalRef) | |
if(enable){ | |
saveIntervalRef = window.setInterval(saveData, SaveInterval) | |
} | |
} | |
// this is preferred over a 'localstorage' polyfill for a frame, | |
// so the programmer doesn't assume this is the browsers localstorage | |
function saveData(){ | |
let note = getNoteModule() | |
if(inframe){ | |
let msg_obj = { | |
action: "saveData", | |
data: JSON.stringify(note['data']) | |
} | |
let message = JSON.stringify(msg_obj) | |
window.parent.postMessage(message) | |
} else { | |
localStorage.setItem(datakey, JSON.stringify(note['data'])) | |
} | |
} | |
let md_subs = [ | |
/(\n|^)\s*######\s([^\s#].*)\n*/g, "\n<h6 id=\"$2\">$2</h6>\n", | |
/(\n|^)\s*#####\s([^\s#].*)\n*/g, "\n<h5 id=\"$2\">$2</h5>\n", | |
/(\n|^)\s*####\s*([^\s#].*)\n*/g, "\n<h4 id=\"$2\">$2</h4>\n", | |
/(\n|^)\s*###\s*([^\s#].*)\n*/g, "\n<h3 id=\"$2\">$2</h3>\n", | |
/(\n|^)\s*##\s*([^\s#].*)\n*/g, "\n<h2 id=\"$2\">$2</h2>\n", | |
/(\n|^)\s*#\s*([^\s#].*)\n*/g, "\n<h1 id=\"$2\">$2</h1>\n", | |
/\n(\s*)[\*\-](.*)/g, '\n<ul><li>$2</li></ul>', | |
/\n+\n(?=[^#\n])/g, "\n\n<br><br>", | |
/\n+\n/g, "\n", | |
/__([^_\n]*)__/g, "<b>$1</b>", | |
/\*\*([^_\n]*)\*\*/g, "<b>$1</b>", | |
/_([^_\n]*)_/g, "<i>$1</i>", | |
/\*([^_\n]*)\*/g, "<i>$1</i>", | |
/\!\[([^\]\n]*)\]\(([^\)\n]*)\)/g, "<img src=\"$2\" alt=\"$1\"></img>", | |
/\[([^\]\n]*)\]\(([^\)\n]*)\)/g, "<a href=\"$2\" target=\"_blank\">$1</a>", | |
] | |
function onMessage(){ | |
try{ | |
let message = JSON.parse(e.data) | |
// console.log('received message', message) | |
if(message.hasOwnProperty("event")){ | |
if(message.event == "keydown" && typeof keydown != "undefined"){ | |
keydown(message) | |
} | |
if(message.event == "keyup" && typeof keyup != "undefined"){ | |
keydown(message) | |
} | |
} | |
} catch(e){ | |
console.warn("error processing message: " + e.data) | |
} | |
} | |
function convertMarkdown(){ | |
let containers = document.getElementsByClassName("markdown-src") | |
for(let j=0; j<containers.length; ++j){ | |
let container = containers[j] | |
let src = container.innerHTML | |
for(var i=0; i<md_subs.length-1; i += 2){ | |
var search = md_subs[i] | |
var replace = md_subs[i+1] | |
src = src.replace(search, replace) | |
} | |
container.innerHTML = src | |
} | |
} | |
})() | |
//window.addEventListener('load', htmlNotesMainFunc) | |
//function htmlNotesMainFunc(){ | |
/* JS */ | |
let data = Note.data | |
let temp = {} | |
let maxlist = 50*1000 | |
window.addEventListener('load', main) | |
async function loadDict(){ | |
let url = id('dicturl_input').value | |
let text = await (await fetch(url)).text() | |
// console.info('load dictionary', text) | |
if(text){ | |
temp.dictionary = text | |
updateWords() | |
} | |
update() | |
} | |
function unexcludeWord(word){ | |
let index = data.exclude.indexOf(word) | |
if(index >= 0){ | |
data.exclude.splice(index, 1) | |
temp.dictionary += ' ' + word | |
if(temp.words.indexOf(word) == -1){ | |
temp.words.push(word) | |
} | |
} | |
update() | |
} | |
function excludeWord(word){ | |
let index = data.exclude.push(word) | |
update() | |
} | |
function updateWords(){ | |
let min = data.minlen = parseInt(id('minlen').value) | |
let max = data.maxlen = parseInt(id('maxlen').value) | |
if(max < min){ | |
max = min | |
id('maxlen').value = max | |
} | |
temp.words = temp.dictionary.split(/\s+/).filter(wordFilter) | |
update() | |
} | |
function main(){ | |
if(data.hasOwnProperty('minlen')) | |
id('minlen').value = "" + data.minlen | |
if(data.hasOwnProperty('maxlen')) | |
id('maxlen').value = "" + data.maxlen | |
init() | |
window.addEventListener('keydown', keydown) | |
} | |
async function init(){ | |
if(!data.hasOwnProperty('exclude')) data.exclude = [] | |
if(!data.hasOwnProperty('minlen')) data.minlen = 2 | |
if(!data.hasOwnProperty('maxlen')) data.maxlen = 5 | |
if(!data.hasOwnProperty('wordIndex')) data.wordIndex = 0 | |
if(!temp.dictionary) | |
temp.dictionary = "a from look say time about get make see" | |
if(id('dicturl_input').value.length > 0){ | |
await loadDict() | |
} | |
updateWords() | |
} | |
function text(_id, _text){ | |
id(_id).innerHTML = '' | |
id(_id).appendChild(document.createTextNode(_text)) | |
} | |
function html(_id, _html){ | |
id(_id).innerHTML = _html | |
} | |
function update(skiplists){ | |
if(temp.words && temp.words.length > 0 && data.wordIndex >= temp.words.length){ | |
console.trace('updating wordIndex', data.wordIndex) | |
data.wordIndex = (temp.words ?? [0]).length - 1 | |
console.trace('updated wordIndex', data.wordIndex)} | |
if(!skiplists){ | |
html('excludelist', excludeListTemplate()) | |
html('includelist', includeListTemplate()) | |
} | |
text('wordindex', data.wordIndex + 1) | |
text('wordchecker', temp.words[data.wordIndex]) | |
} | |
function sortLenAlpha(a, b){ | |
if(a.length == b.length) | |
return a < b? -1 : 1 | |
return a.length - b.length | |
} | |
function includeListTemplate(){ | |
let src = "" | |
let include = temp.words.filter(wordFilter) | |
include = include.sort(sortLenAlpha) | |
if(include.length > maxlist){ | |
include = include.slice(0, maxlist)} | |
include = include.filter(x=>data.exclude.indexOf(x) == -1) | |
for(let i=0; i<include.length; ++i){ | |
let word = include[i] | |
if(data.exclude.indexOf(word) != -1) continue | |
src += ` <span onclick='excludeWord("${word}")' | |
style="text-decoration: underline; cursor: pointer;">${word}</span>` | |
} | |
return src | |
} | |
function excludeListTemplate(){ | |
let src = "" | |
let exclude = data.exclude.toSorted(sortLenAlpha) | |
for(let i=0; i<exclude.length; ++i){ | |
let word = exclude[i] | |
src += ` <span onclick='unexcludeWord("${word}")' | |
style="text-decoration: underline; cursor: pointer;">${word}</span>` | |
} | |
return src | |
} | |
function prev(){ | |
nextWord(true) | |
} | |
function nextWord(back){ | |
let {words} = temp | |
if(!words.length) return | |
for(let i=0; Math.abs(i)!=words.length-1; back? --i : ++i){ | |
if(back? --data.wordIndex < 0 : ++data.wordIndex >= words.length){ | |
data.wordIndex = back? 0: words.length - 1 } | |
if(data.exclude.indexOf(words[data.wordIndex]) < 0){ | |
break} | |
} | |
update('skiplists') | |
} | |
function accept(){ | |
nextWord() | |
} | |
function wordFilter(word){ | |
let min = parseInt(id('minlen').value) | |
let max = parseInt(id('maxlen').value) | |
if(max < min){ | |
max = min | |
id('maxlen').value = max | |
} | |
if(word.length < min || word.length > max) return false | |
return true | |
} | |
function reject(){ | |
data.exclude.push(temp.words[data.wordIndex]) | |
nextWord() | |
} | |
function copyExclude(){ | |
let text = data.exclude.join(' ') | |
if(navigator.clipboard){ | |
navigator.clipboard.writeText(text) | |
} | |
} | |
function copyInclude(){ | |
let include = temp.words.filter(wordFilter) | |
include = include.filter(x=>data.exclude.indexOf(x) == -1) | |
let text = include.join(' ') | |
if(navigator.clipboard){ | |
navigator.clipboard.writeText(text) | |
} | |
} | |
function keydown(e){ | |
let k = e.key | |
if(k == 'ArrowRight'){ | |
accept() | |
} | |
if(k == 'Enter'){ | |
reject() | |
} | |
if(k == "ArrowLeft"){ | |
prev() | |
} | |
} | |
function id(x){ | |
return document.getElementById(x) | |
} | |
/* END */ | |
//} | |
</script> | |
</body> | |
<html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment