style: 4 space indents to the JS files

This commit is contained in:
Cotes Chung 2022-10-25 19:26:44 +08:00
parent 1fd665bf49
commit 339293d0d7
No known key found for this signature in database
GPG key ID: 0D9E54843167A808
21 changed files with 913 additions and 912 deletions

View file

@ -2,10 +2,13 @@ root = true
[*] [*]
charset = utf-8 charset = utf-8
# 2 space indentation
indent_style = space indent_style = space
indent_size = 2 indent_size = 2
trim_trailing_whitespace = true trim_trailing_whitespace = true
# Unix-style newlines with a newline ending every file # Unix-style newlines with a newline ending every file
end_of_line = lf end_of_line = lf
insert_final_newline = true insert_final_newline = true
[*.js]
indent_size = 4

View file

@ -1,20 +1,20 @@
/* /**
Reference: https://bootsnipp.com/snippets/featured/link-to-top-page * Reference: https://bootsnipp.com/snippets/featured/link-to-top-page
*/ */
$(function() { $(function() {
$(window).scroll(() => { $(window).scroll(() => {
if ($(this).scrollTop() > 50 && if ($(this).scrollTop() > 50 &&
$("#sidebar-trigger").css("display") === "none") { $("#sidebar-trigger").css("display") === "none") {
$("#back-to-top").fadeIn(); $("#back-to-top").fadeIn();
} else { } else {
$("#back-to-top").fadeOut(); $("#back-to-top").fadeOut();
} }
}); });
$("#back-to-top").click(() => { $("#back-to-top").click(() => {
$("body,html").animate({ $("body,html").animate({
scrollTop: 0 scrollTop: 0
}, 800); }, 800);
return false; return false;
}); });
}); });

View file

@ -1,13 +1,13 @@
/* /**
* Listener for theme mode toggle * Listener for theme mode toggle
*/ */
$(function() { $(function () {
$(".mode-toggle").click((e) => { $(".mode-toggle").click((e) => {
const $target = $(e.target); const $target = $(e.target);
let $btn = ($target.prop("tagName") === "button".toUpperCase() ? let $btn = ($target.prop("tagName") === "button".toUpperCase() ?
$target : $target.parent()); $target : $target.parent());
$btn.blur(); // remove the clicking outline $btn.blur(); // remove the clicking outline
flipMode(); flipMode();
}); });
}); });

View file

@ -2,35 +2,37 @@
* A tool for smooth scrolling and topbar switcher * A tool for smooth scrolling and topbar switcher
*/ */
const ScrollHelper = (function () { const ScrollHelper = (function () {
const $body = $("body"); const $body = $("body");
const ATTR_TOPBAR_VISIBLE = "data-topbar-visible"; const ATTR_TOPBAR_VISIBLE = "data-topbar-visible";
const topbarHeight = $("#topbar-wrapper").outerHeight(); const topbarHeight = $("#topbar-wrapper").outerHeight();
let scrollUpCount = 0; // the number of times the scroll up was triggered by ToC or anchor let scrollUpCount = 0; // the number of times the scroll up was triggered by ToC or anchor
let topbarLocked = false; let topbarLocked = false;
let orientationLocked = false; let orientationLocked = false;
return { return {
hideTopbar: () => $body.attr(ATTR_TOPBAR_VISIBLE, false), hideTopbar: () => $body.attr(ATTR_TOPBAR_VISIBLE, false),
showTopbar: () => $body.attr(ATTR_TOPBAR_VISIBLE, true), showTopbar: () => $body.attr(ATTR_TOPBAR_VISIBLE, true),
// scroll up // scroll up
addScrollUpTask: () => { addScrollUpTask: () => {
scrollUpCount += 1; scrollUpCount += 1;
if (!topbarLocked) { topbarLocked = true; } if (!topbarLocked) {
}, topbarLocked = true;
popScrollUpTask: () => scrollUpCount -= 1, }
hasScrollUpTask: () => scrollUpCount > 0, },
topbarLocked: () => topbarLocked === true, popScrollUpTask: () => scrollUpCount -= 1,
unlockTopbar: () => topbarLocked = false, hasScrollUpTask: () => scrollUpCount > 0,
getTopbarHeight: () => topbarHeight, topbarLocked: () => topbarLocked === true,
unlockTopbar: () => topbarLocked = false,
getTopbarHeight: () => topbarHeight,
// orientation change // orientation change
orientationLocked: () => orientationLocked === true, orientationLocked: () => orientationLocked === true,
lockOrientation: () => orientationLocked = true, lockOrientation: () => orientationLocked = true,
unLockOrientation: () => orientationLocked = false unLockOrientation: () => orientationLocked = false
}; };
}()); }());

View file

@ -1,129 +1,129 @@
/* /**
* This script make #search-result-wrapper switch to unloaded or shown automatically. * This script make #search-result-wrapper switch to unloaded or shown automatically.
*/ */
$(function() { $(function () {
const btnSbTrigger = $("#sidebar-trigger"); const btnSbTrigger = $("#sidebar-trigger");
const btnSearchTrigger = $("#search-trigger"); const btnSearchTrigger = $("#search-trigger");
const btnCancel = $("#search-cancel"); const btnCancel = $("#search-cancel");
const main = $("#main"); const main = $("#main");
const topbarTitle = $("#topbar-title"); const topbarTitle = $("#topbar-title");
const searchWrapper = $("#search-wrapper"); const searchWrapper = $("#search-wrapper");
const resultWrapper = $("#search-result-wrapper"); const resultWrapper = $("#search-result-wrapper");
const results = $("#search-results"); const results = $("#search-results");
const input = $("#search-input"); const input = $("#search-input");
const hints = $("#search-hints"); const hints = $("#search-hints");
const scrollBlocker = (function () { const scrollBlocker = (function () {
let offset = 0; let offset = 0;
return { return {
block() { block() {
offset = window.scrollY; offset = window.scrollY;
$("html,body").scrollTop(0); $("html,body").scrollTop(0);
}, },
release() { release() {
$("html,body").scrollTop(offset); $("html,body").scrollTop(offset);
}, },
getOffset() { getOffset() {
return offset; return offset;
} }
}; };
}()); }());
/*--- Actions in mobile screens (Sidebar hidden) ---*/ /*--- Actions in mobile screens (Sidebar hidden) ---*/
const mobileSearchBar = (function () { const mobileSearchBar = (function () {
return { return {
on() { on() {
btnSbTrigger.addClass("unloaded"); btnSbTrigger.addClass("unloaded");
topbarTitle.addClass("unloaded"); topbarTitle.addClass("unloaded");
btnSearchTrigger.addClass("unloaded"); btnSearchTrigger.addClass("unloaded");
searchWrapper.addClass("d-flex"); searchWrapper.addClass("d-flex");
btnCancel.addClass("loaded"); btnCancel.addClass("loaded");
}, },
off() { off() {
btnCancel.removeClass("loaded"); btnCancel.removeClass("loaded");
searchWrapper.removeClass("d-flex"); searchWrapper.removeClass("d-flex");
btnSbTrigger.removeClass("unloaded"); btnSbTrigger.removeClass("unloaded");
topbarTitle.removeClass("unloaded"); topbarTitle.removeClass("unloaded");
btnSearchTrigger.removeClass("unloaded"); btnSearchTrigger.removeClass("unloaded");
} }
}; };
}()); }());
const resultSwitch = (function () { const resultSwitch = (function () {
let visible = false; let visible = false;
return { return {
on() { on() {
if (!visible) { if (!visible) {
// the block method must be called before $(#main) unloaded. // the block method must be called before $(#main) unloaded.
scrollBlocker.block(); scrollBlocker.block();
resultWrapper.removeClass("unloaded"); resultWrapper.removeClass("unloaded");
main.addClass("unloaded"); main.addClass("unloaded");
visible = true; visible = true;
} }
}, },
off() { off() {
if (visible) { if (visible) {
results.empty(); results.empty();
if (hints.hasClass("unloaded")) { if (hints.hasClass("unloaded")) {
hints.removeClass("unloaded"); hints.removeClass("unloaded");
} }
resultWrapper.addClass("unloaded"); resultWrapper.addClass("unloaded");
main.removeClass("unloaded"); main.removeClass("unloaded");
// now the release method must be called after $(#main) display // now the release method must be called after $(#main) display
scrollBlocker.release(); scrollBlocker.release();
input.val(""); input.val("");
visible = false; visible = false;
} }
}, },
isVisible() { isVisible() {
return visible; return visible;
} }
}; };
}()); }());
function isMobileView() { function isMobileView() {
return btnCancel.hasClass("loaded"); return btnCancel.hasClass("loaded");
}
btnSearchTrigger.click(function() {
mobileSearchBar.on();
resultSwitch.on();
input.focus();
});
btnCancel.click(function() {
mobileSearchBar.off();
resultSwitch.off();
});
input.focus(function() {
searchWrapper.addClass("input-focus");
});
input.focusout(function() {
searchWrapper.removeClass("input-focus");
});
input.on("input", () => {
if (input.val() === "") {
if (isMobileView()) {
hints.removeClass("unloaded");
} else {
resultSwitch.off();
}
} else {
resultSwitch.on();
if (isMobileView()) {
hints.addClass("unloaded");
}
} }
});
btnSearchTrigger.click(function () {
mobileSearchBar.on();
resultSwitch.on();
input.focus();
});
btnCancel.click(function () {
mobileSearchBar.off();
resultSwitch.off();
});
input.focus(function () {
searchWrapper.addClass("input-focus");
});
input.focusout(function () {
searchWrapper.removeClass("input-focus");
});
input.on("input", () => {
if (input.val() === "") {
if (isMobileView()) {
hints.removeClass("unloaded");
} else {
resultSwitch.off();
}
} else {
resultSwitch.on();
if (isMobileView()) {
hints.addClass("unloaded");
}
}
});
}); });

View file

@ -2,29 +2,27 @@
* Expand or close the sidebar in mobile screens. * Expand or close the sidebar in mobile screens.
*/ */
$(function() { $(function () {
const sidebarUtil = (function () {
const ATTR_DISPLAY = "sidebar-display";
let isExpanded = false;
const body = $("body");
const sidebarUtil = (function () { return {
const ATTR_DISPLAY = "sidebar-display"; toggle() {
let isExpanded = false; if (isExpanded === false) {
const body = $("body"); body.attr(ATTR_DISPLAY, "");
} else {
body.removeAttr(ATTR_DISPLAY);
}
return { isExpanded = !isExpanded;
toggle() { }
if (isExpanded === false) { };
body.attr(ATTR_DISPLAY, "");
} else {
body.removeAttr(ATTR_DISPLAY);
}
isExpanded = !isExpanded; }());
}
};
}()); $("#sidebar-trigger").click(sidebarUtil.toggle);
$("#sidebar-trigger").click(sidebarUtil.toggle);
$("#mask").click(sidebarUtil.toggle);
$("#mask").click(sidebarUtil.toggle);
}); });

View file

@ -1,6 +1,6 @@
/** /**
* Initial Bootstrap Tooltip. * Initial Bootstrap Tooltip.
*/ */
$(function () { $(function () {
$("[data-toggle=\"tooltip\"]").tooltip(); $("[data-toggle=\"tooltip\"]").tooltip();
}); });

View file

@ -1,90 +1,89 @@
/* /**
* Hide Header on scroll down * Hide Header on scroll down
*/ */
$(function() { $(function () {
const $searchInput = $("#search-input"); const $searchInput = $("#search-input");
const delta = ScrollHelper.getTopbarHeight(); const delta = ScrollHelper.getTopbarHeight();
let didScroll; let didScroll;
let lastScrollTop = 0; let lastScrollTop = 0;
function hasScrolled() { function hasScrolled() {
let st = $(this).scrollTop(); let st = $(this).scrollTop();
/* Make sure they scroll more than delta */ /* Make sure they scroll more than delta */
if (Math.abs(lastScrollTop - st) <= delta) { if (Math.abs(lastScrollTop - st) <= delta) {
return; return;
}
if (st > lastScrollTop ) { // Scroll Down
ScrollHelper.hideTopbar();
if ($searchInput.is(":focus")) {
$searchInput.blur(); /* remove focus */
}
} else { // Scroll up
// has not yet scrolled to the bottom of the screen, that is, there is still space for scrolling
if (st + $(window).height() < $(document).height()) {
if (ScrollHelper.hasScrollUpTask()) {
return;
} }
if (ScrollHelper.topbarLocked()) { // avoid redundant scroll up event from smooth scrolling if (st > lastScrollTop) { // Scroll Down
ScrollHelper.unlockTopbar(); ScrollHelper.hideTopbar();
} else {
if (ScrollHelper.orientationLocked()) { // avoid device auto scroll up on orientation change if ($searchInput.is(":focus")) {
ScrollHelper.unLockOrientation(); $searchInput.blur(); /* remove focus */
} else { }
ScrollHelper.showTopbar();
} } else { // Scroll up
// has not yet scrolled to the bottom of the screen, that is, there is still space for scrolling
if (st + $(window).height() < $(document).height()) {
if (ScrollHelper.hasScrollUpTask()) {
return;
}
if (ScrollHelper.topbarLocked()) { // avoid redundant scroll up event from smooth scrolling
ScrollHelper.unlockTopbar();
} else {
if (ScrollHelper.orientationLocked()) { // avoid device auto scroll up on orientation change
ScrollHelper.unLockOrientation();
} else {
ScrollHelper.showTopbar();
}
}
}
} }
}
lastScrollTop = st;
} // hasScrolled()
function handleLandscape() {
if ($(window).scrollTop() === 0) {
return;
}
ScrollHelper.lockOrientation();
ScrollHelper.hideTopbar();
} }
lastScrollTop = st; if (screen.orientation) {
screen.orientation.onchange = () => {
const type = screen.orientation.type;
if (type === "landscape-primary" || type === "landscape-secondary") {
handleLandscape();
}
};
} // hasScrolled() } else {
// for the browsers that not support `window.screen.orientation` API
function handleLandscape() { $(window).on("orientationchange", () => {
if ($(window).scrollTop() === 0) { if ($(window).width() < $(window).height()) { // before rotating, it is still in portrait mode.
return; handleLandscape();
}
});
} }
ScrollHelper.lockOrientation();
ScrollHelper.hideTopbar();
}
if (screen.orientation) { $(window).scroll(() => {
screen.orientation.onchange = () => { if (didScroll) {
const type = screen.orientation.type; return;
if (type === "landscape-primary" || type === "landscape-secondary") { }
handleLandscape(); didScroll = true;
}
};
} else {
// for the browsers that not support `window.screen.orientation` API
$(window).on("orientationchange",() => {
if ($(window).width() < $(window).height()) { // before rotating, it is still in portrait mode.
handleLandscape();
}
}); });
}
$(window).scroll(() => {
if (didScroll) {
return;
}
didScroll = true;
});
setInterval(() => {
if (didScroll) {
hasScrolled();
didScroll = false;
}
}, 250);
setInterval(() => {
if (didScroll) {
hasScrolled();
didScroll = false;
}
}, 250);
}); });

View file

@ -1,67 +1,67 @@
/* /**
* Top bar title auto change while scrolling up/down in mobile screens. * Top bar title auto change while scrolling up/down in mobile screens.
*/ */
$(function() { $(function () {
const titleSelector = "div.post>h1:first-of-type"; const titleSelector = "div.post>h1:first-of-type";
const $pageTitle = $(titleSelector); const $pageTitle = $(titleSelector);
const $topbarTitle = $("#topbar-title"); const $topbarTitle = $("#topbar-title");
if ($pageTitle.length === 0 /* on Home page */ if ($pageTitle.length === 0 /* on Home page */
|| $pageTitle.hasClass("dynamic-title") || $pageTitle.hasClass("dynamic-title")
|| $topbarTitle.is(":hidden")) {/* not in mobile views */ || $topbarTitle.is(":hidden")) {/* not in mobile views */
return; return;
}
const defaultTitleText = $topbarTitle.text().trim();
let pageTitleText = $pageTitle.text().trim();
let hasScrolled = false;
let lastScrollTop = 0;
if ($("#page-category").length || $("#page-tag").length) {
/* The title in Category or Tag page will be "<title> <count_of_posts>" */
if (/\s/.test(pageTitleText)) {
pageTitleText = pageTitleText.replace(/[0-9]/g, "").trim();
}
}
// When the page is scrolled down and then refreshed, the topbar title needs to be initialized
if ($pageTitle.offset().top < $(window).scrollTop()) {
$topbarTitle.text(pageTitleText);
}
let options = {
rootMargin: '-48px 0px 0px 0px', // 48px equals to the topbar height (3rem)
threshold: [0, 1]
};
let observer = new IntersectionObserver((entries) => {
if (!hasScrolled) {
hasScrolled = true;
return;
} }
let curScrollTop = $(window).scrollTop(); const defaultTitleText = $topbarTitle.text().trim();
let isScrollDown = lastScrollTop < curScrollTop; let pageTitleText = $pageTitle.text().trim();
lastScrollTop = curScrollTop; let hasScrolled = false;
let heading = entries[0]; let lastScrollTop = 0;
if (isScrollDown) { if ($("#page-category").length || $("#page-tag").length) {
if (heading.intersectionRatio === 0) { /* The title in Category or Tag page will be "<title> <count_of_posts>" */
if (/\s/.test(pageTitleText)) {
pageTitleText = pageTitleText.replace(/[0-9]/g, "").trim();
}
}
// When the page is scrolled down and then refreshed, the topbar title needs to be initialized
if ($pageTitle.offset().top < $(window).scrollTop()) {
$topbarTitle.text(pageTitleText); $topbarTitle.text(pageTitleText);
}
} else {
if (heading.intersectionRatio === 1) {
$topbarTitle.text(defaultTitleText);
}
} }
}, options);
observer.observe(document.querySelector(titleSelector)); let options = {
rootMargin: '-48px 0px 0px 0px', // 48px equals to the topbar height (3rem)
threshold: [0, 1]
};
/* Click title will scroll to top */ let observer = new IntersectionObserver((entries) => {
$topbarTitle.click(function() { if (!hasScrolled) {
$("body,html").animate({scrollTop: 0}, 800); hasScrolled = true;
}); return;
}
let curScrollTop = $(window).scrollTop();
let isScrollDown = lastScrollTop < curScrollTop;
lastScrollTop = curScrollTop;
let heading = entries[0];
if (isScrollDown) {
if (heading.intersectionRatio === 0) {
$topbarTitle.text(pageTitleText);
}
} else {
if (heading.intersectionRatio === 1) {
$topbarTitle.text(defaultTitleText);
}
}
}, options);
observer.observe(document.querySelector(titleSelector));
/* Click title will scroll to top */
$topbarTitle.click(function () {
$("body,html").animate({scrollTop: 0}, 800);
});
}); });

View file

@ -1,30 +1,30 @@
/* /**
* Tab 'Categories' expand/close effect. * Tab 'Categories' expand/close effect.
*/ */
$(function() { $(function () {
const childPrefix = "l_"; const childPrefix = "l_";
const parentPrefix = "h_"; const parentPrefix = "h_";
const collapse = $(".collapse"); const collapse = $(".collapse");
/* close up top-category */ /* close up top-category */
collapse.on("hide.bs.collapse", function () { /* Bootstrap collapse events. */ collapse.on("hide.bs.collapse", function () { /* Bootstrap collapse events. */
const parentId = parentPrefix + $(this).attr("id").substring(childPrefix.length); const parentId = parentPrefix + $(this).attr("id").substring(childPrefix.length);
if (parentId) { if (parentId) {
$(`#${parentId} .far.fa-folder-open`).attr("class", "far fa-folder fa-fw"); $(`#${parentId} .far.fa-folder-open`).attr("class", "far fa-folder fa-fw");
$(`#${parentId} i.fas`).addClass("rotate"); $(`#${parentId} i.fas`).addClass("rotate");
$(`#${parentId}`).removeClass("hide-border-bottom"); $(`#${parentId}`).removeClass("hide-border-bottom");
} }
}); });
/* expand the top category */ /* expand the top category */
collapse.on("show.bs.collapse", function() { collapse.on("show.bs.collapse", function () {
const parentId = parentPrefix + $(this).attr("id").substring(childPrefix.length); const parentId = parentPrefix + $(this).attr("id").substring(childPrefix.length);
if (parentId) { if (parentId) {
$(`#${parentId} .far.fa-folder`).attr("class", "far fa-folder-open fa-fw"); $(`#${parentId} .far.fa-folder`).attr("class", "far fa-folder-open fa-fw");
$(`#${parentId} i.fas`).removeClass("rotate"); $(`#${parentId} i.fas`).removeClass("rotate");
$(`#${parentId}`).addClass("hide-border-bottom"); $(`#${parentId}`).addClass("hide-border-bottom");
} }
}); });
}); });

View file

@ -1,4 +1,4 @@
/* /**
* Clipboard functions * Clipboard functions
* *
* Dependencies: * Dependencies:
@ -6,128 +6,128 @@
* - clipboard.js (https://github.com/zenorocha/clipboard.js) * - clipboard.js (https://github.com/zenorocha/clipboard.js)
*/ */
$(function() { $(function () {
const btnSelector = '.code-header>button'; const btnSelector = '.code-header>button';
const ICON_SUCCESS = 'fas fa-check'; const ICON_SUCCESS = 'fas fa-check';
const ATTR_TIMEOUT = 'timeout'; const ATTR_TIMEOUT = 'timeout';
const ATTR_TITLE_SUCCEED = 'data-title-succeed'; const ATTR_TITLE_SUCCEED = 'data-title-succeed';
const ATTR_TITLE_ORIGIN = 'data-original-title'; const ATTR_TITLE_ORIGIN = 'data-original-title';
const TIMEOUT = 2000; // in milliseconds const TIMEOUT = 2000; // in milliseconds
function isLocked(node) { function isLocked(node) {
if ($(node)[0].hasAttribute(ATTR_TIMEOUT)) { if ($(node)[0].hasAttribute(ATTR_TIMEOUT)) {
let timeout = $(node).attr(ATTR_TIMEOUT); let timeout = $(node).attr(ATTR_TIMEOUT);
if (Number(timeout) > Date.now()) { if (Number(timeout) > Date.now()) {
return true; return true;
} }
} }
return false; return false;
}
function lock(node) {
$(node).attr(ATTR_TIMEOUT, Date.now() + TIMEOUT);
}
function unlock(node) {
$(node).removeAttr(ATTR_TIMEOUT);
}
/* --- Copy code block --- */
// Initial the clipboard.js object
const clipboard = new ClipboardJS(btnSelector, {
target(trigger) {
let codeBlock = trigger.parentNode.nextElementSibling;
return codeBlock.querySelector('code .rouge-code');
}
});
$(btnSelector).tooltip({
trigger: 'hover',
placement: 'left'
});
function getIcon(btn) {
let iconNode = $(btn).children();
return iconNode.attr('class');
}
const ICON_DEFAULT = getIcon(btnSelector);
function showTooltip(btn) {
const succeedTitle = $(btn).attr(ATTR_TITLE_SUCCEED);
$(btn).attr(ATTR_TITLE_ORIGIN, succeedTitle).tooltip('show');
}
function hideTooltip(btn) {
$(btn).tooltip('hide').removeAttr(ATTR_TITLE_ORIGIN);
}
function setSuccessIcon(btn) {
let btnNode = $(btn);
let iconNode = btnNode.children();
iconNode.attr('class', ICON_SUCCESS);
}
function resumeIcon(btn) {
let btnNode = $(btn);
let iconNode = btnNode.children();
iconNode.attr('class', ICON_DEFAULT);
}
clipboard.on('success', (e) => {
e.clearSelection();
const trigger = e.trigger;
if (isLocked(trigger)) {
return;
} }
setSuccessIcon(trigger); function lock(node) {
showTooltip(trigger); $(node).attr(ATTR_TIMEOUT, Date.now() + TIMEOUT);
lock(trigger);
setTimeout(() => {
hideTooltip(trigger);
resumeIcon(trigger);
unlock(trigger);
}, TIMEOUT);
});
/* --- Post link sharing --- */
$('#copy-link').click((e) => {
let target = $(e.target);
if (isLocked(target)) {
return;
} }
// Copy URL to clipboard function unlock(node) {
$(node).removeAttr(ATTR_TIMEOUT);
}
const url = window.location.href; /* --- Copy code block --- */
const $temp = $("<input>");
$("body").append($temp); // Initial the clipboard.js object
$temp.val(url).select(); const clipboard = new ClipboardJS(btnSelector, {
document.execCommand("copy"); target(trigger) {
$temp.remove(); let codeBlock = trigger.parentNode.nextElementSibling;
return codeBlock.querySelector('code .rouge-code');
}
});
// Switch tooltip title $(btnSelector).tooltip({
trigger: 'hover',
placement: 'left'
});
const defaultTitle = target.attr(ATTR_TITLE_ORIGIN); function getIcon(btn) {
const succeedTitle = target.attr(ATTR_TITLE_SUCCEED); let iconNode = $(btn).children();
return iconNode.attr('class');
}
target.attr(ATTR_TITLE_ORIGIN, succeedTitle).tooltip('show'); const ICON_DEFAULT = getIcon(btnSelector);
lock(target);
setTimeout(() => { function showTooltip(btn) {
target.attr(ATTR_TITLE_ORIGIN, defaultTitle); const succeedTitle = $(btn).attr(ATTR_TITLE_SUCCEED);
unlock(target); $(btn).attr(ATTR_TITLE_ORIGIN, succeedTitle).tooltip('show');
}, TIMEOUT); }
}); function hideTooltip(btn) {
$(btn).tooltip('hide').removeAttr(ATTR_TITLE_ORIGIN);
}
function setSuccessIcon(btn) {
let btnNode = $(btn);
let iconNode = btnNode.children();
iconNode.attr('class', ICON_SUCCESS);
}
function resumeIcon(btn) {
let btnNode = $(btn);
let iconNode = btnNode.children();
iconNode.attr('class', ICON_DEFAULT);
}
clipboard.on('success', (e) => {
e.clearSelection();
const trigger = e.trigger;
if (isLocked(trigger)) {
return;
}
setSuccessIcon(trigger);
showTooltip(trigger);
lock(trigger);
setTimeout(() => {
hideTooltip(trigger);
resumeIcon(trigger);
unlock(trigger);
}, TIMEOUT);
});
/* --- Post link sharing --- */
$('#copy-link').click((e) => {
let target = $(e.target);
if (isLocked(target)) {
return;
}
// Copy URL to clipboard
const url = window.location.href;
const $temp = $("<input>");
$("body").append($temp);
$temp.val(url).select();
document.execCommand("copy");
$temp.remove();
// Switch tooltip title
const defaultTitle = target.attr(ATTR_TITLE_ORIGIN);
const succeedTitle = target.attr(ATTR_TITLE_SUCCEED);
target.attr(ATTR_TITLE_ORIGIN, succeedTitle).tooltip('show');
lock(target);
setTimeout(() => {
target.attr(ATTR_TITLE_ORIGIN, defaultTitle);
unlock(target);
}, TIMEOUT);
});
}); });

View file

@ -1,47 +1,46 @@
/** /**
Lazy load images (https://github.com/ApoorvSaxena/lozad.js) Lazy load images (https://github.com/ApoorvSaxena/lozad.js)
and popup when clicked (https://github.com/dimsemenov/Magnific-Popup) and popup when clicked (https://github.com/dimsemenov/Magnific-Popup)
*/ */
$(function() { $(function () {
const IMG_SCOPE = '#main > div.row:first-child > div:first-child';
const IMG_SCOPE = '#main > div.row:first-child > div:first-child'; if ($(`${IMG_SCOPE} img`).length <= 0) {
return;
if ($(`${IMG_SCOPE} img`).length <= 0 ) {
return;
}
/* lazy loading */
const imgList = document.querySelectorAll(`${IMG_SCOPE} img[data-src]`);
const observer = lozad(imgList);
observer.observe();
/* popup */
$(`${IMG_SCOPE} p > img[data-src],${IMG_SCOPE} img[data-src].preview-img`).each(
function() {
let nextTag = $(this).next();
const title = nextTag.prop('tagName') === 'EM' ? nextTag.text() : '';
const src = $(this).attr('data-src'); // created by lozad.js
$(this).wrap(`<a href="${src}" title="${title}" class="popup"></a>`);
} }
);
$('.popup').magnificPopup({ /* lazy loading */
type: 'image',
closeOnContentClick: true,
showCloseBtn: false,
zoom: {
enabled: true,
duration: 300,
easing: 'ease-in-out'
}
});
/* markup the image links */ const imgList = document.querySelectorAll(`${IMG_SCOPE} img[data-src]`);
const observer = lozad(imgList);
observer.observe();
$(`${IMG_SCOPE} a`).has('img').addClass('img-link'); /* popup */
$(`${IMG_SCOPE} p > img[data-src], ${IMG_SCOPE} img[data-src].preview-img`).each(
function () {
let nextTag = $(this).next();
const title = nextTag.prop('tagName') === 'EM' ? nextTag.text() : '';
const src = $(this).attr('data-src'); // created by lozad.js
$(this).wrap(`<a href="${src}" title="${title}" class="popup"></a>`);
}
);
$('.popup').magnificPopup({
type: 'image',
closeOnContentClick: true,
showCloseBtn: false,
zoom: {
enabled: true,
duration: 300,
easing: 'ease-in-out'
}
});
/* markup the image links */
$(`${IMG_SCOPE} a`).has('img').addClass('img-link');
}); });

View file

@ -6,38 +6,38 @@
/* A tool for locale datetime */ /* A tool for locale datetime */
const LocaleHelper = (function () { const LocaleHelper = (function () {
const locale = $('html').attr('lang').substr(0, 2); const locale = $('html').attr('lang').substr(0, 2);
const attrTimestamp = 'data-ts'; const attrTimestamp = 'data-ts';
const attrDateFormat = 'data-df'; const attrDateFormat = 'data-df';
return { return {
locale: () => locale, locale: () => locale,
attrTimestamp: () => attrTimestamp, attrTimestamp: () => attrTimestamp,
attrDateFormat: () => attrDateFormat, attrDateFormat: () => attrDateFormat,
getTimestamp: ($elem) => Number($elem.attr(attrTimestamp)), // unix timestamp getTimestamp: ($elem) => Number($elem.attr(attrTimestamp)), // unix timestamp
getDateFormat: ($elem) => $elem.attr(attrDateFormat) getDateFormat: ($elem) => $elem.attr(attrDateFormat)
}; };
}()); }());
$(function() { $(function () {
dayjs.locale(LocaleHelper.locale()); dayjs.locale(LocaleHelper.locale());
dayjs.extend(window.dayjs_plugin_localizedFormat); dayjs.extend(window.dayjs_plugin_localizedFormat);
$(`[${LocaleHelper.attrTimestamp()}]`).each(function () { $(`[${LocaleHelper.attrTimestamp()}]`).each(function () {
const date = dayjs.unix(LocaleHelper.getTimestamp($(this))); const date = dayjs.unix(LocaleHelper.getTimestamp($(this)));
const text = date.format(LocaleHelper.getDateFormat($(this))); const text = date.format(LocaleHelper.getDateFormat($(this)));
$(this).text(text); $(this).text(text);
$(this).removeAttr(LocaleHelper.attrTimestamp()); $(this).removeAttr(LocaleHelper.attrTimestamp());
$(this).removeAttr(LocaleHelper.attrDateFormat()); $(this).removeAttr(LocaleHelper.attrDateFormat());
// setup tooltips // setup tooltips
const tooltip = $(this).attr('data-toggle'); const tooltip = $(this).attr('data-toggle');
if (typeof tooltip === 'undefined' || tooltip !== 'tooltip') { if (typeof tooltip === 'undefined' || tooltip !== 'tooltip') {
return; return;
} }
const tooltipText = date.format('llll'); // see: https://day.js.org/docs/en/display/format#list-of-localized-formats const tooltipText = date.format('llll'); // see: https://day.js.org/docs/en/display/format#list-of-localized-formats
$(this).attr('data-original-title', tooltipText); $(this).attr('data-original-title', tooltipText);
}); });
}); });

View file

@ -1,4 +1,4 @@
/* /**
* Count page views form GA or local cache file. * Count page views form GA or local cache file.
* *
* Dependencies: * Dependencies:
@ -7,244 +7,244 @@
*/ */
const getInitStatus = (function () { const getInitStatus = (function () {
let hasInit = false; let hasInit = false;
return () => { return () => {
let ret = hasInit; let ret = hasInit;
if (!hasInit) { if (!hasInit) {
hasInit = true; hasInit = true;
} }
return ret; return ret;
}; };
}()); }());
const PvOpts = (function () { const PvOpts = (function () {
function getContent(selector) { function getContent(selector) {
return $(selector).attr("content"); return $(selector).attr("content");
}
function hasContent(selector) {
let content = getContent(selector);
return (typeof content !== "undefined" && content !== false);
}
return {
getProxyMeta() {
return getContent("meta[name=pv-proxy-endpoint]");
},
getLocalMeta() {
return getContent("meta[name=pv-cache-path]");
},
hasProxyMeta() {
return hasContent("meta[name=pv-proxy-endpoint]");
},
hasLocalMeta() {
return hasContent("meta[name=pv-cache-path]");
} }
};
function hasContent(selector) {
let content = getContent(selector);
return (typeof content !== "undefined" && content !== false);
}
return {
getProxyMeta() {
return getContent("meta[name=pv-proxy-endpoint]");
},
getLocalMeta() {
return getContent("meta[name=pv-cache-path]");
},
hasProxyMeta() {
return hasContent("meta[name=pv-proxy-endpoint]");
},
hasLocalMeta() {
return hasContent("meta[name=pv-cache-path]");
}
};
}()); }());
const PvStorage = (function () { const PvStorage = (function () {
const Keys = { const Keys = {
KEY_PV: "pv", KEY_PV: "pv",
KEY_PV_SRC: "pv_src", KEY_PV_SRC: "pv_src",
KEY_CREATION: "pv_created_date" KEY_CREATION: "pv_created_date"
}; };
const Source = { const Source = {
LOCAL: "same-origin", LOCAL: "same-origin",
PROXY: "cors" PROXY: "cors"
}; };
function get(key) { function get(key) {
return localStorage.getItem(key); return localStorage.getItem(key);
}
function set(key, val) {
localStorage.setItem(key, val);
}
function saveCache(pv, src) {
set(Keys.KEY_PV, pv);
set(Keys.KEY_PV_SRC, src);
set(Keys.KEY_CREATION, new Date().toJSON());
}
return {
keysCount() {
return Object.keys(Keys).length;
},
hasCache() {
return (localStorage.getItem(Keys.KEY_PV) !== null);
},
getCache() {
return JSON.parse(localStorage.getItem(Keys.KEY_PV));
},
saveLocalCache(pv) {
saveCache(pv, Source.LOCAL);
},
saveProxyCache(pv) {
saveCache(pv, Source.PROXY);
},
isExpired() {
let date = new Date(get(Keys.KEY_CREATION));
date.setHours(date.getHours() + 1); // per hour
return Date.now() >= date.getTime();
},
isFromLocal() {
return get(Keys.KEY_PV_SRC) === Source.LOCAL;
},
isFromProxy() {
return get(Keys.KEY_PV_SRC) === Source.PROXY;
},
newerThan(pv) {
return PvStorage.getCache().totalsForAllResults["ga:pageviews"] > pv.totalsForAllResults["ga:pageviews"];
},
inspectKeys() {
if (localStorage.length !== PvStorage.keysCount()) {
localStorage.clear();
return;
}
for(let i = 0; i < localStorage.length; i++){
const key = localStorage.key(i);
switch (key) {
case Keys.KEY_PV:
case Keys.KEY_PV_SRC:
case Keys.KEY_CREATION:
break;
default:
localStorage.clear();
return;
}
}
} }
};
function set(key, val) {
localStorage.setItem(key, val);
}
function saveCache(pv, src) {
set(Keys.KEY_PV, pv);
set(Keys.KEY_PV_SRC, src);
set(Keys.KEY_CREATION, new Date().toJSON());
}
return {
keysCount() {
return Object.keys(Keys).length;
},
hasCache() {
return (localStorage.getItem(Keys.KEY_PV) !== null);
},
getCache() {
return JSON.parse(localStorage.getItem(Keys.KEY_PV));
},
saveLocalCache(pv) {
saveCache(pv, Source.LOCAL);
},
saveProxyCache(pv) {
saveCache(pv, Source.PROXY);
},
isExpired() {
let date = new Date(get(Keys.KEY_CREATION));
date.setHours(date.getHours() + 1); // per hour
return Date.now() >= date.getTime();
},
isFromLocal() {
return get(Keys.KEY_PV_SRC) === Source.LOCAL;
},
isFromProxy() {
return get(Keys.KEY_PV_SRC) === Source.PROXY;
},
newerThan(pv) {
return PvStorage.getCache().totalsForAllResults["ga:pageviews"] > pv.totalsForAllResults["ga:pageviews"];
},
inspectKeys() {
if (localStorage.length !== PvStorage.keysCount()) {
localStorage.clear();
return;
}
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
switch (key) {
case Keys.KEY_PV:
case Keys.KEY_PV_SRC:
case Keys.KEY_CREATION:
break;
default:
localStorage.clear();
return;
}
}
}
};
}()); /* PvStorage */ }()); /* PvStorage */
function countUp(min, max, destId) { function countUp(min, max, destId) {
if (min < max) { if (min < max) {
let numAnim = new CountUp(destId, min, max); let numAnim = new CountUp(destId, min, max);
if (!numAnim.error) { if (!numAnim.error) {
numAnim.start(); numAnim.start();
} else { } else {
console.error(numAnim.error); console.error(numAnim.error);
}
} }
}
} }
function countPV(path, rows) { function countPV(path, rows) {
let count = 0; let count = 0;
if (typeof rows !== "undefined" ) { if (typeof rows !== "undefined") {
for (let i = 0; i < rows.length; ++i) { for (let i = 0; i < rows.length; ++i) {
const gaPath = rows[parseInt(i, 10)][0]; const gaPath = rows[parseInt(i, 10)][0];
if (gaPath === path) { /* path format see: site.permalink */ if (gaPath === path) { /* path format see: site.permalink */
count += parseInt(rows[parseInt(i, 10)][1], 10); count += parseInt(rows[parseInt(i, 10)][1], 10);
break; break;
} }
}
} }
}
return count; return count;
} }
function tacklePV(rows, path, elem, hasInit) { function tacklePV(rows, path, elem, hasInit) {
let count = countPV(path, rows); let count = countPV(path, rows);
count = (count === 0 ? 1 : count); count = (count === 0 ? 1 : count);
if (!hasInit) { if (!hasInit) {
elem.text(new Intl.NumberFormat().format(count)); elem.text(new Intl.NumberFormat().format(count));
} else { } else {
const initCount = parseInt(elem.text().replace(/,/g, ""), 10); const initCount = parseInt(elem.text().replace(/,/g, ""), 10);
if (count > initCount) { if (count > initCount) {
countUp(initCount, count, elem.attr("id")); countUp(initCount, count, elem.attr("id"));
}
} }
}
} }
function displayPageviews(data) { function displayPageviews(data) {
if (typeof data === "undefined") { if (typeof data === "undefined") {
return; return;
} }
let hasInit = getInitStatus(); let hasInit = getInitStatus();
const rows = data.rows; /* could be undefined */ const rows = data.rows; /* could be undefined */
if ($("#post-list").length > 0) { /* the Home page */ if ($("#post-list").length > 0) { /* the Home page */
$(".post-preview").each(function() { $(".post-preview").each(function () {
const path = $(this).find("a").attr("href"); const path = $(this).find("a").attr("href");
tacklePV(rows, path, $(this).find(".pageviews"), hasInit); tacklePV(rows, path, $(this).find(".pageviews"), hasInit);
}); });
} else if ($(".post").length > 0) { /* the post */ } else if ($(".post").length > 0) { /* the post */
const path = window.location.pathname; const path = window.location.pathname;
tacklePV(rows, path, $("#pv"), hasInit); tacklePV(rows, path, $("#pv"), hasInit);
} }
} }
function fetchProxyPageviews() { function fetchProxyPageviews() {
if (PvOpts.hasProxyMeta()) { if (PvOpts.hasProxyMeta()) {
$.ajax({ $.ajax({
type: "GET", type: "GET",
url: PvOpts.getProxyMeta(), url: PvOpts.getProxyMeta(),
dataType: "jsonp", dataType: "jsonp",
jsonpCallback: "displayPageviews", jsonpCallback: "displayPageviews",
success: (data) => { success: (data) => {
PvStorage.saveProxyCache(JSON.stringify(data)); PvStorage.saveProxyCache(JSON.stringify(data));
}, },
error: (jqXHR, textStatus, errorThrown) => { error: (jqXHR, textStatus, errorThrown) => {
console.log("Failed to load pageviews from proxy server: " + errorThrown); console.log("Failed to load pageviews from proxy server: " + errorThrown);
} }
}); });
} }
} }
function fetchLocalPageviews(hasCache = false) { function fetchLocalPageviews(hasCache = false) {
return fetch(PvOpts.getLocalMeta()) return fetch(PvOpts.getLocalMeta())
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (hasCache) { if (hasCache) {
// The cache from the proxy will sometimes be more recent than the local one // The cache from the proxy will sometimes be more recent than the local one
if (PvStorage.isFromProxy() && PvStorage.newerThan(data)) { if (PvStorage.isFromProxy() && PvStorage.newerThan(data)) {
return; return;
} }
} }
displayPageviews(data); displayPageviews(data);
PvStorage.saveLocalCache(JSON.stringify(data)); PvStorage.saveLocalCache(JSON.stringify(data));
}); });
} }
$(function() { $(function () {
if ($(".pageviews").length <= 0) { if ($(".pageviews").length <= 0) {
return; return;
}
PvStorage.inspectKeys();
if (PvStorage.hasCache()) {
displayPageviews(PvStorage.getCache());
if (PvStorage.isExpired()) {
if (PvOpts.hasLocalMeta()) {
fetchLocalPageviews(true).then(fetchProxyPageviews);
} else {
fetchProxyPageviews();
}
} else {
if (PvStorage.isFromLocal()) {
fetchProxyPageviews();
}
} }
} else { // no cached PvStorage.inspectKeys();
if (PvOpts.hasLocalMeta()) { if (PvStorage.hasCache()) {
fetchLocalPageviews().then(fetchProxyPageviews); displayPageviews(PvStorage.getCache());
} else {
fetchProxyPageviews(); if (PvStorage.isExpired()) {
if (PvOpts.hasLocalMeta()) {
fetchLocalPageviews(true).then(fetchProxyPageviews);
} else {
fetchProxyPageviews();
}
} else {
if (PvStorage.isFromLocal()) {
fetchProxyPageviews();
}
}
} else { // no cached
if (PvOpts.hasLocalMeta()) {
fetchLocalPageviews().then(fetchProxyPageviews);
} else {
fetchProxyPageviews();
}
} }
}
}); });

View file

@ -1,96 +1,96 @@
/* /**
Safari doesn't support CSS `scroll-behavior: smooth`, Safari doesn't support CSS `scroll-behavior: smooth`,
so here is a compatible solution for all browser to smooth scrolling so here is a compatible solution for all browser to smooth scrolling
See: <https://css-tricks.com/snippets/jquery/smooth-scrolling/> See: <https://css-tricks.com/snippets/jquery/smooth-scrolling/>
Warning: It must be called after all `<a>` tags (e.g., the dynamic TOC) are ready. Warning: It must be called after all `<a>` tags (e.g., the dynamic TOC) are ready.
*/ */
$(function() { $(function () {
const $topbarTitle = $("#topbar-title"); const $topbarTitle = $("#topbar-title");
const REM = 16; // in pixels const REM = 16; // in pixels
const ATTR_SCROLL_FOCUS = "scroll-focus"; const ATTR_SCROLL_FOCUS = "scroll-focus";
$("a[href*='#']") $("a[href*='#']")
.not("[href='#']") .not("[href='#']")
.not("[href='#0']") .not("[href='#0']")
.click(function(event) { .click(function (event) {
if (this.pathname.replace(/^\//, "") !== if (this.pathname.replace(/^\//, "") !==
location.pathname.replace(/^\//, "")) { location.pathname.replace(/^\//, "")) {
return; return;
} }
if (location.hostname !== this.hostname) { if (location.hostname !== this.hostname) {
return; return;
} }
const hash = decodeURI(this.hash); const hash = decodeURI(this.hash);
let toFootnoteRef = RegExp(/^#fnref:/).test(hash); let toFootnoteRef = RegExp(/^#fnref:/).test(hash);
let toFootnote = toFootnoteRef ? false : RegExp(/^#fn:/).test(hash); let toFootnote = toFootnoteRef ? false : RegExp(/^#fn:/).test(hash);
let selector = hash.includes(":") ? hash.replace(/:/g, "\\:") : hash; let selector = hash.includes(":") ? hash.replace(/:/g, "\\:") : hash;
let $target = $(selector); let $target = $(selector);
let isMobileViews = $topbarTitle.is(":visible"); let isMobileViews = $topbarTitle.is(":visible");
let isPortrait = $(window).width() < $(window).height(); let isPortrait = $(window).width() < $(window).height();
if (typeof $target === "undefined") { if (typeof $target === "undefined") {
return; return;
} }
event.preventDefault(); event.preventDefault();
if (history.pushState) { /* add hash to URL */ if (history.pushState) { /* add hash to URL */
history.pushState(null, null, hash); history.pushState(null, null, hash);
} }
let curOffset = $(window).scrollTop(); let curOffset = $(window).scrollTop();
let destOffset = $target.offset().top -= REM / 2; let destOffset = $target.offset().top -= REM / 2;
if (destOffset < curOffset) { // scroll up if (destOffset < curOffset) { // scroll up
ScrollHelper.hideTopbar(); ScrollHelper.hideTopbar();
ScrollHelper.addScrollUpTask(); ScrollHelper.addScrollUpTask();
if (isMobileViews && isPortrait) { if (isMobileViews && isPortrait) {
destOffset -= ScrollHelper.getTopbarHeight(); destOffset -= ScrollHelper.getTopbarHeight();
} }
} else { // scroll down } else { // scroll down
if (isMobileViews && isPortrait) { if (isMobileViews && isPortrait) {
destOffset -= ScrollHelper.getTopbarHeight(); destOffset -= ScrollHelper.getTopbarHeight();
} }
} }
$("html").animate({ $("html").animate({
scrollTop: destOffset scrollTop: destOffset
}, 500, () => { }, 500, () => {
$target.focus(); $target.focus();
/* clean up old scroll mark */ /* clean up old scroll mark */
if ($(`[${ATTR_SCROLL_FOCUS}=true]`).length) { if ($(`[${ATTR_SCROLL_FOCUS}=true]`).length) {
$(`[${ATTR_SCROLL_FOCUS}=true]`).attr(ATTR_SCROLL_FOCUS, false); $(`[${ATTR_SCROLL_FOCUS}=true]`).attr(ATTR_SCROLL_FOCUS, false);
} }
/* Clean :target links */ /* Clean :target links */
if ($(":target").length) { /* element that visited by the URL with hash */ if ($(":target").length) { /* element that visited by the URL with hash */
$(":target").attr(ATTR_SCROLL_FOCUS, false); $(":target").attr(ATTR_SCROLL_FOCUS, false);
} }
/* set scroll mark to footnotes */ /* set scroll mark to footnotes */
if (toFootnote || toFootnoteRef) { if (toFootnote || toFootnoteRef) {
$target.attr(ATTR_SCROLL_FOCUS, true); $target.attr(ATTR_SCROLL_FOCUS, true);
} }
if ($target.is(":focus")) { /* Checking if the target was focused */ if ($target.is(":focus")) { /* Checking if the target was focused */
return false; return false;
} else { } else {
$target.attr("tabindex", "-1"); /* Adding tabindex for elements not focusable */ $target.attr("tabindex", "-1"); /* Adding tabindex for elements not focusable */
$target.focus(); /* Set focus again */ $target.focus(); /* Set focus again */
} }
if (ScrollHelper.hasScrollUpTask()) { if (ScrollHelper.hasScrollUpTask()) {
ScrollHelper.popScrollUpTask(); ScrollHelper.popScrollUpTask();
} }
}); });
}); /* click() */ }); /* click() */
}); });

View file

@ -5,51 +5,50 @@ layout: compress
--- ---
const resource = [ const resource = [
/* --- CSS --- */
'{{ "/assets/css/style.css" | relative_url }}',
/* --- CSS --- */ /* --- PWA --- */
'{{ "/assets/css/style.css" | relative_url }}', '{{ "/app.js" | relative_url }}',
'{{ "/sw.js" | relative_url }}',
/* --- PWA --- */ /* --- HTML --- */
'{{ "/app.js" | relative_url }}', '{{ "/index.html" | relative_url }}',
'{{ "/sw.js" | relative_url }}', '{{ "/404.html" | relative_url }}',
/* --- HTML --- */ {% for tab in site.tabs %}
'{{ "/index.html" | relative_url }}', '{{ tab.url | relative_url }}',
'{{ "/404.html" | relative_url }}', {% endfor %}
{% for tab in site.tabs %}
'{{ tab.url | relative_url }}',
{% endfor %}
/* --- Favicons & compressed JS --- */
{% assign cache_list = site.static_files | where: 'swcache', true %}
{% for file in cache_list %}
'{{ file.path | relative_url }}'{%- unless forloop.last -%},{%- endunless -%}
{% endfor %}
/* --- Favicons & compressed JS --- */
{% assign cache_list = site.static_files | where: 'swcache', true %}
{% for file in cache_list %}
'{{ file.path | relative_url }}'{%- unless forloop.last -%},{%- endunless -%}
{% endfor %}
]; ];
/* The request url with below domain will be cached */ /* The request url with below domain will be cached */
const allowedDomains = [ const allowedDomains = [
{% if site.google_analytics.id != empty and site.google_analytics.id %} {% if site.google_analytics.id != empty and site.google_analytics.id %}
'www.googletagmanager.com', 'www.googletagmanager.com',
'www.google-analytics.com', 'www.google-analytics.com',
{% endif %} {% endif %}
'{{ site.url | split: "//" | last }}', '{{ site.url | split: "//" | last }}',
{% if site.img_cdn contains '//' and site.img_cdn %} {% if site.img_cdn contains '//' and site.img_cdn %}
'{{ site.img_cdn | split: '//' | last | split: '/' | first }}', '{{ site.img_cdn | split: '//' | last | split: '/' | first }}',
{% endif %} {% endif %}
'fonts.gstatic.com', 'fonts.gstatic.com',
'fonts.googleapis.com', 'fonts.googleapis.com',
'cdn.jsdelivr.net', 'cdn.jsdelivr.net',
'polyfill.io' 'polyfill.io'
]; ];
/* Requests that include the following path will be banned */ /* Requests that include the following path will be banned */
const denyUrls = [ const denyUrls = [
{% if site.google_analytics.pv.cache_path %} {% if site.google_analytics.pv.cache_path %}
'{{ site.google_analytics.pv.cache_path | absolute_url }}' '{{ site.google_analytics.pv.cache_path | absolute_url }}'
{% endif %} {% endif %}
]; ];

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -7,41 +7,41 @@ const $notification = $('#notification');
const $btnRefresh = $('#notification .toast-body>button'); const $btnRefresh = $('#notification .toast-body>button');
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
/* Registering Service Worker */ /* Registering Service Worker */
navigator.serviceWorker.register('{{ "/sw.js" | relative_url }}') navigator.serviceWorker.register('{{ "/sw.js" | relative_url }}')
.then(registration => { .then(registration => {
/* in case the user ignores the notification */ /* in case the user ignores the notification */
if (registration.waiting) { if (registration.waiting) {
$notification.toast('show'); $notification.toast('show');
}
registration.addEventListener('updatefound', () => {
registration.installing.addEventListener('statechange', () => {
if (registration.waiting) {
if (navigator.serviceWorker.controller) {
$notification.toast('show');
} }
}
registration.addEventListener('updatefound', () => {
registration.installing.addEventListener('statechange', () => {
if (registration.waiting) {
if (navigator.serviceWorker.controller) {
$notification.toast('show');
}
}
});
});
$btnRefresh.click(() => {
if (registration.waiting) {
registration.waiting.postMessage('SKIP_WAITING');
}
$notification.toast('hide');
});
}); });
});
$btnRefresh.click(() => { let refreshing = false;
if (registration.waiting) {
registration.waiting.postMessage('SKIP_WAITING'); /* Detect controller change and refresh all the opened tabs */
navigator.serviceWorker.addEventListener('controllerchange', () => {
if (!refreshing) {
window.location.reload();
refreshing = true;
} }
$notification.toast('hide'); });
});
}
);
let refreshing = false;
/* Detect controller change and refresh all the opened tabs */
navigator.serviceWorker.addEventListener('controllerchange', () => {
if (!refreshing) {
window.location.reload();
refreshing = true;
}
});
} }

View file

@ -9,81 +9,82 @@ self.importScripts('{{ "/assets/js/data/swcache.js" | relative_url }}');
const cacheName = 'chirpy-{{ "now" | date: "%Y%m%d.%H%M%S" }}'; const cacheName = 'chirpy-{{ "now" | date: "%Y%m%d.%H%M%S" }}';
function verifyDomain(url) { function verifyDomain(url) {
for (const domain of allowedDomains) { for (const domain of allowedDomains) {
const regex = RegExp(`^http(s)?:\/\/${domain}\/`); const regex = RegExp(`^http(s)?:\/\/${domain}\/`);
if (regex.test(url)) { if (regex.test(url)) {
return true; return true;
}
} }
}
return false; return false;
} }
function isExcluded(url) { function isExcluded(url) {
for (const item of denyUrls) { for (const item of denyUrls) {
if (url === item) { if (url === item) {
return true; return true;
}
} }
} return false;
return false;
} }
self.addEventListener('install', event => { self.addEventListener('install', event => {
event.waitUntil( event.waitUntil(
caches.open(cacheName).then(cache => { caches.open(cacheName).then(cache => {
return cache.addAll(resource); return cache.addAll(resource);
}) })
); );
}); });
self.addEventListener('activate', event => { self.addEventListener('activate', event => {
event.waitUntil( event.waitUntil(
caches.keys().then(keyList => { caches.keys().then(keyList => {
return Promise.all( return Promise.all(
keyList.map(key => { keyList.map(key => {
if (key !== cacheName) { if (key !== cacheName) {
return caches.delete(key); return caches.delete(key);
} }
})
);
}) })
); );
})
);
}); });
self.addEventListener('message', (event) => { self.addEventListener('message', (event) => {
if (event.data === 'SKIP_WAITING') { if (event.data === 'SKIP_WAITING') {
self.skipWaiting(); self.skipWaiting();
} }
}); });
self.addEventListener('fetch', event => { self.addEventListener('fetch', event => {
event.respondWith( event.respondWith(
caches.match(event.request).then(response => { caches.match(event.request).then(response => {
if (response) { if (response) {
return response; return response;
} }
return fetch(event.request).then(response => { return fetch(event.request).then(response => {
const url = event.request.url; const url = event.request.url;
if (event.request.method !== 'GET' || if (event.request.method !== 'GET' ||
!verifyDomain(url) || !verifyDomain(url) ||
isExcluded(url)) { isExcluded(url)) {
return response; return response;
} }
/* /*
see: <https://developers.google.com/web/fundamentals/primers/service-workers#cache_and_return_requests> see: <https://developers.google.com/web/fundamentals/primers/service-workers#cache_and_return_requests>
*/ */
let responseToCache = response.clone(); let responseToCache = response.clone();
caches.open(cacheName).then(cache => { caches.open(cacheName).then(cache => {
/* console.log('[sw] Caching new resource: ' + event.request.url); */ /* console.log('[sw] Caching new resource: ' + event.request.url); */
cache.put(event.request, responseToCache); cache.put(event.request, responseToCache);
}); });
return response; return response;
}); });
}) })
); );
}); });

View file

@ -4,9 +4,9 @@ permalink: '/unregister.js'
--- ---
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then((registrations) => { navigator.serviceWorker.getRegistrations().then((registrations) => {
for (let reg of registrations) { for (let reg of registrations) {
reg.unregister(); reg.unregister();
} }
}); });
} }