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
+98 -30
View File
@@ -157,23 +157,41 @@ function GenerateName() {
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() {
this.getItem = function(key) {
try {
this.getItem = function(key, callback) {
var result;
$.each(document.cookie.split(";"), function (i,cook) {
if (cook.split("=")[0] === key)
result = unescape(cook.split("=")[1]);
});
if (callback)
setTimeout(function () { callback(result); }, 0);
return result;
} catch (err) {
// Probably a JSON parse error
return null;
}
};
this.setItem = function (key, value) {
this.setItem = function (key, value, callback) {
document.cookie = key + "=" + escape(value);
if (callback)
setTimeout(callback, 0);
};
this.removeItem = function (key) {
@@ -181,49 +199,99 @@ function CookieStorage() {
};
}
function SqlStorage() {
this.async = true;
var storage = window.localStorage;
if (!storage)
storage = new CookieStorage();
this.db = window.openDatabase("pq", "", "Progress Quest", 2500);
function loadRoster() {
if (storage) {
var r = storage.getItem("roster");
if (r) {
this.db.transaction(function(tx) {
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 {
return JSON.parse(r);
value = JSON.parse(value);
} catch (err) {
// aight
}
}
storage.games = value || {};
callback(storage.games);
}
return {};
this.getItem("roster", gotItem);
}
function loadSheet(name) {
return loadRoster()[name];
storage.loadSheet = function (name, callback) {
return this.loadRoster(function (games) {
if (callback)
callback(games[name]);
});
}
function storeRoster(roster) {
if (storage) {
storage.storeRoster = function (roster, callback) {
this.games = roster;
try {
storage.setItem("roster", JSON.stringify(roster));
return true;
this.setItem("roster", JSON.stringify(roster), callback);
} catch (err) {
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?)");
storage = null;
return false;
}
this.storeRoster = function (roster, callback) {
setTimeout(callback, 0);
};
setTimeout(callback, 0);
} else {
throw err;
}
}
}
function addToRoster(newguy) {
if (storage) {
var r = loadRoster();
r[newguy.Traits.Name] = newguy;
storeRoster(r);
storage.addToRoster = function (newguy, callback) {
if (this.games) {
this.games[newguy.Traits.Name] = newguy;
this.storeRoster(this.games, callback);
} 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 {
font-size: 30pt;
}
img {
border-style: none;
}
th { text-align: left; padding-right: 2em; }
.good { color: #2a2; }
.bad { color: #a22; }
.ugly { color: #c82; }
</style>
</head>
@@ -62,16 +69,16 @@ coolant, and ported ProgrOS to that.
Between you, me, and that talking pony over there, this is pretty much
beta software at this point. Browser support breakdown:
<center>
<style>
th { text-align: left; padding-right: 2em; }
.good { color: #2a2; }
.bad { color: #a22; }
.ugly { color: #c82; }
</style>
<table>
<tr><th class=good>Chrome, Firefox, Safari <td>Aces! Works great! We think!
<tr><th class=ugly>Internet Explorer, Mobile Safari <td>Visual problems. Can't save games.
<tr><th class=bad>Opera <td>Crashes quite a bit.
<tr>
<th class=good>Chrome, Firefox, Safari
<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>
</center>
+4
View File
@@ -118,3 +118,7 @@ tr { height: 15px; }
#Quests td, #Plots td {
padding-left: 0;
}
body.iOS div.hint {
visibility: visible;
}
+5 -6
View File
@@ -12,9 +12,8 @@
maximum-scale=1.0;
minimum-scale=1.0;
user-scalable=0;" />
<meta names="apple-mobile-web-app-status-bar-style"
content="black" />
<link rel="apple-touch-icon" href="swords.gif" />
<meta names="apple-mobile-web-app-status-bar-style" content="black" />
<link rel="apple-touch-icon" href="touch-icon.png" />
</head>
<body>
@@ -157,7 +156,7 @@
<div class="progress-container" id="PlotBar">
<div class="hint"></div>
<div class="bar" style="width: 50%"></div>
<div class="bar"></div>
</div>
<span class="label head">Quests</span>
@@ -170,7 +169,7 @@
<div class="progress-container" id="QuestBar">
<div class="hint"></div>
<div class="bar" style="width: 50%"></div>
<div class="bar"></div>
</div>
</div>
@@ -180,7 +179,7 @@
<div class="progress-container" id="TaskBar">
<div class="hint"></div>
<div class="bar" style="width: 50%"></div>
<div class="bar"></div>
</div>
</div>
+54 -28
View File
@@ -965,39 +965,37 @@ function FormCreate() {
$(window).unload(function (event) {
StopTimer();
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() {
SaveGame();
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());
$(window).unbind('unload');
SaveGame(function () { window.location = "roster.html"; });
}
@@ -1026,17 +1024,40 @@ function HotOrNot() {
}
function SaveGame() {
function SaveGame(callback) {
Log('Saving game: ' + GameSaveName());
HotOrNot();
game.date = ''+new Date();
game.stamp = +new Date();
game.seed = randseed();
return addToRoster(game);
storage.addToRoster(game, callback);
}
function LoadGame(sheet) {
if (!sheet) {
alert("Error loading game");
window.location = "roster.html";
return;
}
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);
$.each(AllBars.concat(AllLists), function (i, e) { e.load(game); });
if (Kill)
@@ -1121,6 +1142,11 @@ function FormKeyDown(e) {
quit();
}
if (e.which == 19) { // ^S
SaveGame();
alert(JSON.stringify(game).length);
}
/*
if (e.which == 20) { // ^T
TaskBar.reposition(TaskBar.Max());
+3 -2
View File
@@ -205,9 +205,10 @@ function sold() {
newguy.Equips.Weapon = newguy.bestequip;
newguy.Equips.Hauberk = "-3 Burlap";
addToRoster(newguy);
storage.addToRoster(newguy, function () {
window.location.href = "main.html#" + escape(newguy.Traits.Name);
});
}
function cancel() {
+3 -1
View File
@@ -64,7 +64,9 @@ a.x, a.sheet {
.banter {
font-size: 11pt;
}
img {
border-style: none;
}
</style>
</head>
+10 -7
View File
@@ -1,18 +1,22 @@
var games;
function load() {
var roster = $("#roster");
roster.empty();
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.");
return;
}
storage.loadRoster(loadGames);
}
function loadGames(games) {
var roster = $("#roster");
roster.empty();
var newone = window.location.href.split('#')[1];
var count = 0;
games = loadRoster();
$.each(games, function (key, c) {
var name = c.Traits.Name;
@@ -24,7 +28,7 @@ function load() {
if (confirm("Terminate " + Pick(["faithful","noble","loyal","brave"])+
" " + name + "?")) {
delete games[name];
storeRoster(games);
storage.storeRoster(games);
load();
}
});
@@ -58,8 +62,7 @@ function brag(sheet) {
}
function clearRoster() {
storeRoster({});
load();
storage.storeRoster({}, load);
}
$(document).ready(function () {
+15 -8
View File
@@ -30,6 +30,7 @@ var window = {
},
};
var navigator = { userAgent: "v8" };
var location = window.location;
@@ -94,13 +95,13 @@ var timers = [{}];
var setInterval = global.setInterval = function (callback, interval) {
timers.push({callback:callback, interval:interval});
return timers.length-1;
}
};
var setTimeout = setInterval; // TODO: distinguish!
load("config.js");
var cs = 0;
for (var c in loadRoster())
cs++;
storage.loadRoster(function (cs) { for (var c in cs) cs++; });
print(cs, "characters");
load("newguy.js");
@@ -129,14 +130,19 @@ function charsheet(game) {
// It takes 18 sec to simulate 18 hours of play when I just checked (to
// level 10)
for (var j = 1, t = 0; j < 11; ++j) {
for (var j = 1, t = 0; j < 1001; ++j) {
t += LevelUpTime(j);
//if (j % 100 == 0)
if (j % 100 == 0)
print(j, RoughTime(LevelUpTime(j))+",", RoughTime(t));
}
var l = 0;
for (var i = 0; i < 100000000; ++i) {
var tmpl = read("charsheet.txt");
storage.loadSheet("Shienzid", function (sheet) {
game = sheet;
print(template(tmpl, sheet));
var l = 0;
for (var i = 0; i < 1000000000; ++i) {
if (game.Traits.Level != l) {
SaveGame();
charsheet(game);
@@ -146,5 +152,6 @@ for (var i = 0; i < 100000000; ++i) {
//assert(timers.length == 2);// TODO: this is for simplicity
now += timers[1].interval;
timers[1].callback();
}
}
});