diff options
Diffstat (limited to 'modules')
-rw-r--r-- | modules/$common.js | 33 | ||||
-rwxr-xr-x | modules/abbrgen/abbreviation_gen_Darwin | bin | 0 -> 30896 bytes | |||
-rwxr-xr-x | modules/abbrgen/abbreviation_gen_Linux | bin | 0 -> 19364 bytes | |||
-rw-r--r-- | modules/abbrgen/abbrgen.js | 49 | ||||
-rw-r--r-- | modules/changes/changes.html | 257 | ||||
-rw-r--r-- | modules/changes/changes.js | 163 | ||||
-rw-r--r-- | modules/datumpjeprik/datumpjeprik.html | 168 | ||||
-rw-r--r-- | modules/datumpjeprik/datumpjeprik.js | 66 | ||||
-rw-r--r-- | modules/ip/ip.js | 8 | ||||
-rw-r--r-- | modules/nlen/English-Dutch-mistranslations.json | 31 | ||||
-rw-r--r-- | modules/nlen/nlen.html | 11 | ||||
-rw-r--r-- | modules/nlen/nlen.js | 17 | ||||
-rw-r--r-- | modules/poke/poke.html | 125 | ||||
-rw-r--r-- | modules/poke/poke.js | 47 | ||||
-rw-r--r-- | modules/quotesgrab/quotesgrab.js | 65 | ||||
-rw-r--r-- | modules/subd-krokodil/subd-krokodil.js | 21 | ||||
-rwxr-xr-x | modules/ytdl/youtube-dl-Darwin | bin | 0 -> 1149466 bytes | |||
-rw-r--r-- | modules/ytdl/ytdl.html | 45 | ||||
-rw-r--r-- | modules/ytdl/ytdl.js | 31 |
19 files changed, 1137 insertions, 0 deletions
diff --git a/modules/$common.js b/modules/$common.js new file mode 100644 index 0000000..fa29c50 --- /dev/null +++ b/modules/$common.js @@ -0,0 +1,33 @@ +var path=require("path"), + basicAuth=require("basic-auth"); + +var cwd=process.cwd(); +var globalAccounts=require(cwd+"/globalAccounts.json"); + +module.exports={ + "rootdir":path.dirname(cwd), + "serverdir":cwd, + "simpleHTMLescape":function simpleHTMLescape(str){ + return str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); + }, + "authgen":function authgen(accounts){ //omit `accounts` to use the globalAccounts list + if(!accounts)accounts=globalAccounts; + return function (req,res,next){ + function unauth(res){ + res.set("WWW-Authenticate","Basic realm=Authorization required"); + return res.sendStatus(401); + }; + var user=basicAuth(req); + if(!user||!user.name||!user.pass){ + return unauth(res); + } + var i; + for(i=0;i<accounts.length;i++){ + if(accounts[i][0]==user.name&&accounts[i][1]==user.pass){ + return next(); + } + } + return unauth(res); + }; + }, +}; diff --git a/modules/abbrgen/abbreviation_gen_Darwin b/modules/abbrgen/abbreviation_gen_Darwin Binary files differnew file mode 100755 index 0000000..7fd9596 --- /dev/null +++ b/modules/abbrgen/abbreviation_gen_Darwin diff --git a/modules/abbrgen/abbreviation_gen_Linux b/modules/abbrgen/abbreviation_gen_Linux Binary files differnew file mode 100755 index 0000000..dd151e7 --- /dev/null +++ b/modules/abbrgen/abbreviation_gen_Linux diff --git a/modules/abbrgen/abbrgen.js b/modules/abbrgen/abbrgen.js new file mode 100644 index 0000000..64a29b1 --- /dev/null +++ b/modules/abbrgen/abbrgen.js @@ -0,0 +1,49 @@ +var cmn=require("../$common.js"), + process=require("child_process"); + +var moddir,uname; +uname=String(process.execSync("uname")).trim(); + +//PERFORMS NO VALIDATION! +function get_abbreviations(abbr,num,cb){ + var fname=moddir+"/abbreviation_gen_"+uname; + process.execFile(fname,[abbr,num],{},function(err,stdout,stderr){ + //if(err)throw err; + if(err){ + console.log(err.toString()); + console.log(err.stack); + cb([]); + } + cb(stdout.split("\n")); + }); +} + +module.exports=function(app,io,_moddir){ + moddir=_moddir; + app.get("/abbrgen/:abbr",function(req,res){ + var abbr=req.params.abbr; + res.set("Content-Type","text/plain"); + if(!abbr.match(/^[a-z]+$/i)){ + res.send("ERROR: Invalid input value."); + return; + } + get_abbreviations(abbr,"1",function(answers){ + res.send(answers.join("\n")); + }); + }); + app.get("/abbrgen/:abbr/:num",function(req,res){ + var abbr=req.params.abbr,num=req.params.num; + res.set("Content-Type","text/plain"); + if(num>5000){ + res.send("ERROR: Number of abbreviations too large."); + return; + } + if(!abbr.match(/^[a-z]+$/i)||isNaN(+num)){ + res.send("ERROR: Invalid input values."); + return; + } + get_abbreviations(abbr,num,function(answers){ + res.send(answers.join("\n")); + }); + }); +}; diff --git a/modules/changes/changes.html b/modules/changes/changes.html new file mode 100644 index 0000000..cda3da1 --- /dev/null +++ b/modules/changes/changes.html @@ -0,0 +1,257 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>Website change monitor</title> +<script> +"use strict"; + +var urls=[]; + +function pad(s,n,c){ + s=s+""; + if(!c)c="0"; + var fill=c,filllen=n-s.length; + if(filllen<=0)return s; + while(fill.length<filllen)fill+=c; + return fill+s; +} + +function dateformat(date){ + return date.getFullYear()+"/"+pad(date.getMonth(),2)+"/"+pad(date.getDate(),2)+" "+ + pad(date.getHours(),2)+":"+pad(date.getMinutes(),2)+":"+pad(date.getSeconds(),2); +} + +function fetch(method,url,data/*?*/,cb){ + if(!cb){ + cb=data; + data=undefined; + if(!cb)throw new Error("No callback passed to fetch"); + } + var xhr=new XMLHttpRequest(); + xhr.onreadystatechange=function(ev){ + if(xhr.readyState<4)return; + cb(xhr.status,xhr.responseText); + }; + xhr.open(method,url); + xhr.send(data); +} + +function focusData(data){ + document.getElementById("timelinecontainer").classList.add("visible"); + var header=document.getElementById("timelineheader"); + var tbody=document.getElementById("timelinetbody"); + if(!header.firstChild)header.appendChild(document.createTextNode(data.url)); + else header.firstChild.nodeValue=data.url; + + var l=tbody.children,i; + for(i=l.length-1;i>=0;i--)tbody.removeChild(l[i]); + + var tr,td,s; + for(i=data.timeline.length-1;i>=0;i--){ + tr=document.createElement("tr"); + if(i<data.timeline.length-1&&data.timeline[i][1]==data.timeline[i+1][1]){ + tr.classList.add("repeated"); + } + + td=document.createElement("td"); + td.appendChild(document.createTextNode(dateformat(new Date(data.timeline[i][0])))); + tr.appendChild(td); + + td=document.createElement("td"); + s=data.timeline[i][1]; + if(s==null)s="(error while retrieving page)"; + td.appendChild(document.createTextNode(s)); + tr.appendChild(td); + + tbody.appendChild(tr); + } +} + +function makeURLtr(url){ + var tr=document.createElement("tr"); + var td=document.createElement("td"); + + td.appendChild(document.createTextNode(url)); + + var e=document.createElement("input"); + e.type="button"; + e.classList.add("rowdeletebutton"); + e.value="X"; + e.setAttribute("title","Delete URL"); + e.addEventListener("click",function(ev){ + ev.stopPropagation(); + fetch("DELETE","/changes/url",url,function(status,body){ + if(document.getElementById("timelineheader").firstChild.nodeValue==url){ + document.getElementById("timelinecontainer").classList.remove("visible"); + } + if(status!=200){ + alert("Error deleting url: ("+status+") "+body); + return; + } + tr.parentNode.removeChild(tr); + updateURLs(); + }); + }); + td.appendChild(e); + + tr.addEventListener("click",function(ev){ + fetch("GET","/changes/url?url="+encodeURIComponent(url),function(status,body){ + if(status!=200){ + alert("Error getting data: ("+status+") "+body); + return; + } + var data; + try { + data=JSON.parse(body); + } catch(e){ + alert("Server sent invalid data!"); + console.log("Invalid data:",data); + return; + } + focusData(data); + }); + }); + + tr.appendChild(td); + return tr; +} + +function updateURLs(){ + fetch("GET","/changes/urls",function(status,body){ + urls=JSON.parse(body); + if(!urls){ + urls=[]; + alert("Error retrieving URLs!"); + return; + } + var tbody=document.getElementById("urlstable").firstElementChild; + var l=tbody.children,i,tr,td; + for(i=l.length-1;i>=0;i--)tbody.removeChild(l[i]); + for(i=0;i<urls.length;i++){ + tbody.appendChild(makeURLtr(urls[i])); + } + }); +} + +function addURLbutton(){ + var e=document.getElementById("urladdinput"); + var url=e.value; + fetch("POST","/changes/url",url,function(status,body){ + if(status==200){ + e.value=""; + } else { + alert("URL submission error: ("+status+") "+body); + } + updateURLs(); + }); +} + +function addURLkeypress(ev){ + if(ev.keyCode==10||ev.keyCode==13)addURLbutton(); +} + +function sendRefresh(){ + fetch("POST","/changes/refresh",function(status,body){}); +} + +window.addEventListener("load",function(){ + updateURLs(); + //setInterval(updateURLs,10*1000); +}); +</script> +<style> +body{ + font-family:Georgia,Times,serif; + font-size:14px; +} +h1{ + /*font-size:28px;*/ + /*margin:19px 0 19px 0;*/ +} +#urlscontainer{ + width:500px; +} +#urlstablecontainer{ + height:130px; + border:1px #888 solid; + overflow-y:scroll; +} +#urlstable{ + width:100%; + border-collapse:collapse; +} +#urlstable tbody, #urlstable tr, #urlstable td{ + width:100%; +} +#urlstable tr{ + cursor:pointer; +} +#urlstable tr:hover{ + background-color:#ddd; +} +#urlstable tr:nth-child(2n){ + background-color:#eee; +} +#urlstable td{ + height:100%; + vertical-align:middle; +} +#urladdinput{ + width:300px; +} +.rowdeletebutton{ + color:red; + font-family:Monospace; + font-weight:bold; + float:right; + cursor:pointer; + padding:0 3px 0 3px; + background-color:rgba(255,0,0,0.1); + border-radius:2px; + border-width:0; +} + +#timelinetbl{ + border-collapse:collapse; +} +#timelinetbl td, #timelinetbl th{ + border:1px #888 solid; +} + +tr.repeated{ + color:#aaa; + font-size:10px; +} + +#timelinecontainer{ + display:none; +} +#timelinecontainer.visible{ + display:block; +} +</style> +</head> +<body> +<h1>Website change monitor</h1> +<b>URLs:</b> +<input type="button" onclick="sendRefresh();" value="Redownload sites"> +<br> +<div> + <div id="urlscontainer"> + <div id="urlstablecontainer"> + <table id="urlstable"><tbody></tbody></table> + </div> + <input type="text" id="urladdinput" onkeypress="addURLkeypress(event);"> + <input type="button" onclick="addURLbutton();" value="Add URL" title="Add URL to watch list"> + </div> +</div> +<div id="timelinecontainer"> + <h2 id="timelineheader"></h2> + <table id="timelinetbl"> + <thead><tr><th>Date</th><th>Hash</th></tr></thead> + <tbody id="timelinetbody"></tbody> + </table> +</div> +</body> +</html> diff --git a/modules/changes/changes.js b/modules/changes/changes.js new file mode 100644 index 0000000..6a8c8f2 --- /dev/null +++ b/modules/changes/changes.js @@ -0,0 +1,163 @@ +"use strict"; + +var cmn=require("../$common.js"), + persist=require("node-persist"), + crypto=require("crypto"), + http=require("http"), + https=require("https"), + URL=require("url"); + +var moddir=null; + +persist=persist.create({ + dir:"persist/changes", + continuous:false, + interval:false +}); +persist.initSync(); + +//urls: map(url => URLobject) +//url: String +//URLobject: {url, timeline: [[Date, hash]]} +var urls=persist.getItemSync("urls"); +if(!urls){ + urls={}; + persist.setItemSync("urls",urls); +} + + +function URLobject(url){ + if(!(this instanceof URLobject))return new URLobject(url); + this.url=url; + this.timeline=[]; +} + +function fetch(method,url,data/*?*/,cb){ + var cbcalled=false; + + if(!cb){ + cb=data; + data=undefined; + if(!cb)throw new Error("No callback passed to fetch"); + } + + try { + url=URL.parse(url); + } catch(e){ + cb(-1,null); + return; + } + + var httpclass; + switch(url.protocol){ + case "http:": httpclass=http; break; + case "https:": httpclass=https; break; + default: + cb(-1,null); + return; + } + + url.method=method; + + var req=httpclass.request(url,function(res){ + var body=""; + res.on("data",function(data){ + body+=data; + }); + res.on("end",function(){ + if(!cbcalled)cb(res.statusCode,body); + cbcalled=true; + }); + res.on("error",function(err){ + if(!cbcalled)cb(-1,err); + cbcalled=true; + }); + }); + req.on("error",function(err){ + if(!cbcalled)cb(-1,err); + cbcalled=true; + }); + if(data)req.write(data); + req.end(); +} + +function refreshURLs(){ + var hashes={}; + var i; + var url; + var timeout=null; + for(url in urls){ + console.log("Fetching <"+url+">"); + fetch("GET",url,function(url,status,body){ + console.log("Got <"+url+">; status = "+status); + if(status==-1){ + hashes[url]=[new Date(),null]; + } else { + var hash=crypto.createHash("sha256"); + hash.update(body); + hashes[url]=[new Date(),hash.digest("hex")]; + } + + if(!urls[url]){ + console.log("WARNING: url <"+url+"> from hashes not found in urls!"); + return; + } + //var last=urls[url].timeline[urls[url].timeline.length-1]; + //if(last==undefined||hashes[url][1]==null||hashes[url][1]!=last[1]){ + urls[url].timeline.push(hashes[url]); + //} + + if(timeout)clearTimeout(timeout); + timeout=setTimeout(function(){ + persist.setItemSync("urls",urls); + console.log("(persisted after refresh)") + },2000); + }.bind(null,url)); + } +} + + +module.exports=function(app,io,_moddir){ + moddir=_moddir; + app.all(["/changes","/changes/*"],cmn.authgen()); + app.get("/changes",function(req,res){ + res.sendFile(moddir+"/changes.html"); + }); + app.get("/changes/urls",function(req,res){ + var list=[]; + var url; + for(url in urls)list.push(url); + res.send(JSON.stringify(list)); + }); + app.get("/changes/url",function(req,res){ + var url=req.query.url; + if(!urls[url]){ + res.status(404); + res.send("URL not found in watch list"); + return; + } + res.send(JSON.stringify(urls[url])); + }); + app.post("/changes/url",function(req,res){ + var url=req.body; + urls[url]=new URLobject(url); + persist.setItemSync("urls",urls); + // refreshURLs(); + res.send(); + }); + app.delete("/changes/url",function(req,res){ + var url=req.body; + if(!urls[url]){ + res.status(404); + res.send("URL not found in watch list"); + return; + } + delete urls[url]; + persist.setItemSync("urls",urls); + res.send(); + }); + app.post("/changes/refresh",function(req,res){ + refreshURLs(); + res.send(); + }); +}; diff --git a/modules/datumpjeprik/datumpjeprik.html b/modules/datumpjeprik/datumpjeprik.html new file mode 100644 index 0000000..46a21d3 --- /dev/null +++ b/modules/datumpjeprik/datumpjeprik.html @@ -0,0 +1,168 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>Datumpje-prik</title> +<script> +var today,periodStart,periodEnd,periodWeekStart,periodWeekEnd,periodLength, + days,clicked,cantClick=false; + +//Sorry. Prototypes are fun. Using dollars to at least separate these from the built-in methods. +Date.prototype.$dayOfWeek=function(){return (this.getDay()+6)%7;} //monday..sunday = 0..6 +Date.prototype.$startOfDay=function(){return new Date(this-(((this.getHours()*60+this.getMinutes())*60+this.getSeconds())*1000+this.getMilliseconds()));} +Date.prototype.$addDays=function(n){return new Date(this.valueOf()+n*24*3600*1000);} +Date.prototype.$addWeeks=function(n){return new Date(this.valueOf()+n*7*24*3600*1000);} +Date.prototype.$naturalDate=function(){return this.getFullYear()+"-"+("00"+(this.getMonth()+1)).slice(-2)+"-"+("00"+this.getDate()).slice(-2);} + +function diffDays(d1,d2){return ~~(Math.abs(d1-d2)/1000/3600/24);} + +function localDateStringToDate(s){return new Date(s+"T00:00:00");} + +function appendPath(s,p){ + if(s.length==0)return p; + if(p.length==0)return s; + if(s[s.length-1]=="/"){ + if(p[0]=="/")return s+p.slice(1); + else return s+p; + } else { + if(p[0]=="/")return s+p; + else return s+"/"+p; + } +} + +function setupCalendar(){ + var cal=document.getElementById("calendar"), + maxnum=Math.max.apply(null,days)+1, + startOffset=diffDays(periodWeekStart,periodStart), + tr,td,span,w,d,idx; + cal.innerHTML=""; + tr=document.createElement("tr"); + for(d=0;d<7;d++){ + td=document.createElement("td"); + td.innerHTML=["mon","tue","wed","thu","fri","sat","sun"][d]; + tr.appendChild(td); + } + cal.appendChild(tr); + for(w=0;w<~~(periodLength/7);w++){ + tr=document.createElement("tr"); + for(d=0;d<7;d++){ + td=document.createElement("td"); + if(7*w+d>=startOffset){ + idx=7*w+d-startOffset; + span=document.createElement("span"); + span.classList.add("calendar-day-fillspan"); + span.setAttribute("style","width:"+(days[idx]+clicked[idx])/maxnum*100+"%"); + td.appendChild(span); + span=document.createElement("span"); + span.classList.add("calendar-day-datelabel"); + span.appendChild(document.createTextNode(periodStart.$addDays(idx).$naturalDate())); + td.appendChild(span); + if(!cantClick&&!localStorage.getItem("hasSubmitted")) + (function(idx){ + td.addEventListener("click",function(){ + clicked[idx]=!clicked[idx]; + setTimeout(setupCalendar,0); + },false); + })(idx); + } + tr.appendChild(td); + } + cal.appendChild(tr); + } + if(localStorage.getItem("hasSubmitted")){ + document.getElementById("status").innerHTML="You have already submitted!"; + document.getElementById("submitbtn").setAttribute("disabled",""); + } else { + document.getElementById("status").style.display="none"; + document.getElementById("submitbtn").removeAttribute("disabled"); + } +} + +function submitdays(){ + var xhr; + cantClick=true; + xhr=new XMLHttpRequest(); + xhr.addEventListener("readystatechange",function(ev){ + var result; + if(this.readyState!=4)return; + alert(this.responseText); + result=JSON.parse(this.responseText); + if(result==true){ + alert("Submitted successfully!"); + localStorage.setItem("hasSubmitted","true") + } else alert("Error while submitting!"); + },false); + xhr.open("POST",location.protocol+"//"+location.host+appendPath(location.pathname,"/adddays")); + xhr.send(JSON.stringify(clicked)); +} + + +(function(){ + var xhr; + today=new Date().$startOfDay(); + xhr=new XMLHttpRequest(); + xhr.addEventListener("readystatechange",function(ev){ + var result; + if(this.readyState!=4)return; + result=JSON.parse(this.responseText); + periodStart=new Date(localDateStringToDate(result["periodStart"])); + periodEnd=new Date(localDateStringToDate(result["periodEnd"])); + periodWeekStart=periodStart.$addDays(-periodStart.$dayOfWeek()); + periodWeekEnd=periodEnd.$addDays(6-periodEnd.$dayOfWeek()); + periodLength=diffDays(periodStart,periodEnd)+1; + days=Array.apply(null,new Array(periodLength)).map(function(){return 0;}); + clicked=days.map(function(){return false;}); + setupCalendar(); + },false); + xhr.open("GET",location.protocol+"//"+location.host+appendPath(location.pathname,"/dates")); + xhr.send(); + + xhr=new XMLHttpRequest(); + xhr.addEventListener("readystatechange",function(ev){ + var result; + if(this.readyState!=4)return; + days=JSON.parse(this.responseText); + setupCalendar(); + },false); + xhr.open("GET",location.protocol+"//"+location.host+appendPath(location.pathname,"/days")); + xhr.send(); +})(); + +window.addEventListener("load",function(){ + ; +},false); +</script> +<style> +tbody#calendar > tr{ + height:40px; +} +tbody#calendar > tr > td{ + position:relative; + width:80px; + height:inherit; + text-align:center; + vertical-align:middle; + border:1px #ddd solid; +} +.calendar-day-fillspan{ + display:inline-block; + position:absolute; + background-color:#6f6; + opacity:40%; + left:0; + top:0; + height:100%; + z-index:-10000; +} +.calendar-day-datelabel{ + color:#888; + font-size:8pt; +} +</style> +</head> +<body> +<div id="status"></div> +<table><tbody id="calendar"><tr><td>Loading...</td></tr></tbody></table> +<input id="submitbtn" type="button" onclick="submitdays()" value="Submit your days"> +</body> +</html> diff --git a/modules/datumpjeprik/datumpjeprik.js b/modules/datumpjeprik/datumpjeprik.js new file mode 100644 index 0000000..dc56759 --- /dev/null +++ b/modules/datumpjeprik/datumpjeprik.js @@ -0,0 +1,66 @@ +var cmn=require("../$common.js"); +var persist=require("node-persist"), + util=require("util"); + +//Sorry. Prototypes are fun. Using dollars to at least separate these from the built-in methods. +Date.prototype.$dayOfWeek=function(){return (this.getDay()+6)%7;} //monday..sunday = 0..6 +Date.prototype.$startOfDay=function(){return new Date(this-(((this.getHours()*60+this.getMinutes())*60+this.getSeconds())*1000+this.getMilliseconds()));} +Date.prototype.$addDays=function(n){return new Date(this.valueOf()+n*24*3600*1000);} +Date.prototype.$addWeeks=function(n){return new Date(this.valueOf()+n*7*24*3600*1000);} +Date.prototype.$naturalDate=function(){return this.getFullYear()+"-"+("00"+(this.getMonth()+1)).slice(-2)+"-"+("00"+this.getDate()).slice(-2);} + +function diffDays(d1,d2){return ~~(Math.abs(d1-d2)/1000/3600/24);} + +function localDateStringToDate(s){return new Date(s+"T00:00:00");} + + +var periodStart="2015-04-09",periodEnd="2015-07-05", + periodLength=diffDays(localDateStringToDate(periodStart),localDateStringToDate(periodEnd))+1; + +persist=persist.create({ + dir:"persist/datumpjeprik", + continuous:false, + interval:false +}); +persist.initSync(); + +function ensureInitialised(){ + var days; + days=persist.getItem("days"); + if(!days||days.length!=periodLength){ + days=Array.apply(null,new Array(periodLength)).map(function(){return 0;}); + persist.setItemSync("days",days); + } +} +ensureInitialised(); + +module.exports=function(app,io,moddir){ + app.get("/datumpjeprik",function(req,res){ + res.set("Content-Type","text/html"); + res.sendFile(moddir+"/datumpjeprik.html"); + }); + app.get("/datumpjeprik/dates",function(req,res){ + res.set("Content-Type","text/json"); + res.send('{"periodStart":"'+periodStart+'","periodEnd":"'+periodEnd+'"}'); + }); + app.get("/datumpjeprik/days",function(req,res){ + ensureInitialised(); + res.set("Content-Type","text/json"); + res.send(persist.getItem("days")); + }); + app.post("/datumpjeprik/adddays",function(req,res){ + var newdays; + console.log(req.body); + try{ + newdays=JSON.parse(req.body); + if(newdays.length!=periodLength)throw new Error("length mismatch"); + if(!newdays.map(function(v){return typeof v=="boolean";}).reduce(function(a,b){return a&&b;}))throw new Error("not all bools"); + ensureInitialised(); + persist.setItemSync("days",persist.getItem("days").map(function(v,i){return v+newdays[i];})); + res.send("true"); + } catch(e){ + res.send("[false,\""+e.message+"\"]"); + return; + } + }); +}; diff --git a/modules/ip/ip.js b/modules/ip/ip.js new file mode 100644 index 0000000..dec0a98 --- /dev/null +++ b/modules/ip/ip.js @@ -0,0 +1,8 @@ +var cmn=require("../$common.js"), + fs=require("fs"); + +module.exports=function(app,io,moddir){ + app.get("/ip",function(req,res){ + res.send(String(req.socket.remoteAddress)); + }); +}; diff --git a/modules/nlen/English-Dutch-mistranslations.json b/modules/nlen/English-Dutch-mistranslations.json new file mode 100644 index 0000000..dff7229 --- /dev/null +++ b/modules/nlen/English-Dutch-mistranslations.json @@ -0,0 +1,31 @@ +[ +{"nl":"lijge verspreidingen","en":"linux distributions"}, +{"nl":"draadjes","en":"threads"}, +{"nl":"lees-uitvoer-schrijf-cirkel","en":"REPL"}, +{"nl":"ventilator","en":"fan"}, +{"nl":"raamwerk","en":"framework"}, +{"nl":"reagerend","en":"reactive"}, +{"nl":"schoenlipje","en":"bootstrap"}, +{"nl":"materiaal ontwerp","en":"material design"}, +{"nl":"trappende stijl pagina's","en":"CSS"}, +{"nl":"C scherp","en":"C#"}, +{"nl":"Ga","en":"Go"}, +{"nl":"voorkanteinde","en":"front-end"}, +{"nl":"volledige stapel","en":"full-stack"}, +{"nl":"Meteoor, een raamwerk voor reagerende alleen pagina webtoepassingen","en":"Meteor, a framework for single-page web apps"}, +{"nl":"stapeloverloop","en":"stackoverflow"}, +{"nl":"rentijd","en":"runtime"}, +{"nl":"werkelijktijd","en":"realtime"}, +{"nl":"zachte goederen","en":"software"}, +{"nl":"harde goederen","en":"hardware"}, +{"nl":"Hyper-Tekst Markeer-Op Taal","en":"HTML"}, +{"nl":"client","en":"cliënt"}, +{"nl":"serveerder","en":"server"}, +{"nl":"databasis","en":"database"}, +{"nl":"voorhalen","en":"pre-fetch"}, +{"nl":"methoderoepingen","en":"method calls"}, +{"nl":"blauwdruk","en":"template"}, +{"nl":"werktuigen","en":"tools"}, +{"nl":"Toepassing-Programmeer-Tussengezicht","en":"API"}, +{"nl":"Principes van Meteoor\nData op het Snoer. Meteor stuurt geen HTMOT (Hyper Tekst Markeer-Op Taal) over het netwerk. De server stuurt gegevens en laat de cliënt deze weergeven.\nÉén Taal. Meteoor laat je beide de cliënt- en de serveerderdelen van je toepassing in JavaScript schrijven.\nDatabasis Overal. Je kan dezelfde methodes gebruiken om je databasis te bereiken van de cliënt of de serveerder.\nWachttijdcompensatie. Op de cliënt, Meteoor voorhaalt gegevens op en simuleert modellen om het te maken lijken alsof de serveerder methode roepingen instantaan terugkomen.\nVolle-stapel reagerendheid. In Meteoor, werkelijktijd is de standaard. Alle lagen, van databasis tot blauwdruk, vernieuwen zichzelf automatisch wanneer benodigd.\nOmarm het ecosysteem. Meteoor is open bron en integreert met existerende open bron werktuigen en raamwerken.\nEenvoudigheid Evenaart Productiviteit. De beste weg om iets simpel te laten lijken is om het werkelijk simpel te hebben zijn. Meteoor's hoofdfunctionaliteit heeft schone, klassiek-mooie TPT's (Toepassing Programmeer Tussengezichten).","en":"Principles of Meteor\nData on the Wire. Meteor doesn't send HTML over the network. The server sends data and lets the client render it.\nOne Language. Meteor lets you write both the client and the server parts of your application in JavaScript.\nDatabase Everywhere. You can use the same methods to access your database from the client or the server.\nLatency Compensation. On the client, Meteor prefetches data and simulates models to make it look like server method calls return instantly.\nFull Stack Reactivity. In Meteor, realtime is the default. All layers, from database to template, update themselves automatically when necessary.\nEmbrace the Ecosystem. Meteor is open source and integrates with existing open source tools and frameworks.\nSimplicity Equals Productivity. The best way to make something seem simple is to have it actually be simple. Meteor's main functionality has clean, classically beautiful APIs."} +]
\ No newline at end of file diff --git a/modules/nlen/nlen.html b/modules/nlen/nlen.html new file mode 100644 index 0000000..dd09e58 --- /dev/null +++ b/modules/nlen/nlen.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>NL/EN mistranslations</title> +</head> +<body> +<!--DUTCHDUTCH--><br> +<!--ENGLISHENGLISH--><br> +</body> +</html> diff --git a/modules/nlen/nlen.js b/modules/nlen/nlen.js new file mode 100644 index 0000000..c988105 --- /dev/null +++ b/modules/nlen/nlen.js @@ -0,0 +1,17 @@ +var cmn=require("../$common.js"), + fs=require("fs"); + +var translations; + +module.exports=function(app,io,moddir){ + translations=eval(String(fs.readFileSync(moddir+"/English-Dutch-mistranslations.json"))); + app.get("/nlen",function(req,res){ + var translation=translations[+new Date()/60000%translations.length|0]; //new translation each minute + res.set('Content-Type', 'text/html'); + res.send( + String(fs.readFileSync(moddir+"/nlen.html")) + .replace("<!--DUTCHDUTCH-->",cmn.simpleHTMLescape(translation.nl)) + .replace("<!--ENGLISHENGLISH-->",cmn.simpleHTMLescape(translation.en)) + ); + }); +}; diff --git a/modules/poke/poke.html b/modules/poke/poke.html new file mode 100644 index 0000000..de9ce1f --- /dev/null +++ b/modules/poke/poke.html @@ -0,0 +1,125 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>Poke</title> +<script src="/socket.io/socket.io.js"></script> +<script> +var socket=io("/poke"); +var conns=[]; +var self_info={id:-1,nick:"",status:""}; + +function pokeLink(cOrId){ + var c; + if(typeof cOrId=="number"){ + for(var i=0;i<conns.length;i++)if(conns[i].id==cOrId)break; + if(i==conns.length)return document.createElement("span"); + c=conns[i]; + } else c=cOrId; + var span=document.createElement("span"); + if(c.id==self_info.id)span.classList.add("pokelink_self"); + else span.classList.add("pokelink"); + span.appendChild(document.createTextNode(c.nick)); + if(c.id!=self_info.id){ + span.addEventListener("click",function(ev){socket.emit("poke",c.id);}); + } + return span; +} + +socket.on("reset",function(){ + conns=[]; + var ul=document.getElementById("connectionlist"),l=ul.children; + for(var i=l.length-1;i>=0;i--)ul.removeChild(l[i]); + ul=document.getElementById("notificationlist");l=ul.children; + for(var i=l.length-1;i>=0;i--)ul.removeChild(l[i]); +}); +socket.on("self",function(s_i){ + self_info=s_i; +}); +socket.on("connection add",function(c){ + conns.push(c); + var li=document.createElement("li"); + li.classList.add("connection"); + li.setAttribute("poke_id",c.id); + li.appendChild(pokeLink(c)); + var span=document.createElement("span"); + span.classList.add("connectionstatus"); + span.appendChild(document.createTextNode("")); + li.appendChild(span); + document.getElementById("connectionlist").appendChild(li); +}); +socket.on("connection remove",function(id){ + var i; + for(i=0;i<conns.length;i++)if(conns[i].id==id)break; + if(i==conns.length)return; //? + conns.splice(i,1); + var ul=document.getElementById("connectionlist"),l=ul.children; + for(i=0;i<l.length;i++)if(l[i].getAttribute("poke_id")==id)break; + if(i==l.length)return; //? + ul.removeChild(l[i]); +}); +socket.on("status",function(obj){ + var id=obj.id,st=obj.status; + var i; + for(i=0;i<conns.length;i++)if(conns[i].id==id)break; + if(i==conns.length)return; + conns[i].status=st; + var ul=document.getElementById("connectionlist"),l=ul.children; + for(i=0;i<l.length;i++)if(l[i].getAttribute("poke_id")==id)break; + if(i==l.length)return; //? + l[i].children[1].firstChild.nodeValue=st?"("+st+")":""; +}); +socket.on("poke",function(id){ + var notiflist=document.getElementById("notificationlist"); + var li=document.createElement("li"); + li.classList.add("notification"); + var span=document.createElement("span"); + span.appendChild(document.createTextNode("Poke from ")); + span.appendChild(pokeLink(id)); + li.appendChild(span); + notiflist.appendChild(li); + setTimeout(function(){ + notiflist.removeChild(li); + },3000); +}); + +function changeStatus(st){ + socket.emit("status",st); +} +</script> +<style> +body{ + font-family:Monaco,Monospace; +} +span.pokelink{ + color:#44f; + cursor:pointer; + transition:color .3s; +} +span.pokelink:hover{ + color:#77f; +} +ul#notificationlist{ + list-style-type:none; +} +span.connectionstatus{ + font-size:small; + margin-left:10px; + font-style:italic; +} +</style> +</head> +<body> +<table style="width:100%"><tbody><tr> + <td style="vertical-align:top"> + <h2>Users</h2> + <ul id="connectionlist"></ul> + </td> + <td style="vertical-align:top;text-align:right"> + <h2>Notifications</h2> + <ul id="notificationlist"></ul> + </td> +</tr></tbody></table> +<input type="text" placeholder="change status..." size="30" style="margin-top:20px" onkeypress="if(event.keyCode!=13)return true;changeStatus(this.value);this.value=''"> +</body> +</html>
\ No newline at end of file diff --git a/modules/poke/poke.js b/modules/poke/poke.js new file mode 100644 index 0000000..af0285d --- /dev/null +++ b/modules/poke/poke.js @@ -0,0 +1,47 @@ +var cmn=require("../$common.js"), + fs=require("fs"), + Naampje=require("naampje").name; + +var conns=[]; + +var uniqid=(function(){ + var id=0; + return function(){return id++;}; +})(); + +module.exports=function(app,io,moddir){ + var ioNsp=io.of("/poke"); + app.get("/poke",function(req,res){ + res.sendFile(moddir+"/poke.html"); + }); + ioNsp.on("connection",function(socket){ + var id=uniqid(); + var nick=Naampje(); + var status=""; + socket.emit("reset",null); + socket.emit("self",{id:id,nick:nick}); + conns.forEach(function(c){ + socket.emit("connection add",{id:c.id,nick:c.nick}); + socket.emit("status",{id:c.id,status:c.status}); + }); + ioNsp.emit("connection add",{id:id,nick:nick}); + conns.push({id:id,nick:nick,status:"",socket:socket}); + socket.on("disconnect",function(){ + for(var i=0;i<conns.length;i++)if(conns[i].id==id)break; + if(i!=conns.length)conns.splice(i,1); + ioNsp.emit("connection remove",id); + }); + socket.on("status",function(newst){ + status=newst.toString(); + for(var i=0;i<conns.length;i++)if(conns[i].id==id)break; + if(i==conns.length)return; //? + conns[i].status=status; + ioNsp.emit("status",{id:id,status:status}); + }); + socket.on("poke",function(target){ + for(var i=0;i<conns.length;i++)if(conns[i].id==target)break; + if(i==conns.length)return; //hah. + conns[i].socket.emit("poke",id); + }); + }); +}; diff --git a/modules/quotesgrab/quotesgrab.js b/modules/quotesgrab/quotesgrab.js new file mode 100644 index 0000000..6b3b068 --- /dev/null +++ b/modules/quotesgrab/quotesgrab.js @@ -0,0 +1,65 @@ +var cmn=require("../$common.js"), + https=require("https"); + +function parsequoteshtml(html){ + var idx=html.indexOf("row-header-wrapper"); if(idx==-1)return []; idx+=18; + var endidx=html.indexOf("<tbody>",idx); + var idx2; + var list=[],obj; + var keylist=["timestamp","name","quote"],i; + while(true){ + idx=html.indexOf("row-header-wrapper",idx); if(idx==-1)break; idx+=18; + if(idx>endidx)break; + obj={}; + for(i=0;i<keylist.length;i++){ + idx=html.indexOf("<td",idx); if(idx==-1)break; idx+=3; + idx=html.indexOf(">",idx); if(idx==-1)break; idx+=1; + idx2=html.indexOf("</td>",idx); if(idx2==-1)break; + obj[keylist[i]]= + html.slice(idx,idx2) + .replace(/<\/?div[^>]*>/g,"") + .replace(/<br>/g,"\n") + .replace(/"/g,'"') + .replace(/&/g,"&") + .replace(/&#x([0-9a-f][0-9a-f]);/gi,function(match,p1){ + return String.fromCharCode(parseInt(p1,16)); + }) + .replace(/&#([0-9]{1,3});/g,function(match,p1){ + return String.fromCharCode(parseInt(p1,10)); + }); + idx=idx2+5; + } + if(obj.name=="x"||obj.name=="X")continue; + obj.timestamp=new Date(obj.timestamp); + list.push(obj); + } + return list; +} + +function getquoteslist(cb){ + https.get("https://docs.google.com/spreadsheets/d/1ywrThdscubPOC-gHh_qnFGfuPrtYxTap6UsJBDnt88c/htmlview",function(res){ + var body=""; + res.on("data",function(data){ + body+=data.toString(); + }); + res.on("end",function(){ + cb(parsequoteshtml(body)); + }); + res.on("error",function(err){ + console.log("Error in quotes res:",err); + cb(""); + }); + }).on("error",function(err){ + console.log("Error in quotes https:",err); + cb(""); + }); +} + +module.exports=function(app,io,moddir){ + app.get("/quotes",function(req,res){ + res.set("Content-Type", "text/json"); + getquoteslist(function(obj){ + res.end(JSON.stringify(obj)); + }); + }); +}; diff --git a/modules/subd-krokodil/subd-krokodil.js b/modules/subd-krokodil/subd-krokodil.js new file mode 100644 index 0000000..beabddd --- /dev/null +++ b/modules/subd-krokodil/subd-krokodil.js @@ -0,0 +1,21 @@ +var cmn=require("../$common.js"), + fs=require("fs"), + express=require("express"); + +module.exports=function(app,io,moddir){ + var router=express.Router(); + + router.use(function(req,res,next){ + next(); + }); + + router.get("/",function(req,res){ + res.sendFile(cmn.rootdir+"/web_files/krokodil.jpg"); + }); + + app.use(function(req,res,next){ + if(req.subdomains.length&&req.subdomains[req.subdomains.length-1]=="krokodil"){ + router.handle(req,res,next); + } else next(); + }); +}; diff --git a/modules/ytdl/youtube-dl-Darwin b/modules/ytdl/youtube-dl-Darwin Binary files differnew file mode 100755 index 0000000..97238a0 --- /dev/null +++ b/modules/ytdl/youtube-dl-Darwin diff --git a/modules/ytdl/ytdl.html b/modules/ytdl/ytdl.html new file mode 100644 index 0000000..e054b97 --- /dev/null +++ b/modules/ytdl/ytdl.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>YTDL</title> +<script> +function retrieve_link(){ + var link=document.getElementById("inputlink").value; + var xhr=new XMLHttpRequest(); + xhr.addEventListener("readystatechange",function(){ + if(xhr.readyState<4){ + show_output(["(waiting to send)","(request opened)","(headers received)","(loading)"][xhr.readyState]); + } + if(xhr.readyState!=4)return; + var response; + if(xhr.responseText==null)response="(null)"; + else response=xhr.responseText; + show_output(response,/^https?:\/\//.test(response)); + }); + xhr.open("GET","/ytdl/"+encodeURIComponent(link)); + xhr.send(); +} + +function show_output(text,link){ + var output=document.getElementById("output"); + if(output.firstChild)output.removeChild(output.firstChild); + var e; + if(link){ + e=document.createElement("a"); + e.href=text; + e.appendChild(document.createTextNode(text)); + } else e=document.createTextNode(text); + output.appendChild(e); +} +</script> +</head> +<body> +<h1>YTDL</h1> +<p>This page uses the <code>youtube-dl</code> utility to provide a download link for a given youtube video. It just selects whatever format <code>youtube-dl</code> considers "best"; this is normally video+audio, in the highest quality provided by YouTube. +Enter a link or a video id below:</p> +<input type="text" id="inputlink" size="100"><br> +<input type="button" value="Retrieve link" onclick="retrieve_link()"><br> +<pre id="output" style="width:100%;word-wrap:break-word;"></pre> +</body> +</html> diff --git a/modules/ytdl/ytdl.js b/modules/ytdl/ytdl.js new file mode 100644 index 0000000..3bbea16 --- /dev/null +++ b/modules/ytdl/ytdl.js @@ -0,0 +1,31 @@ +var cmn=require("../$common.js"), + child_process=require("child_process"); + +module.exports=function(app,io,moddir){ + app.param("ytdl_id",function(req,res,next,link){ + link=unescape(link); + req.params.ytdl_id=link; + if(/['"\x00-\x1f]/.test(link)){ + res.status(400).end("Invalid youtube link or id"); + return; + } + next(); + }); + app.get("/ytdl",function(req,res){ + res.sendFile(moddir+"/ytdl.html"); + }); + app.get("/ytdl/:ytdl_id",function(req,res){ + var link=req.params.ytdl_id; + var cp=child_process.execFile("/usr/bin/env",["youtube-dl","-gf","best",link],{},function(err,stdout,stderr){ + if(err){ + //console.log(err); + res.status(404); + res.set("Content-Type","text/plain"); + res.end("Youtube video id not found\n\n"+stdout+"\n\n"+stderr); + return; + } + res.set("Content-Type","text/plain"); + res.send(stdout); + }); + }); +}; |