Use Web SQL if available. Works nicely on iPad now.

This commit is contained in:
Eric Fredricksen
2010-07-20 13:40:25 -07:00
parent 64f85f3065
commit 7de90e7533
9 changed files with 228 additions and 111 deletions
+109 -41
View File
@@ -157,73 +157,141 @@ function GenerateName() {
return result.charAt(0).toUpperCase() + result.slice(1); return result.charAt(0).toUpperCase() + result.slice(1);
} }
function LocalStorage() {
this.getItem = function (key, callback) {
var result = window.localStorage.getItem(key);
if (callback)
callback(result);
};
this.setItem = function (key, value, callback) {
window.localStorage.setItem(key, value);
if (callback)
callback();
};
this.removeItem = function (key) {
window.localStorage.removeItem(key);
};
}
function CookieStorage() { function CookieStorage() {
this.getItem = function(key) { this.getItem = function(key, callback) {
try { var result;
var result; $.each(document.cookie.split(";"), function (i,cook) {
$.each(document.cookie.split(";"), function (i,cook) { if (cook.split("=")[0] === key)
if (cook.split("=")[0] === key) result = unescape(cook.split("=")[1]);
result = unescape(cook.split("=")[1]); });
}); if (callback)
return result; setTimeout(function () { callback(result); }, 0);
} catch (err) { return result;
// Probably a JSON parse error
return null;
}
}; };
this.setItem = function (key, value) { this.setItem = function (key, value, callback) {
document.cookie = key + "=" + escape(value); document.cookie = key + "=" + escape(value);
if (callback)
setTimeout(callback, 0);
}; };
this.removeItem = function (key) { this.removeItem = function (key) {
document.cookie = key + "=; expires=Thu, 01-Jan-70 00:00:01 GMT;"; document.cookie = key + "=; expires=Thu, 01-Jan-70 00:00:01 GMT;";
}; };
} }
var storage = window.localStorage; function SqlStorage() {
if (!storage) this.async = true;
storage = new CookieStorage();
function loadRoster() { this.db = window.openDatabase("pq", "", "Progress Quest", 2500);
if (storage) {
var r = storage.getItem("roster"); this.db.transaction(function(tx) {
if (r) { tx.executeSql("CREATE TABLE IF NOT EXISTS Storage(key TEXT UNIQUE, value TEXT)");
});
this.getItem = function(key, callback) {
this.db.transaction(function (tx) {
tx.executeSql("SELECT value FROM Storage WHERE key=?", [key], function(tx, rs) {
if (rs.rows.length)
callback(rs.rows.item(0).value);
else
callback();
});
});
};
this.setItem = function (key, value, callback) {
this.db.transaction(function (tx) {
tx.executeSql("INSERT OR REPLACE INTO Storage (key,value) VALUES (?,?)",
[key, value],
callback);
});
};
this.removeItem = function (key) {
this.db.transaction(function (tx) {
tx.executeSql("DELETE FROM Storage WHERE key=?", [key]);
});
};
}
var iPad = navigator.userAgent.match(/iPad/);
var iPod = navigator.userAgent.match(/iPod/);
var iPhone = navigator.userAgent.match(/iPhone/);
var iOS = iPad || iPod || iPhone;
var storage = ((window.localStorage && !iOS) ? new LocalStorage() :
window.openDatabase ? new SqlStorage() :
new CookieStorage());
storage.loadRoster = function (callback) {
function gotItem(value) {
if (value) {
try { try {
return JSON.parse(r); value = JSON.parse(value);
} catch (err) { } catch (err) {
// aight // aight
} }
} }
storage.games = value || {};
callback(storage.games);
} }
return {}; this.getItem("roster", gotItem);
} }
function loadSheet(name) { storage.loadSheet = function (name, callback) {
return loadRoster()[name]; return this.loadRoster(function (games) {
if (callback)
callback(games[name]);
});
} }
function storeRoster(roster) {
if (storage) { storage.storeRoster = function (roster, callback) {
try { this.games = roster;
storage.setItem("roster", JSON.stringify(roster)); try {
return true; this.setItem("roster", JSON.stringify(roster), callback);
} catch (err) { } catch (err) {
if (err.toString().indexOf("QUOTA_EXCEEDED_ERR") != -1) { if (err.toString().indexOf("QUOTA_EXCEEDED_ERR") != -1) {
alert("This browser lacks storage capacity to save this game. This game can continue but cannot be saved. (Mobile Safari, I'll wager?)"); alert("This browser lacks storage capacity to save this game. This game can continue but cannot be saved. (Mobile Safari, I'll wager?)");
storage = null; this.storeRoster = function (roster, callback) {
return false; setTimeout(callback, 0);
} };
setTimeout(callback, 0);
} else {
throw err;
} }
} }
} }
function addToRoster(newguy) { storage.addToRoster = function (newguy, callback) {
if (storage) { if (this.games) {
var r = loadRoster(); this.games[newguy.Traits.Name] = newguy;
r[newguy.Traits.Name] = newguy; this.storeRoster(this.games, callback);
storeRoster(r); } else {
this.loadRoster(function () {
if (storage.games) // should always be true
storage.addToRoster(newguy, callback);
});
} }
} }
+16 -9
View File
@@ -28,6 +28,13 @@ p {
#go { #go {
font-size: 30pt; font-size: 30pt;
} }
img {
border-style: none;
}
th { text-align: left; padding-right: 2em; }
.good { color: #2a2; }
.bad { color: #a22; }
.ugly { color: #c82; }
</style> </style>
</head> </head>
@@ -62,16 +69,16 @@ coolant, and ported ProgrOS to that.
Between you, me, and that talking pony over there, this is pretty much Between you, me, and that talking pony over there, this is pretty much
beta software at this point. Browser support breakdown: beta software at this point. Browser support breakdown:
<center> <center>
<style>
th { text-align: left; padding-right: 2em; }
.good { color: #2a2; }
.bad { color: #a22; }
.ugly { color: #c82; }
</style>
<table> <table>
<tr><th class=good>Chrome, Firefox, Safari <td>Aces! Works great! We think! <tr>
<tr><th class=ugly>Internet Explorer, Mobile Safari <td>Visual problems. Can't save games. <th class=good>Chrome, Firefox, Safari
<tr><th class=bad>Opera <td>Crashes quite a bit. <td>Aces! Works great! We think!
<tr>
<th class=ugly>Opera, Mobile Safari
<td>Visual glitches.
<tr>
<th class=bad>Internet Explorer
<td>Visual glitches. Can't save games.
</table> </table>
</center> </center>
+4
View File
@@ -118,3 +118,7 @@ tr { height: 15px; }
#Quests td, #Plots td { #Quests td, #Plots td {
padding-left: 0; padding-left: 0;
} }
body.iOS div.hint {
visibility: visible;
}
+5 -6
View File
@@ -12,9 +12,8 @@
maximum-scale=1.0; maximum-scale=1.0;
minimum-scale=1.0; minimum-scale=1.0;
user-scalable=0;" /> user-scalable=0;" />
<meta names="apple-mobile-web-app-status-bar-style" <meta names="apple-mobile-web-app-status-bar-style" content="black" />
content="black" /> <link rel="apple-touch-icon" href="touch-icon.png" />
<link rel="apple-touch-icon" href="swords.gif" />
</head> </head>
<body> <body>
@@ -157,7 +156,7 @@
<div class="progress-container" id="PlotBar"> <div class="progress-container" id="PlotBar">
<div class="hint"></div> <div class="hint"></div>
<div class="bar" style="width: 50%"></div> <div class="bar"></div>
</div> </div>
<span class="label head">Quests</span> <span class="label head">Quests</span>
@@ -170,7 +169,7 @@
<div class="progress-container" id="QuestBar"> <div class="progress-container" id="QuestBar">
<div class="hint"></div> <div class="hint"></div>
<div class="bar" style="width: 50%"></div> <div class="bar"></div>
</div> </div>
</div> </div>
@@ -180,7 +179,7 @@
<div class="progress-container" id="TaskBar"> <div class="progress-container" id="TaskBar">
<div class="hint"></div> <div class="hint"></div>
<div class="bar" style="width: 50%"></div> <div class="bar"></div>
</div> </div>
</div> </div>
+55 -29
View File
@@ -959,45 +959,43 @@ function FormCreate() {
$(document).bind('beforeunload', function () { $(document).bind('beforeunload', function () {
if (!storage) if (!storage)
return "Are you sure you want to quit? All your progress will be lost!"; return "Are you sure you want to quit? All your progress will be lost!";
}); });
$(window).unload(function (event) { $(window).unload(function (event) {
StopTimer(); StopTimer();
SaveGame(); SaveGame();
if (storage.async) {
// Have to give SQL transaction a chance to complete
if (window.showModalDialog)
pause(100);
// Just accept some data loss - alert is too ugly. Maybe increase save
// frequency.
// else alert("Game saved");
}
}); });
if (iOS) $("body").addClass("iOS");
} }
LoadCharacter(); var name = unescape(window.location.href.split('#')[1]);
storage.loadSheet(name, LoadGame);
} }
function pause(msec) {
window.showModalDialog("javascript:document.writeln ('<script>window.setTimeout(" +
"function () { window.close(); }," + msec + ");</script>')",
null,
"dialogWidth:0;dialogHeight:0;dialogHide:yes;unadorned:yes;"+
"status:no;scroll:no;center:no;dialogTop:-10000;dialogLeft:-10000");
}
function quit() { function quit() {
SaveGame(); $(window).unbind('unload');
window.location = "roster.html"; SaveGame(function () { window.location = "roster.html"; });
}
function LoadCharacter() {
var result = true;
var name = unescape(window.location.href.split('#')[1]);
var sheet = loadSheet(name);
if (!sheet) {
alert("Error loading " + name);
window.location = "roster.html";
return;
}
if (!window.localStorage) {
// Cookies can't hold a whole game save
storage.removeItem("roster");
storage = null;
}
LoadGame(sheet);
if (document)
$("#title").text("Progress Quest - " + GameSaveName());
} }
@@ -1026,17 +1024,40 @@ function HotOrNot() {
} }
function SaveGame() { function SaveGame(callback) {
Log('Saving game: ' + GameSaveName()); Log('Saving game: ' + GameSaveName());
HotOrNot(); HotOrNot();
game.date = ''+new Date(); game.date = ''+new Date();
game.stamp = +new Date(); game.stamp = +new Date();
game.seed = randseed(); game.seed = randseed();
return addToRoster(game); storage.addToRoster(game, callback);
} }
function LoadGame(sheet) { function LoadGame(sheet) {
if (!sheet) {
alert("Error loading game");
window.location = "roster.html";
return;
}
game = sheet; game = sheet;
/*
if (!window.localStorage) {
// Cookies can't hold a whole game save
storage.removeItem("roster");
storage = null;
}
*/
if (document) {
var title = "Progress Quest - " + GameSaveName();
$("#title").text(title);
if (iOS) title = GameSaveName();
$("title").text(title);
}
randseed(game.seed); randseed(game.seed);
$.each(AllBars.concat(AllLists), function (i, e) { e.load(game); }); $.each(AllBars.concat(AllLists), function (i, e) { e.load(game); });
if (Kill) if (Kill)
@@ -1121,6 +1142,11 @@ function FormKeyDown(e) {
quit(); quit();
} }
if (e.which == 19) { // ^S
SaveGame();
alert(JSON.stringify(game).length);
}
/* /*
if (e.which == 20) { // ^T if (e.which == 20) { // ^T
TaskBar.reposition(TaskBar.Max()); TaskBar.reposition(TaskBar.Max());
+3 -2
View File
@@ -205,9 +205,10 @@ function sold() {
newguy.Equips.Weapon = newguy.bestequip; newguy.Equips.Weapon = newguy.bestequip;
newguy.Equips.Hauberk = "-3 Burlap"; newguy.Equips.Hauberk = "-3 Burlap";
addToRoster(newguy); storage.addToRoster(newguy, function () {
window.location.href = "main.html#" + escape(newguy.Traits.Name);
});
window.location.href = "main.html#" + escape(newguy.Traits.Name);
} }
function cancel() { function cancel() {
+3 -1
View File
@@ -64,7 +64,9 @@ a.x, a.sheet {
.banter { .banter {
font-size: 11pt; font-size: 11pt;
} }
img {
border-style: none;
}
</style> </style>
</head> </head>
+10 -7
View File
@@ -1,18 +1,22 @@
var games;
function load() { function load() {
var roster = $("#roster");
roster.empty();
if (!window.localStorage) { if (!window.localStorage) {
roster.html("<b>Hrumph:</b> This browser does not support local storage. You can still play fast and loose: your character will live only as long as the game stays running in your browser."); roster.html("<b>Hrumph:</b> This browser does not support local storage. You can still play fast and loose: your character will live only as long as the game stays running in your browser.");
return; return;
} }
storage.loadRoster(loadGames);
}
function loadGames(games) {
var roster = $("#roster");
roster.empty();
var newone = window.location.href.split('#')[1]; var newone = window.location.href.split('#')[1];
var count = 0; var count = 0;
games = loadRoster();
$.each(games, function (key, c) { $.each(games, function (key, c) {
var name = c.Traits.Name; var name = c.Traits.Name;
@@ -24,7 +28,7 @@ function load() {
if (confirm("Terminate " + Pick(["faithful","noble","loyal","brave"])+ if (confirm("Terminate " + Pick(["faithful","noble","loyal","brave"])+
" " + name + "?")) { " " + name + "?")) {
delete games[name]; delete games[name];
storeRoster(games); storage.storeRoster(games);
load(); load();
} }
}); });
@@ -58,8 +62,7 @@ function brag(sheet) {
} }
function clearRoster() { function clearRoster() {
storeRoster({}); storage.storeRoster({}, load);
load();
} }
$(document).ready(function () { $(document).ready(function () {
+23 -16
View File
@@ -30,6 +30,7 @@ var window = {
}, },
}; };
var navigator = { userAgent: "v8" };
var location = window.location; var location = window.location;
@@ -94,13 +95,13 @@ var timers = [{}];
var setInterval = global.setInterval = function (callback, interval) { var setInterval = global.setInterval = function (callback, interval) {
timers.push({callback:callback, interval:interval}); timers.push({callback:callback, interval:interval});
return timers.length-1; return timers.length-1;
} };
var setTimeout = setInterval; // TODO: distinguish!
load("config.js"); load("config.js");
var cs = 0; var cs = 0;
for (var c in loadRoster()) storage.loadRoster(function (cs) { for (var c in cs) cs++; });
cs++;
print(cs, "characters"); print(cs, "characters");
load("newguy.js"); load("newguy.js");
@@ -129,22 +130,28 @@ function charsheet(game) {
// It takes 18 sec to simulate 18 hours of play when I just checked (to // It takes 18 sec to simulate 18 hours of play when I just checked (to
// level 10) // level 10)
for (var j = 1, t = 0; j < 11; ++j) { for (var j = 1, t = 0; j < 1001; ++j) {
t += LevelUpTime(j); t += LevelUpTime(j);
//if (j % 100 == 0) if (j % 100 == 0)
print(j, RoughTime(LevelUpTime(j))+",", RoughTime(t)); print(j, RoughTime(LevelUpTime(j))+",", RoughTime(t));
} }
var l = 0; var tmpl = read("charsheet.txt");
for (var i = 0; i < 100000000; ++i) { storage.loadSheet("Shienzid", function (sheet) {
if (game.Traits.Level != l) { game = sheet;
SaveGame(); print(template(tmpl, sheet));
charsheet(game);
l = game.Traits.Level; var l = 0;
//if (l >= 5) break; for (var i = 0; i < 1000000000; ++i) {
if (game.Traits.Level != l) {
SaveGame();
charsheet(game);
l = game.Traits.Level;
//if (l >= 5) break;
}
//assert(timers.length == 2);// TODO: this is for simplicity
now += timers[1].interval;
timers[1].callback();
} }
//assert(timers.length == 2);// TODO: this is for simplicity });
now += timers[1].interval;
timers[1].callback();
}