This commit is contained in:
37
themes/Eclectic/assets/js/debug.js
Normal file
37
themes/Eclectic/assets/js/debug.js
Normal file
@@ -0,0 +1,37 @@
|
||||
window.onscroll = function(ev) {
|
||||
if (window.innerHeight + window.pageYOffset >= document.body.offsetHeight) {
|
||||
console.log('bottom of page');
|
||||
}
|
||||
};
|
||||
|
||||
var debugToggle = document.querySelector('.debug-toggle');
|
||||
var debugWrapper = document.querySelector('.debug');
|
||||
var debugMainContent = document.querySelector('.debug-content');
|
||||
|
||||
debugToggle.onclick = function() {
|
||||
debugToggle.classList.toggle('active');
|
||||
debugWrapper.classList.toggle('expanded');
|
||||
debugMainContent.classList.toggle('open');
|
||||
// window.scrollTo(0, document.body.scrollHeight);
|
||||
};
|
||||
|
||||
function openTab(evt, tabName) {
|
||||
// Declare all variables
|
||||
var i, tabcontent, tablinks;
|
||||
|
||||
// Get all elements with class="tabcontent" and hide them
|
||||
tabcontent = document.getElementsByClassName('tabcontent');
|
||||
for (i = 0; i < tabcontent.length; i++) {
|
||||
tabcontent[i].style.display = 'none';
|
||||
}
|
||||
|
||||
// Get all elements with class="tablinks" and remove the class "active"
|
||||
tablinks = document.getElementsByClassName('tablinks');
|
||||
for (i = 0; i < tablinks.length; i++) {
|
||||
tablinks[i].className = tablinks[i].className.replace(' active', '');
|
||||
}
|
||||
|
||||
// Show the current tab, and add an "active" class to the button that opened the tab
|
||||
document.getElementById(tabName).style.display = 'block';
|
||||
evt.currentTarget.className += ' active';
|
||||
}
|
496
themes/Eclectic/assets/js/main.js
Normal file
496
themes/Eclectic/assets/js/main.js
Normal file
@@ -0,0 +1,496 @@
|
||||
// Wrap in an IFFE.
|
||||
(() => {
|
||||
///////////////////////////////////////////
|
||||
// Remove the no-js class from the html tag that is meant for the noscript mode.
|
||||
///////////////////////////////////////////
|
||||
document.getElementsByTagName('html')[0].classList.remove('no-js');
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Log for fellow developers.
|
||||
///////////////////////////////////////////
|
||||
console.info(`%c Welcome to ${document.location.hostname}`, "padding:20px; font: 38px Impact, sans-serif; color: #ddd; text-shadow: 0 1px 1px #bbb,0 2px 0 #999, 0 3px 0 #888, 0 4px 0 #777, 0 5px 0 #666, 0 6px 0 #555, 0 7px 0 #444, 0 8px 0 #333, 0 9px 7px #302314;");
|
||||
console.info("If you find something cool and would like to learn more, please contact me using the contact page. Will love to hear from a fellow developer");
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Convert date to hours from now
|
||||
///////////////////////////////////////////
|
||||
function getAgoTime(value) {
|
||||
if (!value) { return ""; }
|
||||
const d = new Date(value.trim());
|
||||
const now = new Date();
|
||||
const seconds = Math.round(Math.abs((now.getTime() - d.getTime()) / 1000));
|
||||
const minutes = Math.round(Math.abs(seconds / 60));
|
||||
const hours = Math.round(Math.abs(minutes / 60));
|
||||
const days = Math.round(Math.abs(hours / 24));
|
||||
const months = Math.round(Math.abs(days / 30.416));
|
||||
const years = Math.round(Math.abs(days / 365));
|
||||
if (seconds <= 45) {
|
||||
return 'a few seconds ago';
|
||||
} else if (seconds <= 90) {
|
||||
return 'a minute ago';
|
||||
} else if (minutes <= 45) {
|
||||
return minutes + ' minutes ago';
|
||||
} else if (minutes <= 90) {
|
||||
return 'an hour ago';
|
||||
} else if (hours <= 22) {
|
||||
return hours + ' hours ago';
|
||||
} else if (hours <= 36) {
|
||||
return 'a day ago';
|
||||
} else if (days <= 25) {
|
||||
return days + ' days ago';
|
||||
} else if (days <= 45) {
|
||||
return 'a month ago';
|
||||
} else if (days <= 345) {
|
||||
return months + ' months ago';
|
||||
} else if (days <= 545) {
|
||||
return 'a year ago';
|
||||
} else { // (days > 545)
|
||||
return years + ' years ago';
|
||||
}
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
Array.from(document.getElementsByTagName('time')).forEach((x) => {
|
||||
if (x.className === 'now') {
|
||||
x.innerText = new Date().getFullYear();
|
||||
} else {
|
||||
x.innerText = getAgoTime(x.getAttribute('datetime'));
|
||||
}
|
||||
// TODO: Attach event listener to update the string here.
|
||||
});
|
||||
});
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Contact Us form
|
||||
///////////////////////////////////////////
|
||||
const contact = document.getElementById("contact-us-form");
|
||||
if (contact) {
|
||||
contact.addEventListener("submit", (e) => {
|
||||
e.preventDefault();
|
||||
fetch(contact.getAttribute("action") || window.location.pathname, {
|
||||
method: 'POST',
|
||||
body: new FormData(contact)
|
||||
}).then(response => {
|
||||
// Remove contact and add Thank You.
|
||||
const div = document.createElement('div');
|
||||
div.className = "contact-response";
|
||||
if (response.ok) {
|
||||
div.innerHTML = '{{- default "Thank you" $.Site.Params.contact.response -}}';
|
||||
} else {
|
||||
div.innerHTML = '{{- default "There was an error sending this message. Please try again later" $.Site.Params.contact.responseError -}}'
|
||||
}
|
||||
contact.parentNode && contact.parentNode.replaceChild(div, contact);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Header
|
||||
///////////////////////////////////////////
|
||||
// Inspired by https://www.sysleaf.com/js-toggle-header-on-scroll/
|
||||
const consideredTop = 200;
|
||||
const height = document.documentElement.clientHeight;
|
||||
let lastKnownScrollY = 0;
|
||||
let currentScrollY = 0;
|
||||
let ticking = false;
|
||||
let eleHeader = null;
|
||||
let eleCheckbox = null;
|
||||
let eleScroll = null;
|
||||
let eleSearch = null;
|
||||
let hitCount = 0;
|
||||
const classes = {
|
||||
pinned: 'header-pin',
|
||||
unpinned: 'header-unpin',
|
||||
};
|
||||
function updateScrollPosition() {
|
||||
let winScroll = document.body.scrollTop || document.documentElement.scrollTop;
|
||||
let height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
|
||||
let scrolled = (winScroll / height) * 100;
|
||||
document.getElementById("scroll-indicator").style.width = scrolled + "%";
|
||||
}
|
||||
function onScroll() {
|
||||
currentScrollY = window.pageYOffset;
|
||||
requestAnimationFrame(updateScrollPosition);
|
||||
requestTick();
|
||||
}
|
||||
function requestTick() {
|
||||
if (eleCheckbox.checked || eleSearch === document.activeElement) {
|
||||
return;
|
||||
}
|
||||
if (!ticking) {
|
||||
requestAnimationFrame(update);
|
||||
}
|
||||
ticking = true;
|
||||
}
|
||||
function update() {
|
||||
ticking = false;
|
||||
if (eleCheckbox.checked) {
|
||||
return;
|
||||
}
|
||||
// Scroll to top hiding.
|
||||
if (currentScrollY > 2 * height) {
|
||||
if (!eleScroll.classList.contains('visible')) {
|
||||
eleScroll.classList.add('visible');
|
||||
}
|
||||
} else {
|
||||
if (eleScroll.classList.contains('visible')) {
|
||||
eleScroll.classList.remove('visible');
|
||||
}
|
||||
}
|
||||
// Header hiding
|
||||
// Ignore first 2 hits for safari reload in the center of the page
|
||||
// It is good enough with 2. First is the Js load,
|
||||
// Second is safari's scroll to position.
|
||||
if (hitCount > 2) {
|
||||
if (currentScrollY + height >= document.documentElement.scrollHeight || currentScrollY < consideredTop) {
|
||||
pin();
|
||||
} else if (currentScrollY < lastKnownScrollY) {
|
||||
pin();
|
||||
} else if (currentScrollY > lastKnownScrollY) {
|
||||
unpin();
|
||||
}
|
||||
}
|
||||
lastKnownScrollY = currentScrollY;
|
||||
hitCount++;
|
||||
}
|
||||
function pin() {
|
||||
if (eleHeader.classList.contains(classes.unpinned)) {
|
||||
eleHeader.classList.remove(classes.unpinned);
|
||||
eleHeader.classList.add(classes.pinned);
|
||||
eleScroll.classList.remove('unpin');
|
||||
}
|
||||
}
|
||||
function unpin() {
|
||||
if (eleHeader.classList.contains(classes.pinned) || !eleHeader.classList.contains(classes.unpinned)) {
|
||||
eleHeader.classList.remove(classes.pinned);
|
||||
eleHeader.classList.add(classes.unpinned);
|
||||
eleScroll.classList.add('unpin');
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
eleCheckbox = document.getElementsByClassName('hamburger')[0];
|
||||
eleSearch = document.querySelector('#searchbox input');
|
||||
eleHeader = document.getElementById('header');
|
||||
eleScroll = document.querySelector('.scroll-up');
|
||||
document.addEventListener('scroll', onScroll, false);
|
||||
});
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Color Picker
|
||||
///////////////////////////////////////////
|
||||
let color = window.localStorage.color || '{{- $.Site.Params.color -}}';
|
||||
function setColor(hex) {
|
||||
color = hex;
|
||||
window.localStorage.color = color;
|
||||
document.documentElement.style.setProperty("--theme-color", hex);
|
||||
}
|
||||
document.querySelectorAll('.theme-choice').forEach(s => {
|
||||
s.addEventListener('click', e => {
|
||||
const color = e.target.getAttribute('data-color');
|
||||
setColor("#" + color);
|
||||
});
|
||||
});
|
||||
|
||||
const colorSelector = document.querySelector('.custom-color');
|
||||
if (colorSelector) {
|
||||
colorSelector.addEventListener('click', evt => {
|
||||
evt.preventDefault();
|
||||
const x = document.createElement("input");
|
||||
x.setAttribute("type", "color");
|
||||
x.value = color;
|
||||
x.click();
|
||||
x.addEventListener('input', () => {
|
||||
setColor(x.value);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
const checkbox = document.querySelector('#color-switch');
|
||||
if (checkbox) {
|
||||
checkbox.checked = document.documentElement.classList.contains("dark");
|
||||
const handler = () => {
|
||||
if (checkbox.checked) {
|
||||
document.documentElement.classList.add("dark");
|
||||
} else {
|
||||
document.documentElement.classList.remove("dark");
|
||||
}
|
||||
window.localStorage.dark = checkbox.checked ? "true" : "false";
|
||||
};
|
||||
checkbox.addEventListener("change", handler);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Search
|
||||
///////////////////////////////////////////
|
||||
// {{- with .Site.Home.OutputFormats.Get "JSON" }}
|
||||
|
||||
class Search {
|
||||
constructor() {
|
||||
// Ignore errors loading search.
|
||||
this.prepare().catch(() => { });
|
||||
}
|
||||
|
||||
get resultTemplate() {
|
||||
return `<div>
|
||||
<img src="data:image/svg+xml;utf8,<svg width='50' height='50' xmlns='http://www.w3.org/2000/svg'/>" width="50" height="50" intrinsicsize="100x100" alt="Result"/>
|
||||
<h2></h2>
|
||||
<div class="description"></div>
|
||||
<span>
|
||||
{{- partialCached "util/icon" (dict "key" "calendar" "size" 12) "calendar-12" -}}
|
||||
<time></time>
|
||||
{{- partialCached "util/icon" (dict "key" "hourglass-1" "size" 12) "hourglass-1-12" -}}
|
||||
<span class="readingTime"></span>
|
||||
{{- partialCached "util/icon" (dict "key" "caret-square-o-right" "size" 12) "caret-square-o-right-12" -}}
|
||||
<span class="category"></span>
|
||||
</span>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
async prepare() {
|
||||
const response = await fetch('/index.json');
|
||||
const data = await response.json();
|
||||
if (data && data.length > 0) {
|
||||
this.data = data;
|
||||
document.querySelector('#searchbox').classList.add('visible');
|
||||
this.input = document.querySelector('#searchbox input');
|
||||
this.input.addEventListener('focus', this.triggerResults.bind(this));
|
||||
this.input.addEventListener('input', this.triggerResults.bind(this));
|
||||
this.input.addEventListener('keyup', this.handleKeyPress.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
triggerResults() {
|
||||
// We are delaying calculations but that's not a big performance issues.
|
||||
// If you type too fast on a slow browser, we won't get an animation frame.
|
||||
window.requestAnimationFrame(this.showSearchResults.bind(this));
|
||||
}
|
||||
|
||||
createResultDivs() {
|
||||
let data = '';
|
||||
for (var i = 0; i < 5; ++i) {
|
||||
data += this.resultTemplate;
|
||||
}
|
||||
document.querySelector('#searchbox .results').innerHTML = data;
|
||||
const divs = Array.from(document.querySelectorAll("#searchbox .results>div"));
|
||||
divs.forEach(x => {
|
||||
x.addEventListener('mousedown', this.handleClick, { passive: true });
|
||||
x.addEventListener('mouseover', () => this.selected = x, { passive: true });
|
||||
});
|
||||
return divs;
|
||||
}
|
||||
|
||||
handleClick(e) {
|
||||
window.location = e.currentTarget.dataset['href'];
|
||||
}
|
||||
|
||||
set selected(element) {
|
||||
if (this._selected !== element) {
|
||||
this._selected && this._selected.classList.remove('selected');
|
||||
element.classList.add('selected');
|
||||
this._selected = element;
|
||||
}
|
||||
}
|
||||
|
||||
get selected() {
|
||||
return this._selected;
|
||||
}
|
||||
|
||||
handleKeyPress(key) {
|
||||
const x = this.resultDivs.indexOf(this.selected);
|
||||
switch (key.code) {
|
||||
case 'ArrowDown':
|
||||
if (this.resultDivs.length > x + 1 && this.resultDivs[x + 1].style.display !== 'none') {
|
||||
this.selected = this.resultDivs[x + 1];
|
||||
}
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
if (x > 0) {
|
||||
this.selected = this.resultDivs[x - 1];
|
||||
}
|
||||
break;
|
||||
case 'Escape':
|
||||
this.input.blur();
|
||||
break;
|
||||
case 'Enter':
|
||||
window.location = this.selected.dataset['href'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
get indexedData() {
|
||||
return this.index = this.index || this.data.map(x => {
|
||||
let index = {};
|
||||
for (let y in x) {
|
||||
index[y] = JSON.stringify(x[y]).toLowerCase()
|
||||
}
|
||||
x._index = index;
|
||||
return x;
|
||||
});
|
||||
}
|
||||
|
||||
search(key) {
|
||||
key = key.toLowerCase();
|
||||
const fields = {
|
||||
'title': 1,
|
||||
'description': 0.3,
|
||||
'series': 0.5,
|
||||
'category': 0.5,
|
||||
'tags': 0.5,
|
||||
'contents': 0.1,
|
||||
'meta': 0.1
|
||||
}
|
||||
let data = this.indexedData;
|
||||
let results = [];
|
||||
if (this.lastKnownResults) {
|
||||
if (key.indexOf(this.lastKnownResults.key) === 0) {
|
||||
data = this.lastKnownResults.results;
|
||||
}
|
||||
}
|
||||
|
||||
// Search algorithm
|
||||
results = data.map(x => ({
|
||||
data: x,
|
||||
score: Object.keys(fields)
|
||||
.map(y => x._index[y].indexOf(key) !== -1 ? fields[y] : 0)
|
||||
.reduce((a, b) => a + b)
|
||||
}))
|
||||
.filter(x => x.score > 0)
|
||||
.sort((a, b) =>
|
||||
b.score - a.score !== 0 ? b.score - a.score : new Date(b.data.date) - new Date(a.data.date)
|
||||
)
|
||||
.map(x => x.data);
|
||||
|
||||
this.lastKnownResults = {
|
||||
key,
|
||||
results
|
||||
};
|
||||
return results;
|
||||
}
|
||||
|
||||
showSearchResults() {
|
||||
this.resultDivs = this.resultDivs || this.createResultDivs();
|
||||
|
||||
let results = [];
|
||||
if (this.input.value.length === 0) {
|
||||
results = this.data.sort((b, a) => new Date(a.date).getTime() - new Date(b.date).getTime());
|
||||
} else {
|
||||
results = this.search(this.input.value);
|
||||
}
|
||||
this.resultDivs.forEach((div, index) => {
|
||||
const result = results[index];
|
||||
if (index === 0) {
|
||||
this.selected = div;
|
||||
}
|
||||
if (!result) {
|
||||
div.style.display = "none";
|
||||
return;
|
||||
}
|
||||
const ago = getAgoTime(result.date);
|
||||
div.style.display = "block";
|
||||
div.dataset['href'] = result.permalink;
|
||||
div.querySelector('img').alt = result.title;
|
||||
div.querySelector('img').src = result.image;
|
||||
div.querySelector('h2').innerText = result.title;
|
||||
div.querySelector('time').innerText = ago.substr(0, ago.indexOf(" ago"));
|
||||
div.querySelector('time').datetime = result.date;
|
||||
div.querySelector('.description').innerText = result.description;
|
||||
div.querySelector('.readingTime').innerText = result.readingTime + 'm';
|
||||
div.querySelector('.category').innerText = result.category;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
new Search();
|
||||
// {{ end }}
|
||||
///////////////////////////////////////////
|
||||
// Service Worker
|
||||
///////////////////////////////////////////
|
||||
/*{{ if not hugo.IsServer }}*/
|
||||
if ('serviceWorker' in navigator && window.location.pathname !== '/offline') {
|
||||
navigator.serviceWorker.register('/sw.min.js', { scope: '/' });
|
||||
}
|
||||
/*{{ end }}*/
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// Link Pre-fetching to improve perceptible load times.
|
||||
////////////////////////////////////////////////////////
|
||||
Array.from(document.querySelectorAll('a')).forEach((link) => {
|
||||
let prefetchComplete = false;
|
||||
|
||||
const prefetch = () => {
|
||||
if (prefetchComplete) {
|
||||
return;
|
||||
}
|
||||
prefetchComplete = true;
|
||||
if (link.href.includes('mailto')) {
|
||||
return;
|
||||
}
|
||||
if (link.href.includes('http') && new URL(link.href).host !== new URL(window.location.href).host) {
|
||||
return;
|
||||
}
|
||||
if (link.href === window.location.href) {
|
||||
return;
|
||||
}
|
||||
const loader = document.createElement("link");
|
||||
loader.rel = "prefetch";
|
||||
loader.as = "document";
|
||||
loader.type = "text/html";
|
||||
loader.href = link.href;
|
||||
document.head.appendChild(loader);
|
||||
};
|
||||
link.addEventListener("mouseenter", prefetch, { passive: true });
|
||||
link.addEventListener("touchstart", prefetch, { passive: true });
|
||||
});
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Intersection Observer for animations
|
||||
///////////////////////////////////////////
|
||||
// Scroll animations for iPad and bigger
|
||||
if (window.innerWidth >= 768) {
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(x => {
|
||||
if (x.isIntersecting && !x.target.classList.contains('visible')) {
|
||||
x.target.classList.add('visible');
|
||||
}
|
||||
if (x.target.classList.contains('visible')) {
|
||||
observer.unobserve(x.target);
|
||||
}
|
||||
});
|
||||
});
|
||||
['.left-image', '.meta.default .item', '.item-icon-left .item', '.meta.default', '.max-2', '.max-2 .item', '.filter', '.filter .item',
|
||||
'.blog', '.blog .item', 'footer .items', '.contact', '.more', '.comments', '.comments form',
|
||||
'.item-icon-left', '.full-width', '.full-width .item'].forEach(s => {
|
||||
|
||||
document.querySelectorAll(s).forEach(x => {
|
||||
const rect = x.getBoundingClientRect();
|
||||
// All elements above the scroll are visible by default.
|
||||
// We only animate and scroll down.
|
||||
if (rect.y < 0) {
|
||||
x.classList.add('visible');
|
||||
} else {
|
||||
observer.observe(x);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// For mobile the hover animations
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(x => {
|
||||
if (x.isIntersecting && !x.target.classList.contains('hover')) {
|
||||
x.target.classList.add('hover');
|
||||
} else if (!x.isIntersecting && x.target.classList.contains('hover')) {
|
||||
x.target.classList.remove('hover')
|
||||
}
|
||||
});
|
||||
}, {
|
||||
rootMargin: "-20% 0px -70% 0px"
|
||||
});
|
||||
['p', 'li', '.meta .content', '.meta .item-cover', '.item-icon', '.main-icon', '.banner-holder', '.img-container', '.tex', '.i-tex', '.post aside svg', '.meta.carousel .item'].forEach(s => {
|
||||
document.querySelectorAll(s).forEach(x => {
|
||||
observer.observe(x);
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
|
100
themes/Eclectic/assets/js/sw.js
Normal file
100
themes/Eclectic/assets/js/sw.js
Normal file
@@ -0,0 +1,100 @@
|
||||
(function () {
|
||||
|
||||
// Update 'version' if you need to refresh the cache
|
||||
const staticCacheName = 'static';
|
||||
const version = 'v1::';
|
||||
const OFFLINE_URL = '{{"/off/" | absURL }}';
|
||||
|
||||
// Store core files in a cache (including a page to display when offline)
|
||||
// {{- $cover := partial "util/backgroundImage.html" . -}}
|
||||
// {{- $coverDark := partial "util/backgroundImageDark.html" . }}
|
||||
function updateStaticCache() {
|
||||
const urls = [
|
||||
'{{"/" | absURL}}',
|
||||
'{{"/manifest.json" | absURL}}',
|
||||
/*{{- range $.res }} /**/
|
||||
'{{.}}',
|
||||
/* {{ end }} /**/
|
||||
'{{$cover.Permalink}}',
|
||||
'{{$coverDark.Permalink}}',
|
||||
OFFLINE_URL,
|
||||
/* {{- if (fileExists "assets/image/logo.svg") -}} /**/
|
||||
'{{ (resources.Get "image/logo.svg" | resources.Minify).Permalink }}',
|
||||
/* {{- else if (fileExists "assets/image/logo.png") -}} /**/
|
||||
'{{ (resources.Get "image/logo.png").Permalink }}',
|
||||
/* {{- end -}} /**/
|
||||
'{{"/index.json" | absURL}}'
|
||||
];
|
||||
return caches.open(version + staticCacheName)
|
||||
.then((cache) => cache.addAll(urls)).catch(e => {
|
||||
console.log("Error = ", e);
|
||||
console.log(e.stack);
|
||||
});
|
||||
}
|
||||
|
||||
self.addEventListener('install', (event) => event.waitUntil(updateStaticCache()));
|
||||
|
||||
self.addEventListener('activate', function (event) {
|
||||
event.waitUntil(
|
||||
caches.keys()
|
||||
.then(function (keys) {
|
||||
// Remove caches whose name is no longer valid
|
||||
return Promise.all(keys
|
||||
.filter((key) => key.indexOf(version) !== 0)
|
||||
.map((key) => caches.delete(key))
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
function offline() {
|
||||
return caches.match(OFFLINE_URL).then(x => x.text()).then(y => new Response(y, { "status": 200, headers: { 'Content-Type': 'text/html' } }));
|
||||
}
|
||||
|
||||
self.addEventListener('fetch', function (event) {
|
||||
const request = event.request;
|
||||
// Always fetch non-GET requests from the network
|
||||
if (request.method !== 'GET') {
|
||||
return event.respondWith(fetch(request).catch(offline));
|
||||
}
|
||||
|
||||
// For HTML requests, try the network first, fall back to the cache, finally the offline page
|
||||
if (request.headers.get('Accept').indexOf('text/html') !== -1) {
|
||||
event.respondWith(
|
||||
fetch(request)
|
||||
.then(function (response) {
|
||||
// Stash a copy of this page in the cache
|
||||
const copy = response.clone();
|
||||
caches.open(version + staticCacheName)
|
||||
.then(function (cache) {
|
||||
cache.put(request, copy);
|
||||
});
|
||||
return response;
|
||||
})
|
||||
.catch(function () {
|
||||
return caches.match(request)
|
||||
.then(function (response) {
|
||||
return response || offline()
|
||||
})
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// For non-HTML requests, look in the cache first, fall back to the network
|
||||
event.respondWith(
|
||||
caches.match(request)
|
||||
.then(function (response) {
|
||||
return response || fetch(request)
|
||||
.catch(function () {
|
||||
// If the request is for an image, show an offline placeholder
|
||||
if (request.url.match(/\.(jpe?g|png|gif|svg)$/)) {
|
||||
return new Response('<svg role="img" aria-labelledby="offline-title" viewBox="0 0 400 225" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice"><title id="offline-title">Offline</title><path fill="rgba(145,145,145,0.5)" d="M0 0h400v225H0z" /><text fill="rgba(0,0,0,0.33)" font-family="Helvetica Neue,Arial,sans-serif" font-size="27" text-anchor="middle" x="200" y="113" dominant-baseline="central">Offline</text></svg>', { headers: { 'Content-Type': 'image/svg+xml' } });
|
||||
}
|
||||
return new Response('');
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
})();
|
Reference in New Issue
Block a user