Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src-ui/css/ui.css
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,19 @@ form#fileform2 {
content: "+";
}

div#saveicon {
float: right;
position: relative;
height: 100%;
display: flex;
align-items: center;
margin-right: 2px;
margin-top: 1px;
}
.hide {
visibility: hidden;
}

div#timertext {
display: inline-block;
padding: 2pt 0;
Expand Down
2 changes: 1 addition & 1 deletion src-ui/js/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
localStorage.getItem("pzprv3_config:ui") || "{}"
);
setting.listsort = typename;
localStorage.setItem("pzprv3_config:ui", JSON.stringify(setting));
pzpr.util.store("pzprv3_config:ui", JSON.stringify(setting));
self.apply_sort();
},

Expand Down
91 changes: 82 additions & 9 deletions src-ui/js/ui/Boot.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,42 @@
// ★boot() window.onload直後の処理
//---------------------------------------------------------------------------
pzpr.on("load", function boot() {
if (importData()) {
startPuzzle();
var pzl;
var storedGame = null;

if (pzpr.env.localStorageAvailable) {
// If localStorage is available and autosave is enabled:
// Get URL search hash and check localStorage to see if a board state is saved

// ui.menuconfig is not yet populated, so need to manually check
var json_menu = localStorage.getItem("pzprv3_config:ui");
if (json_menu && JSON.parse(json_menu)["autosave"]) {
var key = "pzpr_" + getPuzzleString();
storedGame = localStorage.getItem(key);
}
}

if (storedGame) {
var valObject = JSON.parse(storedGame);
pzl = importData(valObject.pzl);
} else {
pzl = importData();
}
if (!pzl) {
setTimeout(boot, 0);
}
startPuzzle();
});

function importData() {
function importData(string) {
if (!onload_pzl) {
/* 1) 盤面複製・index.htmlからのファイル入力/Database入力か */
/* 2) URL(?以降)をチェック */
onload_pzl = importURL();
if (!string) {
onload_pzl = importURL();
} else {
onload_pzl = importFromString(string);
}

/* 指定されたパズルがない場合はさようなら~ */
if (!onload_pzl || !onload_pzl.pid) {
Expand Down Expand Up @@ -90,26 +114,75 @@
//---------------------------------------------------------------------------
function importURL() {
/* index.htmlからURLが入力されていない場合は現在のURLの?以降をとってくる */
var puzString = getPuzzleString();
return importFromString(puzString);
}
//Splitting functionality from above for flexibility.

//Return the string associated with the puzzle
function getPuzzleString() {
var search = location.search;
if (!search) {
return null;
}

/* 一旦先頭の?記号を取り除く */
if (search.charAt(0) === "?") {
search = search.substr(1);
search = search.slice(1);
}

while (search.match(/^(\w+)\=(\w+)\&(.*)/)) {
onload_option[RegExp.$1] = RegExp.$2;
search = RegExp.$3;
}
return search;
}
//Import from a puzzle string. This can come from the URL or from localStorage
function importFromString(string) {
if (!string) {
return null;
}

onload_pzv = search;
var pzl = pzpr.parser.parseURL(search);
onload_pzv = string;
var pzl = pzpr.parser.parseURL(string);
var startmode = pzl.mode || (!pzl.body ? "editor" : "player");
onload_option.type = onload_option.type || startmode;

return pzl;
}

//---------------------------------------------------------------------------
// Functionality to support browser caching
//---------------------------------------------------------------------------

//Save board state. Creates an entry in localStorage whose key is a 'pzpr_' identifier plus the current board state puzzle string.
//Board state puzzle string is the same thing you get from duplicating the board state
//Auto-exits if the correct setting is not set, so safe to call from anywhere without checking
function saveBoardState() {
if (!ui.menuconfig.get("autosave")) {
return;
}
var key = "pzpr_" + getPuzzleString();
var url = ui.puzzle.getURL(
pzpr.parser.URL_PZPRFILE,
ui.puzzle.playeronly ? "player" : "editor"
);
//Strip url to the last option. This is the "puzzle string" we want
url = url.substring(url.indexOf("?") + 1); //Skip to the search parameters part of the url
while (url.match(/^(\w+)\=(\w+)\&(.*)/)) {
url = RegExp.$3;
}
//Add a time signifier so that we can sort and delete oldest if setting fails
var valObject = {
t: Date.now(),
pzl: url
// bufferToForceStorageLimitErrors: "0".repeat(1700000) //Include for testing to force out-of-storage errors
};
pzpr.util.store(key, JSON.stringify(valObject));
}

//Events that trigger a board state save
document.addEventListener("visibilitychange", function() {
if (document.visibilityState === "hidden") {
saveBoardState();
}
});
})();
18 changes: 16 additions & 2 deletions src-ui/js/ui/MenuConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
volatile: true
}); /* マウスの左右ボタンを反転する設定 */

//Autosave feature. Not on by default, but persists once enabled
this.add("autosave", false, {
volatile: false
});

this.add("language", pzpr.lang, { option: ["en", "ja"] }); /* 言語設定 */

/* puzzle.configを一括で扱うため登録 */
Expand Down Expand Up @@ -264,11 +269,11 @@
}

try {
localStorage.setItem(
pzpr.util.store(
"pzprv3_config:puzzle",
JSON.stringify(ui.puzzle.saveConfig())
);
localStorage.setItem("pzprv3_config:ui", JSON.stringify(this.getAll()));
pzpr.util.store("pzprv3_config:ui", JSON.stringify(this.getAll()));
} catch (ex) {
console.warn(ex);
}
Expand Down Expand Up @@ -336,6 +341,15 @@
// config.configevent() 設定変更時の動作を記述する (modeはlistener.onModeChangeで変更)
//---------------------------------------------------------------------------
configevent: function(idname, newval) {
//Need to set save icon visibility here to make sure it goes off before the early exit
if (idname === "autosave") {
var saveIcon = document.getElementById("saveicon");
if (!!newval) {
saveIcon.classList.remove("hide");
} else {
saveIcon.classList.add("hide");
}
}
if (!ui.menuarea.menuitem) {
return;
}
Expand Down
2 changes: 2 additions & 0 deletions src-ui/p.html
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ <h2 id="title2">読み込み中です...</h2>
<li class="check" data-config="lattice"><span>__lattice.menu__</span></li>
<li class="check" data-config="singlenum"><span>__singlenum.menu__</span></li>
<li class="check" data-config="discolor"><span>__discolor__</span></li>
<li class="check" data-config="autosave"><span>__autosave__</span></li>
<li data-config="autocheck_mode"><span>__autocheck__</span>&nbsp;-&gt;<menu label="__autocheck__">
<li class="child" data-value="off"><span>__autocheck.off__</span></li>
<li class="child" data-value="guarded"><span>__autocheck.guarded__</span></li>
Expand All @@ -111,6 +112,7 @@ <h2 id="title2">読み込み中です...</h2>
<li class="link"><a target="_blank" href="https://hosted.weblate.org/projects/pzprjs/">__translate__</a></li>
<li class="link"><a target="_blank" href="https://github.com/robx/pzprjs/issues">__issues__</a></li>
</menu></li>
<div id="saveicon" class="hide">&#x1F4BE</div>
</menu>
<div id="usepanel" style="display:none;">
<div class="config" data-config="mode">
Expand Down
1 change: 1 addition & 0 deletions src-ui/res/p.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"autocheck.off": "Manual",
"autocheck.guarded": "Automatic (guarded)",
"autocheck.simple": "Automatic (always)",
"autosave": "Autosave board state [beta]",
"menu_help": "Help",
"rules": "Rules",
"about": "About puzz.link",
Expand Down
1 change: 1 addition & 0 deletions src-ui/res/p.ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"autocheck.off": "正答を自動で判定しない",
"autocheck.guarded": "自動(guarded)",
"autocheck.simple": "自動(常に)",
"autosave": "Autosave board state [beta]",
"menu_help": "ヘルプ",
"rules": "ルール",
"about": "puzz.linkについて",
Expand Down
14 changes: 13 additions & 1 deletion src/pzpr/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,25 @@ pzpr.env = (function() {
anchor_download:
isbrowser && document.createElement("a").download !== void 0
};
//Taken directly from stackoverflow. Apparently this is the most broadly compatible version. https://stackoverflow.com/questions/16427636/check-if-localstorage-is-available
var localStorageAvailable = (function() {
var test = "test";
try {
localStorage.setItem(test, test);
localStorage.removeItem(test);
return true;
} catch (e) {
return false;
}
})();

return {
bz: bz,
OS: os,
API: api,
browser: isbrowser,
node: pzpr.Candle.env.node
node: pzpr.Candle.env.node,
localStorageAvailable: localStorageAvailable
};
})();

Expand Down
33 changes: 33 additions & 0 deletions src/pzpr/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,39 @@
}
}
return true;
},

// "Safe" set to local storage. Catches quota exceeded errors and removes the oldest puzzles.
// Separated here so that settings can be set safely as well
store: function(key, value) {
try {
localStorage.setItem(key, value);
} catch (e) {
if (e.name === "QuotaExceededError") {
//If storage was full: load all of the puzzles in localStorage, sort by least recent, and delete until saving is successful
var pairs = [];
for (var i = 0; i < localStorage.length; i++) {
var lsKey = localStorage.key(i);
var lsValue = JSON.parse(localStorage.getItem(lsKey));
if (lsKey.indexOf("pzpr_") === 0) {
pairs.push({ key: lsKey, value: lsValue });
}
}
pairs.sort(function(a, b) {
var ta = a.value.t;
var tb = b.value.t;
return ta - tb;
});
for (var i = 0; i < pairs.length; i++) {
try {
localStorage.setItem(key, value);
break;
} catch (e) {
localStorage.removeItem(pairs[i].key);
}
}
}
}
}
};
})();