Skip to content

Commit

Permalink
offline caching preparations
Browse files Browse the repository at this point in the history
  • Loading branch information
OctoSpacc committed May 20, 2024
1 parent 5d9d561 commit d566887
Show file tree
Hide file tree
Showing 7 changed files with 225 additions and 73 deletions.
4 changes: 3 additions & 1 deletion _includes/head.html
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@
icon: {{ page.icon | jsonify }},
software_data: {{ page.software_data | jsonify }},
};
window.SalaMuseoGames = { site: site_config, page: page_config };
window.SMG = window.SalaMuseoGames = { site: site_config, page: page_config };
</script>

<script src="{{ site.baseurl }}/assets/js/util.js"></script>
</head>
120 changes: 120 additions & 0 deletions assets/js/idb-backup-and-restore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
window.idbBackupAndRestore = {

/* <https://gist.github.com/loilo/ed43739361ec718129a15ae5d531095b/> */

/**
* Export all data from an IndexedDB database
*
* @param {IDBDatabase} idbDatabase The database to export from
* @return {Promise<string>}
*/
exportToJson: function (idbDatabase) {
return new Promise((resolve, reject) => {
const exportObject = {}
if (idbDatabase.objectStoreNames.length === 0) {
resolve(JSON.stringify(exportObject))
} else {
const transaction = idbDatabase.transaction(
idbDatabase.objectStoreNames,
'readonly'
)

transaction.addEventListener('error', reject)

for (const storeName of idbDatabase.objectStoreNames) {
const allObjects = []
transaction
.objectStore(storeName)
.openCursor()
.addEventListener('success', event => {
const cursor = event.target.result
if (cursor) {
// Cursor holds value, put it into store data
allObjects.push(cursor.value)
cursor.continue()
} else {
// No more values, store is done
exportObject[storeName] = allObjects

// Last store was handled
if (
idbDatabase.objectStoreNames.length ===
Object.keys(exportObject).length
) {
resolve(JSON.stringify(exportObject))
}
}
})
}
}
})
},

/**
* Import data from JSON into an IndexedDB database.
* This does not delete any existing data from the database, so keys may clash.
*
* @param {IDBDatabase} idbDatabase Database to import into
* @param {string} json Data to import, one key per object store
* @return {Promise<void>}
*/
importFromJson: function (idbDatabase, json) {
return new Promise((resolve, reject) => {
const transaction = idbDatabase.transaction(
idbDatabase.objectStoreNames,
'readwrite'
)
transaction.addEventListener('error', reject)

var importObject = JSON.parse(json)
for (const storeName of idbDatabase.objectStoreNames) {
let count = 0
for (const toAdd of importObject[storeName]) {
const request = transaction.objectStore(storeName).add(toAdd)
request.addEventListener('success', () => {
count++
if (count === importObject[storeName].length) {
// Added all objects for this store
delete importObject[storeName]
if (Object.keys(importObject).length === 0) {
// Added all object stores
resolve()
}
}
})
}
}
})
},

/**
* Clear a database
*
* @param {IDBDatabase} idbDatabase The database to delete all data from
* @return {Promise<void>}
*/
clearDatabase: function (idbDatabase) {
return new Promise((resolve, reject) => {
const transaction = idbDatabase.transaction(
idbDatabase.objectStoreNames,
'readwrite'
)
transaction.addEventListener('error', reject)

let count = 0
for (const storeName of idbDatabase.objectStoreNames) {
transaction
.objectStore(storeName)
.clear()
.addEventListener('success', () => {
count++
if (count === idbDatabase.objectStoreNames.length) {
// Cleared all object stores
resolve()
}
})
}
})
},

};
12 changes: 6 additions & 6 deletions assets/js/preferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

var prefsIndex = 'SalaMuseoGames/Prefs/v1';

var Prefs = {
pwaManifests: { default: true, name: "Allow installing the site as a PWA" },
softwarePwaManifests: { default: true, dependsOn: "pwaManifests", name: "Allow installing individual games as PWAs" },
// offlineCache: { default: true, name: "Cache site pages and game files offline", summary: "Allow faster site navigation, and gameplay without an Internet connection, by caching unlimited data offline. Disable if you want to save storage. (Note that data for some emulators and games is always cached regardless of this setting; you can only manage their data when they show an option in their interface.)" },
var Prefs = window.SalaMuseoGames.Prefs = {
softwarePwaManifests: { default: true, /* dependsOn: "pwaManifests", */ name: "Allow installing individual games as PWAs" },
pwaManifests: { default: false, name: "Allow installing the site home itself as a PWA" },
offlineCache: { default: true, name: "Cache site pages and game files offline", summary: "Allow faster site navigation, and gameplay without an Internet connection, by caching unlimited data offline. Disable if you want to save storage. (Note that data for some emulators and games is always cached regardless of this setting; you can only manage their data when they show an option in their interface.)" },
// dataExport: { onclick: (function(){}), section: "data", name: "Export configuration and gamesaves" },
// dataImport: { onclick: (function(){}), section: "data", name: "Import configuration and gamesaves" },
// featurePreview: { default: false, name: "Use experimental features before their release", summary: "If some new site features or adjustments are scheduled to release soon, you might be chosen to preview them before they are officially available" },
Expand All @@ -28,8 +28,9 @@ Object.keys(Prefs).forEach(function(key){
});

var PrefsSections = {
data: { name: "Data management", visible: true },
// data: { name: "Data management", visible: true },
developer: { name: "Development", visible: Prefs.developerMode.value },
// advanced: { name: "Advanced", visible: true },
};

var configElem = document.querySelector('#ConfigurationCustomizer');
Expand Down Expand Up @@ -64,7 +65,6 @@ if (configElem) {
});
}

window.SalaMuseoGames.Prefs = Prefs;
SavePrefs();

})();
24 changes: 19 additions & 5 deletions assets/js/pwa.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var Software = SalaMuseoGames.page.software_data;
var Screen = (Software && Software.screen);
var Site = SalaMuseoGames.site;
var iconUrl = SalaMuseoGames.page.icon;
var coverUrl = SalaMuseoGames.page.image;
var sitePath = (Site.url + Site.baseurl);

function absoluteUrlFromRelative (url) {
Expand All @@ -17,7 +18,7 @@ function absoluteUrlFromRelative (url) {
}
}

if (Prefs.pwaManifests.value) {
if (Prefs.pwaManifests.value || Prefs.softwarePwaManifests.value) {
var manifestData;
if (Prefs.softwarePwaManifests.value && Software) {
// specific manifests on games pages
Expand All @@ -30,7 +31,7 @@ if (Prefs.pwaManifests.value) {
display: ((Screen && Screen.display) || "standalone"),
orientation: ((Screen && Screen.orientation) || "any"),
};
} else {
} else if (Prefs.pwaManifests.value) {
// site manifest on global pages
var ldData;
for (var elem of document.querySelectorAll('script[type="application/ld+json"]')) {
Expand All @@ -53,7 +54,7 @@ if (Prefs.pwaManifests.value) {
scope: location.href,
background_color: (Software && Software.background_color || getComputedStyle(document.body).backgroundColor),
icons: [{
src: (iconUrl ? absoluteUrlFromRelative(iconUrl) : (sitePath + '/assets/img/icons/mediumtile.png')),
src: ((iconUrl || coverUrl) ? absoluteUrlFromRelative(iconUrl || coverUrl) : (sitePath + '/assets/img/icons/mediumtile.png')),
sizes: "any",
purpose: "any",
}],
Expand All @@ -64,8 +65,21 @@ if (Prefs.pwaManifests.value) {
document.head.appendChild(manifestElem);
}

if (Prefs.offlineCache.value && 'serviceWorker' in navigator) {
navigator.serviceWorker.register('/ServiceWorker.js', { scope: "/SalaMuseoGames/" });
if ('serviceWorker' in navigator) {
var cachingScopes = ['SalaMuseoGames', 'ext-bin-1'];
if (Prefs.offlineCache.value) {
cachingScopes.forEach(function(scope){
navigator.serviceWorker.register('/ServiceWorker.js', { scope: `/${scope}/` });
});
} else {
navigator.serviceWorker.getRegistrations().then((workers) => {
for (var worker of workers) {
if (cachingScopes.includes(worker.scope.split('/').slice(-2)[0])) {
worker.unregister();
}
}
});
}
}

})();
123 changes: 63 additions & 60 deletions assets/js/software-embed.js
Original file line number Diff line number Diff line change
@@ -1,67 +1,70 @@
(function(){
var bin1Path = 'https://gamingshitposting.github.io/ext-bin-1';
var thisElement = document.querySelector(`script[src="${SalaMuseoGames.site.baseurl}/assets/js/software-embed.js"]`);
var data = SalaMuseoGames.page.software_data;
var platform = data.platform;
var core = data.core;
var backend = data.backend;
var romUrl = (data.rom_url || `${bin1Path}/roms/${data.rom_index}.7z`);
var frameUrl = (data.frame_url || `${bin1Path}/${data.frame_index}`);

function button (name, onclick) { return `<button name="${name.split(' ')[0]}" onclick="(${onclick})(this)">${name}</button>` }
var bin1Path = 'https://gamingshitposting.github.io/ext-bin-1';
var thisElement = document.querySelector(`script[src="${SalaMuseoGames.site.baseurl}/assets/js/software-embed.js"]`);
var data = SalaMuseoGames.page.software_data;
var platform = data.platform;
var core = data.core;
var backend = data.backend;
var romUrl = (data.rom_url || `${bin1Path}/roms/${data.rom_index}.7z`);
var frameUrl = (data.frame_url || `${bin1Path}/${data.frame_index}`);

function diyEmbedHtml (frameUrl) { return (
button('Focus 🔳️', function(ctx){
ctx.parentElement.scrollIntoView();
ctx.parentElement.querySelector('iframe#software-embed-frame').focus();
}) + ' ' +
button('Fullscreen 🖼️', function(ctx){
ctx.parentElement.querySelector('iframe#software-embed-frame').requestFullscreen();
}) + ' ' +
button('Enlarge ↔️', function(ctx){
document.body.classList[
!document.body.className.split(' ').includes('cinema-view') ? 'add' : 'remove'
]('cinema-view');
}) + ' ' +
button('Reload ♻️', function(ctx){
var frame = ctx.parentElement.querySelector('iframe#software-embed-frame');
var src = frame.src;
frame.src = '';
frame.src = src;
}) + ' ' +
`<iframe id="software-embed-frame" class="software-embed-frame" src="${frameUrl}"></iframe>`
) }
function button (name, onclick) { return `<button name="${name.split(' ')[0]}" onclick="(${onclick})(this)">${name}</button>` }

// TODO set any overrides if specified ...
var buttonEnlarge = button('Enlarge ↔️', function(){
document.body.classList[
!document.body.className.split(' ').includes('cinema-view') ? 'add' : 'remove'
]('cinema-view');
});

if (platform === 'web') {
function diyEmbedHtml (frameUrl) { return (
button('Focus 🔳️', function(ctx){
ctx.parentElement.scrollIntoView();
ctx.parentElement.querySelector('iframe#software-embed-frame').focus();
}) + ' ' +
button('Fullscreen 🖼️', function(ctx){
ctx.parentElement.querySelector('iframe#software-embed-frame').requestFullscreen();
}) + ' ' +
buttonEnlarge + ' ' +
button('Reload ♻️', function(ctx){
var frame = ctx.parentElement.querySelector('iframe#software-embed-frame');
var src = frame.src;
frame.src = '';
frame.src = src;
}) + ' ' +
`<iframe id="software-embed-frame" class="software-embed-frame" src="${frameUrl}"></iframe>`
) }

// TODO set any overrides if specified ...

if (platform === 'web') {
thisElement.outerHTML = diyEmbedHtml(frameUrl);
} else switch (backend) {
default:
case 'cuttingedge':
case 'emulatorjs':
window.EJS_player = '#software-embed-frame';
window.EJS_pathtodata = 'https://gamingshitposting.github.io/ext-bin-1/EmulatorJS/data/';
window.EJS_core = (core || platform);
window.EJS_gameUrl = romUrl;
window.EJS_screenRecording = { videoBitrate: 150000000 };
thisElement.parentElement.appendChild(SMG.Util.elementFromHtml(buttonEnlarge));
thisElement.parentElement.appendChild(SMG.Util.makeElement('div', {
style: 'width: 640px; height: 480px; max-width: 100%;',
innerHTML: '<div id="software-embed-frame"></div>',
}));
document.body.appendChild(SMG.Util.makeElement('script', { src: `${EJS_pathtodata}loader.js` }));
break;
case 'standalone':
var frameUrl = '';
if (platform === 'nds' || core === 'desmume') {
frameUrl = `https://octospacc.gitlab.io/Web-Archives-Misc/Repo/DeSmuME/#RomUrl=${romUrl}`;
}
else if (platform === 'dos') {
frameUrl = `https://gamingshitposting.github.io/ext-bin-1/dos.zone/${data.rom_index}/index.html`;
}
thisElement.outerHTML = diyEmbedHtml(frameUrl);
} else switch (backend) {
default:
case 'cuttingedge':
case 'emulatorjs':
window.EJS_player = '#software-embed-frame';
window.EJS_pathtodata = 'https://gamingshitposting.github.io/ext-bin-1/EmulatorJS/data/';
window.EJS_core = (core || platform);
window.EJS_gameUrl = romUrl;
window.EJS_screenRecording = { videoBitrate: 150000000 };
var frameElement = document.createElement('div');
frameElement.style = 'width: 640px; height: 480px; max-width: 100%;';
frameElement.innerHTML = '<div id="software-embed-frame"></div>';
thisElement.parentElement.appendChild(frameElement);
var scriptElement = document.createElement('script');
scriptElement.src = EJS_pathtodata+'loader.js';
document.body.appendChild(scriptElement);
break;
case 'standalone':
var frameUrl = '';
if (platform === 'nds' || core === 'desmume') {
frameUrl = `https://octospacc.gitlab.io/Web-Archives-Misc/Repo/DeSmuME/#RomUrl=${romUrl}`;
}
else if (platform === 'dos') {
frameUrl = `https://gamingshitposting.github.io/ext-bin-1/dos.zone/${data.rom_index}/index.html`;
}
thisElement.outerHTML = diyEmbedHtml(frameUrl);
break;
}
break;
}

})();
13 changes: 13 additions & 0 deletions assets/js/util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
(function(){

var Util = window.SalaMuseoGames.Util = {};

Util.makeElement = (function(tag, attrs){
return Object.assign(document.createElement('tag'), attrs);
});

Util.elementFromHtml = (function(html){
return Util.makeElement('div', { innerHTML: html.trim() }).children[0];
});

})();
2 changes: 1 addition & 1 deletion pages/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ All your changes are saved automatically.
* Try to load games marked unavailable
-->

<div id="ConfigurationCustomizer"><p>Loading...</p></div>
<div id="ConfigurationCustomizer"><p>Loading...</p><script src="{{ site.baseurl }}/assets/js/idb-backup-and-restore.js"></script></div>

0 comments on commit d566887

Please sign in to comment.