mirror of
https://github.com/osmarks/website
synced 2025-09-01 10:17:56 +00:00
initial commit
This commit is contained in:
BIN
assets/images/icon.png
Normal file
BIN
assets/images/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 319 B |
12
assets/images/logo.svg
Normal file
12
assets/images/logo.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg viewBox="0 0 384 384" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="128" height="128" shape-rendering="crispEdges" style="fill: none; stroke-width: 0;"></rect>
|
||||
<rect x="128" y="0" width="128" height="128" shape-rendering="crispEdges" style="fill: white; stroke-width: 0;"></rect>
|
||||
<rect x="256" y="0" width="128" height="128" shape-rendering="crispEdges" style="fill: none; stroke-width: 0;"></rect>
|
||||
<rect x="0" y="128" width="128" height="128" shape-rendering="crispEdges" style="fill: none; stroke-width: 0;"></rect>
|
||||
<rect x="128" y="128" width="128" height="128" shape-rendering="crispEdges" style="fill: none; stroke-width: 0;"></rect>
|
||||
<rect x="256" y="128" width="128" height="128" shape-rendering="crispEdges" style="fill: white; stroke-width: 0;"></rect>
|
||||
<rect x="0" y="256" width="128" height="128" shape-rendering="crispEdges" style="fill: white; stroke-width: 0;"></rect>
|
||||
<rect x="128" y="256" width="128" height="128" shape-rendering="crispEdges" style="fill: white; stroke-width: 0;"></rect>
|
||||
<rect x="256" y="256" width="128" height="128" shape-rendering="crispEdges" style="fill: white; stroke-width: 0;"></rect>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
337
assets/js/h4xx0r.js
Normal file
337
assets/js/h4xx0r.js
Normal file
@@ -0,0 +1,337 @@
|
||||
// Chooses a random element from an array
|
||||
function choose(arr) {
|
||||
return arr[Math.floor(Math.random() * arr.length)]
|
||||
}
|
||||
|
||||
String.prototype.capitalizeFirstLetter = function() {
|
||||
return this.charAt(0).toUpperCase() + this.slice(1);
|
||||
}
|
||||
|
||||
// A collection of jargon
|
||||
jargonWords = {
|
||||
acronyms:
|
||||
["TCP", "HTTP", "SDD", "RAM", "GB", "CSS", "SSL", "AGP", "SQL", "FTP", "PCI", "AI", "ADP",
|
||||
"RSS", "XML", "EXE", "COM", "HDD", "THX", "SMTP", "SMS", "USB", "PNG", "PHP", "UDP",
|
||||
"TPS", "RX", "ASCII", "CD-ROM", "CGI", "CPU", "DDR", "DHCP", "BIOS", "IDE", "IP", "MAC",
|
||||
"MP3", "AAC", "PPPoE", "SSD", "SDRAM", "VGA", "XHTML", "Y2K", "GUI", "EPS"],
|
||||
adjectives:
|
||||
["auxiliary", "primary", "back-end", "digital", "open-source", "virtual", "cross-platform",
|
||||
"redundant", "online", "haptic", "multi-byte", "bluetooth", "wireless", "1080p", "neural",
|
||||
"optical", "solid state", "mobile", "unicode", "backup", "high speed", "56k", "analog",
|
||||
"fiber optic", "central", "visual", "ethernet", "Griswold"],
|
||||
nouns:
|
||||
["driver", "protocol", "bandwidth", "panel", "microchip", "program", "port", "card",
|
||||
"array", "interface", "system", "sensor", "firewall", "hard drive", "pixel", "alarm",
|
||||
"feed", "monitor", "application", "transmitter", "bus", "circuit", "capacitor", "matrix",
|
||||
"address", "form factor", "array", "mainframe", "processor", "antenna", "transistor",
|
||||
"virus", "malware", "spyware", "network", "internet", "field", "acutator", "tetryon",
|
||||
"beacon", "resonator"],
|
||||
participles:
|
||||
["backing up", "bypassing", "hacking", "overriding", "compressing", "copying", "navigating",
|
||||
"indexing", "connecting", "generating", "quantifying", "calculating", "synthesizing",
|
||||
"inputting", "transmitting", "programming", "rebooting", "parsing", "shutting down",
|
||||
"injecting", "transcoding", "encoding", "attaching", "disconnecting", "networking",
|
||||
"triaxilating", "multiplexing", "interplexing", "rewriting", "transducing",
|
||||
"acutating", "polarising"
|
||||
]};
|
||||
|
||||
// Generates a random piece of jargon
|
||||
function jargon() {
|
||||
let raw = choose(jargonWords.participles) + " " + choose(jargonWords.adjectives) + " " +
|
||||
choose(jargonWords.acronyms) + " " + choose(jargonWords.nouns);
|
||||
return raw.capitalizeFirstLetter()
|
||||
}
|
||||
|
||||
/* Graphics stuff */
|
||||
function Square(z) {
|
||||
this.width = settings.canvas.width/2;
|
||||
this.height = settings.canvas.height;
|
||||
z = z || 0;
|
||||
|
||||
var canvas = settings.canvas;
|
||||
|
||||
this.points = [
|
||||
new Point({
|
||||
x: (canvas.width / 2) - this.width,
|
||||
y: (canvas.height / 2) - this.height,
|
||||
z: z,
|
||||
}),
|
||||
new Point({
|
||||
x: (canvas.width / 2) + this.width,
|
||||
y: (canvas.height / 2) - this.height,
|
||||
z: z
|
||||
}),
|
||||
new Point({
|
||||
x: (canvas.width / 2) + this.width,
|
||||
y: (canvas.height / 2) + this.height,
|
||||
z: z
|
||||
}),
|
||||
new Point( {
|
||||
x: (canvas.width / 2) - this.width,
|
||||
y: (canvas.height / 2) + this.height,
|
||||
z: z
|
||||
})
|
||||
];
|
||||
this.dist = 0;
|
||||
}
|
||||
|
||||
Square.prototype.update = function () {
|
||||
for (var p = 0; p < this.points.length; p++) {
|
||||
this.points[p].rotateZ(0.001);
|
||||
this.points[p].z -= 3;
|
||||
if (this.points[p].z < -300) {
|
||||
this.points[p].z = 2700;
|
||||
}
|
||||
this.points[p].map2D();
|
||||
}
|
||||
};
|
||||
|
||||
Square.prototype.render = function () {
|
||||
settings.ctx.beginPath();
|
||||
settings.ctx.moveTo(this.points[0].xPos, this.points[0].yPos);
|
||||
|
||||
for (var p = 1; p < this.points.length; p++) {
|
||||
if (this.points[p].z > -(settings.focal - 50)) {
|
||||
settings.ctx.lineTo(this.points[p].xPos, this.points[p].yPos);
|
||||
}
|
||||
}
|
||||
|
||||
settings.ctx.closePath();
|
||||
settings.ctx.stroke();
|
||||
|
||||
this.dist = this.points[this.points.length - 1].z;
|
||||
};
|
||||
|
||||
function Point(pos) {
|
||||
this.x = pos.x - settings.canvas.width / 2 || 0;
|
||||
this.y = pos.y - settings.canvas.height / 2 || 0;
|
||||
this.z = pos.z || 0;
|
||||
|
||||
this.cX = 0;
|
||||
this.cY = 0;
|
||||
this.cZ = 0;
|
||||
|
||||
this.xPos = 0;
|
||||
this.yPos = 0;
|
||||
|
||||
this.map2D();
|
||||
}
|
||||
|
||||
Point.prototype.rotateZ = function (angleZ) {
|
||||
var cosZ = Math.cos(angleZ),
|
||||
sinZ = Math.sin(angleZ),
|
||||
x1 = this.x * cosZ - this.y * sinZ,
|
||||
y1 = this.y * cosZ + this.x * sinZ;
|
||||
|
||||
this.x = x1;
|
||||
this.y = y1;
|
||||
};
|
||||
|
||||
Point.prototype.map2D = function () {
|
||||
var scaleX = settings.focal / (settings.focal + this.z + this.cZ),
|
||||
scaleY = settings.focal / (settings.focal + this.z + this.cZ);
|
||||
|
||||
this.xPos = settings.vpx + (this.cX + this.x) * scaleX;
|
||||
this.yPos = settings.vpy + (this.cY + this.y) * scaleY;
|
||||
};
|
||||
|
||||
// ** Main function **//
|
||||
function GuiHacker(){
|
||||
this.squares = [];
|
||||
|
||||
this.barVals = [];
|
||||
this.sineVal = [];
|
||||
|
||||
for (var i = 0; i < 15; i++) {
|
||||
this.squares.push(new Square(-300 + (i * 200)));
|
||||
}
|
||||
|
||||
// Console stuff
|
||||
this.responses = [
|
||||
'Authorizing ',
|
||||
'Authorized...',
|
||||
'Access Granted..',
|
||||
'Going Deeper....',
|
||||
'Compression Complete.',
|
||||
'Compilation of Data Structures Complete..',
|
||||
'Entering Security Console...',
|
||||
'Encryption Unsuccesful Attempting Retry...',
|
||||
'Waiting for response...',
|
||||
'....Searching...',
|
||||
'Calculating Space Requirements '
|
||||
];
|
||||
this.isProcessing = false;
|
||||
this.processTime = 0;
|
||||
this.lastProcess = 0;
|
||||
|
||||
this.render();
|
||||
this.consoleOutput();
|
||||
}
|
||||
|
||||
GuiHacker.prototype.render = function(){
|
||||
var ctx = settings.ctx,
|
||||
canvas = settings.canvas,
|
||||
ctxBars = settings.ctxBars,
|
||||
canvasBars = settings.canvasBars;
|
||||
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
this.squares.sort(function (a, b) {
|
||||
return b.dist - a.dist;
|
||||
});
|
||||
|
||||
for (var i = 0, len = this.squares.length; i < len; i++) {
|
||||
var square = this.squares[i];
|
||||
square.update();
|
||||
square.render();
|
||||
}
|
||||
|
||||
ctxBars.clearRect(0, 0, canvasBars.width, canvasBars.height);
|
||||
|
||||
ctxBars.beginPath();
|
||||
var y = canvasBars.height/6;
|
||||
ctxBars.moveTo(0,y);
|
||||
|
||||
for(i = 0; i < canvasBars.width; i++){
|
||||
var ran = (Math.random()*20)-10;
|
||||
if(Math.random() > 0.98){
|
||||
ran = (Math.random()*50)-25;
|
||||
}
|
||||
ctxBars.lineTo(i, y + ran);
|
||||
}
|
||||
|
||||
ctxBars.stroke();
|
||||
|
||||
for(i = 0; i < canvasBars.width; i+=20){
|
||||
if(!this.barVals[i]){
|
||||
this.barVals[i] = {
|
||||
val : Math.random()*(canvasBars.height/2),
|
||||
freq : 0.1,
|
||||
sineVal : Math.random()*100
|
||||
};
|
||||
}
|
||||
|
||||
var barVal = this.barVals[i];
|
||||
barVal.sineVal+=barVal.freq;
|
||||
barVal.val+=Math.sin(barVal.sineVal*Math.PI/2)*5;
|
||||
ctxBars.fillRect(i+5,canvasBars.height,15,-barVal.val);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
requestAnimationFrame(function(){self.render();});
|
||||
};
|
||||
|
||||
// Generates a random binary, hexadecimal or floating-point number (as a string)
|
||||
function scaryNum() {
|
||||
let rand = Math.random()
|
||||
let rand2 = Math.random()
|
||||
|
||||
if (rand2 > 0.7) {
|
||||
let bigNum = rand * 1000000000
|
||||
return bigNum.toString(16).split('.')[0] // big hexadecimal
|
||||
} else if (rand2 > 0.4) {
|
||||
let longNum = rand * 100000000000
|
||||
return longNum.toString(2).split('.')[0] // big binary
|
||||
} else {
|
||||
return rand.toString() // float
|
||||
}
|
||||
}
|
||||
|
||||
GuiHacker.prototype.consoleOutput = function(){
|
||||
var textEl = document.createElement('p');
|
||||
|
||||
if(this.isProcessing){
|
||||
textEl = document.createElement('span');
|
||||
textEl.textContent += scaryNum() + " ";
|
||||
if(Date.now() > this.lastProcess + this.processTime){
|
||||
this.isProcessing = false;
|
||||
}
|
||||
}else{
|
||||
var commandType = ~~(Math.random()*4);
|
||||
switch(commandType){
|
||||
case 0:
|
||||
textEl.textContent = jargon()
|
||||
break;
|
||||
case 3:
|
||||
this.isProcessing = true;
|
||||
this.processTime = ~~(Math.random()*5000);
|
||||
this.lastProcess = Date.now();
|
||||
break;
|
||||
default:
|
||||
textEl.textContent = this.responses[~~(Math.random()*this.responses.length)];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var outputConsole = settings.outputConsole;
|
||||
outputConsole.scrollTop = outputConsole.scrollHeight;
|
||||
outputConsole.appendChild(textEl);
|
||||
|
||||
if (outputConsole.scrollHeight > window.innerHeight) {
|
||||
var removeNodes = outputConsole.querySelectorAll('*');
|
||||
for(var n = 0; n < ~~(removeNodes.length/3); n++){
|
||||
outputConsole.removeChild(removeNodes[n]);
|
||||
}
|
||||
}
|
||||
|
||||
var self = this;
|
||||
setTimeout(function(){self.consoleOutput();}, ~~(Math.random()*200));
|
||||
};
|
||||
|
||||
|
||||
// Settings
|
||||
var settings = {
|
||||
canvas : document.querySelector(".hacker-3d-shiz"),
|
||||
ctx : document.querySelector(".hacker-3d-shiz").getContext("2d"),
|
||||
canvasBars : document.querySelector(".bars-and-stuff"),
|
||||
ctxBars : document.querySelector(".bars-and-stuff").getContext("2d"),
|
||||
outputConsole : document.querySelector(".output-console"),
|
||||
vpx : 0,
|
||||
vpy : 0,
|
||||
focal : 0,
|
||||
color : "#00FF00",
|
||||
title : "Gui Hacker",
|
||||
gui : true
|
||||
},
|
||||
hash = decodeURIComponent(document.location.hash.substring(1)),
|
||||
userSettings = {};
|
||||
|
||||
if (hash){
|
||||
userSettings = JSON.parse(hash);
|
||||
if(userSettings && userSettings.title !== undefined){
|
||||
document.title = userSettings.title;
|
||||
}
|
||||
|
||||
if(userSettings && userSettings.gui !== undefined){
|
||||
settings.gui = userSettings.gui;
|
||||
}
|
||||
|
||||
settings.color = userSettings.color || settings.color;
|
||||
}
|
||||
|
||||
var adjustCanvas = function(){
|
||||
if(settings.gui){
|
||||
settings.canvas.width = (window.innerWidth/3)*2;
|
||||
settings.canvas.height = window.innerHeight / 3;
|
||||
|
||||
settings.canvasBars.width = window.innerWidth/3;
|
||||
settings.canvasBars.height = settings.canvas.height;
|
||||
|
||||
settings.outputConsole.style.height = (window.innerHeight / 3) * 2 + 'px';
|
||||
settings.outputConsole.style.top = window.innerHeight / 3 + 'px';
|
||||
|
||||
settings.focal = settings.canvas.width / 2;
|
||||
settings.vpx = settings.canvas.width / 2;
|
||||
settings.vpy = settings.canvas.height / 2;
|
||||
|
||||
settings.ctx.strokeStyle = settings.ctxBars.strokeStyle = settings.ctxBars.fillStyle = settings.color;
|
||||
}else{
|
||||
document.querySelector(".hacker-3d-shiz").style.display = "none";
|
||||
document.querySelector(".bars-and-stuff").style.display = "none";
|
||||
}
|
||||
document.body.style.color = settings.color;
|
||||
}(),
|
||||
guiHacker = new GuiHacker(settings);
|
||||
|
||||
|
||||
window.addEventListener('resize', adjustCanvas);
|
22
assets/js/hyperapp-html.min.js
vendored
Normal file
22
assets/js/hyperapp-html.min.js
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
Copyright © 2017-present [Quentin Gerodel](https://github.com/Swizz)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("hyperapp")):"function"==typeof define&&define.amd?define(["exports","hyperapp"],t):t(n.hyperappHtml={},n.hyperapp)}(this,function(n,u){"use strict";function r(r){return function(n,t){return"object"!=typeof n||Array.isArray(n)?u.h(r,{},n):u.h(r,n,t)}}n.a=function(n,t){return r("a")(n,t)},n.abbr=function(n,t){return r("abbr")(n,t)},n.address=function(n,t){return r("address")(n,t)},n.area=function(n,t){return r("area")(n,t)},n.article=function(n,t){return r("article")(n,t)},n.aside=function(n,t){return r("aside")(n,t)},n.audio=function(n,t){return r("audio")(n,t)},n.b=function(n,t){return r("b")(n,t)},n.bdi=function(n,t){return r("bdi")(n,t)},n.bdo=function(n,t){return r("bdo")(n,t)},n.blockquote=function(n,t){return r("blockquote")(n,t)},n.br=function(n,t){return r("br")(n,t)},n.button=function(n,t){return r("button")(n,t)},n.canvas=function(n,t){return r("canvas")(n,t)},n.caption=function(n,t){return r("caption")(n,t)},n.cite=function(n,t){return r("cite")(n,t)},n.code=function(n,t){return r("code")(n,t)},n.col=function(n,t){return r("col")(n,t)},n.colgroup=function(n,t){return r("colgroup")(n,t)},n.data=function(n,t){return r("data")(n,t)},n.datalist=function(n,t){return r("datalist")(n,t)},n.dd=function(n,t){return r("dd")(n,t)},n.del=function(n,t){return r("del")(n,t)},n.details=function(n,t){return r("details")(n,t)},n.dfn=function(n,t){return r("dfn")(n,t)},n.dialog=function(n,t){return r("dialog")(n,t)},n.div=function(n,t){return r("div")(n,t)},n.dl=function(n,t){return r("dl")(n,t)},n.dt=function(n,t){return r("dt")(n,t)},n.em=function(n,t){return r("em")(n,t)},n.embed=function(n,t){return r("embed")(n,t)},n.fieldset=function(n,t){return r("fieldset")(n,t)},n.figcaption=function(n,t){return r("figcaption")(n,t)},n.figure=function(n,t){return r("figure")(n,t)},n.footer=function(n,t){return r("footer")(n,t)},n.form=function(n,t){return r("form")(n,t)},n.h1=function(n,t){return r("h1")(n,t)},n.h2=function(n,t){return r("h2")(n,t)},n.h3=function(n,t){return r("h3")(n,t)},n.h4=function(n,t){return r("h4")(n,t)},n.h5=function(n,t){return r("h5")(n,t)},n.h6=function(n,t){return r("h6")(n,t)},n.header=function(n,t){return r("header")(n,t)},n.hr=function(n,t){return r("hr")(n,t)},n.i=function(n,t){return r("i")(n,t)},n.iframe=function(n,t){return r("iframe")(n,t)},n.img=function(n,t){return r("img")(n,t)},n.input=function(n,t){return r("input")(n,t)},n.ins=function(n,t){return r("ins")(n,t)},n.kbd=function(n,t){return r("kbd")(n,t)},n.label=function(n,t){return r("label")(n,t)},n.legend=function(n,t){return r("legend")(n,t)},n.li=function(n,t){return r("li")(n,t)},n.main=function(n,t){return r("main")(n,t)},n.map=function(n,t){return r("map")(n,t)},n.mark=function(n,t){return r("mark")(n,t)},n.menu=function(n,t){return r("menu")(n,t)},n.menuitem=function(n,t){return r("menuitem")(n,t)},n.meter=function(n,t){return r("meter")(n,t)},n.nav=function(n,t){return r("nav")(n,t)},n.object=function(n,t){return r("object")(n,t)},n.ol=function(n,t){return r("ol")(n,t)},n.optgroup=function(n,t){return r("optgroup")(n,t)},n.option=function(n,t){return r("option")(n,t)},n.output=function(n,t){return r("output")(n,t)},n.p=function(n,t){return r("p")(n,t)},n.param=function(n,t){return r("param")(n,t)},n.pre=function(n,t){return r("pre")(n,t)},n.progress=function(n,t){return r("progress")(n,t)},n.q=function(n,t){return r("q")(n,t)},n.rp=function(n,t){return r("rp")(n,t)},n.rt=function(n,t){return r("rt")(n,t)},n.rtc=function(n,t){return r("rtc")(n,t)},n.ruby=function(n,t){return r("ruby")(n,t)},n.s=function(n,t){return r("s")(n,t)},n.samp=function(n,t){return r("samp")(n,t)},n.section=function(n,t){return r("section")(n,t)},n.select=function(n,t){return r("select")(n,t)},n.small=function(n,t){return r("small")(n,t)},n.source=function(n,t){return r("source")(n,t)},n.span=function(n,t){return r("span")(n,t)},n.strong=function(n,t){return r("strong")(n,t)},n.sub=function(n,t){return r("sub")(n,t)},n.summary=function(n,t){return r("summary")(n,t)},n.sup=function(n,t){return r("sup")(n,t)},n.svg=function(n,t){return r("svg")(n,t)},n.table=function(n,t){return r("table")(n,t)},n.tbody=function(n,t){return r("tbody")(n,t)},n.td=function(n,t){return r("td")(n,t)},n.textarea=function(n,t){return r("textarea")(n,t)},n.tfoot=function(n,t){return r("tfoot")(n,t)},n.th=function(n,t){return r("th")(n,t)},n.thead=function(n,t){return r("thead")(n,t)},n.time=function(n,t){return r("time")(n,t)},n.tr=function(n,t){return r("tr")(n,t)},n.track=function(n,t){return r("track")(n,t)},n.u=function(n,t){return r("u")(n,t)},n.ul=function(n,t){return r("ul")(n,t)},n.video=function(n,t){return r("video")(n,t)},n.wbr=function(n,t){return r("wbr")(n,t)}});
|
23
assets/js/hyperapp.min.js
vendored
Normal file
23
assets/js/hyperapp.min.js
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// Hyperapp 1.2.10
|
||||
/*
|
||||
Copyright © 2017-present [Jorge Bucaran](https://github.com/jorgebucaran)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n(e.hyperapp={})}(this,function(e){"use strict";e.h=function(e,n){for(var t=[],r=[],o=arguments.length;2<o--;)t.push(arguments[o]);for(;t.length;){var l=t.pop();if(l&&l.pop)for(o=l.length;o--;)t.push(l[o]);else null!=l&&!0!==l&&!1!==l&&r.push(l)}return"function"==typeof e?e(n||{},r):{nodeName:e,attributes:n||{},children:r,key:n&&n.key}},e.app=function(e,n,t,r){var o,l=[].map,u=r&&r.children[0]||null,i=u&&function n(e){return{nodeName:e.nodeName.toLowerCase(),attributes:{},children:l.call(e.childNodes,function(e){return 3===e.nodeType?e.nodeValue:n(e)})}}(u),f=[],m=!0,a=v(e),c=function e(r,o,l){for(var n in l)"function"==typeof l[n]?function(e,t){l[e]=function(e){var n=t(e);return"function"==typeof n&&(n=n(h(r,a),l)),n&&n!==(o=h(r,a))&&!n.then&&d(a=p(r,v(o,n),a)),n}}(n,l[n]):e(r.concat(n),o[n]=v(o[n]),l[n]=v(l[n]));return l}([],a,v(n));return d(),c;function g(e){return"function"==typeof e?g(e(a,c)):null!=e?e:""}function s(){o=!o;var e=g(t);for(r&&!o&&(u=function e(n,t,r,o,l){if(o===r);else if(null==r||r.nodeName!==o.nodeName){var u=k(o,l);n.insertBefore(u,t),null!=r&&T(n,t,r),t=u}else if(null==r.nodeName)t.nodeValue=o;else{x(t,r.attributes,o.attributes,l=l||"svg"===o.nodeName);for(var i={},f={},a=[],c=r.children,s=o.children,d=0;d<c.length;d++){a[d]=t.childNodes[d];var v=N(c[d]);null!=v&&(i[v]=[a[d],c[d]])}for(var d=0,p=0;p<s.length;){var v=N(c[d]),h=N(s[p]=g(s[p]));if(f[v])d++;else if(null==h||h!==N(c[d+1]))if(null==h||m)null==v&&(e(t,a[d],c[d],s[p],l),p++),d++;else{var y=i[h]||[];v===h?(e(t,y[0],y[1],s[p],l),d++):y[0]?e(t,t.insertBefore(y[0],a[d]),y[1],s[p],l):e(t,a[d],null,s[p],l),f[h]=s[p],p++}else null==v&&T(t,a[d],c[d]),d++}for(;d<c.length;)null==N(c[d])&&T(t,a[d],c[d]),d++;for(var d in i)f[d]||T(t,i[d][0],i[d][1])}return t}(r,u,i,i=e)),m=!1;f.length;)f.pop()()}function d(){o||(o=!0,setTimeout(s))}function v(e,n){var t={};for(var r in e)t[r]=e[r];for(var r in n)t[r]=n[r];return t}function p(e,n,t){var r={};return e.length?(r[e[0]]=1<e.length?p(e.slice(1),n,t[e[0]]):n,v(t,r)):n}function h(e,n){for(var t=0;t<e.length;)n=n[e[t++]];return n}function N(e){return e?e.key:null}function y(e){return e.currentTarget.events[e.type](e)}function b(e,n,t,r,o){if("key"===n);else if("style"===n)if("string"==typeof t)e.style.cssText=t;else for(var l in"string"==typeof r&&(r=e.style.cssText=""),v(r,t)){var u=null==t||null==t[l]?"":t[l];"-"===l[0]?e.style.setProperty(l,u):e.style[l]=u}else"o"===n[0]&&"n"===n[1]?(n=n.slice(2),e.events?r||(r=e.events[n]):e.events={},(e.events[n]=t)?r||e.addEventListener(n,y):e.removeEventListener(n,y)):n in e&&"list"!==n&&"type"!==n&&"draggable"!==n&&"spellcheck"!==n&&"translate"!==n&&!o?e[n]=null==t?"":t:null!=t&&!1!==t&&e.setAttribute(n,t),null!=t&&!1!==t||e.removeAttribute(n)}function k(e,n){var t="string"==typeof e||"number"==typeof e?document.createTextNode(e):(n=n||"svg"===e.nodeName)?document.createElementNS("http://www.w3.org/2000/svg",e.nodeName):document.createElement(e.nodeName),r=e.attributes;if(r){r.oncreate&&f.push(function(){r.oncreate(t)});for(var o=0;o<e.children.length;o++)t.appendChild(k(e.children[o]=g(e.children[o]),n));for(var l in r)b(t,l,r[l],null,n)}return t}function x(e,n,t,r){for(var o in v(n,t))t[o]!==("value"===o||"checked"===o?e[o]:n[o])&&b(e,o,t[o],n[o],r);var l=m?t.oncreate:t.onupdate;l&&f.push(function(){l(e,n)})}function T(e,n,t){function r(){e.removeChild(function e(n,t){var r=t.attributes;if(r){for(var o=0;o<t.children.length;o++)e(n.childNodes[o],t.children[o]);r.ondestroy&&r.ondestroy(n)}return n}(n,t))}var o=t.attributes&&t.attributes.onremove;o?o(n,r):r()}}});
|
43
assets/js/infiscroll.js
Executable file
43
assets/js/infiscroll.js
Executable file
@@ -0,0 +1,43 @@
|
||||
// infiscroll.js
|
||||
// A really simple infinite-scroll system.
|
||||
// Places an invisible marker element near the bottom of the screen and adds new content until it's offscreen.
|
||||
|
||||
function insertAfter(newNode, referenceNode) {
|
||||
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
|
||||
}
|
||||
|
||||
function addMarker(reference, markerPos) {
|
||||
var markerPos = markerPos || "5vh";
|
||||
|
||||
var el = document.createElement("div");
|
||||
el.style.position = "relative";
|
||||
el.style.top = "-" + markerPos;
|
||||
el.style.width = "1px";
|
||||
el.style.height = "1px";
|
||||
el.classList.add("marker");
|
||||
|
||||
insertAfter(el, reference)
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
function infiscroll(addNewContent, markerPos, reference, maxRetries) {
|
||||
var maxRetries = maxRetries || 10
|
||||
var marker = addMarker(reference, markerPos);
|
||||
|
||||
var handler = function() {
|
||||
let i = 0;
|
||||
var x = marker.getBoundingClientRect().bottom
|
||||
while (x / document.documentElement.scrollHeight < 0.2 || x < window.innerHeight) {
|
||||
i++
|
||||
if (i - 1 == maxRetries) {
|
||||
break
|
||||
}
|
||||
addNewContent()
|
||||
}
|
||||
}
|
||||
|
||||
handler()
|
||||
window.addEventListener("scroll", handler)
|
||||
setInterval(handler, 500) // evil bodge
|
||||
}
|
61
assets/js/lorem.js
Executable file
61
assets/js/lorem.js
Executable file
@@ -0,0 +1,61 @@
|
||||
var Lorem;
|
||||
(function() {
|
||||
Lorem = {};
|
||||
|
||||
//Static variables
|
||||
Lorem.IMAGE = 1;
|
||||
Lorem.TEXT = 2;
|
||||
Lorem.TYPE = {
|
||||
PARAGRAPH: 1,
|
||||
SENTENCE: 2,
|
||||
WORD: 3
|
||||
};
|
||||
//Words to create lorem ipsum text.
|
||||
Lorem.WORDS = [
|
||||
"lorem", "ipsum", "dolor", "sit", "amet,", "consectetur", "adipiscing", "elit", "ut", "aliquam,", "purus", "sit", "amet", "luctus", "venenatis,", "lectus", "magna", "fringilla", "urna,", "porttitor", "rhoncus", "dolor", "purus", "non", "enim", "praesent", "elementum", "facilisis", "leo,", "vel", "fringilla", "est", "ullamcorper", "eget", "nulla", "facilisi", "etiam", "dignissim", "diam", "quis", "enim", "lobortis", "scelerisque", "fermentum", "dui", "faucibus", "in", "ornare", "quam", "viverra", "orci", "sagittis", "eu", "volutpat", "odio", "facilisis", "mauris", "sit", "amet", "massa", "vitae", "tortor", "condimentum", "lacinia", "quis", "vel", "eros", "donec", "ac", "odio", "tempor", "orci", "dapibus", "ultrices", "in", "iaculis", "nunc", "sed", "augue", "lacus,", "viverra", "vitae", "congue", "eu,", "consequat", "ac", "felis", "donec", "et", "odio", "pellentesque", "diam", "volutpat", "commodo", "sed", "egestas", "egestas", "fringilla", "phasellus", "faucibus", "scelerisque", "eleifend", "donec", "pretium", "vulputate", "sapien", "nec", "sagittis", "aliquam", "malesuada", "bibendum", "arcu", "vitae", "elementum",
|
||||
"curabitur", "vitae", "nunc", "sed", "velit", "dignissim", "sodales", "ut", "eu", "sem", "integer", "vitae", "justo", "eget", "magna", "fermentum", "iaculis", "eu", "non", "diam", "phasellus", "vestibulum", "lorem", "sed", "risus", "ultricies", "tristique", "nulla", "aliquet", "enim", "tortor,", "at", "auctor", "urna", "nunc", "id", "cursus", "metus", "aliquam", "eleifend", "mi", "in", "nulla", "posuere", "sollicitudin", "aliquam", "ultrices", "sagittis", "orci,", "a", "scelerisque", "purus", "semper", "eget", "duis", "at", "tellus", "at", "urna", "condimentum", "mattis", "pellentesque", "id", "nibh", "tortor,", "id", "aliquet", "lectus", "proin", "nibh", "nisl,", "condimentum", "id", "venenatis", "a,", "condimentum", "vitae", "sapien", "pellentesque", "habitant", "morbi", "tristique", "senectus", "et", "netus", "et", "malesuada", "fames", "ac", "turpis", "egestas", "sed", "tempus,", "urna", "et", "pharetra", "pharetra,", "massa", "massa", "ultricies", "mi,", "quis", "hendrerit", "dolor", "magna", "eget", "est", "lorem", "ipsum", "dolor", "sit", "amet,", "consectetur", "adipiscing", "elit", "pellentesque", "habitant", "morbi", "tristique", "senectus", "et", "netus", "et", "malesuada", "fames", "ac", "turpis", "egestas", "integer", "eget", "aliquet", "nibh", "praesent", "tristique", "magna", "sit", "amet", "purus", "gravida", "quis", "blandit", "turpis", "cursus", "in", "hac", "habitasse", "platea", "dictumst", "quisque", "sagittis,", "purus", "sit", "amet", "volutpat", "consequat,", "mauris", "nunc", "congue", "nisi,", "vitae", "suscipit", "tellus", "mauris", "a", "diam",
|
||||
"maecenas", "sed", "enim", "ut", "sem", "viverra", "aliquet", "eget", "sit", "amet", "tellus", "cras", "adipiscing", "enim", "eu", "turpis", "egestas", "pretium", "aenean", "pharetra,", "magna", "ac", "placerat", "vestibulum,", "lectus", "mauris", "ultrices", "eros,", "in", "cursus", "turpis", "massa", "tincidunt", "dui", "ut", "ornare", "lectus", "sit", "amet", "est", "placerat", "in", "egestas", "erat", "imperdiet", "sed", "euismod", "nisi", "porta", "lorem", "mollis", "aliquam", "ut", "porttitor", "leo", "a", "diam", "sollicitudin", "tempor", "id", "eu", "nisl", "nunc", "mi", "ipsum,", "faucibus", "vitae", "aliquet", "nec,", "ullamcorper", "sit", "amet", "risus", "nullam", "eget", "felis", "eget", "nunc", "lobortis", "mattis", "aliquam", "faucibus", "purus", "in", "massa", "tempor", "nec", "feugiat", "nisl", "pretium", "fusce", "id", "velit", "ut", "tortor", "pretium", "viverra", "suspendisse", "potenti", "nullam", "ac", "tortor", "vitae", "purus", "faucibus", "ornare", "suspendisse", "sed", "nisi", "lacus,", "sed", "viverra", "tellus", "in", "hac", "habitasse", "platea", "dictumst", "vestibulum", "rhoncus", "est", "pellentesque", "elit", "ullamcorper", "dignissim", "cras", "tincidunt", "lobortis", "feugiat", "vivamus", "at", "augue", "eget", "arcu", "dictum", "varius", "duis", "at", "consectetur", "lorem",
|
||||
"donec", "massa", "sapien,", "faucibus", "et", "molestie", "ac,", "feugiat", "sed", "lectus", "vestibulum", "mattis", "ullamcorper", "velit", "sed", "ullamcorper", "morbi", "tincidunt", "ornare", "massa,", "eget", "egestas", "purus", "viverra", "accumsan", "in", "nisl", "nisi,", "scelerisque", "eu", "ultrices", "vitae,", "auctor", "eu", "augue", "ut", "lectus", "arcu,", "bibendum", "at", "varius", "vel,", "pharetra", "vel", "turpis", "nunc", "eget", "lorem", "dolor,", "sed", "viverra", "ipsum", "nunc", "aliquet", "bibendum", "enim,", "facilisis", "gravida", "neque", "convallis", "a", "cras", "semper", "auctor", "neque,", "vitae", "tempus", "quam", "pellentesque", "nec", "nam", "aliquam", "sem", "et", "tortor", "consequat", "id", "porta", "nibh", "venenatis", "cras", "sed", "felis", "eget", "velit", "aliquet", "sagittis", "id", "consectetur", "purus", "ut", "faucibus", "pulvinar", "elementum", "integer", "enim", "neque,", "volutpat", "ac", "tincidunt", "vitae,", "semper", "quis", "lectus", "nulla", "at", "volutpat", "diam", "ut", "venenatis", "tellus", "in", "metus", "vulputate", "eu", "scelerisque", "felis", "imperdiet", "proin", "fermentum", "leo", "vel", "orci", "porta", "non", "pulvinar", "neque", "laoreet", "suspendisse", "interdum", "consectetur", "libero,", "id", "faucibus", "nisl", "tincidunt", "eget", "nullam", "non", "nisi", "est,", "sit", "amet", "facilisis", "magna",
|
||||
"etiam", "tempor,", "orci", "eu", "lobortis", "elementum,", "nibh", "tellus", "molestie", "nunc,", "non", "blandit", "massa", "enim", "nec", "dui", "nunc", "mattis", "enim", "ut", "tellus", "elementum", "sagittis", "vitae", "et", "leo", "duis", "ut", "diam", "quam", "nulla", "porttitor", "massa", "id", "neque", "aliquam", "vestibulum", "morbi", "blandit", "cursus", "risus,", "at", "ultrices", "mi", "tempus", "imperdiet", "nulla", "malesuada", "pellentesque", "elit", "eget", "gravida", "cum", "sociis", "natoque", "penatibus", "et", "magnis", "dis", "parturient", "montes,", "nascetur", "ridiculus", "mus", "mauris", "vitae", "ultricies", "leo", "integer", "malesuada", "nunc", "vel", "risus", "commodo", "viverra", "maecenas", "accumsan,", "lacus", "vel", "facilisis", "volutpat,", "est", "velit", "egestas", "dui,", "id", "ornare", "arcu", "odio", "ut", "sem", "nulla", "pharetra", "diam", "sit", "amet", "nisl", "suscipit", "adipiscing", "bibendum", "est", "ultricies", "integer", "quis", "auctor", "elit",
|
||||
"sed", "vulputate", "mi", "sit", "amet", "mauris", "commodo", "quis", "imperdiet", "massa", "tincidunt", "nunc", "pulvinar", "sapien", "et", "ligula", "ullamcorper", "malesuada", "proin", "libero", "nunc,", "consequat", "interdum", "varius", "sit", "amet,", "mattis", "vulputate", "enim", "nulla", "aliquet", "porttitor", "lacus,", "luctus", "accumsan", "tortor", "posuere", "ac", "ut", "consequat", "semper", "viverra", "nam", "libero", "justo,", "laoreet", "sit", "amet", "cursus", "sit", "amet,", "dictum", "sit", "amet", "justo", "donec", "enim", "diam,", "vulputate", "ut", "pharetra", "sit", "amet,", "aliquam", "id", "diam", "maecenas", "ultricies", "mi", "eget", "mauris", "pharetra", "et", "ultrices", "neque", "ornare", "aenean", "euismod", "elementum", "nisi,", "quis", "eleifend", "quam", "adipiscing", "vitae", "proin", "sagittis,", "nisl", "rhoncus", "mattis", "rhoncus,", "urna", "neque", "viverra", "justo,", "nec", "ultrices", "dui", "sapien", "eget", "mi", "proin", "sed", "libero", "enim,", "sed", "faucibus", "turpis", "in", "eu", "mi", "bibendum", "neque", "egestas", "congue", "quisque", "egestas", "diam", "in", "arcu", "cursus", "euismod", "quis", "viverra", "nibh", "cras", "pulvinar", "mattis", "nunc,", "sed", "blandit", "libero", "volutpat", "sed", "cras", "ornare", "arcu", "dui", "vivamus", "arcu", "felis,", "bibendum", "ut", "tristique", "et,", "egestas", "quis", "ipsum", "suspendisse", "ultrices", "gravida", "dictum",
|
||||
"fusce", "ut", "placerat", "orci", "nulla", "pellentesque", "dignissim", "enim,", "sit", "amet", "venenatis", "urna", "cursus", "eget", "nunc", "scelerisque", "viverra", "mauris,", "in", "aliquam", "sem", "fringilla", "ut", "morbi", "tincidunt", "augue", "interdum", "velit", "euismod", "in", "pellentesque", "massa", "placerat", "duis", "ultricies", "lacus", "sed", "turpis", "tincidunt", "id", "aliquet", "risus", "feugiat", "in", "ante", "metus,", "dictum", "at", "tempor", "commodo,", "ullamcorper", "a", "lacus", "vestibulum", "sed", "arcu", "non", "odio", "euismod", "lacinia", "at", "quis", "risus", "sed", "vulputate", "odio", "ut", "enim", "blandit", "volutpat", "maecenas", "volutpat", "blandit", "aliquam", "etiam", "erat", "velit,", "scelerisque", "in", "dictum", "non,", "consectetur", "a", "erat", "nam", "at", "lectus", "urna", "duis", "convallis", "convallis", "tellus,", "id", "interdum", "velit", "laoreet", "id", "donec", "ultrices", "tincidunt", "arcu,", "non", "sodales", "neque", "sodales", "ut", "etiam", "sit", "amet", "nisl", "purus,", "in", "mollis", "nunc",
|
||||
"sed", "id", "semper", "risus", "in", "hendrerit", "gravida", "rutrum", "quisque", "non", "tellus", "orci,", "ac", "auctor", "augue", "mauris", "augue", "neque,", "gravida", "in", "fermentum", "et,", "sollicitudin", "ac", "orci", "phasellus", "egestas", "tellus", "rutrum", "tellus", "pellentesque", "eu", "tincidunt", "tortor", "aliquam", "nulla", "facilisi", "cras", "fermentum,", "odio", "eu", "feugiat", "pretium,", "nibh", "ipsum", "consequat", "nisl,", "vel", "pretium", "lectus", "quam", "id", "leo", "in", "vitae", "turpis", "massa", "sed", "elementum", "tempus", "egestas", "sed", "sed", "risus", "pretium", "quam", "vulputate", "dignissim", "suspendisse", "in", "est", "ante", "in", "nibh", "mauris,", "cursus", "mattis", "molestie", "a,", "iaculis", "at", "erat",
|
||||
"pellentesque", "adipiscing", "commodo", "elit,", "at", "imperdiet", "dui", "accumsan", "sit", "amet", "nulla", "facilisi", "morbi", "tempus", "iaculis", "urna,", "id", "volutpat", "lacus", "laoreet", "non", "curabitur", "gravida", "arcu", "ac", "tortor", "dignissim", "convallis", "aenean", "et", "tortor", "at", "risus", "viverra", "adipiscing", "at", "in", "tellus", "integer", "feugiat", "scelerisque", "varius", "morbi", "enim", "nunc,", "faucibus", "a", "pellentesque", "sit", "amet,", "porttitor", "eget", "dolor", "morbi", "non", "arcu", "risus,", "quis", "varius", "quam", "quisque", "id", "diam", "vel", "quam", "elementum", "pulvinar", "etiam", "non", "quam", "lacus", "suspendisse", "faucibus", "interdum", "posuere", "lorem", "ipsum", "dolor", "sit", "amet,", "consectetur", "adipiscing", "elit", "duis", "tristique", "sollicitudin", "nibh", "sit", "amet", "commodo", "nulla", "facilisi",
|
||||
"nullam", "vehicula", "ipsum", "a", "arcu", "cursus", "vitae", "congue", "mauris", "rhoncus", "aenean", "vel", "elit", "scelerisque", "mauris", "pellentesque", "pulvinar", "pellentesque", "habitant", "morbi", "tristique", "senectus", "et", "netus", "et", "malesuada", "fames", "ac", "turpis", "egestas", "maecenas", "pharetra", "convallis", "posuere", "morbi", "leo", "urna,", "molestie", "at", "elementum", "eu,", "facilisis", "sed", "odio", "morbi", "quis", "commodo", "odio", "aenean", "sed", "adipiscing", "diam", "donec", "adipiscing", "tristique", "risus", "nec", "feugiat", "in", "fermentum", "posuere", "urna", "nec", "tincidunt", "praesent", "semper", "feugiat", "nibh", "sed", "pulvinar", "proin", "gravida", "hendrerit", "lectus", "a", "molestie"
|
||||
];
|
||||
//random integer method.
|
||||
Lorem.randomInt = function (min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
};
|
||||
//text creator method with parameters: how many, what
|
||||
Lorem.createText = function(count, type) {
|
||||
switch (type) {
|
||||
//paragraphs are loads of sentences.
|
||||
case Lorem.TYPE.PARAGRAPH:
|
||||
var paragraphs = new Array;
|
||||
for (var i = 0; i < count; i++) {
|
||||
var paragraphLength = this.randomInt(10, 20);
|
||||
var paragraph = this.createText(paragraphLength, Lorem.TYPE.SENTENCE);
|
||||
paragraphs.push(paragraph + " ");
|
||||
}
|
||||
return paragraphs.join('\n');
|
||||
//sentences are loads of words.
|
||||
case Lorem.TYPE.SENTENCE:
|
||||
var sentences = new Array;
|
||||
for (var i = 0; i < count; i++) {
|
||||
var sentenceLength = this.randomInt(5, 10);
|
||||
var words = this.createText(sentenceLength, Lorem.TYPE.WORD).split(' ');
|
||||
words[0] = words[0].substr(0, 1).toUpperCase() + words[0].substr(1);
|
||||
var sentence = words.join(' ');
|
||||
|
||||
sentences.push(sentence);
|
||||
}
|
||||
return (sentences.join('. ') + '.');
|
||||
//words are words
|
||||
case Lorem.TYPE.WORD:
|
||||
var wordIndex = this.randomInt(0, Lorem.WORDS.length - count - 1);
|
||||
|
||||
return Lorem.WORDS.slice(wordIndex, wordIndex + count).join(' ').replace(/[\.\,]/g,'');
|
||||
}
|
||||
};
|
||||
})();
|
23
assets/js/mithril.js
Normal file
23
assets/js/mithril.js
Normal file
File diff suppressed because one or more lines are too long
3
assets/js/moment.min.js
vendored
Normal file
3
assets/js/moment.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
631
assets/js/mustache.js
Normal file
631
assets/js/mustache.js
Normal file
@@ -0,0 +1,631 @@
|
||||
/*!
|
||||
* mustache.js - Logic-less {{mustache}} templates with JavaScript
|
||||
* http://github.com/janl/mustache.js
|
||||
*/
|
||||
|
||||
/*global define: false Mustache: true*/
|
||||
|
||||
(function defineMustache (global, factory) {
|
||||
if (typeof exports === 'object' && exports && typeof exports.nodeName !== 'string') {
|
||||
factory(exports); // CommonJS
|
||||
} else if (typeof define === 'function' && define.amd) {
|
||||
define(['exports'], factory); // AMD
|
||||
} else {
|
||||
global.Mustache = {};
|
||||
factory(global.Mustache); // script, wsh, asp
|
||||
}
|
||||
}(this, function mustacheFactory (mustache) {
|
||||
|
||||
var objectToString = Object.prototype.toString;
|
||||
var isArray = Array.isArray || function isArrayPolyfill (object) {
|
||||
return objectToString.call(object) === '[object Array]';
|
||||
};
|
||||
|
||||
function isFunction (object) {
|
||||
return typeof object === 'function';
|
||||
}
|
||||
|
||||
/**
|
||||
* More correct typeof string handling array
|
||||
* which normally returns typeof 'object'
|
||||
*/
|
||||
function typeStr (obj) {
|
||||
return isArray(obj) ? 'array' : typeof obj;
|
||||
}
|
||||
|
||||
function escapeRegExp (string) {
|
||||
return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
|
||||
}
|
||||
|
||||
/**
|
||||
* Null safe way of checking whether or not an object,
|
||||
* including its prototype, has a given property
|
||||
*/
|
||||
function hasProperty (obj, propName) {
|
||||
return obj != null && typeof obj === 'object' && (propName in obj);
|
||||
}
|
||||
|
||||
// Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
|
||||
// See https://github.com/janl/mustache.js/issues/189
|
||||
var regExpTest = RegExp.prototype.test;
|
||||
function testRegExp (re, string) {
|
||||
return regExpTest.call(re, string);
|
||||
}
|
||||
|
||||
var nonSpaceRe = /\S/;
|
||||
function isWhitespace (string) {
|
||||
return !testRegExp(nonSpaceRe, string);
|
||||
}
|
||||
|
||||
var entityMap = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
'/': '/',
|
||||
'`': '`',
|
||||
'=': '='
|
||||
};
|
||||
|
||||
function escapeHtml (string) {
|
||||
return String(string).replace(/[&<>"'`=\/]/g, function fromEntityMap (s) {
|
||||
return entityMap[s];
|
||||
});
|
||||
}
|
||||
|
||||
var whiteRe = /\s*/;
|
||||
var spaceRe = /\s+/;
|
||||
var equalsRe = /\s*=/;
|
||||
var curlyRe = /\s*\}/;
|
||||
var tagRe = /#|\^|\/|>|\{|&|=|!/;
|
||||
|
||||
/**
|
||||
* Breaks up the given `template` string into a tree of tokens. If the `tags`
|
||||
* argument is given here it must be an array with two string values: the
|
||||
* opening and closing tags used in the template (e.g. [ "<%", "%>" ]). Of
|
||||
* course, the default is to use mustaches (i.e. mustache.tags).
|
||||
*
|
||||
* A token is an array with at least 4 elements. The first element is the
|
||||
* mustache symbol that was used inside the tag, e.g. "#" or "&". If the tag
|
||||
* did not contain a symbol (i.e. {{myValue}}) this element is "name". For
|
||||
* all text that appears outside a symbol this element is "text".
|
||||
*
|
||||
* The second element of a token is its "value". For mustache tags this is
|
||||
* whatever else was inside the tag besides the opening symbol. For text tokens
|
||||
* this is the text itself.
|
||||
*
|
||||
* The third and fourth elements of the token are the start and end indices,
|
||||
* respectively, of the token in the original template.
|
||||
*
|
||||
* Tokens that are the root node of a subtree contain two more elements: 1) an
|
||||
* array of tokens in the subtree and 2) the index in the original template at
|
||||
* which the closing tag for that section begins.
|
||||
*/
|
||||
function parseTemplate (template, tags) {
|
||||
if (!template)
|
||||
return [];
|
||||
|
||||
var sections = []; // Stack to hold section tokens
|
||||
var tokens = []; // Buffer to hold the tokens
|
||||
var spaces = []; // Indices of whitespace tokens on the current line
|
||||
var hasTag = false; // Is there a {{tag}} on the current line?
|
||||
var nonSpace = false; // Is there a non-space char on the current line?
|
||||
|
||||
// Strips all whitespace tokens array for the current line
|
||||
// if there was a {{#tag}} on it and otherwise only space.
|
||||
function stripSpace () {
|
||||
if (hasTag && !nonSpace) {
|
||||
while (spaces.length)
|
||||
delete tokens[spaces.pop()];
|
||||
} else {
|
||||
spaces = [];
|
||||
}
|
||||
|
||||
hasTag = false;
|
||||
nonSpace = false;
|
||||
}
|
||||
|
||||
var openingTagRe, closingTagRe, closingCurlyRe;
|
||||
function compileTags (tagsToCompile) {
|
||||
if (typeof tagsToCompile === 'string')
|
||||
tagsToCompile = tagsToCompile.split(spaceRe, 2);
|
||||
|
||||
if (!isArray(tagsToCompile) || tagsToCompile.length !== 2)
|
||||
throw new Error('Invalid tags: ' + tagsToCompile);
|
||||
|
||||
openingTagRe = new RegExp(escapeRegExp(tagsToCompile[0]) + '\\s*');
|
||||
closingTagRe = new RegExp('\\s*' + escapeRegExp(tagsToCompile[1]));
|
||||
closingCurlyRe = new RegExp('\\s*' + escapeRegExp('}' + tagsToCompile[1]));
|
||||
}
|
||||
|
||||
compileTags(tags || mustache.tags);
|
||||
|
||||
var scanner = new Scanner(template);
|
||||
|
||||
var start, type, value, chr, token, openSection;
|
||||
while (!scanner.eos()) {
|
||||
start = scanner.pos;
|
||||
|
||||
// Match any text between tags.
|
||||
value = scanner.scanUntil(openingTagRe);
|
||||
|
||||
if (value) {
|
||||
for (var i = 0, valueLength = value.length; i < valueLength; ++i) {
|
||||
chr = value.charAt(i);
|
||||
|
||||
if (isWhitespace(chr)) {
|
||||
spaces.push(tokens.length);
|
||||
} else {
|
||||
nonSpace = true;
|
||||
}
|
||||
|
||||
tokens.push([ 'text', chr, start, start + 1 ]);
|
||||
start += 1;
|
||||
|
||||
// Check for whitespace on the current line.
|
||||
if (chr === '\n')
|
||||
stripSpace();
|
||||
}
|
||||
}
|
||||
|
||||
// Match the opening tag.
|
||||
if (!scanner.scan(openingTagRe))
|
||||
break;
|
||||
|
||||
hasTag = true;
|
||||
|
||||
// Get the tag type.
|
||||
type = scanner.scan(tagRe) || 'name';
|
||||
scanner.scan(whiteRe);
|
||||
|
||||
// Get the tag value.
|
||||
if (type === '=') {
|
||||
value = scanner.scanUntil(equalsRe);
|
||||
scanner.scan(equalsRe);
|
||||
scanner.scanUntil(closingTagRe);
|
||||
} else if (type === '{') {
|
||||
value = scanner.scanUntil(closingCurlyRe);
|
||||
scanner.scan(curlyRe);
|
||||
scanner.scanUntil(closingTagRe);
|
||||
type = '&';
|
||||
} else {
|
||||
value = scanner.scanUntil(closingTagRe);
|
||||
}
|
||||
|
||||
// Match the closing tag.
|
||||
if (!scanner.scan(closingTagRe))
|
||||
throw new Error('Unclosed tag at ' + scanner.pos);
|
||||
|
||||
token = [ type, value, start, scanner.pos ];
|
||||
tokens.push(token);
|
||||
|
||||
if (type === '#' || type === '^') {
|
||||
sections.push(token);
|
||||
} else if (type === '/') {
|
||||
// Check section nesting.
|
||||
openSection = sections.pop();
|
||||
|
||||
if (!openSection)
|
||||
throw new Error('Unopened section "' + value + '" at ' + start);
|
||||
|
||||
if (openSection[1] !== value)
|
||||
throw new Error('Unclosed section "' + openSection[1] + '" at ' + start);
|
||||
} else if (type === 'name' || type === '{' || type === '&') {
|
||||
nonSpace = true;
|
||||
} else if (type === '=') {
|
||||
// Set the tags for the next time around.
|
||||
compileTags(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure there are no open sections when we're done.
|
||||
openSection = sections.pop();
|
||||
|
||||
if (openSection)
|
||||
throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos);
|
||||
|
||||
return nestTokens(squashTokens(tokens));
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines the values of consecutive text tokens in the given `tokens` array
|
||||
* to a single token.
|
||||
*/
|
||||
function squashTokens (tokens) {
|
||||
var squashedTokens = [];
|
||||
|
||||
var token, lastToken;
|
||||
for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
|
||||
token = tokens[i];
|
||||
|
||||
if (token) {
|
||||
if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {
|
||||
lastToken[1] += token[1];
|
||||
lastToken[3] = token[3];
|
||||
} else {
|
||||
squashedTokens.push(token);
|
||||
lastToken = token;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return squashedTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forms the given array of `tokens` into a nested tree structure where
|
||||
* tokens that represent a section have two additional items: 1) an array of
|
||||
* all tokens that appear in that section and 2) the index in the original
|
||||
* template that represents the end of that section.
|
||||
*/
|
||||
function nestTokens (tokens) {
|
||||
var nestedTokens = [];
|
||||
var collector = nestedTokens;
|
||||
var sections = [];
|
||||
|
||||
var token, section;
|
||||
for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
|
||||
token = tokens[i];
|
||||
|
||||
switch (token[0]) {
|
||||
case '#':
|
||||
case '^':
|
||||
collector.push(token);
|
||||
sections.push(token);
|
||||
collector = token[4] = [];
|
||||
break;
|
||||
case '/':
|
||||
section = sections.pop();
|
||||
section[5] = token[2];
|
||||
collector = sections.length > 0 ? sections[sections.length - 1][4] : nestedTokens;
|
||||
break;
|
||||
default:
|
||||
collector.push(token);
|
||||
}
|
||||
}
|
||||
|
||||
return nestedTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple string scanner that is used by the template parser to find
|
||||
* tokens in template strings.
|
||||
*/
|
||||
function Scanner (string) {
|
||||
this.string = string;
|
||||
this.tail = string;
|
||||
this.pos = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the tail is empty (end of string).
|
||||
*/
|
||||
Scanner.prototype.eos = function eos () {
|
||||
return this.tail === '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Tries to match the given regular expression at the current position.
|
||||
* Returns the matched text if it can match, the empty string otherwise.
|
||||
*/
|
||||
Scanner.prototype.scan = function scan (re) {
|
||||
var match = this.tail.match(re);
|
||||
|
||||
if (!match || match.index !== 0)
|
||||
return '';
|
||||
|
||||
var string = match[0];
|
||||
|
||||
this.tail = this.tail.substring(string.length);
|
||||
this.pos += string.length;
|
||||
|
||||
return string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Skips all text until the given regular expression can be matched. Returns
|
||||
* the skipped string, which is the entire tail if no match can be made.
|
||||
*/
|
||||
Scanner.prototype.scanUntil = function scanUntil (re) {
|
||||
var index = this.tail.search(re), match;
|
||||
|
||||
switch (index) {
|
||||
case -1:
|
||||
match = this.tail;
|
||||
this.tail = '';
|
||||
break;
|
||||
case 0:
|
||||
match = '';
|
||||
break;
|
||||
default:
|
||||
match = this.tail.substring(0, index);
|
||||
this.tail = this.tail.substring(index);
|
||||
}
|
||||
|
||||
this.pos += match.length;
|
||||
|
||||
return match;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a rendering context by wrapping a view object and
|
||||
* maintaining a reference to the parent context.
|
||||
*/
|
||||
function Context (view, parentContext) {
|
||||
this.view = view;
|
||||
this.cache = { '.': this.view };
|
||||
this.parent = parentContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new context using the given view with this context
|
||||
* as the parent.
|
||||
*/
|
||||
Context.prototype.push = function push (view) {
|
||||
return new Context(view, this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the value of the given name in this context, traversing
|
||||
* up the context hierarchy if the value is absent in this context's view.
|
||||
*/
|
||||
Context.prototype.lookup = function lookup (name) {
|
||||
var cache = this.cache;
|
||||
|
||||
var value;
|
||||
if (cache.hasOwnProperty(name)) {
|
||||
value = cache[name];
|
||||
} else {
|
||||
var context = this, names, index, lookupHit = false;
|
||||
|
||||
while (context) {
|
||||
if (name.indexOf('.') > 0) {
|
||||
value = context.view;
|
||||
names = name.split('.');
|
||||
index = 0;
|
||||
|
||||
/**
|
||||
* Using the dot notion path in `name`, we descend through the
|
||||
* nested objects.
|
||||
*
|
||||
* To be certain that the lookup has been successful, we have to
|
||||
* check if the last object in the path actually has the property
|
||||
* we are looking for. We store the result in `lookupHit`.
|
||||
*
|
||||
* This is specially necessary for when the value has been set to
|
||||
* `undefined` and we want to avoid looking up parent contexts.
|
||||
**/
|
||||
while (value != null && index < names.length) {
|
||||
if (index === names.length - 1)
|
||||
lookupHit = hasProperty(value, names[index]);
|
||||
|
||||
value = value[names[index++]];
|
||||
}
|
||||
} else {
|
||||
value = context.view[name];
|
||||
lookupHit = hasProperty(context.view, name);
|
||||
}
|
||||
|
||||
if (lookupHit)
|
||||
break;
|
||||
|
||||
context = context.parent;
|
||||
}
|
||||
|
||||
cache[name] = value;
|
||||
}
|
||||
|
||||
if (isFunction(value))
|
||||
value = value.call(this.view);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
/**
|
||||
* A Writer knows how to take a stream of tokens and render them to a
|
||||
* string, given a context. It also maintains a cache of templates to
|
||||
* avoid the need to parse the same template twice.
|
||||
*/
|
||||
function Writer () {
|
||||
this.cache = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all cached templates in this writer.
|
||||
*/
|
||||
Writer.prototype.clearCache = function clearCache () {
|
||||
this.cache = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses and caches the given `template` and returns the array of tokens
|
||||
* that is generated from the parse.
|
||||
*/
|
||||
Writer.prototype.parse = function parse (template, tags) {
|
||||
var cache = this.cache;
|
||||
var tokens = cache[template];
|
||||
|
||||
if (tokens == null)
|
||||
tokens = cache[template] = parseTemplate(template, tags);
|
||||
|
||||
return tokens;
|
||||
};
|
||||
|
||||
/**
|
||||
* High-level method that is used to render the given `template` with
|
||||
* the given `view`.
|
||||
*
|
||||
* The optional `partials` argument may be an object that contains the
|
||||
* names and templates of partials that are used in the template. It may
|
||||
* also be a function that is used to load partial templates on the fly
|
||||
* that takes a single argument: the name of the partial.
|
||||
*/
|
||||
Writer.prototype.render = function render (template, view, partials) {
|
||||
var tokens = this.parse(template);
|
||||
var context = (view instanceof Context) ? view : new Context(view);
|
||||
return this.renderTokens(tokens, context, partials, template);
|
||||
};
|
||||
|
||||
/**
|
||||
* Low-level method that renders the given array of `tokens` using
|
||||
* the given `context` and `partials`.
|
||||
*
|
||||
* Note: The `originalTemplate` is only ever used to extract the portion
|
||||
* of the original template that was contained in a higher-order section.
|
||||
* If the template doesn't use higher-order sections, this argument may
|
||||
* be omitted.
|
||||
*/
|
||||
Writer.prototype.renderTokens = function renderTokens (tokens, context, partials, originalTemplate) {
|
||||
var buffer = '';
|
||||
|
||||
var token, symbol, value;
|
||||
for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
|
||||
value = undefined;
|
||||
token = tokens[i];
|
||||
symbol = token[0];
|
||||
|
||||
if (symbol === '#') value = this.renderSection(token, context, partials, originalTemplate);
|
||||
else if (symbol === '^') value = this.renderInverted(token, context, partials, originalTemplate);
|
||||
else if (symbol === '>') value = this.renderPartial(token, context, partials, originalTemplate);
|
||||
else if (symbol === '&') value = this.unescapedValue(token, context);
|
||||
else if (symbol === 'name') value = this.escapedValue(token, context);
|
||||
else if (symbol === 'text') value = this.rawValue(token);
|
||||
|
||||
if (value !== undefined)
|
||||
buffer += value;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
};
|
||||
|
||||
Writer.prototype.renderSection = function renderSection (token, context, partials, originalTemplate) {
|
||||
var self = this;
|
||||
var buffer = '';
|
||||
var value = context.lookup(token[1]);
|
||||
|
||||
// This function is used to render an arbitrary template
|
||||
// in the current context by higher-order sections.
|
||||
function subRender (template) {
|
||||
return self.render(template, context, partials);
|
||||
}
|
||||
|
||||
if (!value) return;
|
||||
|
||||
if (isArray(value)) {
|
||||
for (var j = 0, valueLength = value.length; j < valueLength; ++j) {
|
||||
buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate);
|
||||
}
|
||||
} else if (typeof value === 'object' || typeof value === 'string' || typeof value === 'number') {
|
||||
buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate);
|
||||
} else if (isFunction(value)) {
|
||||
if (typeof originalTemplate !== 'string')
|
||||
throw new Error('Cannot use higher-order sections without the original template');
|
||||
|
||||
// Extract the portion of the original template that the section contains.
|
||||
value = value.call(context.view, originalTemplate.slice(token[3], token[5]), subRender);
|
||||
|
||||
if (value != null)
|
||||
buffer += value;
|
||||
} else {
|
||||
buffer += this.renderTokens(token[4], context, partials, originalTemplate);
|
||||
}
|
||||
return buffer;
|
||||
};
|
||||
|
||||
Writer.prototype.renderInverted = function renderInverted (token, context, partials, originalTemplate) {
|
||||
var value = context.lookup(token[1]);
|
||||
|
||||
// Use JavaScript's definition of falsy. Include empty arrays.
|
||||
// See https://github.com/janl/mustache.js/issues/186
|
||||
if (!value || (isArray(value) && value.length === 0))
|
||||
return this.renderTokens(token[4], context, partials, originalTemplate);
|
||||
};
|
||||
|
||||
Writer.prototype.renderPartial = function renderPartial (token, context, partials) {
|
||||
if (!partials) return;
|
||||
|
||||
var value = isFunction(partials) ? partials(token[1]) : partials[token[1]];
|
||||
if (value != null)
|
||||
return this.renderTokens(this.parse(value), context, partials, value);
|
||||
};
|
||||
|
||||
Writer.prototype.unescapedValue = function unescapedValue (token, context) {
|
||||
var value = context.lookup(token[1]);
|
||||
if (value != null)
|
||||
return value;
|
||||
};
|
||||
|
||||
Writer.prototype.escapedValue = function escapedValue (token, context) {
|
||||
var value = context.lookup(token[1]);
|
||||
if (value != null)
|
||||
return mustache.escape(value);
|
||||
};
|
||||
|
||||
Writer.prototype.rawValue = function rawValue (token) {
|
||||
return token[1];
|
||||
};
|
||||
|
||||
mustache.name = 'mustache.js';
|
||||
mustache.version = '2.3.0';
|
||||
mustache.tags = [ '{{', '}}' ];
|
||||
|
||||
// All high-level mustache.* functions use this writer.
|
||||
var defaultWriter = new Writer();
|
||||
|
||||
/**
|
||||
* Clears all cached templates in the default writer.
|
||||
*/
|
||||
mustache.clearCache = function clearCache () {
|
||||
return defaultWriter.clearCache();
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses and caches the given template in the default writer and returns the
|
||||
* array of tokens it contains. Doing this ahead of time avoids the need to
|
||||
* parse templates on the fly as they are rendered.
|
||||
*/
|
||||
mustache.parse = function parse (template, tags) {
|
||||
return defaultWriter.parse(template, tags);
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders the `template` with the given `view` and `partials` using the
|
||||
* default writer.
|
||||
*/
|
||||
mustache.render = function render (template, view, partials) {
|
||||
if (typeof template !== 'string') {
|
||||
throw new TypeError('Invalid template! Template should be a "string" ' +
|
||||
'but "' + typeStr(template) + '" was given as the first ' +
|
||||
'argument for mustache#render(template, view, partials)');
|
||||
}
|
||||
|
||||
return defaultWriter.render(template, view, partials);
|
||||
};
|
||||
|
||||
// This is here for backwards compatibility with 0.4.x.,
|
||||
/*eslint-disable */ // eslint wants camel cased function name
|
||||
mustache.to_html = function to_html (template, view, partials, send) {
|
||||
/*eslint-enable*/
|
||||
|
||||
var result = mustache.render(template, view, partials);
|
||||
|
||||
if (isFunction(send)) {
|
||||
send(result);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// Export the escaping function so that the user may override it.
|
||||
// See https://github.com/janl/mustache.js/issues/244
|
||||
mustache.escape = escapeHtml;
|
||||
|
||||
// Export these mainly for testing, but also for advanced usage.
|
||||
mustache.Scanner = Scanner;
|
||||
mustache.Context = Context;
|
||||
mustache.Writer = Writer;
|
||||
|
||||
return mustache;
|
||||
}));
|
||||
|
289
assets/js/page.js
Normal file
289
assets/js/page.js
Normal file
@@ -0,0 +1,289 @@
|
||||
// https://github.com/jakearchibald/idb-keyval/blob/master/dist/idb-keyval-iife.min.js
|
||||
// This is small enough that I decided to just embed it directly here to save hassle and network bandwidth.
|
||||
const idbk=function(e){"use strict";class t{constructor(e="keyval-store",t="keyval"){this.storeName=t,this._dbp=new Promise((r,n)=>{const o=indexedDB.open(e,1);o.onerror=(()=>n(o.error)),o.onsuccess=(()=>r(o.result)),o.onupgradeneeded=(()=>{o.result.createObjectStore(t)})})}
|
||||
_withIDBStore(e,t){return this._dbp.then(r=>new Promise((n,o)=>{const s=r.transaction(this.storeName,e);s.oncomplete=(()=>n()),s.onabort=s.onerror=(()=>o(s.error)),t(s.objectStore(this.storeName))}))}}let r;function n(){return r||(r=new t),r}return e.Store=t,e.get=function(e,t=n()){let r;return t._withIDBStore("readonly",t=>{r=t.get(e)}).then(()=>r.result)},e.set=function(e,t,r=n()){return r._withIDBStore("readwrite",r=>{r.put(t,e)})},e.del=function(e,t=n()){return t._withIDBStore("readwrite",t=>{t.delete(e)})},e.clear=function(e=n()){return e._withIDBStore("readwrite",e=>{e.clear()})},e.keys=function(e=n()){const t=[];return e._withIDBStore("readonly",e=>{(e.openKeyCursor||e.openCursor).call(e).onsuccess=function(){this.result&&(t.push(this.result.key),this.result.continue())}}).then(()=>t)},e}({});
|
||||
|
||||
// attempt to register service worker
|
||||
if ("serviceWorker" in navigator) {
|
||||
navigator.serviceWorker.register("/sw.js", { scope: "/" }).then(reg => {
|
||||
if (reg.installing) {
|
||||
console.log("Service worker installing");
|
||||
} else if (reg.waiting) {
|
||||
console.log("Service worker installed");
|
||||
} else if (reg.active) {
|
||||
console.log("Service worker active");
|
||||
}
|
||||
}).catch(error => {
|
||||
// registration failed
|
||||
console.log("Registration failed with " + error);
|
||||
});
|
||||
} else {
|
||||
console.log("Service workers are not supported.");
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript
|
||||
const hash = function(str, seed = 0) {
|
||||
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed
|
||||
for (let i = 0, ch; i < str.length; i++) {
|
||||
ch = str.charCodeAt(i)
|
||||
h1 = Math.imul(h1 ^ ch, 2654435761)
|
||||
h2 = Math.imul(h2 ^ ch, 1597334677)
|
||||
}
|
||||
h1 = Math.imul(h1 ^ h1>>>16, 2246822507) ^ Math.imul(h2 ^ h2>>>13, 3266489909);
|
||||
h2 = Math.imul(h2 ^ h2>>>16, 2246822507) ^ Math.imul(h1 ^ h1>>>13, 3266489909)
|
||||
return 4294967296 * (2097151 & h2) + (h1>>>0)
|
||||
}
|
||||
|
||||
const colHash = (str, saturation = 100, lightness = 70) => `hsl(${hash(str) % 360}, ${saturation}%, ${lightness}%)`
|
||||
|
||||
// Arbitrary Points code, wrapped in an IIFE to not pollute the global environment much more than it already is
|
||||
window.points = (() => {
|
||||
const achievementInfo = {
|
||||
test: {
|
||||
title: "Test",
|
||||
conditions: "testing",
|
||||
description: "This achievement is for testing purposes.",
|
||||
points: 10
|
||||
},
|
||||
firstAchievement: {
|
||||
title: "Achievement-Achieving Achievement™",
|
||||
conditions: "unlocking another achievement",
|
||||
description: "You achieved your first achievement, so here's an achievement to commemorate your achievement of an achievement! Enjoy the sense of achievement you get from this achievement!",
|
||||
points: 5.5
|
||||
},
|
||||
timeSpent1Hour: {
|
||||
title: "Causal Mondays",
|
||||
conditions: "using the site for a total of 1 hour",
|
||||
description: "Apparently you've spent an hour on this site. Weird. You get an achievement for it, though.",
|
||||
points: 9.3
|
||||
},
|
||||
visitArbitraryPoints: {
|
||||
title: "Arbitrary Arbitration",
|
||||
conditions: "visiting the Arbitrary Points management page",
|
||||
description: "You've now visited the Arbitrary Points page, from which you can see your achievements, point count and tracked metrics.",
|
||||
points: 15
|
||||
},
|
||||
reset: {
|
||||
title: "Burn It Down",
|
||||
conditions: "resetting",
|
||||
description: "So you wiped your Arbitrary Points data for whatever reason. Now you get this exclusive achievement!",
|
||||
points: 11.4
|
||||
},
|
||||
pagesVisited64: {
|
||||
title: "Real Dedication",
|
||||
conditions: "visiting 64 pages",
|
||||
points: 15.01,
|
||||
description: "You've visited something between 64 pages or 1 page 64 times and are thus being rewarded for your frequent use of the site."
|
||||
},
|
||||
blindLuck: {
|
||||
title: "Ridiculous Blind Luck",
|
||||
conditions: "0.001% chance of getting this every second",
|
||||
points: 66.6,
|
||||
description: "Through sheer chance you have obtained this achievement, which provides more points than all the other ones. This is probably a metaphor for life."
|
||||
},
|
||||
offline: {
|
||||
title: "Not The Dinosaur Game",
|
||||
conditions: "seeing the offline page",
|
||||
points: 10.1,
|
||||
description: "Something broke somewhere and you're seeing this. Sadly this no longer has the Chrome dinosaur game, but you can use other stuff."
|
||||
},
|
||||
attemptedXSS: {
|
||||
title: "1337 h4xx0r",
|
||||
conditions: "attempting an XSS attack",
|
||||
points: 43.01,
|
||||
description: "You appear to have attempted a cross-site-scripting attack. This probably hasn't worked. If it has, please tell me as this is a problem."
|
||||
},
|
||||
emuwar10: {
|
||||
title: "Emu Warrior",
|
||||
conditions: "vanquishing 10 or more foes in Emu War",
|
||||
points: 28.5,
|
||||
description: "You have become a mighty Emu Warrior by defeating 10 or more monsters and/or probably things which live in Australia."
|
||||
},
|
||||
lorem400: {
|
||||
title: "quare?",
|
||||
conditions: "seeing 400 paragraphs of Lorem Ipsum",
|
||||
points: 42.3,
|
||||
description: "Apparently you viewed 400 paragraphs of randomly generated Lorem Ipsum. I don't know why."
|
||||
},
|
||||
firstComment: {
|
||||
title: "That's just, like, your opinion, man",
|
||||
conditions: "posting a comment",
|
||||
points: 30.5,
|
||||
description: "You (probably, the detection isn't 100% accurate) posted a comment! Enjoy expressing your opinion (or random meaningless message) to random internet people!"
|
||||
}
|
||||
}
|
||||
|
||||
const e = (cls, parent, content) => {
|
||||
const element = document.createElement("div")
|
||||
element.classList.add(cls)
|
||||
if (content) { element.appendChild(document.createTextNode(content)) }
|
||||
if (parent) { parent.appendChild(element) }
|
||||
return element
|
||||
}
|
||||
|
||||
const achievementsContainer = e("achievements", document.body)
|
||||
const displayAchievement = (title, description, conditions, points) => {
|
||||
const elem = e("achievement", achievementsContainer)
|
||||
elem.title = "click to dismiss"
|
||||
e("title", elem, "Achievement achieved!")
|
||||
e("title", elem, title)
|
||||
elem.style.backgroundColor = colHash(title)
|
||||
e("description", elem, description)
|
||||
e("conditions", elem, `Unlocked by: ${conditions}`)
|
||||
e("points", elem, `${points} points`)
|
||||
// disappear on click
|
||||
elem.addEventListener("click", () => {
|
||||
achievementsContainer.removeChild(elem)
|
||||
})
|
||||
}
|
||||
|
||||
const metricsStore = new idbk.Store("arbitrary-metrics", "metrics")
|
||||
const dataStore = new idbk.Store("arbitrary-points", "data")
|
||||
|
||||
const fireUpdatedEvent = () => document.dispatchEvent(new Event("points-update"))
|
||||
|
||||
let pointsCount
|
||||
|
||||
const getPoints = async () => {
|
||||
if (pointsCount) { return pointsCount }
|
||||
let value = await idbk.get("points", dataStore)
|
||||
if (value === undefined) {
|
||||
await idbk.set("points", 0, dataStore)
|
||||
value = 0
|
||||
}
|
||||
pointsCount = value
|
||||
return value
|
||||
}
|
||||
|
||||
const updateStoredValue = async (store, name, fn, def) => {
|
||||
const newValue = fn(await idbk.get(name, store) || def)
|
||||
await idbk.set(name, newValue, store)
|
||||
return newValue
|
||||
}
|
||||
|
||||
const updateMetric = async (name, fn, def) => {
|
||||
const newValue = await updateStoredValue(metricsStore, name, fn, def)
|
||||
switch (name) {
|
||||
case "achievements":
|
||||
if (newValue === 1) {
|
||||
await unlockAchievement("firstAchievement")
|
||||
}
|
||||
break
|
||||
}
|
||||
return newValue
|
||||
}
|
||||
const incrementPoints = inc => {
|
||||
pointsCount += inc
|
||||
updateStoredValue(dataStore, "points", x => x + inc, 0)
|
||||
fireUpdatedEvent()
|
||||
}
|
||||
|
||||
// increment pages visited count, since this should be run when a page is visited
|
||||
updateMetric("pagesVisited", x => x + 1, 0)
|
||||
|
||||
const visitStart = Date.now()
|
||||
window.onbeforeunload = () => {
|
||||
const elapsedMs = Date.now() - visitStart
|
||||
updateMetric("timeSpent", x => x + (elapsedMs / 1000), 0)
|
||||
}
|
||||
|
||||
const setMetric = (metric, value) => idbk.set(metric, value, metricsStore)
|
||||
|
||||
const readAllMetrics = async () => {
|
||||
const keys = await idbk.keys(metricsStore)
|
||||
const out = new Map()
|
||||
await Promise.all(keys.map(async k => {
|
||||
out.set(k, await idbk.get(k, metricsStore))
|
||||
}))
|
||||
return out
|
||||
}
|
||||
|
||||
const reset = async () => {
|
||||
pointsCount = 0
|
||||
achievementsList = []
|
||||
await idbk.clear(metricsStore)
|
||||
await idbk.clear(dataStore)
|
||||
await unlockAchievement("reset")
|
||||
}
|
||||
|
||||
let achievementsList
|
||||
const getAchievements = async () => {
|
||||
if (achievementsList) { return achievementsList }
|
||||
const value = await idbk.get("achievements", dataStore) || []
|
||||
achievementsList = value
|
||||
return value
|
||||
}
|
||||
|
||||
const unlockAchievement = async id => {
|
||||
const achievementsUnlocked = await getAchievements()
|
||||
if (achievementsUnlocked.filter(a => a.id === id).length > 0) { return "already unlocked" }
|
||||
const info = achievementInfo[id]
|
||||
if (!info) { throw new Error("Achievement not recognized") }
|
||||
info.points = info.points || 10
|
||||
displayAchievement(info.title, info.description, info.conditions, info.points)
|
||||
const item = {
|
||||
id,
|
||||
timestamp: Date.now(),
|
||||
page: window.location.pathname,
|
||||
points: info.points
|
||||
}
|
||||
achievementsList = achievementsList.concat([item])
|
||||
await Promise.all([
|
||||
idbk.set("achievements", achievementsList, dataStore),
|
||||
updateMetric("achievements", x => x + 1, 0),
|
||||
incrementPoints(info.points)
|
||||
])
|
||||
|
||||
fireUpdatedEvent()
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
const metrics = await readAllMetrics()
|
||||
if (metrics.get("timeSpent") > 3600) { // one hour in seconds
|
||||
unlockAchievement("timeSpent1Hour")
|
||||
}
|
||||
if (metrics.get("pagesVisited") > 64) {
|
||||
unlockAchievement("pagesVisited64")
|
||||
}
|
||||
})
|
||||
|
||||
setInterval(() => {
|
||||
if (Math.random() < 0.00001) {
|
||||
unlockAchievement("blindLuck")
|
||||
}
|
||||
}, 1000)
|
||||
|
||||
window.addEventListener("input", e => {
|
||||
if (e.target) {
|
||||
const text = e.target.value || e.target.textContent
|
||||
// extremely advanced XSS detection algorithm
|
||||
if (text && (text.includes("<script") || text.includes("onload="))) {
|
||||
unlockAchievement("attemptedXSS")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
window.addEventListener("click", e => {
|
||||
// detect clicking of comment "submit" button
|
||||
if (e.target &&
|
||||
e.target.value === "Submit" &&
|
||||
e.target.parentElement &&
|
||||
e.target.parentElement.parentElement &&
|
||||
e.target.parentElement.parentElement.className === "auth-section") {
|
||||
unlockAchievement("firstComment")
|
||||
points.updateMetric("commentsPosted", function(x) { return x + 1 }, 0)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
reset,
|
||||
updateMetric,
|
||||
readAllMetrics,
|
||||
getPoints,
|
||||
incrementPoints,
|
||||
unlockAchievement,
|
||||
getAchievements,
|
||||
achievementInfo,
|
||||
setMetric
|
||||
}
|
||||
})()
|
6
assets/js/vue.js
Normal file
6
assets/js/vue.js
Normal file
File diff suppressed because one or more lines are too long
21
assets/manifest.webmanifest
Normal file
21
assets/manifest.webmanifest
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"dir": "ltr",
|
||||
"lang": "en",
|
||||
"name": "{{{name}}}",
|
||||
"scope": "/",
|
||||
"display": "browser",
|
||||
"start_url": "https://{{domain}}/",
|
||||
"short_name": "{{domain}}",
|
||||
"theme_color": "transparent",
|
||||
"description": "{{siteDescription}}",
|
||||
"orientation": "any",
|
||||
"background_color": "transparent",
|
||||
"related_applications": "",
|
||||
"prefer_related_applications": "false",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/assets/images/icon.png?reloadyoustupidicon=4",
|
||||
"sizes": "96x96"
|
||||
}
|
||||
]
|
||||
}
|
22
assets/offline.html
Normal file
22
assets/offline.html
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: You Are Probably Offline
|
||||
comments: off
|
||||
---
|
||||
<p>If you're seeing this page and this message, then:
|
||||
<ul>
|
||||
<li>you are offline.</li>
|
||||
<li>this site is offline.</li>
|
||||
<li>the "internet" connection on your end or this site's is limited somehow and so you can't connect.</li>
|
||||
<li>you decided to look at the source or something and found this page.</li>
|
||||
<li>the Earth has been destroyed or nuclear war has occured, threatening the integrity of the DNS system.</li>
|
||||
<li>Contingency Iota has been initiated.</li>
|
||||
<li>your browser is being weird.</li>
|
||||
<li>I broke something somewhere quite badly.</li>
|
||||
<li>your internet connection is too high-latency - the timeout on requests is 5 seconds.</li>
|
||||
<li>???</li>
|
||||
</ul>
|
||||
This site uses a service worker to provide a limited offline mode, but it doesn't work for everything (dynamic pages, ones requiring a bit of server logic, the comments system, pages which haven't been cached), so you are being shown this instead.
|
||||
</p>
|
||||
<script defer>
|
||||
window.addEventListener("load", function() { if ("points" in window) { points.unlockAchievement("offline") }})
|
||||
</script>
|
87
assets/sw.js
Normal file
87
assets/sw.js
Normal file
@@ -0,0 +1,87 @@
|
||||
const siteVersion = "{{buildID}}"
|
||||
const offlinePage = "/assets/offline.html"
|
||||
const cacheName = `${siteVersion}-v1`
|
||||
const precache = [
|
||||
offlinePage,
|
||||
"/index.html",
|
||||
"/assets/images/logo.svg",
|
||||
"/assets/images/icon.png",
|
||||
"/assets/js/page.js",
|
||||
"/points/index.html",
|
||||
"/points/index.js",
|
||||
"/assets/js/mithril.js"
|
||||
]
|
||||
|
||||
// Preload important things
|
||||
self.addEventListener("install", async event => {
|
||||
console.log("Installed service worker for site version", siteVersion)
|
||||
event.waitUntil(
|
||||
caches.open(cacheName)
|
||||
.then(cache => cache.addAll(precache))
|
||||
.then(self.skipWaiting())
|
||||
)
|
||||
})
|
||||
|
||||
// Delete caches from outdated versions of the site
|
||||
self.addEventListener("activate", event => {
|
||||
console.log("Activated service worker for site version", siteVersion)
|
||||
event.waitUntil(
|
||||
caches.keys()
|
||||
.then(cacheNames => cacheNames.filter(cache => cacheName != cache))
|
||||
.then(cachesToDelete => Promise.all(cachesToDelete.map(cacheToDelete => caches.delete(cacheToDelete))))
|
||||
.then(() => self.clients.claim())
|
||||
)
|
||||
})
|
||||
|
||||
const ignorePaths = [
|
||||
"/isso",
|
||||
"/infipage"
|
||||
]
|
||||
|
||||
const shouldRespond = req => {
|
||||
if (req.method !== "GET") { return false } // do not respond to non-GET requests
|
||||
if (!req.url.startsWith(self.location.origin)) { return false } // do not respond to cross-origin requests
|
||||
const path = new URL(req.url).pathname
|
||||
for (ignorePath of ignorePaths) {
|
||||
if (path.startsWith(ignorePath)) { return false }
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const fetchWithTimeout = (req, timeout) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const timerID = setTimeout(() => reject("timed out"), timeout)
|
||||
fetch(req).then(res => {
|
||||
clearTimeout(timerID)
|
||||
resolve(res)
|
||||
}).catch(reject)
|
||||
})
|
||||
|
||||
const getResponse = async req => {
|
||||
const cache = await caches.open(cacheName)
|
||||
const cachedResponse = await cache.match(req)
|
||||
if (cachedResponse) {
|
||||
console.log("Serving", req.url, "from cache")
|
||||
return cachedResponse
|
||||
}
|
||||
try {
|
||||
console.log("Requesting", req.url)
|
||||
const response = await fetchWithTimeout(req.clone(), 5000)
|
||||
if (response.status < 400) {
|
||||
console.log("Caching request to", req.url)
|
||||
cache.put(req, response.clone())
|
||||
} else {
|
||||
console.log("Error requesting", req.url, "status", response.status)
|
||||
}
|
||||
return response
|
||||
} catch(e) {
|
||||
console.log("Error", e, "occured, sending offline page")
|
||||
return cache.match(offlinePage)
|
||||
}
|
||||
}
|
||||
|
||||
self.addEventListener("fetch", event => {
|
||||
if (shouldRespond(event.request)) {
|
||||
event.respondWith(getResponse(event.request))
|
||||
}
|
||||
})
|
Reference in New Issue
Block a user