diff --git a/css/main.css b/css/main.css
index da932355c..1c84c4de6 100644
--- a/css/main.css
+++ b/css/main.css
@@ -55,7 +55,14 @@ div#header {
cursor: pointer;
}
-.deleteSave:hover, .share:hover {
+.manualSave {
+ position: absolute;
+ right: 120px;
+ bottom: 10px;
+ cursor: pointer;
+}
+
+.deleteSave:hover, .share:hover, .manualSave:hover {
text-decoration: underline;
}
diff --git a/index.html b/index.html
index 6b254561f..68c46fe68 100644
--- a/index.html
+++ b/index.html
@@ -20,6 +20,7 @@
+
diff --git a/lib/base64.js b/lib/base64.js
new file mode 100644
index 000000000..0790dd789
--- /dev/null
+++ b/lib/base64.js
@@ -0,0 +1,141 @@
+/**
+*
+* Base64 encode / decode
+* http://www.webtoolkit.info/
+*
+**/
+var Base64 = {
+
+// private property
+_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
+
+// public method for encoding
+encode : function (input) {
+ var output = "";
+ var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+ var i = 0;
+
+ input = Base64._utf8_encode(input);
+
+ while (i < input.length) {
+
+ chr1 = input.charCodeAt(i++);
+ chr2 = input.charCodeAt(i++);
+ chr3 = input.charCodeAt(i++);
+
+ enc1 = chr1 >> 2;
+ enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+ enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+ enc4 = chr3 & 63;
+
+ if (isNaN(chr2)) {
+ enc3 = enc4 = 64;
+ } else if (isNaN(chr3)) {
+ enc4 = 64;
+ }
+
+ output = output +
+ this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
+ this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
+
+ }
+
+ return output;
+},
+
+// public method for decoding
+decode : function (input) {
+ var output = "";
+ var chr1, chr2, chr3;
+ var enc1, enc2, enc3, enc4;
+ var i = 0;
+
+ input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+
+ while (i < input.length) {
+
+ enc1 = this._keyStr.indexOf(input.charAt(i++));
+ enc2 = this._keyStr.indexOf(input.charAt(i++));
+ enc3 = this._keyStr.indexOf(input.charAt(i++));
+ enc4 = this._keyStr.indexOf(input.charAt(i++));
+
+ chr1 = (enc1 << 2) | (enc2 >> 4);
+ chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+ chr3 = ((enc3 & 3) << 6) | enc4;
+
+ output = output + String.fromCharCode(chr1);
+
+ if (enc3 != 64) {
+ output = output + String.fromCharCode(chr2);
+ }
+ if (enc4 != 64) {
+ output = output + String.fromCharCode(chr3);
+ }
+
+ }
+
+ output = Base64._utf8_decode(output);
+
+ return output;
+
+},
+
+// private method for UTF-8 encoding
+_utf8_encode : function (string) {
+ string = string.replace(/\r\n/g,"\n");
+ var utftext = "";
+
+ for (var n = 0; n < string.length; n++) {
+
+ var c = string.charCodeAt(n);
+
+ if (c < 128) {
+ utftext += String.fromCharCode(c);
+ }
+ else if((c > 127) && (c < 2048)) {
+ utftext += String.fromCharCode((c >> 6) | 192);
+ utftext += String.fromCharCode((c & 63) | 128);
+ }
+ else {
+ utftext += String.fromCharCode((c >> 12) | 224);
+ utftext += String.fromCharCode(((c >> 6) & 63) | 128);
+ utftext += String.fromCharCode((c & 63) | 128);
+ }
+
+ }
+
+ return utftext;
+},
+
+// private method for UTF-8 decoding
+_utf8_decode : function (utftext) {
+ var string = "";
+ var i = 0;
+ var c = c1 = c2 = 0;
+
+ while ( i < utftext.length ) {
+
+ c = utftext.charCodeAt(i);
+
+ if (c < 128) {
+ string += String.fromCharCode(c);
+ i++;
+ }
+ else if((c > 191) && (c < 224)) {
+ c2 = utftext.charCodeAt(i+1);
+ string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
+ i += 2;
+ }
+ else {
+ c2 = utftext.charCodeAt(i+1);
+ c3 = utftext.charCodeAt(i+2);
+ string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
+ i += 3;
+ }
+
+ }
+
+ return string;
+}
+
+}
\ No newline at end of file
diff --git a/script/engine.js b/script/engine.js
index 2dd82ef9c..21c43c06e 100644
--- a/script/engine.js
+++ b/script/engine.js
@@ -105,6 +105,12 @@ var Engine = {
.text('share.')
.click(Engine.share)
.appendTo('body');
+
+ $('')
+ .addClass('manualSave')
+ .text('save.')
+ .click(Engine.exportImport)
+ .appendTo('body');
// Register keypress handlers
$('body').off('keydown').keydown(Engine.keyDown);
@@ -178,6 +184,58 @@ var Engine = {
}
},
+ exportImport: function() {
+ Events.startEvent({
+ title: 'Export / Import',
+ scenes: {
+ start: {
+ text: ['export or import save data, for backing up',
+ 'or migrating computers'],
+ buttons: {
+ 'export': {
+ text: 'export',
+ nextScene: 'end',
+ onChoose: Engine.export64
+ },
+ 'import': {
+ text: 'import',
+ nextScene: {1: 'confirm'},
+ }
+ }
+ },
+ 'confirm': {
+ text: ['are you sure?',
+ 'if the code is invalid, all data will be lost.',
+ 'this is irreversible.'],
+ buttons: {
+ 'yes': {
+ text: 'yes',
+ nextScene: 'end',
+ onChoose: Engine.import64
+ },
+ 'no': {
+ text: 'no',
+ nextScene: 'end'
+ }
+ }
+ }
+ }
+ });
+ },
+
+ export64: function() {
+ Engine.saveGame();
+ var string64 = Base64.encode(localStorage.gameState);
+ prompt("save this.",string64);
+ },
+
+ import64: function() {
+ var string64 = prompt("put the save code here.","");
+ var decodedSave = Base64.decode(string64);
+ localStorage.gameState = decodedSave;
+ location.reload();
+ },
+
event: function(cat, act) {
if(typeof ga === 'function') {
ga('send', 'event', cat, act);