mirror of
https://gitlab.com/skysthelimit.dev/selenite.git
synced 2025-06-19 12:02:07 -05:00
Upload files to "trump"
This commit is contained in:
parent
0e8c6c3f71
commit
e0e10901f9
4
trump/jquery-2.1.1.min.js
vendored
Normal file
4
trump/jquery-2.1.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
trump/loading-logo.png
Normal file
BIN
trump/loading-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
260
trump/offline.js
Normal file
260
trump/offline.js
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
{
|
||||||
|
"version": 1525266111,
|
||||||
|
"fileList": [
|
||||||
|
"data.js",
|
||||||
|
"c2runtime.js",
|
||||||
|
"jquery-2.1.1.min.js",
|
||||||
|
"offlineClient.js",
|
||||||
|
"images/fadein-sheet0.png",
|
||||||
|
"images/gamepanel-sheet0.png",
|
||||||
|
"images/gamepanelcannon-sheet0.png",
|
||||||
|
"images/gamepanelredbutton-sheet0.png",
|
||||||
|
"images/gamepanelweaponbutton1-sheet0.png",
|
||||||
|
"images/gamepanelweaponbutton1-sheet1.png",
|
||||||
|
"images/gamepanelweaponbutton1-sheet2.png",
|
||||||
|
"images/gamepanelweaponbutton2-sheet0.png",
|
||||||
|
"images/gamepanelweaponbutton2-sheet1.png",
|
||||||
|
"images/gamepanelweaponbutton2-sheet2.png",
|
||||||
|
"images/gamepanelweaponbutton3-sheet0.png",
|
||||||
|
"images/gamepanelweaponbutton3-sheet1.png",
|
||||||
|
"images/gamepanelweaponbutton3-sheet2.png",
|
||||||
|
"images/gamepanelweaponbutton4-sheet0.png",
|
||||||
|
"images/gamepanelweaponbutton4-sheet1.png",
|
||||||
|
"images/gamepanelweaponbutton4-sheet2.png",
|
||||||
|
"images/gamepanelbuttonproperty-sheet0.png",
|
||||||
|
"images/gamepanelbuttonproperty-sheet1.png",
|
||||||
|
"images/gamepanelbuttonproperty-sheet2.png",
|
||||||
|
"images/gamecharacter-sheet0.png",
|
||||||
|
"images/gamecharacterhealth-sheet0.png",
|
||||||
|
"images/gamecharacterhealthnegative-sheet0.png",
|
||||||
|
"images/gamecharacterhealthbg-sheet0.png",
|
||||||
|
"images/gamepowerupbox-sheet0.png",
|
||||||
|
"images/gamepowerupheal-sheet0.png",
|
||||||
|
"images/gamepowerupice-sheet0.png",
|
||||||
|
"images/gamemoney-sheet0.png",
|
||||||
|
"images/gamecomponent-sheet0.png",
|
||||||
|
"images/gamecomponent-sheet1.png",
|
||||||
|
"images/gamecomponentparticle.png",
|
||||||
|
"images/gameki-sheet0.png",
|
||||||
|
"images/tutorialhand-sheet0.png",
|
||||||
|
"images/tutorialhand-sheet1.png",
|
||||||
|
"images/tutorialpointer-sheet0.png",
|
||||||
|
"images/game-sheet0.png",
|
||||||
|
"images/gameprojectile-sheet0.png",
|
||||||
|
"images/gameprojectile-sheet1.png",
|
||||||
|
"images/camera-sheet0.png",
|
||||||
|
"images/gamebackground-sheet0.png",
|
||||||
|
"images/gamebackground-sheet1.png",
|
||||||
|
"images/gamepoints-sheet0.png",
|
||||||
|
"images/gametutorial-sheet0.png",
|
||||||
|
"images/pausegear-sheet0.png",
|
||||||
|
"images/pausegear-sheet1.png",
|
||||||
|
"images/pausehome-sheet0.png",
|
||||||
|
"images/pausehome-sheet1.png",
|
||||||
|
"images/pauseplaypause-sheet0.png",
|
||||||
|
"images/pausemute-sheet0.png",
|
||||||
|
"images/pause25g-sheet0.png",
|
||||||
|
"images/pause25g-sheet1.png",
|
||||||
|
"images/pauseveil-sheet0.png",
|
||||||
|
"images/pausebg-sheet0.png",
|
||||||
|
"images/huddivider-sheet0.png",
|
||||||
|
"images/hudtutorialpointer-sheet0.png",
|
||||||
|
"images/hudtutorialpoint-sheet0.png",
|
||||||
|
"images/hudtutorialplatform.png",
|
||||||
|
"images/fullscreenicon-sheet0.png",
|
||||||
|
"images/effectexplosion-sheet0.png",
|
||||||
|
"images/effectexplosion-sheet1.png",
|
||||||
|
"images/patreonbutton-sheet0.png",
|
||||||
|
"images/25gbutton-sheet0.png",
|
||||||
|
"images/kordexbutton-sheet0.png",
|
||||||
|
"images/startgamebutton-sheet0.png",
|
||||||
|
"images/startgamebutton-sheet1.png",
|
||||||
|
"images/startgamebutton-sheet2.png",
|
||||||
|
"images/highscorebutton-sheet0.png",
|
||||||
|
"images/highscorebutton-sheet1.png",
|
||||||
|
"images/highscorebutton-sheet2.png",
|
||||||
|
"images/halloffamebutton-sheet0.png",
|
||||||
|
"images/halloffamebutton-sheet1.png",
|
||||||
|
"images/halloffamebutton-sheet2.png",
|
||||||
|
"images/shopbutton-sheet0.png",
|
||||||
|
"images/shopbutton-sheet1.png",
|
||||||
|
"images/shopbutton-sheet2.png",
|
||||||
|
"images/menuselectionsingle-sheet0.png",
|
||||||
|
"images/menuselectionmulti-sheet0.png",
|
||||||
|
"images/menuselectionmodeoverlay-sheet0.png",
|
||||||
|
"images/menuselectionnationoverlay-sheet0.png",
|
||||||
|
"images/menuselectionkim-sheet0.png",
|
||||||
|
"images/menuselectionkim-sheet1.png",
|
||||||
|
"images/menuselectionkim-sheet2.png",
|
||||||
|
"images/menuselectiontrump-sheet0.png",
|
||||||
|
"images/menuselectiontrump-sheet1.png",
|
||||||
|
"images/menuselectiontrump-sheet2.png",
|
||||||
|
"images/menuselectionmodeclose-sheet0.png",
|
||||||
|
"images/menuselectionmodeclose-sheet1.png",
|
||||||
|
"images/menuselectionmodeclose-sheet2.png",
|
||||||
|
"images/title-sheet0.png",
|
||||||
|
"images/tmpclear-sheet0.png",
|
||||||
|
"images/menubackground-sheet0.png",
|
||||||
|
"images/menupatreonbg-sheet0.png",
|
||||||
|
"images/menuface-sheet0.png",
|
||||||
|
"images/menuweapons-sheet0.png",
|
||||||
|
"images/menuclouds-sheet0.png",
|
||||||
|
"images/highscorefirstpage-sheet0.png",
|
||||||
|
"images/highscorefirstpage-sheet1.png",
|
||||||
|
"images/highscorefirstpage-sheet2.png",
|
||||||
|
"images/highscoreclose-sheet0.png",
|
||||||
|
"images/highscoreclose-sheet1.png",
|
||||||
|
"images/highscoreclose-sheet2.png",
|
||||||
|
"images/highscorelastpage-sheet0.png",
|
||||||
|
"images/highscorelastpage-sheet1.png",
|
||||||
|
"images/highscorelastpage-sheet2.png",
|
||||||
|
"images/highscorenextpage-sheet0.png",
|
||||||
|
"images/highscorenextpage-sheet1.png",
|
||||||
|
"images/highscorenextpage-sheet2.png",
|
||||||
|
"images/highscorepersonalbest-sheet0.png",
|
||||||
|
"images/highscorepersonalbest-sheet1.png",
|
||||||
|
"images/highscorepersonalbest-sheet2.png",
|
||||||
|
"images/highscorepreviouspage-sheet0.png",
|
||||||
|
"images/highscorepreviouspage-sheet1.png",
|
||||||
|
"images/highscorepreviouspage-sheet2.png",
|
||||||
|
"images/highscoreentriesbackground.png",
|
||||||
|
"images/highscorenamebg.png",
|
||||||
|
"images/highscoretitle-sheet0.png",
|
||||||
|
"images/highscorepersonalbackground.png",
|
||||||
|
"images/betaoverlay.png",
|
||||||
|
"images/betalogo-sheet0.png",
|
||||||
|
"images/betatitlebg.png",
|
||||||
|
"images/betahome-sheet0.png",
|
||||||
|
"images/betahome-sheet1.png",
|
||||||
|
"images/betarestart-sheet0.png",
|
||||||
|
"images/betarestart-sheet1.png",
|
||||||
|
"images/betatextstripe.png",
|
||||||
|
"images/betatextboxbg.png",
|
||||||
|
"images/betacallsign-sheet0.png",
|
||||||
|
"images/challengeblast-sheet0.png",
|
||||||
|
"images/challengedroids-sheet0.png",
|
||||||
|
"images/challengefists-sheet0.png",
|
||||||
|
"images/challengekillvictims-sheet0.png",
|
||||||
|
"images/challengerebels-sheet0.png",
|
||||||
|
"images/challengerescuevictims-sheet0.png",
|
||||||
|
"images/challengestormtroopers-sheet0.png",
|
||||||
|
"images/challengecrystal-sheet0.png",
|
||||||
|
"images/challengecrystalmanager-sheet0.png",
|
||||||
|
"images/challengeshopsocket-sheet0.png",
|
||||||
|
"images/challengeshopsocket-sheet1.png",
|
||||||
|
"images/challengecrystaliconcosts-sheet0.png",
|
||||||
|
"images/challengeitemicon-sheet0.png",
|
||||||
|
"images/challengeitemicon-sheet1.png",
|
||||||
|
"images/challengeitemicon-sheet2.png",
|
||||||
|
"images/challengebuy-sheet0.png",
|
||||||
|
"images/challengeshopmanager-sheet0.png",
|
||||||
|
"images/challengeexplanationbg.png",
|
||||||
|
"images/challengeposition-sheet0.png",
|
||||||
|
"images/challengemanager-sheet0.png",
|
||||||
|
"images/hofrowname.png",
|
||||||
|
"images/hofrowtext.png",
|
||||||
|
"images/hofbadget-sheet0.png",
|
||||||
|
"images/hofbadget-sheet1.png",
|
||||||
|
"images/hofprev-sheet0.png",
|
||||||
|
"images/hofnext-sheet0.png",
|
||||||
|
"images/hofheader-sheet0.png",
|
||||||
|
"images/hofbackground.png",
|
||||||
|
"images/hofback-sheet0.png",
|
||||||
|
"images/hofback-sheet1.png",
|
||||||
|
"images/hofback-sheet2.png",
|
||||||
|
"images/hoffooter-sheet0.png",
|
||||||
|
"images/hofbecomepatron-sheet0.png",
|
||||||
|
"images/loginclosebutton-sheet0.png",
|
||||||
|
"images/loginclosebutton-sheet1.png",
|
||||||
|
"images/loginclosebutton-sheet2.png",
|
||||||
|
"images/25glogo-sheet0.png",
|
||||||
|
"images/loginicon-sheet0.png",
|
||||||
|
"images/loginicon-sheet1.png",
|
||||||
|
"images/loginmanager-sheet0.png",
|
||||||
|
"images/intrologo-sheet0.png",
|
||||||
|
"images/introhole-sheet0.png",
|
||||||
|
"images/introbew-sheet0.png",
|
||||||
|
"images/introquote-sheet0.png",
|
||||||
|
"images/endwinner-sheet0.png",
|
||||||
|
"images/endface-sheet0.png",
|
||||||
|
"images/endface-sheet1.png",
|
||||||
|
"images/endglow-sheet0.png",
|
||||||
|
"images/pointcalcnamebg.png",
|
||||||
|
"images/pointcalcrestart-sheet0.png",
|
||||||
|
"images/pointcalcrestart-sheet1.png",
|
||||||
|
"images/pointcalcrestart-sheet2.png",
|
||||||
|
"images/pointcalchome-sheet0.png",
|
||||||
|
"images/pointcalchome-sheet1.png",
|
||||||
|
"images/pointcalchome-sheet2.png",
|
||||||
|
"images/pointcalcpatreon-sheet0.png",
|
||||||
|
"images/pointbackground-sheet0.png",
|
||||||
|
"images/pointscorebg-sheet0.png",
|
||||||
|
"images/pointscorebg-sheet1.png",
|
||||||
|
"images/pointscoreicon-sheet0.png",
|
||||||
|
"images/pointscoreicon-sheet1.png",
|
||||||
|
"images/pointrankflag-sheet0.png",
|
||||||
|
"images/pointflag-sheet0.png",
|
||||||
|
"images/pointflag-sheet1.png",
|
||||||
|
"images/patreonbg-sheet0.png",
|
||||||
|
"images/patreontext-sheet0.png",
|
||||||
|
"images/patreonsupport-sheet0.png",
|
||||||
|
"images/patreonsupport-sheet1.png",
|
||||||
|
"images/patreonskip-sheet0.png",
|
||||||
|
"images/patreonskip-sheet1.png",
|
||||||
|
"images/patreonliked-sheet0.png",
|
||||||
|
"images/patreonpics-sheet0.png",
|
||||||
|
"images/patreonveil-sheet0.png",
|
||||||
|
"media/button1.m4a",
|
||||||
|
"media/button1.ogg",
|
||||||
|
"media/menu.m4a",
|
||||||
|
"media/menu.ogg",
|
||||||
|
"media/bomb_explosion.m4a",
|
||||||
|
"media/bomb_explosion.ogg",
|
||||||
|
"media/bomb_explosion_large.m4a",
|
||||||
|
"media/bomb_explosion_large.ogg",
|
||||||
|
"media/combine_parts.m4a",
|
||||||
|
"media/combine_parts.ogg",
|
||||||
|
"media/get_hit.m4a",
|
||||||
|
"media/get_hit.ogg",
|
||||||
|
"media/loose_game.m4a",
|
||||||
|
"media/loose_game.ogg",
|
||||||
|
"media/powerup_health.m4a",
|
||||||
|
"media/powerup_health.ogg",
|
||||||
|
"media/powerup_iced.m4a",
|
||||||
|
"media/powerup_iced.ogg",
|
||||||
|
"media/unlock_weapon.m4a",
|
||||||
|
"media/unlock_weapon.ogg",
|
||||||
|
"media/weapon_select_basic.m4a",
|
||||||
|
"media/weapon_select_basic.ogg",
|
||||||
|
"media/weapon_select_bazooka.m4a",
|
||||||
|
"media/weapon_select_bazooka.ogg",
|
||||||
|
"media/weapon_select_sniper.m4a",
|
||||||
|
"media/weapon_select_sniper.ogg",
|
||||||
|
"media/missile_shot_basic.m4a",
|
||||||
|
"media/missile_shot_basic.ogg",
|
||||||
|
"media/missile_shot_machinegun.m4a",
|
||||||
|
"media/missile_shot_machinegun.ogg",
|
||||||
|
"media/weapon_select_machinegun.m4a",
|
||||||
|
"media/weapon_select_machinegun.ogg",
|
||||||
|
"media/button_redbutton.m4a",
|
||||||
|
"media/button_redbutton.ogg",
|
||||||
|
"media/missile_shot_bazooka.m4a",
|
||||||
|
"media/missile_shot_bazooka.ogg",
|
||||||
|
"media/money_cash.m4a",
|
||||||
|
"media/money_cash.ogg",
|
||||||
|
"media/fire_burn2.m4a",
|
||||||
|
"media/fire_burn2.ogg",
|
||||||
|
"media/fire_burn1.m4a",
|
||||||
|
"media/fire_burn1.ogg",
|
||||||
|
"media/game2.m4a",
|
||||||
|
"media/game2.ogg",
|
||||||
|
"media/game.m4a",
|
||||||
|
"media/game.ogg",
|
||||||
|
"icon-16.png",
|
||||||
|
"icon-32.png",
|
||||||
|
"icon-114.png",
|
||||||
|
"icon-128.png",
|
||||||
|
"icon-256.png",
|
||||||
|
"loading-logo.png"
|
||||||
|
]
|
||||||
|
}
|
53
trump/offlineClient.js
Normal file
53
trump/offlineClient.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
class OfflineClient
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
// Create a BroadcastChannel, if supported.
|
||||||
|
this._broadcastChannel = (typeof BroadcastChannel === "undefined" ? null : new BroadcastChannel("offline"));
|
||||||
|
|
||||||
|
// Queue of messages received before a message callback is set.
|
||||||
|
this._queuedMessages = [];
|
||||||
|
|
||||||
|
// The message callback.
|
||||||
|
this._onMessageCallback = null;
|
||||||
|
|
||||||
|
// If BroadcastChannel is supported, listen for messages.
|
||||||
|
if (this._broadcastChannel)
|
||||||
|
this._broadcastChannel.onmessage = (e => this._OnBroadcastChannelMessage(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
_OnBroadcastChannelMessage(e)
|
||||||
|
{
|
||||||
|
// Have a message callback set: just forward the call.
|
||||||
|
if (this._onMessageCallback)
|
||||||
|
{
|
||||||
|
this._onMessageCallback(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise the app hasn't loaded far enough to set a message callback.
|
||||||
|
// Buffer the incoming messages to replay when the app sets a callback.
|
||||||
|
this._queuedMessages.push(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetMessageCallback(f)
|
||||||
|
{
|
||||||
|
this._onMessageCallback = f;
|
||||||
|
|
||||||
|
// Replay any queued messages through the handler, then clear the queue.
|
||||||
|
for (let e of this._queuedMessages)
|
||||||
|
this._onMessageCallback(e);
|
||||||
|
|
||||||
|
this._queuedMessages.length = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the offline client ASAP so we receive and start queueing any messages the SW broadcasts.
|
||||||
|
window.OfflineClientInfo = new OfflineClient();
|
||||||
|
|
||||||
|
}());
|
||||||
|
|
334
trump/sw.js
Normal file
334
trump/sw.js
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const OFFLINE_DATA_FILE = "offline.js";
|
||||||
|
const CACHE_NAME_PREFIX = "c2offline";
|
||||||
|
const BROADCASTCHANNEL_NAME = "offline";
|
||||||
|
const CONSOLE_PREFIX = "[SW] ";
|
||||||
|
|
||||||
|
// Create a BroadcastChannel if supported.
|
||||||
|
const broadcastChannel = (typeof BroadcastChannel === "undefined" ? null : new BroadcastChannel(BROADCASTCHANNEL_NAME));
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
// Utility methods
|
||||||
|
function PostBroadcastMessage(o)
|
||||||
|
{
|
||||||
|
if (!broadcastChannel)
|
||||||
|
return; // not supported
|
||||||
|
|
||||||
|
// Impose artificial (and arbitrary!) delay of 3 seconds to make sure client is listening by the time the message is sent.
|
||||||
|
// Note we could remove the delay on some messages, but then we create a race condition where sometimes messages can arrive
|
||||||
|
// in the wrong order (e.g. "update ready" arrives before "started downloading update"). So to keep the consistent ordering,
|
||||||
|
// delay all messages by the same amount.
|
||||||
|
setTimeout(() => broadcastChannel.postMessage(o), 3000);
|
||||||
|
};
|
||||||
|
|
||||||
|
function Broadcast(type)
|
||||||
|
{
|
||||||
|
PostBroadcastMessage({
|
||||||
|
"type": type
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function BroadcastDownloadingUpdate(version)
|
||||||
|
{
|
||||||
|
PostBroadcastMessage({
|
||||||
|
"type": "downloading-update",
|
||||||
|
"version": version
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function BroadcastUpdateReady(version)
|
||||||
|
{
|
||||||
|
PostBroadcastMessage({
|
||||||
|
"type": "update-ready",
|
||||||
|
"version": version
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetCacheBaseName()
|
||||||
|
{
|
||||||
|
// Include the scope to avoid name collisions with any other SWs on the same origin.
|
||||||
|
// e.g. "c2offline-https://example.com/foo/" (won't collide with anything under bar/)
|
||||||
|
return CACHE_NAME_PREFIX + "-" + self.registration.scope;
|
||||||
|
};
|
||||||
|
|
||||||
|
function GetCacheVersionName(version)
|
||||||
|
{
|
||||||
|
// Append the version number to the cache name.
|
||||||
|
// e.g. "c2offline-https://example.com/foo/-v2"
|
||||||
|
return GetCacheBaseName() + "-v" + version;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return caches.keys() filtered down to just caches we're interested in (with the right base name).
|
||||||
|
// This filters out caches from unrelated scopes.
|
||||||
|
function GetAvailableCacheNames()
|
||||||
|
{
|
||||||
|
return caches.keys()
|
||||||
|
.then(cacheNames =>
|
||||||
|
{
|
||||||
|
const cacheBaseName = GetCacheBaseName();
|
||||||
|
return cacheNames.filter(n => n.startsWith(cacheBaseName));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Identify if an update is pending, which is the case when we have 2 or more available caches.
|
||||||
|
// One must be an update that is waiting, since the next navigate that does an upgrade will
|
||||||
|
// delete all the old caches leaving just one currently-in-use cache.
|
||||||
|
function IsUpdatePending()
|
||||||
|
{
|
||||||
|
return GetAvailableCacheNames()
|
||||||
|
.then(availableCacheNames => availableCacheNames.length >= 2);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Automatically deduce the main page URL (e.g. index.html or main.aspx) from the available browser windows.
|
||||||
|
// This prevents having to hard-code an index page in the file list, implicitly caching it like AppCache did.
|
||||||
|
function GetMainPageUrl()
|
||||||
|
{
|
||||||
|
return clients.matchAll({
|
||||||
|
includeUncontrolled: true,
|
||||||
|
type: "window"
|
||||||
|
})
|
||||||
|
.then(clients =>
|
||||||
|
{
|
||||||
|
for (let c of clients)
|
||||||
|
{
|
||||||
|
// Parse off the scope from the full client URL, e.g. https://example.com/index.html -> index.html
|
||||||
|
let url = c.url;
|
||||||
|
if (url.startsWith(self.registration.scope))
|
||||||
|
url = url.substring(self.registration.scope.length);
|
||||||
|
|
||||||
|
if (url && url !== "/") // ./ is also implicitly cached so don't bother returning that
|
||||||
|
{
|
||||||
|
// If the URL is solely a search string, prefix it with / to ensure it caches correctly.
|
||||||
|
// e.g. https://example.com/?foo=bar needs to cache as /?foo=bar, not just ?foo=bar.
|
||||||
|
if (url.startsWith("?"))
|
||||||
|
url = "/" + url;
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""; // no main page URL could be identified
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hack to fetch optionally bypassing HTTP cache until fetch cache options are supported in Chrome (crbug.com/453190)
|
||||||
|
function fetchWithBypass(request, bypassCache)
|
||||||
|
{
|
||||||
|
if (typeof request === "string")
|
||||||
|
request = new Request(request);
|
||||||
|
|
||||||
|
if (bypassCache)
|
||||||
|
{
|
||||||
|
// bypass enabled: add a random search parameter to avoid getting a stale HTTP cache result
|
||||||
|
const url = new URL(request.url);
|
||||||
|
url.search += Math.floor(Math.random() * 1000000);
|
||||||
|
|
||||||
|
return fetch(url, {
|
||||||
|
headers: request.headers,
|
||||||
|
mode: request.mode,
|
||||||
|
credentials: request.credentials,
|
||||||
|
redirect: request.redirect,
|
||||||
|
cache: "no-store"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// bypass disabled: perform normal fetch which is allowed to return from HTTP cache
|
||||||
|
return fetch(request);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Effectively a cache.addAll() that only creates the cache on all requests being successful (as a weak attempt at making it atomic)
|
||||||
|
// and can optionally cache-bypass with fetchWithBypass in every request
|
||||||
|
function CreateCacheFromFileList(cacheName, fileList, bypassCache)
|
||||||
|
{
|
||||||
|
// Kick off all requests and wait for them all to complete
|
||||||
|
return Promise.all(fileList.map(url => fetchWithBypass(url, bypassCache)))
|
||||||
|
.then(responses =>
|
||||||
|
{
|
||||||
|
// Check if any request failed. If so don't move on to opening the cache.
|
||||||
|
// This makes sure we only open a cache if all requests succeeded.
|
||||||
|
let allOk = true;
|
||||||
|
|
||||||
|
for (let response of responses)
|
||||||
|
{
|
||||||
|
if (!response.ok)
|
||||||
|
{
|
||||||
|
allOk = false;
|
||||||
|
console.error(CONSOLE_PREFIX + "Error fetching '" + originalUrl + "' (" + response.status + " " + response.statusText + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allOk)
|
||||||
|
throw new Error("not all resources were fetched successfully");
|
||||||
|
|
||||||
|
// Can now assume all responses are OK. Open a cache and write all responses there.
|
||||||
|
// TODO: ideally we can do this transactionally to ensure a complete cache is written as one atomic operation.
|
||||||
|
// This needs either new transactional features in the spec, or at the very least a way to rename a cache
|
||||||
|
// (so we can write to a temporary name that won't be returned by GetAvailableCacheNames() and then rename it when ready).
|
||||||
|
return caches.open(cacheName)
|
||||||
|
.then(cache =>
|
||||||
|
{
|
||||||
|
return Promise.all(responses.map(
|
||||||
|
(response, i) => cache.put(fileList[i], response)
|
||||||
|
));
|
||||||
|
})
|
||||||
|
.catch(err =>
|
||||||
|
{
|
||||||
|
// Not sure why cache.put() would fail (maybe if storage quota exceeded?) but in case it does,
|
||||||
|
// clean up the cache to try to avoid leaving behind an incomplete cache.
|
||||||
|
console.error(CONSOLE_PREFIX + "Error writing cache entries: ", err);
|
||||||
|
caches.delete(cacheName);
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function UpdateCheck(isFirst)
|
||||||
|
{
|
||||||
|
// Always bypass cache when requesting offline.js to make sure we find out about new versions.
|
||||||
|
return fetchWithBypass(OFFLINE_DATA_FILE, true)
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data =>
|
||||||
|
{
|
||||||
|
const version = data.version;
|
||||||
|
let fileList = data.fileList;
|
||||||
|
const currentCacheName = GetCacheVersionName(version);
|
||||||
|
|
||||||
|
return caches.has(currentCacheName)
|
||||||
|
.then(cacheExists =>
|
||||||
|
{
|
||||||
|
// Don't recache if there is already a cache that exists for this version. Assume it is complete.
|
||||||
|
if (cacheExists)
|
||||||
|
{
|
||||||
|
// Log whether we are up-to-date or pending an update.
|
||||||
|
return IsUpdatePending()
|
||||||
|
.then(isUpdatePending =>
|
||||||
|
{
|
||||||
|
if (isUpdatePending)
|
||||||
|
{
|
||||||
|
console.log(CONSOLE_PREFIX + "Update pending");
|
||||||
|
Broadcast("update-pending");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
console.log(CONSOLE_PREFIX + "Up to date");
|
||||||
|
Broadcast("up-to-date");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implicitly add the main page URL to the file list, e.g. "index.html", so we don't have to assume a specific name.
|
||||||
|
return GetMainPageUrl()
|
||||||
|
.then(mainPageUrl =>
|
||||||
|
{
|
||||||
|
// Prepend the main page URL to the file list if we found one and it is not already in the list.
|
||||||
|
// Also make sure we request the base / which should serve the main page.
|
||||||
|
fileList.unshift("./");
|
||||||
|
|
||||||
|
if (mainPageUrl && fileList.indexOf(mainPageUrl) === -1)
|
||||||
|
fileList.unshift(mainPageUrl);
|
||||||
|
|
||||||
|
console.log(CONSOLE_PREFIX + "Caching " + fileList.length + " files for offline use");
|
||||||
|
|
||||||
|
if (isFirst)
|
||||||
|
Broadcast("downloading");
|
||||||
|
else
|
||||||
|
BroadcastDownloadingUpdate(version);
|
||||||
|
|
||||||
|
// Note we don't bypass the cache on the first update check. This is because SW installation and the following
|
||||||
|
// update check caching will race with the normal page load requests. For any normal loading fetches that have already
|
||||||
|
// completed or are in-flight, it is pointless and wasteful to cache-bust the request for offline caching, since that
|
||||||
|
// forces a second network request to be issued when a response from the browser HTTP cache would be fine.
|
||||||
|
return CreateCacheFromFileList(currentCacheName, fileList, !isFirst)
|
||||||
|
.then(IsUpdatePending)
|
||||||
|
.then(isUpdatePending =>
|
||||||
|
{
|
||||||
|
if (isUpdatePending)
|
||||||
|
{
|
||||||
|
console.log(CONSOLE_PREFIX + "All resources saved, update ready");
|
||||||
|
BroadcastUpdateReady(version);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
console.log(CONSOLE_PREFIX + "All resources saved, offline support ready");
|
||||||
|
Broadcast("offline-ready");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(err =>
|
||||||
|
{
|
||||||
|
// Update check fetches fail when we're offline, but in case there's any other kind of problem with it, log a warning.
|
||||||
|
console.warn(CONSOLE_PREFIX + "Update check failed: ", err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
self.addEventListener('install', event =>
|
||||||
|
{
|
||||||
|
// On install kick off an update check to cache files on first use.
|
||||||
|
// If it fails we can still complete the install event and leave the SW running, we'll just
|
||||||
|
// retry on the next navigate.
|
||||||
|
event.waitUntil(
|
||||||
|
UpdateCheck(true) // first update
|
||||||
|
.catch(() => null)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener('fetch', event =>
|
||||||
|
{
|
||||||
|
const isNavigateRequest = (event.request.mode === "navigate");
|
||||||
|
|
||||||
|
let responsePromise = GetAvailableCacheNames()
|
||||||
|
.then(availableCacheNames =>
|
||||||
|
{
|
||||||
|
// No caches available: go to network
|
||||||
|
if (!availableCacheNames.length)
|
||||||
|
return fetch(event.request);
|
||||||
|
|
||||||
|
// Resolve with the cache name to use.
|
||||||
|
return Promise.resolve().then(() =>
|
||||||
|
{
|
||||||
|
// Prefer the oldest cache available. This avoids mixed-version responses by ensuring that if a new cache
|
||||||
|
// is created and filled due to an update check while the page is running, we keep returning resources
|
||||||
|
// from the original (oldest) cache only.
|
||||||
|
if (availableCacheNames.length === 1 || !isNavigateRequest)
|
||||||
|
return availableCacheNames[0];
|
||||||
|
|
||||||
|
// We are making a navigate request with more than one cache available. Check if we can expire any old ones.
|
||||||
|
return clients.matchAll().then(clients =>
|
||||||
|
{
|
||||||
|
// If there are other clients open, don't expire anything yet. We don't want to delete any caches they
|
||||||
|
// might be using, which could cause mixed-version responses.
|
||||||
|
// TODO: verify client count is as expected in navigate requests.
|
||||||
|
// TODO: find a way to upgrade on reloading the only client. Chrome seems to think there are 2 clients in that case.
|
||||||
|
if (clients.length > 1)
|
||||||
|
return availableCacheNames[0];
|
||||||
|
|
||||||
|
// Identify newest cache to use. Delete all the others.
|
||||||
|
let latestCacheName = availableCacheNames[availableCacheNames.length - 1];
|
||||||
|
console.log(CONSOLE_PREFIX + "Updating to new version");
|
||||||
|
|
||||||
|
return Promise.all(availableCacheNames.slice(0, -1)
|
||||||
|
.map(c => caches.delete(c)))
|
||||||
|
.then(() => latestCacheName);
|
||||||
|
});
|
||||||
|
}).then(useCacheName =>
|
||||||
|
{
|
||||||
|
return caches.open(useCacheName)
|
||||||
|
.then(c => c.match(event.request))
|
||||||
|
.then(response => response || fetch(event.request));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isNavigateRequest)
|
||||||
|
{
|
||||||
|
// allow the main request to complete, then check for updates
|
||||||
|
event.waitUntil(responsePromise
|
||||||
|
.then(() => UpdateCheck(false))); // not first check
|
||||||
|
}
|
||||||
|
|
||||||
|
event.respondWith(responsePromise);
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user