forked from cory/tildefriends
		
	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:
		@@ -16,6 +16,8 @@ Tilde Friends is [routinely](https://www.unprompted.com/projects/build/tildefrie
 | 
			
		||||
  scons uv=path/to/libuv v8=path/to/v8
 | 
			
		||||
  ```
 | 
			
		||||
 | 
			
		||||
Note for Raspberry Pi: http://www.mccarroll.net/blog/v8_pi2/index.html
 | 
			
		||||
 | 
			
		||||
## Running
 | 
			
		||||
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.split([
 | 
			
		||||
	{type: "horizontal", children: [
 | 
			
		||||
		{name: "windows", basis: "2in", grow: "0", shrink: "0"},
 | 
			
		||||
		{name: "windows", basis: "1in", grow: "0", shrink: "0"},
 | 
			
		||||
		{name: "terminal", grow: "1"},
 | 
			
		||||
		{name: "users", basis: "2in", grow: "0", shrink: "0"},
 | 
			
		||||
		{name: "users", basis: "1in", grow: "0", shrink: "0"},
 | 
			
		||||
	]},
 | 
			
		||||
]);
 | 
			
		||||
updateTitle();
 | 
			
		||||
@@ -444,7 +444,7 @@ function printMessage(message) {
 | 
			
		||||
			{class: "base3", value: from},
 | 
			
		||||
			" ",
 | 
			
		||||
			formatMessage(message.message));
 | 
			
		||||
	} else {
 | 
			
		||||
	} else if (message.message) {
 | 
			
		||||
		terminal.print(
 | 
			
		||||
			{class: "base0", value: niceTime(lastTimestamp, now)},
 | 
			
		||||
			" ",
 | 
			
		||||
@@ -453,6 +453,11 @@ function printMessage(message) {
 | 
			
		||||
			{class: "base00", value: ">"},
 | 
			
		||||
			" ",
 | 
			
		||||
			formatMessage(message.message));
 | 
			
		||||
	} else {
 | 
			
		||||
		terminal.print(
 | 
			
		||||
			{class: "base0", value: niceTime(lastTimestamp, now)},
 | 
			
		||||
			" ",
 | 
			
		||||
			{class: "base3", value: JSON.stringify(message)});
 | 
			
		||||
	}
 | 
			
		||||
	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) {
 | 
			
		||||
	var firstLine;
 | 
			
		||||
	var headers = {};
 | 
			
		||||
	var headerArray = [];
 | 
			
		||||
 | 
			
		||||
	while (true) {
 | 
			
		||||
		var endLine = data.indexOf("\r\n");
 | 
			
		||||
@@ -33,11 +34,14 @@ function parseResponse(data) {
 | 
			
		||||
			break;
 | 
			
		||||
		} else {
 | 
			
		||||
			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);
 | 
			
		||||
	}
 | 
			
		||||
	return {body: data, headers: headers};
 | 
			
		||||
	return {body: data, headers: headers, headerArray: headerArray};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 conversation = parts[1];
 | 
			
		||||
				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") {
 | 
			
		||||
				let person = prefix.split('!')[0];
 | 
			
		||||
				let conversations = this._service.getConversations();
 | 
			
		||||
@@ -164,9 +166,15 @@ class IrcService {
 | 
			
		||||
			self._service.notifyStateChanged("connected");
 | 
			
		||||
			self._send("USER " + options.user + " 0 * :" + options.realName);
 | 
			
		||||
			self._send("NICK " + options.nick);
 | 
			
		||||
			self._sendPing();
 | 
			
		||||
		}).catch(self._service.reportError);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_sendPing() {
 | 
			
		||||
		this._send("PING :" + new Date().getTime());
 | 
			
		||||
		setTimeout(this._sendPing.bind(this), 3000);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sendMessage(target, text) {
 | 
			
		||||
		if (!target) {
 | 
			
		||||
			this._socket.write(text + "\r\n");
 | 
			
		||||
 
 | 
			
		||||
@@ -771,11 +771,11 @@ class XmppService {
 | 
			
		||||
							} 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._schedulePing();
 | 
			
		||||
								//self._conversations["chadhappyfuntime@conference.jabber.troubleimpact.com"] = {participants: [], history: []};
 | 
			
		||||
							} else if (stanza.children.length && stanza.children[0].name == "ping") {
 | 
			
		||||
								// Ping response.
 | 
			
		||||
							} else {
 | 
			
		||||
								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") {
 | 
			
		||||
							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>
 | 
			
		||||
			<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%;
 | 
			
		||||
@@ -61,6 +62,20 @@ function editPage(event) {
 | 
			
		||||
				}
 | 
			
		||||
				.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 {
 | 
			
		||||
@@ -98,7 +113,12 @@ function editPage(event) {
 | 
			
		||||
 | 
			
		||||
				window.addEventListener("load", function() {
 | 
			
		||||
					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);
 | 
			
		||||
				});
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
TlsContext_openssl::TlsContext_openssl() {
 | 
			
		||||
	SSL_library_init();
 | 
			
		||||
	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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,3 +22,4 @@ certbot renew \
 | 
			
		||||
	--work-dir data/global/letsencrypt/work \
 | 
			
		||||
	--cert-path data/global/httpd/certificate.pem \
 | 
			
		||||
	--key-path data/global/httpd/privatekey.pem \
 | 
			
		||||
	$*
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user