All of the changes that have been sitting on tildepi for ages. For posterity.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3530 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
d6018736d5
commit
d293637741
@ -16,6 +16,8 @@ Tilde Friends is [routinely](https://www.unprompted.com/projects/build/tildefrie
|
|||||||
scons uv=path/to/libuv v8=path/to/v8
|
scons uv=path/to/libuv v8=path/to/v8
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note for Raspberry Pi: http://www.mccarroll.net/blog/v8_pi2/index.html
|
||||||
|
|
||||||
## Running
|
## Running
|
||||||
Running the built tildefriends executable will start a web server. This is a good starting point: <http://localhost:12345/>.
|
Running the built tildefriends executable will start a web server. This is a good starting point: <http://localhost:12345/>.
|
||||||
|
|
||||||
|
34
packages/cory/blink/blink.js
Normal file
34
packages/cory/blink/blink.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
//! {"require": ["libiframe"]}
|
||||||
|
|
||||||
|
// Server-side blink! Woo!
|
||||||
|
|
||||||
|
require("libiframe").iframe({
|
||||||
|
source: `
|
||||||
|
document.body.style.color = '#fff';
|
||||||
|
document.body.style.backgroundColor = '#000';
|
||||||
|
var canvas = document.createElement("canvas");
|
||||||
|
document.body.append(canvas);
|
||||||
|
canvas.width = 640;
|
||||||
|
canvas.height = 480;
|
||||||
|
var context = canvas.getContext("2d");
|
||||||
|
rpc.export({
|
||||||
|
setFillStyle: function(style) { context.fillStyle = style; },
|
||||||
|
fillRect: context.fillRect.bind(context),
|
||||||
|
fillText: context.fillText.bind(context),
|
||||||
|
});`,
|
||||||
|
style: `
|
||||||
|
border: 0;
|
||||||
|
`}).then(draw).catch(terminal.print);
|
||||||
|
|
||||||
|
var blink = true;
|
||||||
|
|
||||||
|
function draw(iframe) {
|
||||||
|
iframe.setFillStyle('#222');
|
||||||
|
iframe.fillRect(0, 0, 640, 480);
|
||||||
|
if (blink) {
|
||||||
|
iframe.setFillStyle('#fff');
|
||||||
|
iframe.fillText("Hello, world!", 50, 50);
|
||||||
|
}
|
||||||
|
blink = !blink;
|
||||||
|
setTimeout(function() { draw(iframe); }, 500);
|
||||||
|
}
|
@ -286,9 +286,9 @@ function updateUsers() {
|
|||||||
terminal.cork();
|
terminal.cork();
|
||||||
terminal.split([
|
terminal.split([
|
||||||
{type: "horizontal", children: [
|
{type: "horizontal", children: [
|
||||||
{name: "windows", basis: "2in", grow: "0", shrink: "0"},
|
{name: "windows", basis: "1in", grow: "0", shrink: "0"},
|
||||||
{name: "terminal", grow: "1"},
|
{name: "terminal", grow: "1"},
|
||||||
{name: "users", basis: "2in", grow: "0", shrink: "0"},
|
{name: "users", basis: "1in", grow: "0", shrink: "0"},
|
||||||
]},
|
]},
|
||||||
]);
|
]);
|
||||||
updateTitle();
|
updateTitle();
|
||||||
@ -444,7 +444,7 @@ function printMessage(message) {
|
|||||||
{class: "base3", value: from},
|
{class: "base3", value: from},
|
||||||
" ",
|
" ",
|
||||||
formatMessage(message.message));
|
formatMessage(message.message));
|
||||||
} else {
|
} else if (message.message) {
|
||||||
terminal.print(
|
terminal.print(
|
||||||
{class: "base0", value: niceTime(lastTimestamp, now)},
|
{class: "base0", value: niceTime(lastTimestamp, now)},
|
||||||
" ",
|
" ",
|
||||||
@ -453,6 +453,11 @@ function printMessage(message) {
|
|||||||
{class: "base00", value: ">"},
|
{class: "base00", value: ">"},
|
||||||
" ",
|
" ",
|
||||||
formatMessage(message.message));
|
formatMessage(message.message));
|
||||||
|
} else {
|
||||||
|
terminal.print(
|
||||||
|
{class: "base0", value: niceTime(lastTimestamp, now)},
|
||||||
|
" ",
|
||||||
|
{class: "base3", value: JSON.stringify(message)});
|
||||||
}
|
}
|
||||||
lastTimestamp = now;
|
lastTimestamp = now;
|
||||||
}
|
}
|
||||||
|
76
packages/cory/chattest/chattest.js
Normal file
76
packages/cory/chattest/chattest.js
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
//! {
|
||||||
|
//! "chat": {
|
||||||
|
//! "version": 1,
|
||||||
|
//! "settings": [
|
||||||
|
//! {"name": "nickname", "type": "text"},
|
||||||
|
//! {"name": "password", "type": "password"}
|
||||||
|
//! ]
|
||||||
|
//! },
|
||||||
|
//! "category": "libraries"
|
||||||
|
//! }
|
||||||
|
|
||||||
|
let gServices = {};
|
||||||
|
|
||||||
|
class ChatService {
|
||||||
|
constructor(options) {
|
||||||
|
let self = this;
|
||||||
|
self._name = options.name;
|
||||||
|
self._callback = options.callback;
|
||||||
|
setTimeout(function() {
|
||||||
|
self._callback({
|
||||||
|
action: "message",
|
||||||
|
from: "service",
|
||||||
|
to: self._name,
|
||||||
|
message: "Hello, world!",
|
||||||
|
conversation: null,
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage(target, message) {
|
||||||
|
let self = this;
|
||||||
|
setTimeout(function() {
|
||||||
|
self._callback({
|
||||||
|
action: "message",
|
||||||
|
from: target,
|
||||||
|
to: self._name,
|
||||||
|
message: "I saw you say: " + message,
|
||||||
|
conversation: target,
|
||||||
|
});
|
||||||
|
}, 500);
|
||||||
|
return self._callback({
|
||||||
|
action: "message",
|
||||||
|
from: self._name,
|
||||||
|
to: target,
|
||||||
|
message: message,
|
||||||
|
conversation: target,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
core.register("onMessage", function(sender, message) {
|
||||||
|
print(message);
|
||||||
|
let service = gServices[message.name];
|
||||||
|
if (!service) {
|
||||||
|
service = new ChatService(message);
|
||||||
|
gServices[message.name] = service;
|
||||||
|
} else {
|
||||||
|
service._callback = message.callback || service._callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
service._callback({
|
||||||
|
action: "message",
|
||||||
|
from: "service",
|
||||||
|
to: service._name,
|
||||||
|
message: "I got your message.",
|
||||||
|
conversation: null,
|
||||||
|
});
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
return {
|
||||||
|
sendMessage: service.sendMessage.bind(service),
|
||||||
|
};
|
||||||
|
});
|
198
packages/cory/contest/contest.js
Normal file
198
packages/cory/contest/contest.js
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
//! {"require": ["ui"]}
|
||||||
|
|
||||||
|
terminal.setEcho(false);
|
||||||
|
terminal.setTitle("Programming Contest Test");
|
||||||
|
|
||||||
|
let kProblem = {
|
||||||
|
description: "Write a function foo that returns a number.",
|
||||||
|
tests: [
|
||||||
|
[1, 1],
|
||||||
|
[2, 2],
|
||||||
|
[4, 4],
|
||||||
|
],
|
||||||
|
default: `// Problem 1
|
||||||
|
// ${core.user.name}
|
||||||
|
// ${new Date()}
|
||||||
|
|
||||||
|
function foo() {
|
||||||
|
print("hi cory");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
foo();`,
|
||||||
|
};
|
||||||
|
|
||||||
|
function back() {
|
||||||
|
terminal.split([{name: "terminal"}]);
|
||||||
|
if (gEditEvent) {
|
||||||
|
gEditEvent.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runScript(script, input) {
|
||||||
|
var results = {};
|
||||||
|
try {
|
||||||
|
var output = [];
|
||||||
|
function print() {
|
||||||
|
for (var i in arguments) {
|
||||||
|
output.push(arguments[i].toString());
|
||||||
|
}
|
||||||
|
output.push("\n");
|
||||||
|
}
|
||||||
|
results.results = eval(script || "");
|
||||||
|
results.output = output.join("");
|
||||||
|
} catch (error) {
|
||||||
|
results.error = error;
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendResults(document) {
|
||||||
|
var results = runScript(document);
|
||||||
|
var message = `${kProblem.description}
|
||||||
|
|
||||||
|
Return value:
|
||||||
|
${results.results}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
${results.output}
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (results.error) {
|
||||||
|
message += `Error:
|
||||||
|
${results.error}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
terminal.postMessageToIframe("iframe", { results: message });
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadScript() {
|
||||||
|
return database.get(core.user.name).then(function(script) {
|
||||||
|
return script || kProblem.default;
|
||||||
|
}).catch(function(error) {
|
||||||
|
return kProblem.default;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
core.register("onWindowMessage", function(event) {
|
||||||
|
if (event.message.ready) {
|
||||||
|
loadScript().then(function(script) {
|
||||||
|
terminal.postMessageToIframe("iframe", {contents: script});
|
||||||
|
sendResults(script);
|
||||||
|
});
|
||||||
|
} else if (event.message.contents) {
|
||||||
|
database.set(core.user.name, event.message.contents).then(function() {
|
||||||
|
sendResults(event.message.contents);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//if (!core.user.credentials.permissions || !core.user.credentials.permissions.authenticated) {
|
||||||
|
// terminal.print("Please authenticate.");
|
||||||
|
//} else {
|
||||||
|
showEditor();
|
||||||
|
//}
|
||||||
|
|
||||||
|
function showEditor() {
|
||||||
|
terminal.split([{name: "terminal", type: "vertical"}]);
|
||||||
|
terminal.clear();
|
||||||
|
terminal.print({iframe: `<html>
|
||||||
|
<head>
|
||||||
|
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.13.2/codemirror.min.js"></script>
|
||||||
|
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.13.2/codemirror.min.css"></link>
|
||||||
|
<style>
|
||||||
|
html {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
#menu {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
#container {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
width: 100%;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
.CodeMirror {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.CodeMirror-scroll {
|
||||||
|
}
|
||||||
|
#edit { background-color: white }
|
||||||
|
#preview {
|
||||||
|
background-color: #ccc;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
#edit, #preview {
|
||||||
|
display: flex;
|
||||||
|
overflow: auto;
|
||||||
|
flex: 0 0 50%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
var gEditor;
|
||||||
|
function index() {
|
||||||
|
parent.postMessage({index: true}, "*");
|
||||||
|
}
|
||||||
|
function submit() {
|
||||||
|
parent.postMessage({
|
||||||
|
contents: gEditor.getValue(),
|
||||||
|
}, "*");
|
||||||
|
}
|
||||||
|
function cursorActivity() {
|
||||||
|
var selection = gEditor.listSelections();
|
||||||
|
var key = "test";
|
||||||
|
var a = selection[0].anchor;
|
||||||
|
var b = selection[0].head;
|
||||||
|
if (b.line < a.line || a.line == b.line && b.ch < a.ch) {
|
||||||
|
[a, b] = [b, a];
|
||||||
|
}
|
||||||
|
parent.postMessage({cursor: {start: a, end: b}}, "*");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
window.addEventListener("message", function(message) {
|
||||||
|
if (message.data.contents) {
|
||||||
|
gEditor.setValue(message.data.contents);
|
||||||
|
} else if (message.data.results) {
|
||||||
|
preview.innerText = message.data.results;
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
window.addEventListener("load", function() {
|
||||||
|
gEditor = CodeMirror.fromTextArea(document.getElementById("contents"), {
|
||||||
|
lineNumbers: true
|
||||||
|
});
|
||||||
|
|
||||||
|
parent.postMessage({ready: true}, "*");
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="menu">
|
||||||
|
<input type="button" value="Back" onclick="index()">
|
||||||
|
` + (core.user.credentials.permissions && core.user.credentials.permissions.authenticated ? `
|
||||||
|
<input type="button" value="Save" onclick="submit()">
|
||||||
|
` : "") +
|
||||||
|
` </div>
|
||||||
|
<div id="container">
|
||||||
|
<div id="edit"><textarea id="contents"></textarea></div>
|
||||||
|
<div id="preview"></div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>`, name: "iframe", style: "flex: 1 1 auto; border: 0; width: 100%"});
|
||||||
|
}
|
269
packages/cory/db/db.js
Normal file
269
packages/cory/db/db.js
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
//! {"category": "libraries"}
|
||||||
|
|
||||||
|
class DatabaseList {
|
||||||
|
constructor(name) {
|
||||||
|
this._name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getList() {
|
||||||
|
let self = this;
|
||||||
|
return database.get(this._name).then(function(node) {
|
||||||
|
return JSON.parse(node);
|
||||||
|
}).catch(function(error) {
|
||||||
|
return self._emptyNode();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_randomKey() {
|
||||||
|
let kAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||||
|
let kBytes = 32;
|
||||||
|
let result = "";
|
||||||
|
for (let i = 0; i < kBytes; i++) {
|
||||||
|
result += kAlphabet.charAt(Math.floor(Math.random() * kAlphabet.length));
|
||||||
|
}
|
||||||
|
return this._name + "_" + result;
|
||||||
|
}
|
||||||
|
|
||||||
|
_emptyNode() {
|
||||||
|
return {next: null, previous: null};
|
||||||
|
}
|
||||||
|
|
||||||
|
_getNode() {
|
||||||
|
var self = this;
|
||||||
|
var key = self._randomKey();
|
||||||
|
return database.get(key).then(function(found) {
|
||||||
|
if (!found) {
|
||||||
|
return key;
|
||||||
|
} else {
|
||||||
|
return self._getNode();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_getRelative(node, next, count) {
|
||||||
|
}
|
||||||
|
|
||||||
|
_insertEnd(item, after) {
|
||||||
|
let self = this;
|
||||||
|
return this._getList().then(function(listNode) {
|
||||||
|
let readPromises = [self._getNode()];
|
||||||
|
readPromises.push(listNode.previous ? database.get(listNode.previous).then(JSON.parse) : null);
|
||||||
|
readPromises.push(listNode.next ? database.get(listNode.next).then(JSON.parse) : null);
|
||||||
|
return Promise.all(readPromises).then(function(results) {
|
||||||
|
let newNodeName = results[0];
|
||||||
|
let previousNode = results[1];
|
||||||
|
let nextNode = results[2];
|
||||||
|
let newNode = self._emptyNode();
|
||||||
|
let oldPrevious = listNode.previous;
|
||||||
|
let oldNext = listNode.next;
|
||||||
|
newNode.value = item;
|
||||||
|
newNode.previous = listNode.previous || newNodeName;
|
||||||
|
newNode.next = listNode.next || newNodeName;
|
||||||
|
if (after) {
|
||||||
|
listNode.previous = newNodeName;
|
||||||
|
listNode.next = listNode.next || newNodeName;
|
||||||
|
} else {
|
||||||
|
listNode.next = newNodeName;
|
||||||
|
listNode.previous = listNode.previous || newNodeName;
|
||||||
|
}
|
||||||
|
let writePromises = [
|
||||||
|
database.set(self._name, JSON.stringify(listNode)),
|
||||||
|
database.set(newNodeName, JSON.stringify(newNode)),
|
||||||
|
];
|
||||||
|
if (oldPrevious && oldPrevious == oldNext) {
|
||||||
|
previousNode.next = newNodeName;
|
||||||
|
previousNode.previous = newNodeName;
|
||||||
|
writePromises.push(database.set(oldPrevious, JSON.stringify(previousNode)));
|
||||||
|
} else {
|
||||||
|
if (previousNode) {
|
||||||
|
previousNode.next = newNodeName;
|
||||||
|
writePromises.push(database.set(oldPrevious, JSON.stringify(previousNode)));
|
||||||
|
}
|
||||||
|
if (nextNode) {
|
||||||
|
nextNode.previous = newNodeName;
|
||||||
|
writePromises.push(database.set(oldNext, JSON.stringify(nextNode)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.all(writePromises);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_removeEnd(after) {
|
||||||
|
let self = this;
|
||||||
|
return this._getList().then(function(listNode) {
|
||||||
|
if (listNode.next) {
|
||||||
|
if (listNode.next == listNode.previous) {
|
||||||
|
return database.get(listNode.next).then(JSON.parse).then(function(removedNode) {
|
||||||
|
let removedName = listNode.next;
|
||||||
|
listNode.next = null;
|
||||||
|
listNode.previous = null;
|
||||||
|
return Promise.all([
|
||||||
|
database.remove(removedName),
|
||||||
|
database.set(self._name, JSON.stringify(listNode)),
|
||||||
|
]).then(function() {
|
||||||
|
return removedNode.value;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let name1 = listNode.previous;
|
||||||
|
let name2 = listNode.next;
|
||||||
|
return Promise.all([
|
||||||
|
database.get(listNode.previous),
|
||||||
|
database.get(listNode.next),
|
||||||
|
]).then(function(nodes) {
|
||||||
|
var node1 = JSON.parse(nodes[0]);
|
||||||
|
var node2 = JSON.parse(nodes[1]);
|
||||||
|
if (after) {
|
||||||
|
let name0 = node1.previous;
|
||||||
|
return database.get(name0).then(JSON.parse).then(function(node0) {
|
||||||
|
node0.next = name2;
|
||||||
|
node2.previous = name0;
|
||||||
|
listNode.previous = name0;
|
||||||
|
return Promise.all([
|
||||||
|
database.set(name0, JSON.stringify(node0)),
|
||||||
|
database.remove(name1),
|
||||||
|
database.set(name2, JSON.stringify(node2)),
|
||||||
|
database.set(self._name, JSON.stringify(listNode)),
|
||||||
|
])
|
||||||
|
}).then(function() {
|
||||||
|
return node1.value;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let name3 = node2.next;
|
||||||
|
return database.get(name3).then(JSON.parse).then(function(node3) {
|
||||||
|
node1.next = name3;
|
||||||
|
node3.previous = name1;
|
||||||
|
listNode.next = name3;
|
||||||
|
terminal.print(name1, " ", name2, " ", name3, " ", self._name);
|
||||||
|
terminal.print(JSON.stringify(listNode));
|
||||||
|
return Promise.all([
|
||||||
|
database.set(name1, JSON.stringify(node1)),
|
||||||
|
database.remove(name2),
|
||||||
|
database.set(name3, JSON.stringify(node3)),
|
||||||
|
database.set(self._name, JSON.stringify(listNode)),
|
||||||
|
])
|
||||||
|
}).then(function() {
|
||||||
|
return node2.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
push(item) {
|
||||||
|
return this._insertEnd(item, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
pop() {
|
||||||
|
return this._removeEnd(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
shift() {
|
||||||
|
return this._removeEnd(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
unshift(item) {
|
||||||
|
return this._insertEnd(item, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_sliceInternal(head, count, next, end, result) {
|
||||||
|
let self = this;
|
||||||
|
if (head[next] == end && count > 0) {
|
||||||
|
result.push(head.value);
|
||||||
|
return new Promise(function(resolve, reject) { resolve(result); });
|
||||||
|
} else {
|
||||||
|
if (count > 0) {
|
||||||
|
return database.get(head[next]).then(JSON.parse).then(function(retrieved) {
|
||||||
|
return self._sliceInternal(retrieved, count - 1, next, end, result).then(function(result) {
|
||||||
|
result.push(head.value);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return new Promise(function(resolve, reject) { resolve(result); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get(count) {
|
||||||
|
let self = this;
|
||||||
|
return self._getList().then(function(listNode) {
|
||||||
|
if (listNode.next) {
|
||||||
|
return database.get(count > 0 ? listNode.next : listNode.previous).then(JSON.parse).then(function(head) {
|
||||||
|
if (head) {
|
||||||
|
return self._sliceInternal(
|
||||||
|
head,
|
||||||
|
Math.abs(count),
|
||||||
|
count > 0 ? "next" : "previous",
|
||||||
|
count > 0 ? listNode.next : listNode.previous,
|
||||||
|
[]).then(function(result) {
|
||||||
|
result.reverse();
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function wipeDatabase() {
|
||||||
|
let promises = [];
|
||||||
|
return database.getAll().then(function(list) {
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
promises.push(database.remove(list[i]));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dumpDatabase() {
|
||||||
|
return database.getAll().then(function(list) {
|
||||||
|
let promises = [];
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
promises.push(database.get(list[i]));
|
||||||
|
}
|
||||||
|
return Promise.all(promises).then(function(values) {
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
terminal.print(list[i], " ", values[i]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).catch(function(error) {
|
||||||
|
terminal.print(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imports.terminal) {
|
||||||
|
x = new DatabaseList("list");
|
||||||
|
core.register("onInput", function(input) {
|
||||||
|
if (input == "clear") {
|
||||||
|
wipeDatabase().then(function() {
|
||||||
|
terminal.print("Database is now empty.");
|
||||||
|
});
|
||||||
|
} else if (input.substring(0, "push ".length) == "push ") {
|
||||||
|
x.push(input.substring("push ".length)).then(dumpDatabase).catch(terminal.print);
|
||||||
|
} else if (input.substring(0, "unshift ".length) == "unshift ") {
|
||||||
|
x.unshift(input.substring("unshift ".length)).then(dumpDatabase).catch(terminal.print);
|
||||||
|
} else if (input == "pop") {
|
||||||
|
x.pop().then(function(out) {
|
||||||
|
terminal.print("POPPED: ", out);
|
||||||
|
}).then(dumpDatabase).catch(terminal.print);
|
||||||
|
} else if (input == "shift") {
|
||||||
|
x.shift().then(function(out) {
|
||||||
|
terminal.print("SHIFTED: ", out);
|
||||||
|
}).then(dumpDatabase).catch(terminal.print);
|
||||||
|
} else if (input.substring(0, "get ".length) == "get ") {
|
||||||
|
let parts = input.split(" ");
|
||||||
|
x.get(parseInt(parts[1])).then(function(result) {
|
||||||
|
terminal.print(JSON.stringify(result))
|
||||||
|
}).catch(terminal.print);
|
||||||
|
} else {
|
||||||
|
dumpDatabase();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
201
packages/cory/emojipush/emojipush.js
Normal file
201
packages/cory/emojipush/emojipush.js
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
class Client {
|
||||||
|
async start() {
|
||||||
|
let self = this;
|
||||||
|
terminal.setTitle("Emoji Push");
|
||||||
|
terminal.setSendKeyEvents(true);
|
||||||
|
self.send({action: "ready"});
|
||||||
|
|
||||||
|
core.register("key", function(event) {
|
||||||
|
if (event.type == "keydown") {
|
||||||
|
switch (event.keyCode) {
|
||||||
|
case 37:
|
||||||
|
self.requestMove(-1, 0);
|
||||||
|
break;
|
||||||
|
case 38:
|
||||||
|
self.requestMove(0, -1);
|
||||||
|
break;
|
||||||
|
case 39:
|
||||||
|
self.requestMove(1, 0);
|
||||||
|
break;
|
||||||
|
case 40:
|
||||||
|
self.requestMove(0, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
core.register("onMessage", function(sender, message) {
|
||||||
|
if (message.action == "display") {
|
||||||
|
terminal.cork();
|
||||||
|
terminal.clear();
|
||||||
|
terminal.print(message.board);
|
||||||
|
terminal.uncork();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async send(message) {
|
||||||
|
(await core.getService("server")).postMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
async requestMove(dx, dy) {
|
||||||
|
await this.send({action: "move", delta: [dx, dy]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Server {
|
||||||
|
constructor() {
|
||||||
|
this.rows = 17;
|
||||||
|
this.columns = 17;
|
||||||
|
this.players = {};
|
||||||
|
this.objects = [];
|
||||||
|
|
||||||
|
for (let i = 2; i < this.rows - 1; i += 2) {
|
||||||
|
for (let j = 2; j < this.columns - 1; j += 2) {
|
||||||
|
let block = new Entity();
|
||||||
|
block.position = [j, i];
|
||||||
|
block.symbol = '#';
|
||||||
|
this.objects.push(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async start() {
|
||||||
|
let self = this;
|
||||||
|
core.register("onMessage", function(sender, message) {
|
||||||
|
if (!self.players[sender.index]) {
|
||||||
|
let player = new Entity();
|
||||||
|
player.symbol = '@';
|
||||||
|
player.position = self.findNewPlayerPosition();
|
||||||
|
self.players[sender.index] = player;
|
||||||
|
self.objects.push(player);
|
||||||
|
}
|
||||||
|
switch (message.action) {
|
||||||
|
case "move":
|
||||||
|
self.move(sender.index, message.delta);
|
||||||
|
self.redisplay();
|
||||||
|
break;
|
||||||
|
case "ready":
|
||||||
|
self.redisplay();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
core.register("onSessionEnd", function(session) {
|
||||||
|
let player = self.players[session.index];
|
||||||
|
if (player) {
|
||||||
|
delete self.players[session.index];
|
||||||
|
self.objects.splice(self.objects.indexOf(player), 1);
|
||||||
|
self.redisplay();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
occupied(position) {
|
||||||
|
let board = this.getBoard();
|
||||||
|
|
||||||
|
if (position[0] < 0 || position[1] < 0 || position[1] >= board.length || position[0] >= board[position[1]].length) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (board[position[1]].charAt(position[0]) != ' ') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.objects.some(x => position[0] == x.position[0] && position[1] == x.position[1])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
move(index, delta) {
|
||||||
|
let newPosition = [
|
||||||
|
this.players[index].position[0] + delta[0],
|
||||||
|
this.players[index].position[1] + delta[1],
|
||||||
|
];
|
||||||
|
if (!this.occupied(newPosition)) {
|
||||||
|
this.players[index].position = newPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
findNewPlayerPosition() {
|
||||||
|
let best = null;
|
||||||
|
let bestDistance = -1;
|
||||||
|
let players = Object.values(this.players);
|
||||||
|
|
||||||
|
for (let i = 0; i < this.rows; i++) {
|
||||||
|
for (let j = 0; j < this.columns; j++) {
|
||||||
|
if (!this.occupied([j, i])) {
|
||||||
|
let distance = Math.min.apply(null, players.map(x => Math.sqrt(
|
||||||
|
(x.position[0] - j) * (x.position[0] - j)
|
||||||
|
+ (x.position[1] - i) * (x.position[1] - i))));
|
||||||
|
if (distance > bestDistance) {
|
||||||
|
best = [j, i];
|
||||||
|
bestDistance = distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
redisplay() {
|
||||||
|
let board;
|
||||||
|
try {
|
||||||
|
board = this.getBoardString();
|
||||||
|
} catch (error) {
|
||||||
|
board = error;
|
||||||
|
}
|
||||||
|
core.broadcast({action: "display", board: board});
|
||||||
|
}
|
||||||
|
|
||||||
|
getBoard() {
|
||||||
|
let board = [];
|
||||||
|
for (let i = 0; i < this.rows; i++) {
|
||||||
|
let row = "";
|
||||||
|
for (let j = 0; j < this.columns; j++) {
|
||||||
|
if (i == 0 && j == 0) {
|
||||||
|
row += '┌';
|
||||||
|
} else if (i == 0 && j == this.columns - 1) {
|
||||||
|
row += '┐';
|
||||||
|
} else if (i == this.rows - 1 && j == 0) {
|
||||||
|
row += '└';
|
||||||
|
} else if (i == this.rows - 1 && j == this.columns - 1) {
|
||||||
|
row += '┘';
|
||||||
|
} else if (i == 0 || i == this.rows - 1) {
|
||||||
|
row += '─';
|
||||||
|
} else if (j == 0 || j == this.columns - 1) {
|
||||||
|
row += '│';
|
||||||
|
} else {
|
||||||
|
row += ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
board.push(row);
|
||||||
|
}
|
||||||
|
for (let object of this.objects) {
|
||||||
|
board[object.position[1]] = board[object.position[1]].slice(0, object.position[0]) + object.symbol + board[object.position[1]].slice(object.position[0] + 1);
|
||||||
|
}
|
||||||
|
return board;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBoardString() {
|
||||||
|
let board = this.getBoard();
|
||||||
|
let colors = board.map(x => x.split("").map(y => '#fff'));
|
||||||
|
return board.map((r, row) => r.split("").map((v, column) => ({style: "color: " + colors[row][column], value: v})).concat(["\n"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Entity {
|
||||||
|
constructor() {
|
||||||
|
this.position = [0, 0];
|
||||||
|
this.symbol = '?';
|
||||||
|
this.color = '#fff';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imports.terminal) {
|
||||||
|
new Client().start().catch(terminal.print);
|
||||||
|
} else {
|
||||||
|
new Server().start().catch(print);
|
||||||
|
}
|
31
packages/cory/guess/guess.js
Normal file
31
packages/cory/guess/guess.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
//! {"category": "sample"}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
terminal.print("Hi. What's your name?");
|
||||||
|
let name = await terminal.readLine();
|
||||||
|
terminal.print("Hello, " + name + ".");
|
||||||
|
|
||||||
|
let number = Math.floor(Math.random() * 100);
|
||||||
|
let guesses = 0;
|
||||||
|
while (true) {
|
||||||
|
terminal.print("Guess the number.");
|
||||||
|
try {
|
||||||
|
let guess = parseInt(await terminal.readLine());
|
||||||
|
guesses++;
|
||||||
|
if (guess < number) {
|
||||||
|
terminal.print("Too low.");
|
||||||
|
} else if (guess > number) {
|
||||||
|
terminal.print("Too high.");
|
||||||
|
} else {
|
||||||
|
terminal.print("You got it in " + guesses.toString() + " guesses! It was " + number.toString() + ". Good job, " + name + ".");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
terminal.print(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(terminal.print);
|
3
packages/cory/images/images.js
Normal file
3
packages/cory/images/images.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
core.register("fileDrop", function(event) {
|
||||||
|
terminal.print({image: event.file});
|
||||||
|
});
|
260
packages/cory/invite/invite.js
Normal file
260
packages/cory/invite/invite.js
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
terminal.setEcho(false);
|
||||||
|
terminal.setPrompt("🍔");
|
||||||
|
|
||||||
|
terminal.split([
|
||||||
|
{
|
||||||
|
type: "horizontal",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: "form",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "vertical",
|
||||||
|
basis: "240px",
|
||||||
|
grow: "0",
|
||||||
|
shrink: "0",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: "image",
|
||||||
|
basis: "8em",
|
||||||
|
shrink: "0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "fizzbuzz",
|
||||||
|
grow: "1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
database.getAll().then(function(data) {
|
||||||
|
for (var i = 0; i < data.length; i++) {
|
||||||
|
database.remove(data[i]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let kBurger = ` ......
|
||||||
|
/'.'.'.\\
|
||||||
|
&%@%@%@&
|
||||||
|
(######)
|
||||||
|
\\,,,,,,/`;
|
||||||
|
|
||||||
|
terminal.select("image");
|
||||||
|
terminal.print(kBurger);
|
||||||
|
terminal.select("form");
|
||||||
|
|
||||||
|
function countUp() {
|
||||||
|
var current = 0;
|
||||||
|
function countUpNext() {
|
||||||
|
terminal.select("fizzbuzz");
|
||||||
|
++current;
|
||||||
|
var result = "";
|
||||||
|
if (current % 3 == 0) {
|
||||||
|
result += "Fizz";
|
||||||
|
}
|
||||||
|
if (current % 5 == 0) {
|
||||||
|
result += "Buzz";
|
||||||
|
}
|
||||||
|
if (current % 150 == 0) {
|
||||||
|
result += "Grumble";
|
||||||
|
}
|
||||||
|
if (result == "") {
|
||||||
|
result = current.toString();
|
||||||
|
}
|
||||||
|
terminal.print(result);
|
||||||
|
terminal.select("form");
|
||||||
|
setTimeout(countUpNext, 1000);
|
||||||
|
};
|
||||||
|
setTimeout(countUpNext, 1000);
|
||||||
|
}
|
||||||
|
countUp();
|
||||||
|
|
||||||
|
var user = core.user.name;
|
||||||
|
|
||||||
|
let kIntroduction = [
|
||||||
|
{style: "font-size: xx-large; font-family: sans", value: "Summer of Cory"},
|
||||||
|
"Hi, it is warm outside again, and as is tradition, I want you to join me "+
|
||||||
|
"after work for some burgers on my deck. There are some quick questions "+
|
||||||
|
"below to gauge interest and availability. Please answer as best you can "+
|
||||||
|
"by clicking the options below, and I will get back to you with a plan.",
|
||||||
|
[],
|
||||||
|
];
|
||||||
|
|
||||||
|
let kBottom = [
|
||||||
|
{
|
||||||
|
iframe: `<html style="width: 100%; height: 100%; margin: 0; padding: 0">
|
||||||
|
<body style="width: 100%; height: 100%; margin: 0; padding: 0">
|
||||||
|
<textarea id="text" style="left: 0; top: 0; width: 100vw; height: 100vh; resize: none; margin: 0; padding: 0"></textarea>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`,
|
||||||
|
style: "border: 0; width: 640px; height: 240px; margin: 0; padding: 0",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let kQuestions = [
|
||||||
|
{text: "Do you want to be a part of the Summer of Cory?", options: ["yes", "no"]},
|
||||||
|
{text: "Tuesday after work works best for me. Does that work for you?", options: ["yes", "no"]},
|
||||||
|
{text: "How often would you expect to join us?", options: ["weekly", "fortnightly", "monthly", "impossible to predict"]},
|
||||||
|
|
||||||
|
//{text: "Pick numbers", options: ["1", "2", "3"], allowMultiple: true},
|
||||||
|
//{text: "Pick a letter", options: ["a", "b", "c", "d"]},
|
||||||
|
//{text: "Pick whatever", options: ["1", "2", "3", "a", "b", "c", "d"], writeIn: true},
|
||||||
|
];
|
||||||
|
|
||||||
|
let choices = [];
|
||||||
|
let writeIn = [];
|
||||||
|
let writingIn = undefined;
|
||||||
|
|
||||||
|
function getOptions(index) {
|
||||||
|
return kQuestions[index].options.concat(writeIn[index] || []);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isChosen(question, choice) {
|
||||||
|
let selected = false;
|
||||||
|
if (kQuestions[question].allowMultiple) {
|
||||||
|
selected = choices[question] && choices[question].indexOf(choice) != -1;
|
||||||
|
} else {
|
||||||
|
selected = choices[question] == choice;
|
||||||
|
}
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readData() {
|
||||||
|
return database.getAll().then(function(keys) {
|
||||||
|
var promises = [];
|
||||||
|
for (var i = 0; i < keys.length; i++) {
|
||||||
|
promises.push(database.get(keys[i]));
|
||||||
|
}
|
||||||
|
return Promise.all(promises);
|
||||||
|
}).then(function(allData) {
|
||||||
|
writeIn = [];
|
||||||
|
for (var i = 0; i < allData.length; i++) {
|
||||||
|
if (allData[i]) {
|
||||||
|
var entry = JSON.parse(allData[i]);
|
||||||
|
if (entry && entry.writeIn) {
|
||||||
|
for (var j = 0; j < entry.writeIn.length; j++) {
|
||||||
|
if (writeIn.indexOf(entry.writeIn[j]) == -1) {
|
||||||
|
writeIn.push(entry.writeIn[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return database.get("data_" + user);
|
||||||
|
}).then(function(data) {
|
||||||
|
if (data) {
|
||||||
|
choices = JSON.parse(data).choices;
|
||||||
|
}
|
||||||
|
}).catch(function(error) {
|
||||||
|
terminal.select("image");
|
||||||
|
terminal.print(error);
|
||||||
|
terminal.select("form");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeData() {
|
||||||
|
return database.set("data_" + user, JSON.stringify({choices: choices, writeIn: writeIn})).catch(function(error) {
|
||||||
|
terminal.select("image");
|
||||||
|
terminal.print(error);
|
||||||
|
terminal.select("form");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
return readData().then(function() {
|
||||||
|
terminal.cork();
|
||||||
|
try {
|
||||||
|
terminal.clear();
|
||||||
|
for (let line in kIntroduction) {
|
||||||
|
terminal.print(kIntroduction[line]);
|
||||||
|
}
|
||||||
|
let allowFill = writingIn === undefined;
|
||||||
|
for (let i in kQuestions) {
|
||||||
|
let availableOptions = getOptions(i);
|
||||||
|
let options = [];
|
||||||
|
for (let j in availableOptions) {
|
||||||
|
options.push(" ");
|
||||||
|
|
||||||
|
let value = (isChosen(i, availableOptions[j]) ? "☒" : "☐") + availableOptions[j];
|
||||||
|
if (allowFill) {
|
||||||
|
options.push({
|
||||||
|
command: JSON.stringify({action: "toggle", question: i, option: availableOptions[j]}),
|
||||||
|
value: value,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
options.push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (kQuestions[i].writeIn) {
|
||||||
|
options.push(" or ");
|
||||||
|
if (writingIn !== undefined) {
|
||||||
|
options.push("write in another option below")
|
||||||
|
options.push(" ");
|
||||||
|
options.push({command: JSON.stringify({action: "endWriteIn"}), value: "cancel"});
|
||||||
|
} else {
|
||||||
|
options.push({command: JSON.stringify({action: "beginWriteIn", question: i}), value: "write in another option"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
terminal.print(kQuestions[i].text, ":", options);
|
||||||
|
terminal.print();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
for (let line in kBottom) {
|
||||||
|
terminal.print(kBottom[line]);
|
||||||
|
}
|
||||||
|
terminal.uncork();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
core.register("onInput", function(input) {
|
||||||
|
if (writingIn !== undefined && input != "cancel") {
|
||||||
|
readData().then(function() {
|
||||||
|
if (!writeIn[writingIn]) {
|
||||||
|
writeIn[writingIn] = [];
|
||||||
|
}
|
||||||
|
if (input && writeIn[writingIn].indexOf(input) == -1) {
|
||||||
|
writeIn[writingIn].push(input);
|
||||||
|
}
|
||||||
|
writingIn = undefined;
|
||||||
|
terminal.setPrompt("> ");
|
||||||
|
return writeData();
|
||||||
|
}).then(render);
|
||||||
|
} else {
|
||||||
|
let command = JSON.parse(input);
|
||||||
|
if (command.action == "toggle") {
|
||||||
|
readData().then(function() {
|
||||||
|
if (kQuestions[command.question].allowMultiple) {
|
||||||
|
if (choices[command.question] && choices[command.question].indexOf(command.option) != -1) {
|
||||||
|
choices[command.question].splice(choices[command.question].indexOf(command.option), 1);
|
||||||
|
} else {
|
||||||
|
if (!choices[command.question]) {
|
||||||
|
choices[command.question] = [];
|
||||||
|
}
|
||||||
|
choices[command.question].push(command.option);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
choices[command.question] = command.option;
|
||||||
|
}
|
||||||
|
return writeData();
|
||||||
|
}).then(render);
|
||||||
|
} else if (command.action == "beginWriteIn") {
|
||||||
|
writingIn = command.question;
|
||||||
|
terminal.setPrompt('Enter new option for "' + kQuestions[command.question].text + '": ');
|
||||||
|
render();
|
||||||
|
} else if (command.action == "endWriteIn") {
|
||||||
|
writingIn = undefined;
|
||||||
|
terminal.setPrompt("> ");
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
render().catch(function(error) {
|
||||||
|
terminal.print(error);
|
||||||
|
});
|
179
packages/cory/invite2/invite2.js
Normal file
179
packages/cory/invite2/invite2.js
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
//! {"require": ["smtp"], "permissions": ["network"]}
|
||||||
|
|
||||||
|
let administrator = core.user.name == core.user.packageOwner;
|
||||||
|
|
||||||
|
function lameHash(value) {
|
||||||
|
let result = 12945;
|
||||||
|
for (let i = 0; i < value.length; i++) {
|
||||||
|
result += value.charCodeAt(i);
|
||||||
|
result *= 31;
|
||||||
|
result &= 0x7fffffff;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
terminal.setEcho(false);
|
||||||
|
if (administrator) {
|
||||||
|
terminal.print({style: "font-size: x-large", value: "Edit", command: "command:" + JSON.stringify("command")});
|
||||||
|
}
|
||||||
|
|
||||||
|
let gInvite = null;
|
||||||
|
|
||||||
|
function display() {
|
||||||
|
return database.get("invite").then(function(data) {
|
||||||
|
if (data) {
|
||||||
|
let invite = JSON.parse(data);
|
||||||
|
let promises = [];
|
||||||
|
for (let i in invite.emails) {
|
||||||
|
promises.push(database.get("rsvp." + lameHash(invite.emails[i])));
|
||||||
|
}
|
||||||
|
return Promise.all(promises).then(function(rsvps) {
|
||||||
|
for (let i in rsvps) {
|
||||||
|
if (rsvps[i]) {
|
||||||
|
rsvps[i] = JSON.parse(rsvps[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
terminal.print({style: "font-size: xx-large", value: invite.title});
|
||||||
|
terminal.print(invite.message);
|
||||||
|
for (let i in invite.food) {
|
||||||
|
let line = [invite.food[i], "?", " ", "+", " ", "-"];
|
||||||
|
// hi
|
||||||
|
terminal.print({style: "font-size: x-large", value: line});
|
||||||
|
}
|
||||||
|
gInvite = invite;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).catch(function(error) {
|
||||||
|
terminal.print(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
core.register("hashChange", function(event) {
|
||||||
|
let hash = event.hash;
|
||||||
|
if (hash && hash.charAt(0) == '#') {
|
||||||
|
hash = hash.substring(1);
|
||||||
|
}
|
||||||
|
if (hash) {
|
||||||
|
let invite = gInvite || {};
|
||||||
|
for (let i in invite.emails) {
|
||||||
|
let email = invite.emails[i];
|
||||||
|
if (lameHash(email).toString() == hash) {
|
||||||
|
terminal.print("hash match!", email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
core.register("submit", function(event) {
|
||||||
|
let invite = event.value;
|
||||||
|
invite.emails = invite.emails.split(",");
|
||||||
|
invite.food = invite.food.split(",");
|
||||||
|
for (let i in invite.emails) {
|
||||||
|
let url = "https://www.tildefriends.net/~cory/invite2#" + lameHash(invite.emails[i]).toString();
|
||||||
|
terminal.print({href: url});
|
||||||
|
}
|
||||||
|
database.set("invite", JSON.stringify(invite));
|
||||||
|
});
|
||||||
|
|
||||||
|
display();
|
||||||
|
|
||||||
|
core.register("onInput", function(input) {
|
||||||
|
if (input.substring(0, "command:".length) == "command:") {
|
||||||
|
command = JSON.parse(input.substring("command:".length));
|
||||||
|
if (command == "create") {
|
||||||
|
let invite = gInvite || {};
|
||||||
|
terminal.print("Title:", {input: "text", name: "title", style: "width: 512px", value: invite.title});
|
||||||
|
terminal.print("Message:", {input: "text", name: "message", style: "width: 512px", value: invite.message});
|
||||||
|
terminal.print("Email Addresses:", {input: "text", name: "emails", style: "width: 512px", value: invite.emails.join(",")});
|
||||||
|
terminal.print("Food:", {input: "text", name: "food", style: "width: 512px", value: invite.food.join(",")});
|
||||||
|
terminal.print({input: "submit", name: "Create", value: "Create"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
require("smtp").sendMail({
|
||||||
|
from: core.user.name + "@unprompted.com",
|
||||||
|
to: "test1@unprompted.com",
|
||||||
|
subject: input,
|
||||||
|
body: input,
|
||||||
|
}).then(function() {
|
||||||
|
terminal.print("sent");
|
||||||
|
}).catch(function(error) {
|
||||||
|
terminal.print("error: ", error);
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
let kEmojis = [
|
||||||
|
"🍇 Grapes",
|
||||||
|
"🍈 Melon",
|
||||||
|
"🍉 Watermelon",
|
||||||
|
"🍊 Tangerine",
|
||||||
|
"🍋 Lemon",
|
||||||
|
"🍌 Banana",
|
||||||
|
"🍍 Pineapple",
|
||||||
|
"🍎 Red Apple",
|
||||||
|
"🍏 Green Apple",
|
||||||
|
"🍐 Pear",
|
||||||
|
"🍑 Peach",
|
||||||
|
"🍒 Cherries",
|
||||||
|
"🍓 Strawberry",
|
||||||
|
"🍅 Tomato",
|
||||||
|
"🍆 Aubergine",
|
||||||
|
"🌽 Ear of Maize",
|
||||||
|
"🌶 Hot Pepper",
|
||||||
|
"🍄 Mushroom",
|
||||||
|
"🌰 Chestnut",
|
||||||
|
"🍞 Bread",
|
||||||
|
"🧀 Cheese Wedge",
|
||||||
|
"🍖 Meat on Bone",
|
||||||
|
"🍗 Poultry Leg",
|
||||||
|
"🍔 Hamburger",
|
||||||
|
"🍟 French Fries",
|
||||||
|
"🍕 Slice of Pizza",
|
||||||
|
"🌭 Hot Dog",
|
||||||
|
"🌮 Taco",
|
||||||
|
"🌯 Burrito",
|
||||||
|
"🍳 Cooking",
|
||||||
|
"🍲 Pot of Food",
|
||||||
|
"🍿 Popcorn",
|
||||||
|
"🍱 Bento Box",
|
||||||
|
"🍘 Rice Cracker",
|
||||||
|
"🍙 Rice Ball",
|
||||||
|
"🍚 Cooked Rice",
|
||||||
|
"🍛 Curry and Rice",
|
||||||
|
"🍜 Steaming Bowl",
|
||||||
|
"🍝 Spaghetti",
|
||||||
|
"🍠 Roasted Sweet Potato",
|
||||||
|
"🍢 Oden",
|
||||||
|
"🍣 Sushi",
|
||||||
|
"🍤 Fried Shrimp",
|
||||||
|
"🍥 Fish Cake With Swirl Design",
|
||||||
|
"🍡 Dango",
|
||||||
|
"🍦 Soft Ice Cream",
|
||||||
|
"🍧 Shaved Ice",
|
||||||
|
"🍨 Ice Cream",
|
||||||
|
"🍩 Doughnut",
|
||||||
|
"🍪 Cookie",
|
||||||
|
"🎂 Birthday Cake",
|
||||||
|
"🍰 Shortcake",
|
||||||
|
"🍫 Chocolate Bar",
|
||||||
|
"🍬 Candy",
|
||||||
|
"🍭 Lollipop",
|
||||||
|
"🍮 Custard",
|
||||||
|
"🍯 Honey Pot",
|
||||||
|
"🍼 Baby Bottle",
|
||||||
|
"☕ Hot Beverage",
|
||||||
|
"🍵 Teacup Without Handle",
|
||||||
|
"🍶 Sake Bottle and Cup",
|
||||||
|
"🍾 Bottle With Popping Cork",
|
||||||
|
"🍷 Wine Glass",
|
||||||
|
"🍸 Cocktail Glass",
|
||||||
|
"🍹 Tropical Drink",
|
||||||
|
"🍺 Beer Mug",
|
||||||
|
"🍻 Clinking Beer Mugs",
|
||||||
|
"🍽 Fork and Knife With Plate",
|
||||||
|
"🍴 Fork and Knife",
|
||||||
|
];
|
936
packages/cory/ldjam34/ldjam34.js
Normal file
936
packages/cory/ldjam34/ldjam34.js
Normal file
@ -0,0 +1,936 @@
|
|||||||
|
imports.terminal.print({iframe: `
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>become a game developer in 60 seconds</title>
|
||||||
|
<script language="javascript">
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function ImageEditor() {
|
||||||
|
this.kWidth = 256;
|
||||||
|
this.kHeight = 256;
|
||||||
|
this.kTop = 30;
|
||||||
|
this.kLeft = 640 / 2 - this.kWidth / 2;
|
||||||
|
this.kSpriteSize = 8;
|
||||||
|
this.image = new Array(this.kSpriteSize * this.kSpriteSize);
|
||||||
|
for (var i = 0; i < this.kSpriteSize * this.kSpriteSize; i++) {
|
||||||
|
this.image[i] = 0;
|
||||||
|
}
|
||||||
|
this.pixelsFilled = 0;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageEditor.prototype.update = function() {
|
||||||
|
if (this.title) {
|
||||||
|
var fontSize = 16;
|
||||||
|
gContext.font = 'bold ' + fontSize + 'px courier new';
|
||||||
|
gContext.fillStyle = '#fff';
|
||||||
|
gContext.fillText(this.title, 640 / 2 - gContext.measureText(this.title).width / 2, fontSize);
|
||||||
|
}
|
||||||
|
for (var i = 0; i < this.kSpriteSize; i++) {
|
||||||
|
for (var j = 0; j < this.kSpriteSize; j++) {
|
||||||
|
var p = j * this.kSpriteSize + i;
|
||||||
|
if (this.pixelsFilled > p) {
|
||||||
|
gContext.fillStyle = this.image[p] ? '#fff' : '#000';
|
||||||
|
gContext.fillRect(
|
||||||
|
this.kLeft + (i * this.kWidth / this.kSpriteSize),
|
||||||
|
this.kTop + (j * this.kHeight / this.kSpriteSize),
|
||||||
|
this.kWidth / this.kSpriteSize,
|
||||||
|
this.kHeight / this.kSpriteSize);
|
||||||
|
} else if (this.pixelsFilled == p) {
|
||||||
|
gContext.fillStyle = pulseColor();
|
||||||
|
gContext.fillRect(
|
||||||
|
this.kLeft + (i * this.kWidth / this.kSpriteSize),
|
||||||
|
this.kTop + (j * this.kHeight / this.kSpriteSize),
|
||||||
|
this.kWidth / this.kSpriteSize,
|
||||||
|
this.kHeight / this.kSpriteSize);
|
||||||
|
} else {
|
||||||
|
gContext.strokeStyle = '#fff';
|
||||||
|
gContext.strokeRect(
|
||||||
|
this.kLeft + (i * this.kWidth / this.kSpriteSize),
|
||||||
|
this.kTop + (j * this.kHeight / this.kSpriteSize),
|
||||||
|
this.kWidth / this.kSpriteSize,
|
||||||
|
this.kHeight / this.kSpriteSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.suggested) {
|
||||||
|
var fontSize = 16;
|
||||||
|
gContext.font = fontSize + 'px courier new';
|
||||||
|
gContext.fillStyle = '#fff';
|
||||||
|
gContext.fillText('suggested: ' + this.suggested.substring(this.pixelsFilled, this.pixelsFilled + 8), 10, this.kTop + this.kHeight + 2 * fontSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gKeyPressed['0']) {
|
||||||
|
this.image[this.pixelsFilled++] = 0;
|
||||||
|
} else if (gKeyPressed['1']) {
|
||||||
|
this.image[this.pixelsFilled++] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.pixelsFilled == this.kSpriteSize * this.kSpriteSize && this.completed) {
|
||||||
|
var image = [];
|
||||||
|
for (var i = 0; i < this.kSpriteSize; i++) {
|
||||||
|
var line = '';
|
||||||
|
for (var j = 0; j < this.kSpriteSize; j++) {
|
||||||
|
line += this.image[i * this.kSpriteSize + j] ? '1' : '0';
|
||||||
|
}
|
||||||
|
image.push(line);
|
||||||
|
}
|
||||||
|
this.completed(makeImageData(image, 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function SoundEditor() {
|
||||||
|
this.start = true;
|
||||||
|
this.data = [null, null, null];
|
||||||
|
this.downTime = null;
|
||||||
|
this.part = 0;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
SoundEditor.prototype.update = function() {
|
||||||
|
if (this.start && (gKeyDown['0'] || gKeyDown['1'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.start = false;
|
||||||
|
var min = [20, 20, 0.001];
|
||||||
|
var max = [20000, 20000, 1.0];
|
||||||
|
|
||||||
|
var fontSize = 16;
|
||||||
|
gContext.font = fontSize + 'px courier new';
|
||||||
|
gContext.fillStyle = '#fff';
|
||||||
|
gContext.fillText("start frequency", 1.5 * 640 / 7 - gContext.measureText('start frequency').width / 2, 2 * fontSize);
|
||||||
|
gContext.fillText("end frequency", 3.5 * 640 / 7 - gContext.measureText('end frequency').width / 2, 2 * fontSize);
|
||||||
|
gContext.fillText("duration", 5.5 * 640 / 7 - gContext.measureText('duration').width / 2, 2 * fontSize);
|
||||||
|
|
||||||
|
for (var i = 0; i < this.data.length; i++) {
|
||||||
|
var kHeight = 240;
|
||||||
|
gContext.strokeStyle = '#fff';
|
||||||
|
gContext.strokeRect((2 * i + 1) * 640 / 7, 3 * fontSize, 640 / 7, kHeight);
|
||||||
|
if (this.data[i] != null) {
|
||||||
|
var v = (this.data[i] - min[i]) / (max[i] - min[i]);
|
||||||
|
gContext.fillStyle = '#fff';
|
||||||
|
gContext.fillRect((2 * i + 1) * 640 / 7, 3 * fontSize + kHeight * (1 - v), 640 / 7, kHeight * v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.part < this.data.length) {
|
||||||
|
if (gKeyPressed['0']) {
|
||||||
|
this.data[this.part] = Math.random() * (max[this.part] - min[this.part]) + min[this.part];
|
||||||
|
this.part++;
|
||||||
|
} else {
|
||||||
|
var now = Date.now();
|
||||||
|
if (!this.downTime && gKeyDown['1']) {
|
||||||
|
this.downTime = now;
|
||||||
|
}
|
||||||
|
if (this.downTime) {
|
||||||
|
var v = Math.min((now - this.downTime) / 1000.0, 1.0);
|
||||||
|
this.data[this.part] = v * (max[this.part] - min[this.part]) + min[this.part];
|
||||||
|
if (!gKeyDown['1'] || (now - this.downTime) / 1000.0 > 1.0) {
|
||||||
|
this.part++;
|
||||||
|
this.downTime = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var names = ['start frequency', 'end frequency', 'duration'];
|
||||||
|
gContext.fillStyle = '#fff';
|
||||||
|
gContext.font = 'bold ' + fontSize + 'px courier new';
|
||||||
|
gContext.fillText("set " + names[this.part], 640 / 2 - gContext.measureText('set ' + names[this.part]).width / 2, 320);
|
||||||
|
gContext.font = fontSize + 'px courier new';
|
||||||
|
gContext.fillText("1 hold to set", 640 / 2 - gContext.measureText('1 hold to set').width / 2, 320 + fontSize);
|
||||||
|
gContext.fillText("0 random", 640 / 2 - gContext.measureText('0 random').width / 2, 320 + fontSize * 2);
|
||||||
|
} else {
|
||||||
|
gContext.fillStyle = '#fff';
|
||||||
|
gContext.fillText("1 hear it", 640 / 2 - gContext.measureText('1 hear it').width / 2, 320 + fontSize);
|
||||||
|
gContext.fillText("0 done", 640 / 2 - gContext.measureText('0 done').width / 2, 320 + fontSize * 2);
|
||||||
|
if (gKeyPressed['1']) {
|
||||||
|
tone(this.data[0], this.data[1], this.data[2]);
|
||||||
|
}
|
||||||
|
if (gKeyPressed['0'] && this.completed) {
|
||||||
|
this.completed(this.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function Game(options) {
|
||||||
|
this.options = {};
|
||||||
|
for (var k in options) {
|
||||||
|
this.options[k] = options[k];
|
||||||
|
}
|
||||||
|
this.velocity = [0, 0];
|
||||||
|
this.position = [0, 0];
|
||||||
|
this.gone = {};
|
||||||
|
this.camera = 10;
|
||||||
|
this.projectile = null;
|
||||||
|
this.gameOver = false;
|
||||||
|
this.start = true;
|
||||||
|
this.countdown = 3;
|
||||||
|
|
||||||
|
this.points = 0;
|
||||||
|
this.kills = 0;
|
||||||
|
|
||||||
|
document.location.hash = store(gGame);
|
||||||
|
document.getElementById("share").href = document.location.href;
|
||||||
|
|
||||||
|
if (this.options.button0 == undefined) {
|
||||||
|
this.options.button0 = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.button1 == undefined) {
|
||||||
|
this.options.button1 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.options.score) {
|
||||||
|
this.options.score = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.options.player) {
|
||||||
|
this.options.player = makeImageData([
|
||||||
|
'11111111',
|
||||||
|
'10000001',
|
||||||
|
'10100101',
|
||||||
|
'10000001',
|
||||||
|
'10100101',
|
||||||
|
'10111101',
|
||||||
|
'10000001',
|
||||||
|
'11111111',
|
||||||
|
], 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.options.enemy) {
|
||||||
|
this.options.enemy = makeImageData([
|
||||||
|
'11111111',
|
||||||
|
'10000001',
|
||||||
|
'10100101',
|
||||||
|
'10000001',
|
||||||
|
'10111101',
|
||||||
|
'10100101',
|
||||||
|
'10000001',
|
||||||
|
'11111111',
|
||||||
|
], 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.options.point) {
|
||||||
|
this.options.point = makeImageData([
|
||||||
|
'00000000',
|
||||||
|
'00000000',
|
||||||
|
'00011000',
|
||||||
|
'00100100',
|
||||||
|
'01011010',
|
||||||
|
'01010010',
|
||||||
|
'00100100',
|
||||||
|
'00011000',
|
||||||
|
], 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.background = makeImageData([
|
||||||
|
'1110',
|
||||||
|
'0101',
|
||||||
|
'0011',
|
||||||
|
'1001',
|
||||||
|
], 16);
|
||||||
|
|
||||||
|
this.timer = [
|
||||||
|
makeImageData([
|
||||||
|
'110',
|
||||||
|
'010',
|
||||||
|
'010',
|
||||||
|
'010',
|
||||||
|
'111',
|
||||||
|
], 16),
|
||||||
|
makeImageData([
|
||||||
|
'111',
|
||||||
|
'001',
|
||||||
|
'111',
|
||||||
|
'100',
|
||||||
|
'111',
|
||||||
|
], 16),
|
||||||
|
makeImageData([
|
||||||
|
'111',
|
||||||
|
'001',
|
||||||
|
'011',
|
||||||
|
'001',
|
||||||
|
'111',
|
||||||
|
], 16)
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!this.options.level) {
|
||||||
|
this.options.level = '00102';
|
||||||
|
} else {
|
||||||
|
this.options.level = '0' + this.options.level;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.options.jump) {
|
||||||
|
this.options.jump = [1000, 3000, 0.25];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.options.shoot) {
|
||||||
|
this.options.shoot = [300, 300, 0.1];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.collect = [this.options.shoot[0], this.options.jump[1], (this.options.shoot[2] + this.options.jump[2]) / 2];
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Game.prototype.update = function() {
|
||||||
|
if (this.start && (gKeyDown['0'] || gKeyDown['1'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.start = false;
|
||||||
|
|
||||||
|
if (this.countdown > 0) {
|
||||||
|
this.countdown -= gTimeDelta / 1000;
|
||||||
|
if (this.countdown > 0) {
|
||||||
|
var i = Math.min(Math.floor(this.countdown), 2);
|
||||||
|
gContext.putImageData(this.timer[i], 640 / 2 - 16 * 3, 480 / 2 - 16 * 3);
|
||||||
|
} else {
|
||||||
|
this.countdown = 0;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < this.options.level.length; i++) {
|
||||||
|
if (!this.gone[i]) {
|
||||||
|
var c = this.options.level.charAt(i);
|
||||||
|
if (c == '1') {
|
||||||
|
gContext.putImageData(this.options.enemy, i * 16 * 8 * 2 - this.position[0] + this.camera, 240);
|
||||||
|
} else if (c == '2') {
|
||||||
|
gContext.putImageData(this.options.point, i * 16 * 8 * 2 - this.position[0] + this.camera, 240);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var tile = Math.round(this.position[0] / (16 * 8 * 2));
|
||||||
|
if (!this.gone[tile]) {
|
||||||
|
var hit = this.options.level.charAt(tile);
|
||||||
|
if (hit == '1' && this.position[1] <= 16 * 8) {
|
||||||
|
if (this.velocity[1] < 0) {
|
||||||
|
this.gone[tile] = true;
|
||||||
|
this.velocity[1] = 1.5;
|
||||||
|
this.kills++;
|
||||||
|
tone(this.collect[1], this.collect[0], this.collect[2]);
|
||||||
|
} else {
|
||||||
|
this.gameOver = true;
|
||||||
|
this.gone[tile] = true;
|
||||||
|
}
|
||||||
|
} else if (hit == '2' && this.position[1] <= 16 * 8) {
|
||||||
|
tone(this.collect[0], this.collect[1], this.collect[2]);
|
||||||
|
this.points += this.options.score;
|
||||||
|
this.gone[tile] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.projectile != null) {
|
||||||
|
gContext.fillStyle = '#fff';
|
||||||
|
var screenPosition = this.projectile - this.position[0] + this.camera;
|
||||||
|
gContext.fillRect(screenPosition, 240 + 16 * 8 / 2, 16, 16);
|
||||||
|
this.projectile += gTimeDelta;
|
||||||
|
var p = Math.floor(this.projectile / (16 * 8 * 2));
|
||||||
|
var hit = this.options.level.charAt(p);
|
||||||
|
if (hit == '1' && !this.gone[p]) {
|
||||||
|
this.gone[p] = true;
|
||||||
|
this.projectile = null;
|
||||||
|
}
|
||||||
|
if (screenPosition > 640) {
|
||||||
|
this.projectile = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gContext.putImageData(this.options.player, this.camera, 240 - this.position[1]);
|
||||||
|
this.position[0] += this.velocity[0] * gTimeDelta;
|
||||||
|
this.position[1] += this.velocity[1] * gTimeDelta;
|
||||||
|
this.velocity[1] -= 0.005 * gTimeDelta;
|
||||||
|
if (this.position[1] <= 0 && !this.gameOver) {
|
||||||
|
this.position[1] = 0;
|
||||||
|
this.velocity[1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.gameOver) {
|
||||||
|
this.position[0] -= 2 * gTimeDelta;
|
||||||
|
if (this.position[1] > -480) {
|
||||||
|
this.position[1] -= gTimeDelta * 0.1;
|
||||||
|
} else {
|
||||||
|
var fontSize = 16;
|
||||||
|
gContext.fillStyle = '#fff';
|
||||||
|
gContext.font = 'bold ' + fontSize + 'px courier new';
|
||||||
|
gContext.fillText('game over', 640 / 2 - gContext.measureText('game over').width / 2, 240 - 2 * fontSize);
|
||||||
|
gContext.font = fontSize + 'px courier new';
|
||||||
|
gContext.fillText('1 retry', 640 / 2 - gContext.measureText('1 retry').width / 2, 240 + 16 * 8 + 2 * fontSize);
|
||||||
|
gContext.fillText('0 back', 640 / 2 - gContext.measureText('1 retry').width / 2, 240 + 16 * 8 + 3 * fontSize);
|
||||||
|
if (gKeyPressed['1'] && this.completed) {
|
||||||
|
this.completed(1);
|
||||||
|
}
|
||||||
|
if (gKeyPressed['0'] && this.completed) {
|
||||||
|
this.completed(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (tile > this.options.level.length) {
|
||||||
|
var targetCamera = 640 / 2 - (16 * 8) / 2;
|
||||||
|
if (this.camera < targetCamera) {
|
||||||
|
this.camera += gTimeDelta / 5;
|
||||||
|
} else {
|
||||||
|
this.camera = targetCamera;
|
||||||
|
if (this.velocity[1] == 0) {
|
||||||
|
this.velocity[1] = 1.5;
|
||||||
|
}
|
||||||
|
var fontSize = 16;
|
||||||
|
gContext.font = fontSize + 'px courier new';
|
||||||
|
gContext.fillStyle = '#fff';
|
||||||
|
gContext.fillText('1 again', 640 / 2 - gContext.measureText('1 again').width / 2, 240 + 16 * 8 + 2 * fontSize);
|
||||||
|
gContext.fillText('0 back', 640 / 2 - gContext.measureText('1 again').width / 2, 240 + 16 * 8 + 3 * fontSize);
|
||||||
|
|
||||||
|
var results = '';
|
||||||
|
if (this.points) {
|
||||||
|
if (results.length) {
|
||||||
|
results += ', ';
|
||||||
|
}
|
||||||
|
results += this.points + (this.points == 1 ? ' point' : ' points');
|
||||||
|
}
|
||||||
|
if (this.kills) {
|
||||||
|
if (results.length) {
|
||||||
|
results += ', ';
|
||||||
|
}
|
||||||
|
results += this.kills + (this.kills == 1 ? ' enemy vanquished' : ' enemies vanquished');
|
||||||
|
}
|
||||||
|
gContext.fillText(results, 640 / 2 - gContext.measureText(results).width / 2, 240 + 16 * 8 + 5 * fontSize);
|
||||||
|
if (gKeyPressed['1'] && this.completed) {
|
||||||
|
this.completed(1);
|
||||||
|
}
|
||||||
|
if (gKeyPressed['0'] && this.completed) {
|
||||||
|
this.completed(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (var i = 0; i < 12; i++) {
|
||||||
|
for (var j = 0; j < 2; j++) {
|
||||||
|
gContext.putImageData(this.background, i * 16 * 4 - this.position[0] % (16 * 4), 240 + 16 * 8 + j * 16 * 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var jump = this.velocity[1] == 0 &&
|
||||||
|
(this.options.button0 == 0 && gKeyDown['0'] ||
|
||||||
|
this.options.button1 == 0 && gKeyDown['1']);
|
||||||
|
var shoot = this.projectile == null &&
|
||||||
|
(this.options.button0 == 1 && gKeyDown['0'] ||
|
||||||
|
this.options.button1 == 1 && gKeyDown['1']);
|
||||||
|
var move = this.projectile == null &&
|
||||||
|
(this.options.button0 == 2 && gKeyDown['0'] ||
|
||||||
|
this.options.button1 == 2 && gKeyDown['1'] ||
|
||||||
|
this.options.button0 != 2 && this.options.button1 != 2);
|
||||||
|
if (jump) {
|
||||||
|
this.velocity[1] = 1.5;
|
||||||
|
tone(this.options.jump[0], this.options.jump[1], this.options.jump[2]);
|
||||||
|
}
|
||||||
|
if (shoot) {
|
||||||
|
this.projectile = this.position[0];
|
||||||
|
tone(this.options.shoot[0], this.options.shoot[1], this.options.shoot[2]);
|
||||||
|
}
|
||||||
|
if (move) {
|
||||||
|
this.velocity[0] = 1;
|
||||||
|
} else {
|
||||||
|
this.velocity[0] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function packImage(imageData) {
|
||||||
|
var result = '';
|
||||||
|
for (var i = 0; i < imageData.width / 16; i++) {
|
||||||
|
for (var j = 0; j < imageData.height / 16; j++) {
|
||||||
|
result += (imageData.data[j * imageData.width * 4 * 16 + i * 4 * 16] ? '1' : '0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unpackImageData(data) {
|
||||||
|
var pixels = [];
|
||||||
|
console.debug(data);
|
||||||
|
for (var i = 0; i < 8; i++) {
|
||||||
|
var line = '';
|
||||||
|
for (var j = 0; j < 8; j++) {
|
||||||
|
line += data.charAt(j * 8 + i);
|
||||||
|
}
|
||||||
|
pixels.push(line);
|
||||||
|
}
|
||||||
|
return makeImageData(pixels, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
function store(game) {
|
||||||
|
var simple = {};
|
||||||
|
for (var k in game) {
|
||||||
|
if (game[k] instanceof ImageData) {
|
||||||
|
simple[k] = packImage(game[k]);
|
||||||
|
} else {
|
||||||
|
simple[k] = game[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return JSON.stringify(simple);
|
||||||
|
};
|
||||||
|
|
||||||
|
function load(game) {
|
||||||
|
var simple = JSON.parse(game);
|
||||||
|
for (var k in simple) {
|
||||||
|
if (['player', 'enemy'].indexOf(k) != -1) {
|
||||||
|
simple[k] = unpackImageData(simple[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return simple;
|
||||||
|
};
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var gAudio = null;
|
||||||
|
var gGain = null;
|
||||||
|
|
||||||
|
function tone(frequency0, frequency1, duration) {
|
||||||
|
var Context = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.oAudioContext || window.msAudioContext;
|
||||||
|
if (!gAudio && Context) {
|
||||||
|
gAudio = new Context();
|
||||||
|
}
|
||||||
|
if (!gGain && gAudio) {
|
||||||
|
gGain = gAudio.createGain();
|
||||||
|
gGain.connect(gAudio.destination);
|
||||||
|
gGain.gain.value = 0.15;
|
||||||
|
}
|
||||||
|
var now = gAudio.currentTime;
|
||||||
|
var oscillator = gAudio.createOscillator();
|
||||||
|
oscillator.frequency.setValueAtTime(frequency0, now);
|
||||||
|
oscillator.frequency.linearRampToValueAtTime(frequency1, now + duration);
|
||||||
|
oscillator.type = 'sine';
|
||||||
|
oscillator.connect(gGain);
|
||||||
|
oscillator.start(now);
|
||||||
|
oscillator.stop(gAudio.currentTime + duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function Menu(configuration) {
|
||||||
|
this.title = configuration.title;
|
||||||
|
this.options = configuration.options;
|
||||||
|
this.footer = configuration.footer;
|
||||||
|
this.image = configuration.image;
|
||||||
|
this.bitsNeeded = 0;
|
||||||
|
this.fontSize = 16;
|
||||||
|
this.bits = '';
|
||||||
|
var count = this.options.length;
|
||||||
|
while (count > 1) {
|
||||||
|
this.bitsNeeded += 1;
|
||||||
|
count /= 2;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Menu.prototype.update = function() {
|
||||||
|
gContext.font = 'bold ' + this.fontSize + 'px courier new';
|
||||||
|
gContext.fillStyle = '#fff';
|
||||||
|
gContext.fillText(this.title, 640 / 2 - gContext.measureText(this.title).width / 2, this.fontSize);
|
||||||
|
|
||||||
|
var maxWidth = 0;
|
||||||
|
for (var i = 0; i < this.options.length; i++) {
|
||||||
|
maxWidth = Math.max(maxWidth, gContext.measureText(this.options[i]).width);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < this.options.length; i++) {
|
||||||
|
gContext.font = this.fontSize + 'px courier new';
|
||||||
|
gContext.fillStyle = '#fff';
|
||||||
|
gContext.fillText(bits(i, this.bitsNeeded) + ' ' + this.options[i], 640 / 2 - maxWidth / 2, this.fontSize * (i + 3));
|
||||||
|
}
|
||||||
|
gContext.font = this.fontSize + 'px courier new';
|
||||||
|
gContext.fillStyle = '#fff';
|
||||||
|
var text = '';
|
||||||
|
for (var i = 0; i < this.bitsNeeded; i++) {
|
||||||
|
text += '0';
|
||||||
|
}
|
||||||
|
gContext.fillText(this.bits, 640 / 2 - maxWidth / 2, this.fontSize * (this.options.length + 4));
|
||||||
|
gContext.fillStyle = blinkColor();
|
||||||
|
gContext.fillRect(640 / 2 - maxWidth / 2 + gContext.measureText(this.bits).width, this.fontSize * (this.options.length + 3), 10, this.fontSize);
|
||||||
|
|
||||||
|
if (this.footer) {
|
||||||
|
gContext.font = this.fontSize + 'px courier new';
|
||||||
|
gContext.fillStyle = '#fff';
|
||||||
|
gContext.fillText(this.footer, 640 / 2 - gContext.measureText(this.footer).width / 2, 480 - this.fontSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.image) {
|
||||||
|
gContext.putImageData(this.image, 640 / 2 - this.image.width / 2, 480 / 2 - this.image.height / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gKeyPressed['0']) {
|
||||||
|
this.bits += '0';
|
||||||
|
} else if (gKeyPressed['1']) {
|
||||||
|
this.bits += '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.bits.length >= this.bitsNeeded && this.completed) {
|
||||||
|
var result = 0;
|
||||||
|
for (var i = 0; i < this.bits.length; i++) {
|
||||||
|
result *= 2;
|
||||||
|
if (this.bits.charAt(i) == '1') {
|
||||||
|
result |= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.completed(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
var gContext;
|
||||||
|
var gKeyDown = {}
|
||||||
|
var gKeyPressed = {}
|
||||||
|
var gMode;
|
||||||
|
var gTimeDelta = 0;
|
||||||
|
var gLastFrame = Date.now();
|
||||||
|
var gTimer;
|
||||||
|
|
||||||
|
var gGame = {};
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
var canvas = document.getElementById("canvas");
|
||||||
|
gContext = canvas.getContext("2d");
|
||||||
|
|
||||||
|
if (document.location.hash) {
|
||||||
|
try {
|
||||||
|
gGame = load(document.location.hash.substring(1));
|
||||||
|
} catch (e) {
|
||||||
|
console.debug(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.onkeydown = function(event) {
|
||||||
|
if (event.which == 48 || event.which == 96) {
|
||||||
|
gKeyDown['0'] = true;
|
||||||
|
}
|
||||||
|
if (event.which == 49 || event.which == 97) {
|
||||||
|
gKeyDown['1'] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.onkeyup = function(event) {
|
||||||
|
if (event.which == 48 || event.which == 96) {
|
||||||
|
gKeyDown['0'] = false;
|
||||||
|
}
|
||||||
|
if (event.which == 49 || event.which == 97) {
|
||||||
|
gKeyDown['1'] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.onkeypress = function(event) {
|
||||||
|
if (event.which == 48 || event.which == 96) {
|
||||||
|
gKeyPressed['0'] = true;
|
||||||
|
}
|
||||||
|
if (event.which == 49 || event.which == 97) {
|
||||||
|
gKeyPressed['1'] = true;
|
||||||
|
}
|
||||||
|
if ((event.which == 48 || event.which == 49 || event.which == 96 || event.which == 97) &&
|
||||||
|
!(gMode instanceof Game) &&
|
||||||
|
!(gMode instanceof SoundEditor)) {
|
||||||
|
|
||||||
|
var min = 40;
|
||||||
|
var max = 2000;
|
||||||
|
var f = Math.random() * (max - min) + min;
|
||||||
|
tone(f, f, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
makeMainMenu();
|
||||||
|
window.requestAnimationFrame(update);
|
||||||
|
});
|
||||||
|
|
||||||
|
function makeMainMenu() {
|
||||||
|
var menu = new Menu({
|
||||||
|
title: 'become a game developer in 60 seconds',
|
||||||
|
options: ['create', 'test'],
|
||||||
|
footer: 'buttons: 0 and 1',
|
||||||
|
image: makeImageData([
|
||||||
|
'11001110110011101100111',
|
||||||
|
'01001010010010100100101',
|
||||||
|
'01001010010010100100101',
|
||||||
|
'01001010010010100100101',
|
||||||
|
'11101110111011101110111',
|
||||||
|
'00000000000000000000000',
|
||||||
|
'00111100100100010111000',
|
||||||
|
'00100001010110110100000',
|
||||||
|
'00101101110101010110000',
|
||||||
|
'00100101010100010100000',
|
||||||
|
'00111101010100010111000',
|
||||||
|
], 16)}
|
||||||
|
);
|
||||||
|
menu.completed = function(result) {
|
||||||
|
if (result == 0) {
|
||||||
|
gTimer = 60;
|
||||||
|
makeCreateMenu();
|
||||||
|
} else {
|
||||||
|
gTimer = undefined;
|
||||||
|
makeGame();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
gMode = menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeCreateMenu() {
|
||||||
|
var menu = new Menu({title: 'make game', options: [
|
||||||
|
'[' + (gGame.player ? 'x' : ' ') + '] player',
|
||||||
|
'[' + (gGame.enemy ? 'x' : ' ') + '] enemy',
|
||||||
|
'[' + (gGame.score ? 'x' : ' ') + '] score',
|
||||||
|
'[' + (gGame.jump ? 'x' : ' ') + '] jump',
|
||||||
|
'[' + (gGame.shoot ? 'x' : ' ') + '] shoot',
|
||||||
|
'[' + (gGame.button0 != undefined ? 'x' : ' ') + '] buttons',
|
||||||
|
'[' + (gGame.level ? 'x' : ' ') + '] level',
|
||||||
|
' ship it',
|
||||||
|
]});
|
||||||
|
menu.completed = function(result) {
|
||||||
|
if (result == 0) {
|
||||||
|
makeImageEditor('player',
|
||||||
|
'00111100'+
|
||||||
|
'00111100'+
|
||||||
|
'00111100'+
|
||||||
|
'10011001'+
|
||||||
|
'11111111'+
|
||||||
|
'00011000'+
|
||||||
|
'00100100'+
|
||||||
|
'00100100');
|
||||||
|
} else if (result == 1) {
|
||||||
|
makeImageEditor('enemy',
|
||||||
|
'00111100'+
|
||||||
|
'01000010'+
|
||||||
|
'10000001'+
|
||||||
|
'10100101'+
|
||||||
|
'10000001'+
|
||||||
|
'11011011'+
|
||||||
|
'01011010'+
|
||||||
|
'01111110');
|
||||||
|
} else if (result == 2) {
|
||||||
|
gGame.score = 1;
|
||||||
|
makeScoreEditor();
|
||||||
|
} else if (result == 3) {
|
||||||
|
makeSoundEditor('jump');
|
||||||
|
} else if (result == 4) {
|
||||||
|
makeSoundEditor('shoot');
|
||||||
|
} else if (result == 5) {
|
||||||
|
makeButtonsMenu();
|
||||||
|
} else if (result == 6) {
|
||||||
|
gGame.level = '';
|
||||||
|
makeLevelEditor();
|
||||||
|
} else if (result == 7) {
|
||||||
|
gTimer = undefined;
|
||||||
|
makeGame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gMode = menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeImageEditor(what, suggested) {
|
||||||
|
var editor = new ImageEditor();
|
||||||
|
editor.suggested = suggested;
|
||||||
|
editor.title = 'draw ' + what + ' by pressing 1 and 0';
|
||||||
|
editor.completed = function(result) {
|
||||||
|
gGame[what] = result;
|
||||||
|
makeCreateMenu();
|
||||||
|
};
|
||||||
|
gMode = editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeSoundEditor(what) {
|
||||||
|
var editor = new SoundEditor();
|
||||||
|
editor.completed = function(result) {
|
||||||
|
gGame[what] = result;
|
||||||
|
makeCreateMenu();
|
||||||
|
};
|
||||||
|
gMode = editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeButtonMenu(button) {
|
||||||
|
var menu = new Menu({title: 'what does ' + button + ' do?', options: [
|
||||||
|
'jump',
|
||||||
|
'shoot',
|
||||||
|
'move',
|
||||||
|
'nothing',
|
||||||
|
]});
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeButtonsMenu(button) {
|
||||||
|
var menu = makeButtonMenu('0');
|
||||||
|
menu.completed = function(result) {
|
||||||
|
gGame['button0'] = result;
|
||||||
|
menu = makeButtonMenu('1');
|
||||||
|
menu.completed = function(result) {
|
||||||
|
gGame['button1'] = result;
|
||||||
|
makeCreateMenu();
|
||||||
|
}
|
||||||
|
gMode = menu;
|
||||||
|
};
|
||||||
|
gMode = menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
gContext.fillStyle = '#000';
|
||||||
|
gContext.fillRect(0, 0, 640, 480);
|
||||||
|
|
||||||
|
var now = Date.now();
|
||||||
|
gTimeDelta = now - gLastFrame;
|
||||||
|
gLastFrame = now;
|
||||||
|
|
||||||
|
if (gTimer != undefined) {
|
||||||
|
if (gTimer > 0) {
|
||||||
|
gTimer -= gTimeDelta / 1000;
|
||||||
|
} else {
|
||||||
|
gTimer = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gMode) {
|
||||||
|
gMode.update();
|
||||||
|
} else {
|
||||||
|
gContext.fillStyle = pulseColor();
|
||||||
|
gContext.fillRect(0, 0, 640, 480);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i in gKeyPressed) {
|
||||||
|
gKeyPressed[i] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(gMode instanceof Game) && gTimer !== undefined) {
|
||||||
|
var fontSize = 16;
|
||||||
|
gContext.font = 'bold ' + fontSize + 'px courier new';
|
||||||
|
gContext.fillStyle = '#fff';
|
||||||
|
var remaining = Math.round(gTimer);
|
||||||
|
if (gTimer <= 0) {
|
||||||
|
remaining = 'OUT OF TIME! SHIP IT! OUT OF TIME! SHIP IT! OUT OF TIME! 111!';
|
||||||
|
gContext.fillStyle = pulseColor();
|
||||||
|
}
|
||||||
|
gContext.fillText(remaining, 640 - gContext.measureText(remaining).width - 1, 480 - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.requestAnimationFrame(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pulseColor() {
|
||||||
|
var pulse = (Math.sin(15 * Date.now() / 1000) + 1) / 2;
|
||||||
|
var value = Math.floor(255 * pulse).toString();
|
||||||
|
return 'rgb(' + value + ',' + value + ',' + value + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
function blinkColor() {
|
||||||
|
var blink = (Math.floor(4 * Date.now() / 1000) & 1) == 0;
|
||||||
|
var value = Math.floor(255 * blink).toString();
|
||||||
|
return 'rgb(' + value + ',' + value + ',' + value + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
function bits(value, count) {
|
||||||
|
var result = '';
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
if ((value & (1 << i)) != 0) {
|
||||||
|
result = '1' + result;
|
||||||
|
} else {
|
||||||
|
result = '0' + result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeImageData(pixels, scale) {
|
||||||
|
var height = pixels.length;
|
||||||
|
var width = pixels[0].length;
|
||||||
|
|
||||||
|
var data = gContext.createImageData(width * scale, height * scale);
|
||||||
|
for (var i = 0; i < width * scale; i++) {
|
||||||
|
for (var j = 0; j < height * scale; j++) {
|
||||||
|
var v = pixels[Math.floor(j / scale)].charAt(Math.floor(i / scale)) == '1';
|
||||||
|
data.data[4 * (j * width * scale + i) + 0] = v ? 255 : 0;
|
||||||
|
data.data[4 * (j * width * scale + i) + 1] = v ? 255 : 0;
|
||||||
|
data.data[4 * (j * width * scale + i) + 2] = v ? 255 : 0;
|
||||||
|
data.data[4 * (j * width * scale + i) + 3] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeGame() {
|
||||||
|
var game = new Game(gGame);
|
||||||
|
game.completed = function(result) {
|
||||||
|
if (result == 1) {
|
||||||
|
makeGame();
|
||||||
|
} else {
|
||||||
|
makeMainMenu();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
gMode = game;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeLevelEditor() {
|
||||||
|
var menu = new Menu({title: 'choose object for location ' + gGame.level.length, options: [
|
||||||
|
'nothing',
|
||||||
|
'enemy',
|
||||||
|
'points',
|
||||||
|
'finish',
|
||||||
|
]});
|
||||||
|
menu.completed = function(result) {
|
||||||
|
if (result == 0) {
|
||||||
|
gGame.level += '0';
|
||||||
|
makeLevelEditor();
|
||||||
|
} else if (result == 1) {
|
||||||
|
gGame.level += '1';
|
||||||
|
makeLevelEditor();
|
||||||
|
} else if (result == 2) {
|
||||||
|
gGame.level += '2';
|
||||||
|
makeLevelEditor();
|
||||||
|
} else if (result == 3) {
|
||||||
|
makeCreateMenu();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
gMode = menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeScoreEditor() {
|
||||||
|
var menu = new Menu({title: 'how many points?: ' + gGame.score, options: [
|
||||||
|
'more',
|
||||||
|
'done',
|
||||||
|
]});
|
||||||
|
menu.completed = function(result) {
|
||||||
|
if (result == 0) {
|
||||||
|
gGame.score *= 10;
|
||||||
|
if (gGame.score < 10000000000000000000) {
|
||||||
|
makeScoreEditor();
|
||||||
|
} else {
|
||||||
|
makeCreateMenu();
|
||||||
|
}
|
||||||
|
} else if (result == 1) {
|
||||||
|
makeCreateMenu();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
gMode = menu;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #444;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
display: block;
|
||||||
|
width: 640px;
|
||||||
|
}
|
||||||
|
#share {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas id="canvas" width="640" height="480"></canvas>
|
||||||
|
<div><a id="share" href="http://www.unprompted.com/ldjam/compo34/">share link, updated on test</a></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`,
|
||||||
|
width: 720,
|
||||||
|
height: 512,
|
||||||
|
});
|
@ -23,6 +23,7 @@ function parseUrl(url) {
|
|||||||
function parseResponse(data) {
|
function parseResponse(data) {
|
||||||
var firstLine;
|
var firstLine;
|
||||||
var headers = {};
|
var headers = {};
|
||||||
|
var headerArray = [];
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
var endLine = data.indexOf("\r\n");
|
var endLine = data.indexOf("\r\n");
|
||||||
@ -33,11 +34,14 @@ function parseResponse(data) {
|
|||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
var colon = line.indexOf(":");
|
var colon = line.indexOf(":");
|
||||||
headers[line.substring(0, colon).toLowerCase()] = line.substring(colon + 1).trim();
|
var key = line.substring(0, colon);
|
||||||
|
var value = line.substring(colon + 1).trim();
|
||||||
|
headers[key.toLowerCase()] = value;
|
||||||
|
headerArray.push([key, value]);
|
||||||
}
|
}
|
||||||
data = data.substring(endLine + 2);
|
data = data.substring(endLine + 2);
|
||||||
}
|
}
|
||||||
return {body: data, headers: headers};
|
return {body: data, headers: headers, headerArray: headerArray};
|
||||||
}
|
}
|
||||||
|
|
||||||
function get(url) {
|
function get(url) {
|
||||||
|
137
packages/cory/libiframe/libiframe.js
Normal file
137
packages/cory/libiframe/libiframe.js
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
//! {"category": "libraries"}
|
||||||
|
|
||||||
|
const kRpcSource = `
|
||||||
|
class RPC {
|
||||||
|
constructor() {
|
||||||
|
this._functions = [];
|
||||||
|
this.exports = null;
|
||||||
|
this.sendCallback = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export(functions) {
|
||||||
|
return this._send(["export", functions]);
|
||||||
|
}
|
||||||
|
|
||||||
|
_call(index) {
|
||||||
|
return this._send(["call", index, Array.prototype.slice.call(arguments, 1)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
_send(message) {
|
||||||
|
var serialized = this._serialize(message);
|
||||||
|
if (this.sendCallback) {
|
||||||
|
return this.sendCallback(serialized);
|
||||||
|
}
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
deliver(message) {
|
||||||
|
var deserialized = this._deserialize(message);
|
||||||
|
if (deserialized[0] == "export") {
|
||||||
|
this.exports = deserialized[1];
|
||||||
|
} else if (deserialized[0] == "call") {
|
||||||
|
var f = this._functions[deserialized[1]];
|
||||||
|
f.apply(null, deserialized[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_deserialize(data) {
|
||||||
|
var result = null;
|
||||||
|
if (data[0] == "array") {
|
||||||
|
result = new Array(data[1].length);
|
||||||
|
for (var i = 0; i < data[1].length; i++) {
|
||||||
|
result[i] = this._deserialize(data[1][i]);
|
||||||
|
}
|
||||||
|
} else if (data[0] == "object") {
|
||||||
|
result = {};
|
||||||
|
for (var i in data[1]) {
|
||||||
|
result[i] = this._deserialize(data[1][i]);
|
||||||
|
}
|
||||||
|
} else if (data[0] == "number" || data[0] == "boolean" || data[0] == "string") {
|
||||||
|
result = data[1];
|
||||||
|
} else if (data[0] == "function") {
|
||||||
|
result = this._call.bind(this, data[1]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
_serialize(data) {
|
||||||
|
var result = null;
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
result = ["array", data.map(x => this._serialize(x, this.functions))];
|
||||||
|
} else if (typeof data == "function") {
|
||||||
|
var index = this._functions.indexOf(data);
|
||||||
|
if (index == -1) {
|
||||||
|
index = this._functions.length;
|
||||||
|
this._functions.push(data);
|
||||||
|
}
|
||||||
|
result = ["function", index];
|
||||||
|
} else if (typeof data == "object") {
|
||||||
|
var fields = {};
|
||||||
|
while (data) {
|
||||||
|
var own = Object.getOwnPropertyNames(data);
|
||||||
|
for (var i = 0; i < own.length; i++) {
|
||||||
|
var name = own[i];
|
||||||
|
if (name != "constructor") {
|
||||||
|
var descriptor = Object.getOwnPropertyDescriptor(data, name);
|
||||||
|
if (descriptor&& typeof descriptor.value == "function") {
|
||||||
|
fields[name] = this._serialize(descriptor.value, this.functions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.__proto__ == Object.prototype) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
data = Object.getPrototypeOf(data);
|
||||||
|
}
|
||||||
|
result = ["object", fields];
|
||||||
|
} else {
|
||||||
|
result = [typeof data, data];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
exports.iframe = function(options) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
var iframeName = options.name || "iframe";
|
||||||
|
var RPC = eval(kRpcSource);
|
||||||
|
var rpc = new RPC();
|
||||||
|
rpc.sendCallback = terminal.postMessageToIframe.bind(null, iframeName);
|
||||||
|
|
||||||
|
core.register("onWindowMessage", function(message) {
|
||||||
|
if (message.message == "ready") {
|
||||||
|
resolve(rpc.exports);
|
||||||
|
} else {
|
||||||
|
rpc.deliver(message.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
terminal.split([{name: "terminal", type: "vertical"}]);
|
||||||
|
terminal.print({
|
||||||
|
iframe: `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<body style="background-color: #fff"></body>
|
||||||
|
<script language="javascript">
|
||||||
|
${kRpcSource}
|
||||||
|
window.addEventListener("load", function() {
|
||||||
|
parent.postMessage("ready", "*");
|
||||||
|
});
|
||||||
|
let rpc = new RPC();
|
||||||
|
window.addEventListener("message", function(event) {
|
||||||
|
rpc.deliver(event.data);
|
||||||
|
});
|
||||||
|
rpc.sendCallback = function(data) {
|
||||||
|
parent.postMessage(data, "*");
|
||||||
|
}
|
||||||
|
${options.source}
|
||||||
|
</script>
|
||||||
|
`,
|
||||||
|
name: iframeName,
|
||||||
|
style: "flex: 1 1; " + options.style,
|
||||||
|
width: null,
|
||||||
|
height: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -98,6 +98,8 @@ class IrcService {
|
|||||||
let person = prefix.split('!')[0];
|
let person = prefix.split('!')[0];
|
||||||
let conversation = parts[1];
|
let conversation = parts[1];
|
||||||
this._service.notifyPresenceChanged(conversation, person, "unavailable");
|
this._service.notifyPresenceChanged(conversation, person, "unavailable");
|
||||||
|
} else if (parts[0] == "PONG") {
|
||||||
|
// this._service.notifyMessageReceived("", {from: prefix, message: "RTT: " + (new Date().getTime() - parseInt(parts[2]))});
|
||||||
} else if (parts[0] == "QUIT") {
|
} else if (parts[0] == "QUIT") {
|
||||||
let person = prefix.split('!')[0];
|
let person = prefix.split('!')[0];
|
||||||
let conversations = this._service.getConversations();
|
let conversations = this._service.getConversations();
|
||||||
@ -164,9 +166,15 @@ class IrcService {
|
|||||||
self._service.notifyStateChanged("connected");
|
self._service.notifyStateChanged("connected");
|
||||||
self._send("USER " + options.user + " 0 * :" + options.realName);
|
self._send("USER " + options.user + " 0 * :" + options.realName);
|
||||||
self._send("NICK " + options.nick);
|
self._send("NICK " + options.nick);
|
||||||
|
self._sendPing();
|
||||||
}).catch(self._service.reportError);
|
}).catch(self._service.reportError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_sendPing() {
|
||||||
|
this._send("PING :" + new Date().getTime());
|
||||||
|
setTimeout(this._sendPing.bind(this), 3000);
|
||||||
|
}
|
||||||
|
|
||||||
sendMessage(target, text) {
|
sendMessage(target, text) {
|
||||||
if (!target) {
|
if (!target) {
|
||||||
this._socket.write(text + "\r\n");
|
this._socket.write(text + "\r\n");
|
||||||
|
@ -771,11 +771,11 @@ class XmppService {
|
|||||||
} else if (stanza.attributes.id == "session0") {
|
} else if (stanza.attributes.id == "session0") {
|
||||||
self._socket.write("<presence to='chadhappyfuntime@conference.jabber.troubleimpact.com/" + userName + "'><priority>1</priority><x xmlns='http://jabber.org/protocol/muc'/></presence>");
|
self._socket.write("<presence to='chadhappyfuntime@conference.jabber.troubleimpact.com/" + userName + "'><priority>1</priority><x xmlns='http://jabber.org/protocol/muc'/></presence>");
|
||||||
self._schedulePing();
|
self._schedulePing();
|
||||||
//self._conversations["chadhappyfuntime@conference.jabber.troubleimpact.com"] = {participants: [], history: []};
|
|
||||||
} else if (stanza.children.length && stanza.children[0].name == "ping") {
|
} else if (stanza.children.length && stanza.children[0].name == "ping") {
|
||||||
// Ping response.
|
// Ping response.
|
||||||
} else {
|
} else {
|
||||||
self._service.notifyMessageReceived(null, {unknown: stanza});
|
self._service.notifyMessageReceived(null, {unknown: stanza});
|
||||||
|
self._socket.write(`<iq id="${stanza.attributes.id}" type="error" from="${stanza.attributes.to}" to="${stanza.attributes.from}"><error type="cancel"><item-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error></iq>`);
|
||||||
}
|
}
|
||||||
} else if (stanza.name == "message") {
|
} else if (stanza.name == "message") {
|
||||||
let message = self._convertMessage(stanza);
|
let message = self._convertMessage(stanza);
|
||||||
|
128
packages/cory/meetup/meetup.js
Normal file
128
packages/cory/meetup/meetup.js
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
//! {"permissions": ["network"], "require": ["libencoding", "libhttp"]}
|
||||||
|
|
||||||
|
let libhttp = require("libhttp");
|
||||||
|
|
||||||
|
async function getEvents() {
|
||||||
|
let now = new Date();
|
||||||
|
let cacheVersion = "1";
|
||||||
|
let useCache = await database.get("cacheVersion") == cacheVersion;
|
||||||
|
let events = [];
|
||||||
|
let url = "https://api.meetup.com/The-Most-Informal-Running-Club-Ever-TMIRCE-Upstate/events?&sign=true&photo-host=public&status=past";
|
||||||
|
let done = false;
|
||||||
|
while (!done) {
|
||||||
|
let response;
|
||||||
|
if (useCache) {
|
||||||
|
response = await database.get(url);
|
||||||
|
if (response) {
|
||||||
|
response = JSON.parse(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!response) {
|
||||||
|
response = await libhttp.get(url);
|
||||||
|
await database.set(url, JSON.stringify(response));
|
||||||
|
}
|
||||||
|
let nextLink;
|
||||||
|
let theseEvents = JSON.parse(response.body);
|
||||||
|
for (let j in theseEvents) {
|
||||||
|
if (new Date(theseEvents[j].time) < now) {
|
||||||
|
events.push(theseEvents[j]);
|
||||||
|
} else {
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let j in response.headerArray) {
|
||||||
|
let link = response.headerArray[j];
|
||||||
|
if (link[0] == "Link" && link[1].split("; ")[1] == 'rel="next"') {
|
||||||
|
link = link[1].split("; ")[0];
|
||||||
|
link = link.substring(1, link.length - 1);
|
||||||
|
nextLink = link;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nextLink) {
|
||||||
|
url = nextLink;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await database.set("cacheVersion", cacheVersion);
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function trackWorkouts(events) {
|
||||||
|
for (let i in events) {
|
||||||
|
let event = events[i];
|
||||||
|
if (event && event.venue && event.venue.name
|
||||||
|
&& new Date(event.time).getDay() == 1) {
|
||||||
|
let match = /follow (?:my|our) (.*?) workout/.exec(event.description);
|
||||||
|
let workout;
|
||||||
|
if (match) {
|
||||||
|
workout = match[1];
|
||||||
|
} else if (/snowshoe/.exec(event.description)
|
||||||
|
|| /No run/.exec(event.description)) {
|
||||||
|
// nothing
|
||||||
|
} else {
|
||||||
|
terminal.print(event.description);
|
||||||
|
}
|
||||||
|
if (workout) {
|
||||||
|
terminal.print(workout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function locationByDay(day, events) {
|
||||||
|
for (let i in events) {
|
||||||
|
let event = events[i];
|
||||||
|
if (event && event.venue && event.venue.name
|
||||||
|
&& new Date(event.time).getDay() == day) {
|
||||||
|
terminal.print(event.venue.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function redisplay(action, actions, events) {
|
||||||
|
terminal.clear();
|
||||||
|
if (actions[action]) {
|
||||||
|
terminal.setHash(action);
|
||||||
|
terminal.print({style: "font-size: xx-large", value: action});
|
||||||
|
}
|
||||||
|
for (let i in actions) {
|
||||||
|
terminal.print({command: i});
|
||||||
|
}
|
||||||
|
if (actions[action]) {
|
||||||
|
await actions[action](events);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
terminal.print("loading events...");
|
||||||
|
let events = await getEvents();
|
||||||
|
terminal.print("loaded ", events.length.toString(), " events");
|
||||||
|
|
||||||
|
let actions = {
|
||||||
|
"Sunday Locations": locationByDay.bind(null, 0),
|
||||||
|
"Monday Track Workouts": trackWorkouts,
|
||||||
|
"Tuesday Locations": locationByDay.bind(null, 2),
|
||||||
|
"Wednesday Locations": locationByDay.bind(null, 3),
|
||||||
|
"Thursday Locations": locationByDay.bind(null, 4),
|
||||||
|
"Friday Locations": locationByDay.bind(null, 5),
|
||||||
|
"Saturday Locations": locationByDay.bind(null, 6),
|
||||||
|
}
|
||||||
|
|
||||||
|
core.register("hashChange", function(event) {
|
||||||
|
if (event.hash && event.hash[0] == '#') {
|
||||||
|
terminal.clear();
|
||||||
|
let hash = event.hash.substring(1);
|
||||||
|
redisplay(hash, actions, events);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let action = null;
|
||||||
|
while (true) {
|
||||||
|
redisplay(action, actions, events);
|
||||||
|
action = await terminal.readLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(terminal.print);
|
15
packages/cory/messages/messages.js
Normal file
15
packages/cory/messages/messages.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
if (imports.terminal) {
|
||||||
|
core.register("onMessage", function(sender, message) {
|
||||||
|
terminal.print(JSON.stringify(core.user.index), " ", message);
|
||||||
|
});
|
||||||
|
|
||||||
|
core.register("onInput", function(input) {
|
||||||
|
core.getService("global").then(function(service) {
|
||||||
|
service.postMessage(input);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
core.register("onMessage", function(sender, message) {
|
||||||
|
core.broadcast(message);
|
||||||
|
});
|
||||||
|
}
|
30
packages/cory/reminder/reminder.js
Normal file
30
packages/cory/reminder/reminder.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
//! {"category": "tests"}
|
||||||
|
|
||||||
|
|
||||||
|
terminal.print("Time to buy a new life jacket.");
|
||||||
|
/*
|
||||||
|
terminal.print("hi");
|
||||||
|
terminal.print("here is some svg:");
|
||||||
|
terminal.print({svg: {
|
||||||
|
attributes: {
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: "circle",
|
||||||
|
attributes: {
|
||||||
|
cx: 16,
|
||||||
|
cy: 16,
|
||||||
|
r: 15,
|
||||||
|
stroke: "green",
|
||||||
|
"stroke-width": "4",
|
||||||
|
fill: "yellow",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}});
|
||||||
|
|
||||||
|
terminal.print("end of svg");*/
|
33
packages/cory/splits/splits.js
Normal file
33
packages/cory/splits/splits.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
//! {"category": "tests"}
|
||||||
|
|
||||||
|
terminal.print("Hello, world!");
|
||||||
|
terminal.split([
|
||||||
|
{name: "h1", grow: 4},
|
||||||
|
{name: "h2", grow: 1},
|
||||||
|
{type: "vertical", children: [
|
||||||
|
{name: "h3.v1"},
|
||||||
|
{name: "h3.v2"},
|
||||||
|
{type: "horizontal", children: [
|
||||||
|
{name: "h3.v3.h1"},
|
||||||
|
{name: "h3.v3.h2"},
|
||||||
|
{name: "h3.v3.h3"},
|
||||||
|
]},
|
||||||
|
{name: "h3.v4"},
|
||||||
|
]},
|
||||||
|
{name: "h4"}
|
||||||
|
]);
|
||||||
|
|
||||||
|
function multiprint(text) {
|
||||||
|
var terminals = ["h1", "h2", "h3.v1", "h3.v2", "h3.v3.h1", "h3.v3.h2", "h3.v3.h3", "h3.v4", "h4"];
|
||||||
|
for (var i = 0; i < terminals.length; i++) {
|
||||||
|
terminal.select(terminals[i]);
|
||||||
|
terminal.print({style: "color: red", value: terminals[i]}, " ", text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
multiprint("hello");
|
||||||
|
core.register("onInput", function(input) {
|
||||||
|
multiprint(input);
|
||||||
|
});
|
208
packages/cory/tanks/tanks.js
Normal file
208
packages/cory/tanks/tanks.js
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
//! {"require": ["libiframe"]}
|
||||||
|
|
||||||
|
let isClient = imports.terminal != null;
|
||||||
|
if (isClient) {
|
||||||
|
require("libiframe").iframe({
|
||||||
|
source: `
|
||||||
|
document.documentElement.setAttribute("style", "width: 100%; height: 100%; margin: 0; padding: 0");
|
||||||
|
document.body.setAttribute("style", "display: flex; flex-direction: column; height: 100%; width: 100%; margin: 0; padding: 0");
|
||||||
|
document.body.style.color = '#fff';
|
||||||
|
document.body.style.backgroundColor = '#000';
|
||||||
|
|
||||||
|
var canvas = document.createElement("canvas");
|
||||||
|
canvas.setAttribute("style", "flex: 1 1; image-rendering: pixelated");
|
||||||
|
canvas.width = 128;
|
||||||
|
canvas.height = 128;
|
||||||
|
document.body.append(canvas);
|
||||||
|
|
||||||
|
var buttonBar = document.createElement("div");
|
||||||
|
buttonBar.setAttribute("style", "flex: 0 1 1in; display: flex; flex-direction: row");
|
||||||
|
var buttons = ["<", "^", "v", ">", "Fire!"];
|
||||||
|
for (var i = 0; i < buttons.length; i++) {
|
||||||
|
var button = document.createElement("input");
|
||||||
|
button.setAttribute("type", "button");
|
||||||
|
button.setAttribute("style", "flex: 1 1");
|
||||||
|
button.value = buttons[i];
|
||||||
|
button.onclick = buttonClicked.bind(null, buttons[i]);
|
||||||
|
buttonBar.append(button);
|
||||||
|
}
|
||||||
|
document.body.append(buttonBar);
|
||||||
|
|
||||||
|
var buttonCallback;
|
||||||
|
function buttonClicked(button) {
|
||||||
|
if (buttonCallback) {
|
||||||
|
buttonCallback(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var kExportNames = [
|
||||||
|
"beginPath",
|
||||||
|
"fill",
|
||||||
|
"save",
|
||||||
|
"restore",
|
||||||
|
"scale",
|
||||||
|
"rotate",
|
||||||
|
"translate",
|
||||||
|
"transform",
|
||||||
|
"arc",
|
||||||
|
"moveTo",
|
||||||
|
"lineTo",
|
||||||
|
"fillRect",
|
||||||
|
"fillText",
|
||||||
|
"stroke",
|
||||||
|
];
|
||||||
|
var context = canvas.getContext("2d");
|
||||||
|
var exports = {};
|
||||||
|
for (var i = 0; i < kExportNames.length; i++) {
|
||||||
|
exports[kExportNames[i]] = context[kExportNames[i]].bind(context);
|
||||||
|
}
|
||||||
|
exports.setButtonCallback = function(callback) { buttonCallback = callback; };
|
||||||
|
exports.setFillStyle = function(style) { context.fillStyle = style; };
|
||||||
|
exports.setStrokeStyle = function(style) { context.strokeStyle = style; };
|
||||||
|
exports.setLineWidth = function(width) { context.lineWidth = width; };
|
||||||
|
rpc.export(exports);
|
||||||
|
`,
|
||||||
|
style: `
|
||||||
|
border: 0;
|
||||||
|
`}).then(initialize).catch(terminal.print);
|
||||||
|
|
||||||
|
function initialize(iframe) {
|
||||||
|
iframe.setButtonCallback(click);
|
||||||
|
draw(iframe);
|
||||||
|
core.register("onMessage", onMessage.bind(null, iframe));
|
||||||
|
core.getService("server").then(function(server) {
|
||||||
|
server.postMessage({ready: true});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function click(button) {
|
||||||
|
(await core.getService("server")).postMessage({button: button});
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw(iframe) {
|
||||||
|
iframe.setFillStyle('#222');
|
||||||
|
iframe.fillRect(0, 0, 640, 480);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawTank(iframe, x, y, color, angle) {
|
||||||
|
iframe.save();
|
||||||
|
iframe.setLineWidth(1);
|
||||||
|
iframe.setFillStyle(color);
|
||||||
|
iframe.setStrokeStyle(color);
|
||||||
|
iframe.translate(x, y);
|
||||||
|
iframe.beginPath();
|
||||||
|
iframe.arc(0, 0, 4, Math.PI, 2 * Math.PI);
|
||||||
|
iframe.fill();
|
||||||
|
iframe.beginPath();
|
||||||
|
var radians = Math.PI + angle * Math.PI / 180.0;
|
||||||
|
iframe.moveTo(Math.cos(radians) * 2, Math.sin(radians) * 2 - 0.5);
|
||||||
|
iframe.lineTo(Math.cos(radians) * 10, Math.sin(radians) * 10 - 0.5);
|
||||||
|
iframe.stroke();
|
||||||
|
iframe.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMessage(iframe, sender, message) {
|
||||||
|
iframe.setFillStyle('#222');
|
||||||
|
iframe.fillRect(0, 0, 640, 480);
|
||||||
|
|
||||||
|
for (let i = 0; i < message.users.length; i++) {
|
||||||
|
var tank = message.users[i];
|
||||||
|
drawTank(iframe, tank.position.x, tank.position.y, tank.user == core.user.index ? '#f00' : '#0f0', tank.angle);
|
||||||
|
}
|
||||||
|
iframe.setFillStyle('#0f0');
|
||||||
|
for (let i = 0; i < message.terrain.length; i++) {
|
||||||
|
let h = message.terrain[i];
|
||||||
|
iframe.fillRect(i, 128 - h, i + 1, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let users = {};
|
||||||
|
let terrain = new Array(128);
|
||||||
|
let updating = false;
|
||||||
|
|
||||||
|
function startUpdating() {
|
||||||
|
if (!updating) {
|
||||||
|
setTimeout(100, update);
|
||||||
|
updating = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
updating = false;
|
||||||
|
for (let u of Object.values(users)) {
|
||||||
|
user.position.y++;
|
||||||
|
updating = true;
|
||||||
|
}
|
||||||
|
broadcastUsers();
|
||||||
|
if (updating) {
|
||||||
|
setTimeout(100, update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeTerrain() {
|
||||||
|
for (let i = 0; i < terrain.length; i++) {
|
||||||
|
terrain[i] = 32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function broadcastUsers() {
|
||||||
|
core.broadcast({
|
||||||
|
users: Object.keys(users).map(x => ({user: x, position: users[x].position, angle: users[x].angle})),
|
||||||
|
terrain: terrain,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function placeNewUser() {
|
||||||
|
let positions = Object.keys(users).map(x => users[x].position.x);
|
||||||
|
var best = 128 / 2;
|
||||||
|
var bestDistance = -1;
|
||||||
|
if (positions.length) {
|
||||||
|
for (var i = 10; i < 118; i++) {
|
||||||
|
var thisDistance = Math.min.apply(null, positions.map(x => Math.abs(i - x)));
|
||||||
|
if (thisDistance > bestDistance) {
|
||||||
|
bestDistance = thisDistance;
|
||||||
|
best = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {x: best, y: 10, d: bestDistance};
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeTerrain();
|
||||||
|
|
||||||
|
core.register("onMessage", function(sender, message) {
|
||||||
|
if (message.ready) {
|
||||||
|
users[sender.index] = {
|
||||||
|
user: sender,
|
||||||
|
angle: 0,
|
||||||
|
velocity: 100,
|
||||||
|
position: placeNewUser(),
|
||||||
|
};
|
||||||
|
broadcastUsers();
|
||||||
|
startUpdating();
|
||||||
|
} else if (message.button) {
|
||||||
|
switch (message.button) {
|
||||||
|
case "<":
|
||||||
|
users[sender.index].angle = Math.round(Math.max(users[sender.index].angle - 5.0, 0.0));
|
||||||
|
break;
|
||||||
|
case ">":
|
||||||
|
users[sender.index].angle = Math.round(Math.min(users[sender.index].angle + 5.0, 180.0));
|
||||||
|
break;
|
||||||
|
case "^":
|
||||||
|
users[sender.index].velocity++;
|
||||||
|
break;
|
||||||
|
case "v":
|
||||||
|
users[sender.index].velocity--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
broadcastUsers();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
core.register("onSessionEnd", function(user) {
|
||||||
|
delete users[user.index];
|
||||||
|
broadcastUsers();
|
||||||
|
});
|
||||||
|
}
|
13
packages/cory/test/test.js
Normal file
13
packages/cory/test/test.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
async function main() {
|
||||||
|
terminal.print("Hello, world!");
|
||||||
|
|
||||||
|
terminal.print("Please enter your name:");
|
||||||
|
var name = await terminal.readLine();
|
||||||
|
terminal.print("Hello, " + name + ".");
|
||||||
|
|
||||||
|
for (var i = 0; i < 5; i++) {
|
||||||
|
terminal.print(i.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(terminal.print);
|
187
packages/cory/tmirce/tmirce.js
Normal file
187
packages/cory/tmirce/tmirce.js
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
//! {"permissions": ["network"], "require": ["libencoding", "libhttp"]}
|
||||||
|
|
||||||
|
let libhttp = require("libhttp");
|
||||||
|
|
||||||
|
async function getEvents() {
|
||||||
|
let now = new Date();
|
||||||
|
let cacheVersion = "1-" + now.toDateString();
|
||||||
|
if (await database.get("cacheVersion") != cacheVersion) {
|
||||||
|
terminal.print("Refreshing database...");
|
||||||
|
let keys = await database.getAll();
|
||||||
|
await Promise.all(keys.map(x => database.remove(x)));
|
||||||
|
await database.set("cacheVersion", cacheVersion);
|
||||||
|
}
|
||||||
|
let events = [];
|
||||||
|
let url = "https://api.meetup.com/The-Most-Informal-Running-Club-Ever-TMIRCE-Upstate/events?&sign=true&photo-host=public&status=past";
|
||||||
|
let done = false;
|
||||||
|
while (!done) {
|
||||||
|
let response;
|
||||||
|
response = await database.get(url);
|
||||||
|
if (response) {
|
||||||
|
response = JSON.parse(response);
|
||||||
|
}
|
||||||
|
if (!response) {
|
||||||
|
response = await libhttp.get(url);
|
||||||
|
await database.set(url, JSON.stringify(response));
|
||||||
|
}
|
||||||
|
let nextLink;
|
||||||
|
let theseEvents = JSON.parse(response.body);
|
||||||
|
for (let j in theseEvents) {
|
||||||
|
if (new Date(theseEvents[j].time) < now) {
|
||||||
|
events.push(theseEvents[j]);
|
||||||
|
} else {
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let j in response.headerArray) {
|
||||||
|
let link = response.headerArray[j];
|
||||||
|
if (link[0] == "Link" && link[1].split("; ")[1] == 'rel="next"') {
|
||||||
|
link = link[1].split("; ")[0];
|
||||||
|
link = link.substring(1, link.length - 1);
|
||||||
|
nextLink = link;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nextLink) {
|
||||||
|
url = nextLink;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function trackWorkouts(events) {
|
||||||
|
let results = [];
|
||||||
|
for (let i in events) {
|
||||||
|
let event = events[i];
|
||||||
|
if (event && event.venue && event.venue.name
|
||||||
|
&& new Date(event.time).getDay() == 1) {
|
||||||
|
let match = /follow (?:my|our) (.*?) workout/.exec(event.description);
|
||||||
|
let workout;
|
||||||
|
if (match) {
|
||||||
|
workout = match[1];
|
||||||
|
} else if (/snowshoe/.exec(event.description)
|
||||||
|
|| /No run/.exec(event.description)) {
|
||||||
|
// nothing
|
||||||
|
} else {
|
||||||
|
results.push(event.description);
|
||||||
|
}
|
||||||
|
if (workout) {
|
||||||
|
results.push(workout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function locationByDay(day, events) {
|
||||||
|
let results = [];
|
||||||
|
for (let i in events) {
|
||||||
|
let event = events[i];
|
||||||
|
if (event && event.venue && event.venue.name
|
||||||
|
&& new Date(event.time).getDay() == day) {
|
||||||
|
results.push(event.venue.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function redisplay(action, filter, actions, filters, events) {
|
||||||
|
//terminal.cork();
|
||||||
|
terminal.clear();
|
||||||
|
if (actions[action]) {
|
||||||
|
terminal.setHash(action);
|
||||||
|
terminal.print({style: "font-size: xx-large", value: action});
|
||||||
|
}
|
||||||
|
terminal.print("What:");
|
||||||
|
for (let i in actions) {
|
||||||
|
terminal.print(action == i ? "-> " : " * ", {command: i});
|
||||||
|
}
|
||||||
|
terminal.print("How:");
|
||||||
|
for (let i in filters) {
|
||||||
|
terminal.print(filter == i ? "-> " : " * ", {command: i});
|
||||||
|
}
|
||||||
|
terminal.print("--");
|
||||||
|
if (actions[action]) {
|
||||||
|
filters[filter](await actions[action](events));
|
||||||
|
}
|
||||||
|
//terminal.uncork();
|
||||||
|
}
|
||||||
|
|
||||||
|
function rawFilter(items) {
|
||||||
|
for (let i in items) {
|
||||||
|
terminal.print(items[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function histogramFilter(items) {
|
||||||
|
let table = {};
|
||||||
|
for (let i in items) {
|
||||||
|
if (!table[items[i]]) {
|
||||||
|
table[items[i]] = 0;
|
||||||
|
}
|
||||||
|
table[items[i]] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let results = [];
|
||||||
|
for (let i in table) {
|
||||||
|
results.push([table[i], i]);
|
||||||
|
}
|
||||||
|
results.sort((x, y) => y[0] - x[0]);
|
||||||
|
|
||||||
|
for (let i in results) {
|
||||||
|
terminal.print(results[i][0].toString(), " ", results[i][1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
terminal.print("loading events...");
|
||||||
|
let events = await getEvents();
|
||||||
|
terminal.print("loaded ", events.length.toString(), " events");
|
||||||
|
|
||||||
|
let actions = {
|
||||||
|
"Sunday Locations": locationByDay.bind(null, 0),
|
||||||
|
"Monday Track Workouts": trackWorkouts,
|
||||||
|
"Tuesday Locations": locationByDay.bind(null, 2),
|
||||||
|
"Wednesday Locations": locationByDay.bind(null, 3),
|
||||||
|
"Thursday Locations": locationByDay.bind(null, 4),
|
||||||
|
"Friday Locations": locationByDay.bind(null, 5),
|
||||||
|
"Saturday Locations": locationByDay.bind(null, 6),
|
||||||
|
}
|
||||||
|
|
||||||
|
let filters = {
|
||||||
|
"Chronological": rawFilter,
|
||||||
|
"Histogram": histogramFilter,
|
||||||
|
};
|
||||||
|
|
||||||
|
let action = null;
|
||||||
|
let filter = "Chronological";
|
||||||
|
|
||||||
|
core.register("hashChange", function(event) {
|
||||||
|
if (event.hash && event.hash[0] == '#') {
|
||||||
|
terminal.clear();
|
||||||
|
let hash = event.hash.substring(1);
|
||||||
|
if (filters[hash]) {
|
||||||
|
filter = hash;
|
||||||
|
} else if (actions[hash]) {
|
||||||
|
action = hash;
|
||||||
|
}
|
||||||
|
redisplay(action, filter, actions, filters, events);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
redisplay(action, filter, actions, filters, events);
|
||||||
|
let command = await terminal.readLine();
|
||||||
|
if (filters[command]) {
|
||||||
|
filter = command;
|
||||||
|
} else if (actions[command]) {
|
||||||
|
action = command;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(terminal.print);
|
143
packages/cory/whatnext/whatnext.js
Normal file
143
packages/cory/whatnext/whatnext.js
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
//! {"require": ["ui"]}
|
||||||
|
|
||||||
|
terminal.setEcho(false);
|
||||||
|
terminal.setTitle("What Next?");
|
||||||
|
|
||||||
|
let gEditEvent = null;
|
||||||
|
|
||||||
|
function back() {
|
||||||
|
terminal.split([{name: "terminal"}]);
|
||||||
|
if (gEditEvent) {
|
||||||
|
gEditEvent.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
core.register("onWindowMessage", function(event) {
|
||||||
|
if (event.message.ready) {
|
||||||
|
terminal.postMessageToIframe("iframe", {title: gEditEvent.name, contents: gEditEvent.value});
|
||||||
|
} else if (event.message.index) {
|
||||||
|
back();
|
||||||
|
} else {
|
||||||
|
gEditEvent.save(event.message.title, event.message.contents).then(back);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function editPage(event) {
|
||||||
|
gEditEvent = event;
|
||||||
|
terminal.split([{name: "terminal", type: "vertical"}]);
|
||||||
|
terminal.clear();
|
||||||
|
terminal.print({iframe: `<html>
|
||||||
|
<head>
|
||||||
|
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.13.2/codemirror.min.js"></script>
|
||||||
|
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.13.2/codemirror.min.css"></link>
|
||||||
|
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.13.2/theme/base16-dark.min.css"></link>
|
||||||
|
<style>
|
||||||
|
html {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
#menu {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
#container {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
width: 100%;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
.CodeMirror {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.CodeMirror-scroll {
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-tab {
|
||||||
|
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAYAAAAkuj5RAAAAAXNSR0IArs4c6QAAAGFJREFUSMft1LsRQFAQheHPowAKoACx3IgEKtaEHujDjORSgWTH/ZOdnZOcM/sgk/kFFWY0qV8foQwS4MKBCS3qR6ixBJvElOobYAtivseIE120FaowJPN75GMu8j/LfMwNjh4HUpwg4LUAAAAASUVORK5CYII=);
|
||||||
|
background-position: right;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
-webkit-filter: invert(100%);
|
||||||
|
filter: invert(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-trailingspace {
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAYAAAB/qH1jAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QUXCToH00Y1UgAAACFJREFUCNdjPMDBUc/AwNDAAAFMTAwMDA0OP34wQgX/AQBYgwYEx4f9lQAAAABJRU5ErkJggg==);
|
||||||
|
background-position: bottom left;
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
#edit { background-color: white }
|
||||||
|
#preview { background-color: white }
|
||||||
|
#edit, #preview {
|
||||||
|
display: flex;
|
||||||
|
overflow: auto;
|
||||||
|
flex: 0 0 50%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
var gEditor;
|
||||||
|
function index() {
|
||||||
|
parent.postMessage({index: true}, "*");
|
||||||
|
}
|
||||||
|
function submit() {
|
||||||
|
var contents = gEditor.getValue();
|
||||||
|
var lines = contents.split("\\n");
|
||||||
|
var title = lines[0].trim();
|
||||||
|
parent.postMessage({
|
||||||
|
title: title,
|
||||||
|
contents: contents,
|
||||||
|
}, "*");
|
||||||
|
}
|
||||||
|
function textChanged() {
|
||||||
|
var preview = document.getElementById("preview");
|
||||||
|
preview.innerText = gEditor.getValue();
|
||||||
|
}
|
||||||
|
window.addEventListener("message", function(message) {
|
||||||
|
gEditor.setValue(message.data.contents);
|
||||||
|
textChanged();
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
window.addEventListener("load", function() {
|
||||||
|
gEditor = CodeMirror.fromTextArea(document.getElementById("contents"), {
|
||||||
|
theme: 'base16-dark',
|
||||||
|
lineNumbers: true,
|
||||||
|
tabSize: 4,
|
||||||
|
intentUnit: 4,
|
||||||
|
indentWithTabs: true,
|
||||||
|
showTrailingSpace: true,
|
||||||
|
});
|
||||||
|
gEditor.on("change", textChanged);
|
||||||
|
});
|
||||||
|
|
||||||
|
parent.postMessage({ready: true}, "*");
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="menu">
|
||||||
|
<input type="button" value="Back" onclick="index()">
|
||||||
|
` + (core.user.credentials.permissions && core.user.credentials.permissions.authenticated ? `
|
||||||
|
<input type="button" value="Save" onclick="submit()">
|
||||||
|
` : "") + `
|
||||||
|
</div>
|
||||||
|
<div id="container">
|
||||||
|
<div id="edit"><textarea id="contents" oninput="textChanged()"></textarea></div>
|
||||||
|
<div id="preview"></div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>`, name: "iframe", style: "flex: 1 1 auto; border: 0; width: 100%"});
|
||||||
|
}
|
||||||
|
|
||||||
|
require("ui").fileList({
|
||||||
|
title: "What Next?",
|
||||||
|
edit: editPage,
|
||||||
|
});
|
@ -32,6 +32,7 @@ function editPage(event) {
|
|||||||
<head>
|
<head>
|
||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.13.2/codemirror.min.js"></script>
|
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.13.2/codemirror.min.js"></script>
|
||||||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.13.2/codemirror.min.css"></link>
|
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.13.2/codemirror.min.css"></link>
|
||||||
|
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.13.2/theme/base16-dark.min.css"></link>
|
||||||
<style>
|
<style>
|
||||||
html {
|
html {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -61,6 +62,20 @@ function editPage(event) {
|
|||||||
}
|
}
|
||||||
.CodeMirror-scroll {
|
.CodeMirror-scroll {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cm-tab {
|
||||||
|
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAYAAAAkuj5RAAAAAXNSR0IArs4c6QAAAGFJREFUSMft1LsRQFAQheHPowAKoACx3IgEKtaEHujDjORSgWTH/ZOdnZOcM/sgk/kFFWY0qV8foQwS4MKBCS3qR6ixBJvElOobYAtivseIE120FaowJPN75GMu8j/LfMwNjh4HUpwg4LUAAAAASUVORK5CYII=);
|
||||||
|
background-position: right;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
-webkit-filter: invert(100%);
|
||||||
|
filter: invert(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-trailingspace {
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAYAAAB/qH1jAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QUXCToH00Y1UgAAACFJREFUCNdjPMDBUc/AwNDAAAFMTAwMDA0OP34wQgX/AQBYgwYEx4f9lQAAAABJRU5ErkJggg==);
|
||||||
|
background-position: bottom left;
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
#edit { background-color: white }
|
#edit { background-color: white }
|
||||||
#preview { background-color: white }
|
#preview { background-color: white }
|
||||||
#edit, #preview {
|
#edit, #preview {
|
||||||
@ -98,7 +113,12 @@ function editPage(event) {
|
|||||||
|
|
||||||
window.addEventListener("load", function() {
|
window.addEventListener("load", function() {
|
||||||
gEditor = CodeMirror.fromTextArea(document.getElementById("contents"), {
|
gEditor = CodeMirror.fromTextArea(document.getElementById("contents"), {
|
||||||
lineNumbers: true
|
theme: 'base16-dark',
|
||||||
|
lineNumbers: true,
|
||||||
|
tabSize: 4,
|
||||||
|
intentUnit: 4,
|
||||||
|
indentWithTabs: true,
|
||||||
|
showTrailingSpace: true,
|
||||||
});
|
});
|
||||||
gEditor.on("change", textChanged);
|
gEditor.on("change", textChanged);
|
||||||
});
|
});
|
||||||
|
176
packages/cory/xmas2016/xmas2016.js
Normal file
176
packages/cory/xmas2016/xmas2016.js
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const kWidth = 15;
|
||||||
|
const kHeight = 10;
|
||||||
|
|
||||||
|
class Client {
|
||||||
|
async start() {
|
||||||
|
let self = this;
|
||||||
|
terminal.setTitle("Xmas 2016");
|
||||||
|
terminal.setSendKeyEvents(true);
|
||||||
|
terminal.split([{name: "terminal", style: "text-align: center"}]);
|
||||||
|
self.send({action: "ready"});
|
||||||
|
|
||||||
|
core.register("key", function(event) {
|
||||||
|
if (event.type == "keydown") {
|
||||||
|
switch (event.keyCode) {
|
||||||
|
case 37:
|
||||||
|
self.requestMove(-1, 0);
|
||||||
|
break;
|
||||||
|
case 38:
|
||||||
|
self.requestMove(0, -1);
|
||||||
|
break;
|
||||||
|
case 39:
|
||||||
|
self.requestMove(1, 0);
|
||||||
|
break;
|
||||||
|
case 40:
|
||||||
|
self.requestMove(0, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
core.register("onMessage", function(sender, message) {
|
||||||
|
if (message.action == "display") {
|
||||||
|
terminal.cork();
|
||||||
|
terminal.clear();
|
||||||
|
terminal.print(message.board);
|
||||||
|
terminal.uncork();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async send(message) {
|
||||||
|
(await core.getService("server")).postMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
async requestMove(dx, dy) {
|
||||||
|
this.send({action: "move", delta: [dx, dy]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Server {
|
||||||
|
constructor() {
|
||||||
|
this.players = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
async start() {
|
||||||
|
let self = this;
|
||||||
|
core.register("onMessage", function(sender, message) {
|
||||||
|
if (!self.players[sender.index]) {
|
||||||
|
self.players[sender.index] = [Math.floor(Math.random() * 8) + 1, 8];
|
||||||
|
}
|
||||||
|
switch (message.action) {
|
||||||
|
case "move":
|
||||||
|
self.move(sender.index, message.delta);
|
||||||
|
self.redisplay();
|
||||||
|
break;
|
||||||
|
case "ready":
|
||||||
|
self.redisplay();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
core.register("onSessionEnd", function(session) {
|
||||||
|
delete self.players[session.index];
|
||||||
|
self.redisplay();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
occupied(position) {
|
||||||
|
if (position[0] < 1 || position[1] < 1 || position[0] >= kWidth - 1 || position[1] >= kHeight - 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.values(this.players).some(x => position[0] == x[0] && position[1] == x[1])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
move(index, delta) {
|
||||||
|
let newPosition = [
|
||||||
|
this.players[index][0] + delta[0],
|
||||||
|
this.players[index][1] + delta[1],
|
||||||
|
];
|
||||||
|
let pushPosition = [
|
||||||
|
this.players[index][0] + 2 * delta[0],
|
||||||
|
this.players[index][1] + 2 * delta[1],
|
||||||
|
];
|
||||||
|
if (!this.occupied(newPosition)) {
|
||||||
|
this.players[index] = newPosition;
|
||||||
|
} else if (this.occupied(newPosition) && !this.occupied(pushPosition)) {
|
||||||
|
for (var i in this.players) {
|
||||||
|
if (this.players[i][0] == newPosition[0] && this.players[i][1] == newPosition[1]) {
|
||||||
|
this.players[i] = pushPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.players[index] = newPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redisplay() {
|
||||||
|
core.broadcast({action: "display", board: this.getBoard()});
|
||||||
|
}
|
||||||
|
|
||||||
|
getBoard() {
|
||||||
|
let board = [
|
||||||
|
"┌─────────────┐",
|
||||||
|
"│ . │",
|
||||||
|
"│ .#. │",
|
||||||
|
"│ .%##. │",
|
||||||
|
"│ .##%##. │",
|
||||||
|
"│ .%###%##. │",
|
||||||
|
"│ .##%###%##. │",
|
||||||
|
"│.#%##%###%##.│",
|
||||||
|
"│ | │",
|
||||||
|
"└─────────────┘",
|
||||||
|
];
|
||||||
|
let merry = false;
|
||||||
|
let colors = board.map(x => x.split("").map(y => '#fff'));
|
||||||
|
for (let i in this.players) {
|
||||||
|
let player = this.players[i];
|
||||||
|
merry = merry || (player[0] == 7 && player[1] == 1);
|
||||||
|
let character = player[0] == 7 && player[1] == 1 ? '*' : '@';
|
||||||
|
board[player[1]] = board[player[1]].slice(0, player[0]) + character + board[player[1]].slice(player[0] + 1);
|
||||||
|
}
|
||||||
|
if (merry) {
|
||||||
|
for (let i = 0; i < colors.length; i++) {
|
||||||
|
for (let j = 0; j < colors[i].length; j++) {
|
||||||
|
switch (board[i].charAt(j)) {
|
||||||
|
case '#':
|
||||||
|
case '.':
|
||||||
|
colors[i][j] = '#080';
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
colors[i][j] = '#f00';
|
||||||
|
break;
|
||||||
|
case '|':
|
||||||
|
colors[i][j] = '#880';
|
||||||
|
break;
|
||||||
|
case '*':
|
||||||
|
colors[i][j] = '#ff0';
|
||||||
|
break;
|
||||||
|
case '@':
|
||||||
|
{
|
||||||
|
const palette = ['#f88', '#8f8', '#88f', '#fff'];
|
||||||
|
colors[i][j] = palette[(i * colors.length + j) % palette.length];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let result = board.map((r, row) => r.split("").map((v, column) => ({style: "color: " + colors[row][column], value: v})).concat(["\n"]));
|
||||||
|
if (merry) {
|
||||||
|
result.push("Merry Christmas!");
|
||||||
|
} else {
|
||||||
|
result.push("use ←↑→↓");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imports.terminal) {
|
||||||
|
new Client().start().catch(terminal.print);
|
||||||
|
} else {
|
||||||
|
new Server().start().catch(print);
|
||||||
|
}
|
63
packages/cory/youtube/youtube.js
Normal file
63
packages/cory/youtube/youtube.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
terminal.print({iframe: `
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<!-- 1. The <iframe> (and video player) will replace this <div> tag. -->
|
||||||
|
<div id="player"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 2. This code loads the IFrame Player API code asynchronously.
|
||||||
|
var tag = document.createElement('script');
|
||||||
|
|
||||||
|
tag.src = "https://www.youtube.com/iframe_api";
|
||||||
|
var firstScriptTag = document.getElementsByTagName('script')[0];
|
||||||
|
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
|
||||||
|
|
||||||
|
// 3. This function creates an <iframe> (and YouTube player)
|
||||||
|
// after the API code downloads.
|
||||||
|
var player;
|
||||||
|
function onYouTubeIframeAPIReady() {
|
||||||
|
player = new YT.Player('player', {
|
||||||
|
height: '390',
|
||||||
|
width: '640',
|
||||||
|
videoId: 'M7lc1UVf-VE',
|
||||||
|
events: {
|
||||||
|
'onReady': onPlayerReady,
|
||||||
|
'onStateChange': onPlayerStateChange
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. The API will call this function when the video player is ready.
|
||||||
|
function onPlayerReady(event) {
|
||||||
|
event.target.playVideo();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. The API calls this function when the player's state changes.
|
||||||
|
// The function indicates that when playing a video (state=1),
|
||||||
|
// the player should play for six seconds and then stop.
|
||||||
|
var done = false;
|
||||||
|
function onPlayerStateChange(event) {
|
||||||
|
parent.postMessage({
|
||||||
|
state: event.data,
|
||||||
|
url: event.target.getVideoUrl(),
|
||||||
|
}, "*");
|
||||||
|
if (event.data == YT.PlayerState.PLAYING && !done) {
|
||||||
|
setTimeout(stopVideo, 6000);
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function stopVideo() {
|
||||||
|
player.stopVideo();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
`, width: 640, height: 390, style: "padding: 0; margin: 0; border: 0"});
|
||||||
|
|
||||||
|
terminal.print("hi");
|
||||||
|
core.register("onWindowMessage", function(event) {
|
||||||
|
terminal.print("message", JSON.stringify(event.message));
|
||||||
|
});
|
14
src/Tls.cpp
14
src/Tls.cpp
@ -67,11 +67,23 @@ TlsSession* TlsContext_openssl::createSession() {
|
|||||||
return new TlsSession_openssl(this);
|
return new TlsSession_openssl(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
TlsContext_openssl::TlsContext_openssl() {
|
TlsContext_openssl::TlsContext_openssl() {
|
||||||
SSL_library_init();
|
SSL_library_init();
|
||||||
SSL_load_error_strings();
|
SSL_load_error_strings();
|
||||||
|
|
||||||
_context = SSL_CTX_new(SSLv23_method());
|
const SSL_METHOD* method = SSLv23_method();
|
||||||
|
if (!method)
|
||||||
|
{
|
||||||
|
std::cerr << "SSLv23_method returned NULL\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
_context = SSL_CTX_new(method);
|
||||||
|
if (!_context)
|
||||||
|
{
|
||||||
|
std::cerr << "SSL_CTX_new returned NULL\n";
|
||||||
|
}
|
||||||
SSL_CTX_set_default_verify_paths(_context);
|
SSL_CTX_set_default_verify_paths(_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,3 +22,4 @@ certbot renew \
|
|||||||
--work-dir data/global/letsencrypt/work \
|
--work-dir data/global/letsencrypt/work \
|
||||||
--cert-path data/global/httpd/certificate.pem \
|
--cert-path data/global/httpd/certificate.pem \
|
||||||
--key-path data/global/httpd/privatekey.pem \
|
--key-path data/global/httpd/privatekey.pem \
|
||||||
|
$*
|
||||||
|
Loading…
Reference in New Issue
Block a user