mirror of
https://github.com/Open-Argon/http.ar.git
synced 2026-10-28 09:43:55 +00:00
make canvas example
This commit is contained in:
1 parent
ee781a224b
commit
c3c026a7d4
27 files changed
+369
-104
No files matched your search
+2
-1
@@ -1,3 +1,4 @@
|
||||
.DS_Store
|
||||
|
||||
movie.mp4
|
||||
movie.mp4
|
||||
examples/canvas/canvas.json
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
import "http" as http
|
||||
import "http/bleach" as bleach
|
||||
|
||||
app = http.server()
|
||||
|
||||
app.use(http.logger)
|
||||
|
||||
app.use(http.cookie)
|
||||
|
||||
let setCookie(req, res) = do
|
||||
if (not ("token" in req.cookies)) do
|
||||
res.setCookie("token", {value: string(random())})
|
||||
res.next()
|
||||
app.use(setCookie)
|
||||
|
||||
app.use(http.static("static"))
|
||||
|
||||
let form(req, res) = do
|
||||
term.log(req, res)
|
||||
res.send("<pre>"+bleach.clean(req.buffer.header.to('string'))+bleach.clean(req.buffer.body.to('string'))+"</pre><form method='POST'><input type='text' name='test'><input type='submit'></form>")
|
||||
app.route("/form", ['POST'], form)
|
||||
|
||||
let api(req, res) = do
|
||||
data = "cool"
|
||||
res.json({(data): "Hello, World!", description: "This is a test.", π: π, πRational: fraction(π)})
|
||||
app.route("/api", null, api)
|
||||
|
||||
let google(req, res) = do
|
||||
res.redirect("https://google.com/")
|
||||
app.route("/google", null, google)
|
||||
|
||||
let api(req, res) = do
|
||||
res.json(req.params)
|
||||
app.route("/params/example/<name>", null, api)
|
||||
|
||||
let wait(req, res) = do
|
||||
for (i from 0 to number(req.params.seconds)) do
|
||||
time.snooze(1)
|
||||
res.text((1+i)+" seconds have passed. ")
|
||||
res.close()
|
||||
app.route("/wait/<seconds>", null, wait)
|
||||
|
||||
let rand(req, res) = do
|
||||
res.json(random())
|
||||
app.route("/random", null, rand)
|
||||
|
||||
let about(req, res) = do
|
||||
res.send("<h1>About</h1>")
|
||||
app.route("/about", null, about)
|
||||
|
||||
let contact(req, res) = do
|
||||
res.send("<h1>Contact</h1>")
|
||||
app.route("/contact", null, contact)
|
||||
|
||||
let err404(req, res) = do
|
||||
res.code = 404
|
||||
res.sendFile("static/404.html")
|
||||
app.use(err404)
|
||||
|
||||
app.run({port: 8000, callback: (port) = term.log("Listening on port "+string(port))})
|
||||
@@ -0,0 +1,82 @@
|
||||
import "../../src/http" as http
|
||||
|
||||
let parent = path.parent(program.file.path)
|
||||
|
||||
let saveFile = path.join([parent, "canvas.json"])
|
||||
|
||||
let changes = []
|
||||
let canvas = []
|
||||
|
||||
let save() = do
|
||||
let f = file.write(saveFile)
|
||||
f.json({canvas:canvas, changes: changes})
|
||||
let load() = do
|
||||
try do
|
||||
let data = file.read(saveFile).json()
|
||||
canvas = data.canvas
|
||||
changes = data.changes
|
||||
catch (e) do
|
||||
let row = []
|
||||
for (i from 0 to size.width) do
|
||||
row.append(null)
|
||||
for (i from 0 to size.height) do
|
||||
canvas.append(row.copy())
|
||||
|
||||
|
||||
|
||||
let app = http.server()
|
||||
|
||||
let size = { width: 500, height: 500 }
|
||||
|
||||
app.use(http.logger)
|
||||
|
||||
let setPixel(x,y, colour) = do
|
||||
if (x >= size.width || y >= size.height) do
|
||||
return
|
||||
canvas[y][x] = colour
|
||||
changes.insert(0, {x: x, y: y, colour: colour, time: time.now().unixMilli()})
|
||||
|
||||
load()
|
||||
let saveThread() = do
|
||||
forever do
|
||||
time.snooze(30)
|
||||
save()
|
||||
let t = thread(saveThread)
|
||||
t.start()
|
||||
|
||||
let err404path = path.join([parent,"static", "404.html"])
|
||||
|
||||
app.use(http.static(path.join([parent,"static"])))
|
||||
|
||||
let getCanvas(req, res) = do
|
||||
res.json(canvas)
|
||||
|
||||
app.route('/get-canvas', null, getCanvas)
|
||||
|
||||
let getCanvasUpdates(req, res) = do
|
||||
if ("lastTime" not in req.query) do
|
||||
res.json(changes)
|
||||
return
|
||||
let lastTime = number(req.query.lastTime)
|
||||
let filtered = []
|
||||
for (i from 0 to changes.length) do
|
||||
if (changes[i].time > lastTime) do
|
||||
filtered.append(changes[i])
|
||||
else break
|
||||
res.json(filtered)
|
||||
|
||||
app.route('/get-canvas-updates', null, getCanvasUpdates)
|
||||
|
||||
let updatePixel(req, res) = do
|
||||
let data = json.parse(req.buffer.body.to('string'))
|
||||
setPixel(data.x, data.y, data.colour)
|
||||
res.send('200 OK')
|
||||
app.route('/update-canvas', ['POST'], updatePixel)
|
||||
|
||||
let err404(req, res) = do
|
||||
res.code = 404
|
||||
res.sendFile(err404path)
|
||||
app.use(err404)
|
||||
|
||||
app.port = 5000
|
||||
app.run()
|
||||
File renamed without changes.
@@ -0,0 +1,15 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>canvas.ar</title>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<script src="/script.js" defer async></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 class="title">Canvas.AR</h1>
|
||||
<canvas id="canvas"></canvas>
|
||||
<div class="colourSelect">
|
||||
<input type="color" id="colour" value="#000000">
|
||||
<input type="text" id="colourhex" value="#000000">
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,107 @@
|
||||
const canvas = document.getElementById("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
const size = { width: 500, height: 500 };
|
||||
|
||||
const colourPicker = document.getElementById("colour");
|
||||
const colourHex = document.getElementById("colourhex");
|
||||
colourHex.innerText = colourPicker.value;
|
||||
let currentColour = colourPicker.value;
|
||||
|
||||
console.log(colourPicker, colourHex);
|
||||
|
||||
colourPicker.addEventListener("input", (e) => {
|
||||
colourHex.value = e.target.value;
|
||||
currentColour = e.target.value;
|
||||
});
|
||||
|
||||
colourHex.addEventListener("input", () => {
|
||||
colourPicker.value = colourHex.value;
|
||||
currentColour = colourHex.value;
|
||||
});
|
||||
|
||||
const addPixel = (x, y, colour) => {
|
||||
pixels[y][x] = colour;
|
||||
};
|
||||
|
||||
new Event("colour-change");
|
||||
|
||||
let pixels = [];
|
||||
let updatesSince = new Date();
|
||||
|
||||
const resize = () => {
|
||||
const { innerWidth, innerHeight } = window;
|
||||
const ratio = Math.min(innerWidth / size.width, innerHeight / size.height);
|
||||
canvas.style.width = `${size.width * ratio}px`;
|
||||
canvas.style.height = `${size.height * ratio}px`;
|
||||
canvas.width = size.width * ratio;
|
||||
canvas.height = size.height * ratio;
|
||||
};
|
||||
window.addEventListener("resize", resize);
|
||||
|
||||
const draw = () => {
|
||||
ctx.fillStyle = "#ffffff";
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
const pixelsize = canvas.width / size.width;
|
||||
for (let i = 0; i < pixels.length; i++) {
|
||||
for (let j = 0; j < pixels[i].length; j++) {
|
||||
if (!pixels[i][j]) continue;
|
||||
ctx.fillStyle = pixels[i][j];
|
||||
ctx.fillRect(j * pixelsize, i * pixelsize, pixelsize, pixelsize);
|
||||
}
|
||||
}
|
||||
requestAnimationFrame(draw);
|
||||
};
|
||||
|
||||
const getCanvas = async () => {
|
||||
const resp = await fetch("/get-canvas");
|
||||
const data = await resp.json();
|
||||
pixels = data;
|
||||
};
|
||||
|
||||
const getUpdates = async () => {
|
||||
const url = new URL("/get-canvas-updates", window.location.href);
|
||||
url.searchParams.append("lastTime", updatesSince.getTime());
|
||||
const resp = await fetch(url);
|
||||
const data = await resp.json();
|
||||
for (const update of data) {
|
||||
addPixel(update.x, update.y, update.colour);
|
||||
}
|
||||
updatesSince = new Date();
|
||||
};
|
||||
|
||||
canvas.addEventListener("click", async (e) => {
|
||||
const pixelsize = canvas.width / size.width;
|
||||
const x = Math.floor(e.offsetX / pixelsize);
|
||||
const y = Math.floor(e.offsetY / pixelsize);
|
||||
addPixel(x, y, currentColour);
|
||||
updatesSince = new Date();
|
||||
await fetch("/update-canvas", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
x,
|
||||
y,
|
||||
colour: currentColour,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
resize();
|
||||
requestAnimationFrame(draw);
|
||||
const snooze = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
const main = async () => {
|
||||
await getCanvas();
|
||||
while (true) {
|
||||
try {
|
||||
await getUpdates();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
await snooze(500);
|
||||
}
|
||||
};
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,44 @@
|
||||
body {
|
||||
background-color: #f0f0f0;
|
||||
font-family: sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#canvas {
|
||||
display: block;
|
||||
background-color: #ffffff;
|
||||
margin: 0 auto 1rem;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.colourSelect {
|
||||
display: flex;
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
margin: 0 auto 1rem;
|
||||
background-color: rgb(188, 188, 188);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
#colour {
|
||||
border: none;
|
||||
background-color: #ffffff;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#colourhex {
|
||||
background-color: #ffffff;
|
||||
width: 100%;
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import "../../src/http" as http
|
||||
import "../../src/bleach" as bleach
|
||||
|
||||
let app = http.server()
|
||||
|
||||
app.use(http.logger)
|
||||
|
||||
let err404path = path.join([path.parent(program.file.path),"static", "404.html"])
|
||||
|
||||
app.use(http.static(path.join([path.parent(program.file.path),"static"])))
|
||||
|
||||
let form(req, res) = do
|
||||
term.log(req, res)
|
||||
res.send("<pre>"+bleach.clean(req.buffer.header.to('string'))+bleach.clean(req.buffer.body.to('string'))+"</pre><form method='POST'><input type='text' name='test'><input type='submit'></form>")
|
||||
app.route("/form", ['POST'], form)
|
||||
|
||||
let err404(req, res) = do
|
||||
res.code = 404
|
||||
res.sendFile(err404path)
|
||||
app.use(err404)
|
||||
|
||||
app.run()
|
||||
@@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>404</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>404</h1>
|
||||
<p>Page not found</p>
|
||||
</body>
|
||||
</html>
|
||||
File renamed without changes.
File renamed without changes.
@@ -1,43 +0,0 @@
|
||||
import "listener" as listener
|
||||
import "logger" as logger
|
||||
import "static" as static
|
||||
import "cookie" as cookie
|
||||
|
||||
let cookie = cookie.cookie
|
||||
let static = static.static
|
||||
let logger = logger.logger
|
||||
|
||||
let server() = do
|
||||
let class = {}
|
||||
let threads = []
|
||||
let numThreads = 10
|
||||
let routes = []
|
||||
let Socket = null
|
||||
class.setNumberOfThreads(num) = do
|
||||
numThreads = num
|
||||
class.run(options) = do
|
||||
Socket = socket.server("tcp", options.port)
|
||||
let onErr(e) = do
|
||||
term.error(e.type+": "+e.message)
|
||||
term.error(e.code)
|
||||
term.error(e.path+" on line "+e.line)
|
||||
if ("onErr" in options) do
|
||||
onErr = options.onErr
|
||||
for (i from 0 to numThreads) do
|
||||
t = thread(()=listener.listener(Socket, routes, onErr))
|
||||
threads.append(t)
|
||||
t.start()
|
||||
if ("callback" in options) do
|
||||
options.callback(options.port)
|
||||
for (i from 0 to threads.length) do
|
||||
threads[i].join()
|
||||
|
||||
class.close() = Socket.close()
|
||||
|
||||
class.route(path, methods, callback) = do
|
||||
if (methods == null) do
|
||||
methods = ["GET"]
|
||||
routes.append({path: path, callback: callback, methods: methods, type: "route"})
|
||||
class.use(callback) = do
|
||||
routes.append({callback: callback, type: "middleware"})
|
||||
return class.object()
|
||||
@@ -0,0 +1 @@
|
||||
import "http/init" as *
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,54 @@
|
||||
let formdata(req, res) = do
|
||||
let boundary = null
|
||||
if ("Content-Type" in req.headers) do
|
||||
let contentType = req.headers["Content-Type"]
|
||||
if (contentType.startsWith("multipart/form-data")) do
|
||||
let parts = contentType.split("; ")
|
||||
for (i from 0 to parts.length) do
|
||||
let part = parts[i]
|
||||
if (part.startsWith("boundary=")) do
|
||||
boundary = part.split("=")[1]
|
||||
break
|
||||
if (boundary == null) do
|
||||
res.next()
|
||||
return
|
||||
let body = req.body
|
||||
let parts = body.split("--"+boundary)
|
||||
let data = {}
|
||||
for (i from 0 to parts.length) do
|
||||
let part = parts[i]
|
||||
if (part == "" || part == "--") do
|
||||
continue
|
||||
let lines = part.split("\r\n")
|
||||
let headers = {}
|
||||
let body = ""
|
||||
for (j from 0 to lines.length) do
|
||||
let line = lines[j]
|
||||
if (line == "") do
|
||||
body = lines.slice(j+1).join("\r\n")
|
||||
break
|
||||
let header = line.split(": ")
|
||||
headers[header[0]] = header[1]
|
||||
if ("Content-Disposition" in headers) do
|
||||
let disposition = headers["Content-Disposition"]
|
||||
let name = null
|
||||
let filename = null
|
||||
let dispositionParts = disposition.split("; ")
|
||||
for (j from 0 to dispositionParts.length) do
|
||||
let part = dispositionParts[j]
|
||||
if (part.startsWith("name=")) do
|
||||
name = part.split("=")[1]
|
||||
else if (part.startsWith("filename=")) do
|
||||
filename = part.split("=")[1]
|
||||
if (name == null) do
|
||||
continue
|
||||
if (filename == null) do
|
||||
data[name] = body
|
||||
else do
|
||||
data[name] = {
|
||||
"filename": filename,
|
||||
"contentType": headers["Content-Type"],
|
||||
"data": body
|
||||
}
|
||||
req.formdata = data
|
||||
res.next()
|
||||
File renamed without changes.
@@ -0,0 +1,33 @@
|
||||
import "listener" as listener
|
||||
import "logger" as *
|
||||
import "static" as *
|
||||
import "cookie" as *
|
||||
|
||||
let server() = do
|
||||
let class = {port: 3000, numThreads: 10, callback: ()=term.log("Server running on port "+class.port)}
|
||||
let threads = []
|
||||
let routes = []
|
||||
let Socket = null
|
||||
class.onErr(e) = do
|
||||
term.error(e.type+": "+e.message)
|
||||
term.error(e.code)
|
||||
term.error(e.path+" on line "+e.line)
|
||||
class.run() = do
|
||||
Socket = socket.server("tcp", class.port)
|
||||
for (i from 0 to class.numThreads) do
|
||||
t = thread(()=listener.listener(Socket, routes, class.onErr))
|
||||
threads.append(t)
|
||||
t.start()
|
||||
class.callback()
|
||||
for (i from 0 to threads.length) do
|
||||
threads[i].join()
|
||||
|
||||
class.close() = Socket.close()
|
||||
|
||||
class.route(path, methods, callback) = do
|
||||
if (methods == null) do
|
||||
methods = ["GET"]
|
||||
routes.append({path: path, callback: callback, methods: methods, type: "route"})
|
||||
class.use(callback) = do
|
||||
routes.append({callback: callback, type: "middleware"})
|
||||
return class
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Vendored
BIN
Binary file not shown.
Reference in new issue
Block a user