--- /dev/null
+<html>
+ <head>
+ <title>WEBCART1000</title>
+ <meta charset="utf-8" />
+ <script type="text/javascript" src="webcart1000.js"></script>
+ <style>
+ body {
+ font-family: sans-serif;
+ margin: 1em 15%;
+ }
+
+ h1 { text-align: center; }
+ h2 { margin-top: 2em; }
+ </style>
+
+ </head>
+ <body>
+ <h1>WEBCART1000</h1>
+ <p>
+ WEBCART1000 lets you make and play games that share the same
+ save files across several websites. It's inspired
+ by <a href="http://sharecart1000.com/">SHARECART10000</a>, which
+ did the same thing for non-web games.
+ </p>
+ <h2>Some Games Using It</h2>
+ <p>
+ coming soon.
+ </p>
+ <h2>How do I use it?</h2>
+ <p>
+ If you're making a web-based game, just
+ download <a href="webcart1000.js">webcart1000.js</a> and include
+ it via a <script> tag in your own game. It's
+ self-contained and sets up all its own listeners.
+ </p>
+ <p>
+ Once you're including it, you can access the save data with
+ <code>webcart1000.data.Whatever</code>
+ - <code>MapY</code>, <code>Switch2</code>, or any of
+ the <a href="http://sharecart1000.com/img/SHARECART1000guide.png">SHARECART1000</a>
+ keys, with the same restrictions. (It may take a few seconds
+ after the page is done loading to load the save data.)
+ </p>
+ <p>
+ To save and synchronize the data,
+ use <code>webcart10000.update()</code>. You can also pass an
+ object of changes, e.g.
+ <code>webcart1000.update({ MapX: 12, MapY: 13, Switch0: false
+ })</code>. Running <code>update()</code> will also scrub the
+ data so if you put any non-standard keys or values in it,
+ they'll be deleted.
+ </p>
+ <p>
+ If someone's playing two WEBCART1000 games at the same time, the
+ save data is updated in real-time!
+ </p>
+ <h2>Is it safe? Is it private?</h2>
+ <p>
+ Yes.
+ </p>
+ <h3>For Players</h3>
+ <p>
+ There's no cookies. There's no server-mediated
+ messages. Everything WEBCART1000, aside from loading its own
+ static JavaScript, stays on your computer.
+ It's as safe and private as the game would be otherwise.
+ </p>
+ <p>
+ Loading the game leaves at most a default web server log entry -
+ which you've already triggered just by reading this page. Since
+ no data is collected, we don't even have to make a promise we
+ won't share it with anyone - but if we do accidentally collect
+ some, we won't, and we'll just delete it.
+ </p>
+ <h3>For Developers</h3>
+ <p>
+ The provided WEBCART1000 scripts take pains to scrub and
+ normalize the data as much as possible. But this is the web, and
+ they're not served over HTTPS, and people make mistakes. Take
+ the kind of precautions you'd always take in talking to a remote
+ server.
+ </p><p>
+ The <a href="webcart1000.js">webcart1000.js</a> script can be
+ moved to your own server and loaded from there. I recommend this
+ - it'll be faster and stabler. The <a href="o_o.js">o_o.js</a>
+ script and HTML can't be moved unless you're going to make your own
+ WEBCART1000 - which you can if you want, but it also kind of
+ defeats the point.
+ </p>
+ </body>
+</html>
--- /dev/null
+<html>
+ <head>
+ <title>WEBCART1000</title>
+ <meta charset="utf-8" />
+ <script type="text/javascript" src="o_o.js"></script>
+ </head>
+ <body></body>
+</html>
--- /dev/null
+(function () {
+ "use strict";
+
+ var broken = false;
+ var _window = window.parent;
+
+ function clamp (i, hi) { return Math.max(0, Math.min(hi, +i | 0)); }
+ function force10 (i) { return clamp(i, 1023); }
+ function force16 (i) { return clamp(i, 65535); }
+ function forcebool (i) { return !!(+i || i === "true"); }
+ function forcestr (s) { return (s || "").toString().substring(0, 1023); }
+
+ var KEYS = {
+ MapX: force10, MapY: force10,
+ PlayerName: forcestr,
+ Misc0: force16, Misc1: force16, Misc2: force16, Misc3: force16,
+ Switch0: forcebool, Switch1: forcebool,
+ Switch2: forcebool, Switch3: forcebool,
+ Switch4: forcebool, Switch5: forcebool,
+ Switch6: forcebool, Switch7: forcebool
+ };
+
+ function generateMessage () {
+ var r = {};
+ for (var k in KEYS)
+ r[k] = KEYS[k](localStorage[k]);
+ return r;
+ }
+
+ function storeMessage (data) {
+ if (!data || typeof data !== "object")
+ return;
+ for (var k in KEYS) {
+ if (k in data) {
+ localStorage[k] = KEYS[k](data[k]);
+ }
+ }
+ }
+
+ function postMessage () {
+ _window.postMessage(generateMessage(), "*");
+ }
+
+ function onMessage (event) {
+ if (broken)
+ return;
+ if (event.data === "refresh") {
+ _window = event.source;
+ postMessage();
+ } else {
+ storeMessage(event.data);
+ }
+ }
+
+ function onStorage (event) {
+ postMessage();
+ }
+
+ window.addEventListener("load", function () {
+ try {
+ localStorage.setItem("WEBCART1000", "WEBCART1000");
+ } catch (exc) {
+ broken = true;
+ return;
+ }
+ window.addEventListener("message", onMessage, false);
+ window.addEventListener("storage", onStorage, false);
+ postMessage();
+ }, false);
+
+}).call(window.webcart1000 = {});
--- /dev/null
+(function () {
+ "use strict";
+
+ var ORIGIN = "http://yukkurigames.com";
+ var TARGET = ORIGIN + "/webcart1000/o_o.html";
+ var wc1kFrame;
+ var wc1kWindow;
+ var saveData;
+ var _this = this;
+
+ function clamp (i, hi) { return Math.max(0, Math.min(hi, +i | 0)); }
+ function force10 (i) { return clamp(i, 1023); }
+ function force16 (i) { return clamp(i, 65535); }
+ function forcebool (i) { return !!(+i || i === "true"); }
+ function forcestr (s) { return (s || "").toString().substring(0, 1023); }
+
+ var KEYS = {
+ MapX: force10, MapY: force10,
+ PlayerName: forcestr,
+ Misc0: force16, Misc1: force16, Misc2: force16, Misc3: force16,
+ Switch0: forcebool, Switch1: forcebool,
+ Switch2: forcebool, Switch3: forcebool,
+ Switch4: forcebool, Switch5: forcebool,
+ Switch6: forcebool, Switch7: forcebool
+ };
+
+ function scrub (data) {
+ if (typeof data !== "object")
+ data = {};
+ var r = {};
+ for (var k in KEYS)
+ r[k] = KEYS[k](data[k]);
+ return r;
+ }
+
+ function onMessage (event) {
+ if (event.origin !== ORIGIN)
+ return;
+ wc1kWindow = event.source;
+ _this.data = scrub(event.data);
+ }
+
+ function refresh () {
+ wc1kWindow.postMessage("refresh", ORIGIN);
+ }
+
+ function update (data) {
+ for (var k in data)
+ _this.data[k] = data[k];
+ _this.data = scrub(_this.data);
+ wc1kWindow.postMessage(data || _this.data, ORIGIN);
+ }
+
+ window.addEventListener("load", function () {
+ window.addEventListener("message", onMessage, false);
+ wc1kFrame = document.createElement('iframe');
+ wc1kFrame.style.display = "none";
+ wc1kFrame.src = TARGET;
+ document.body.appendChild(wc1kFrame);
+ }, false);
+
+ this.refresh = refresh;
+ this.update = update;
+ this.scrub = scrub;
+}).call(window.webcart1000 = {});