perf: replace jQuery with Vanilla JS (#1681)
Also replaced `magnific-popup` with `GLightbox`
This commit is contained in:
parent
c85e9e2394
commit
fe7afa379f
22 changed files with 274 additions and 305 deletions
|
@ -4,9 +4,6 @@ webfonts: /assets/lib/fonts/main.css
|
||||||
|
|
||||||
# Libraries
|
# Libraries
|
||||||
|
|
||||||
jquery:
|
|
||||||
js: /assets/lib/jquery/jquery.min.js
|
|
||||||
|
|
||||||
bootstrap:
|
bootstrap:
|
||||||
css: /assets/lib/bootstrap/bootstrap.min.css
|
css: /assets/lib/bootstrap/bootstrap.min.css
|
||||||
js: /assets/lib/bootstrap/bootstrap.bundle.min.js
|
js: /assets/lib/bootstrap/bootstrap.bundle.min.js
|
||||||
|
@ -31,9 +28,9 @@ dayjs:
|
||||||
relativeTime: /assets/lib/dayjs/plugin/relativeTime.min.js
|
relativeTime: /assets/lib/dayjs/plugin/relativeTime.min.js
|
||||||
localizedFormat: /assets/lib/dayjs/plugin/localizedFormat.min.js
|
localizedFormat: /assets/lib/dayjs/plugin/localizedFormat.min.js
|
||||||
|
|
||||||
magnific-popup:
|
glightbox:
|
||||||
css: /assets/lib/magnific-popup/magnific-popup.css
|
css: /assets/lib/glightbox/glightbox.min.css
|
||||||
js: /assets/lib/magnific-popup/jquery.magnific-popup.min.js
|
js: /assets/lib/glightbox/glightbox.min.js
|
||||||
|
|
||||||
lazy-polyfill:
|
lazy-polyfill:
|
||||||
css: /assets/lib/loading-attribute-polyfill/loading-attribute-polyfill.min.css
|
css: /assets/lib/loading-attribute-polyfill/loading-attribute-polyfill.min.css
|
||||||
|
|
|
@ -17,9 +17,6 @@ webfonts: https://fonts.googleapis.com/css2?family=Lato:wght@300;400&family=Sour
|
||||||
|
|
||||||
# Libraries
|
# Libraries
|
||||||
|
|
||||||
jquery:
|
|
||||||
js: https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js
|
|
||||||
|
|
||||||
bootstrap:
|
bootstrap:
|
||||||
css: https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css
|
css: https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css
|
||||||
js: https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js
|
js: https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js
|
||||||
|
@ -44,9 +41,9 @@ dayjs:
|
||||||
relativeTime: https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/relativeTime.min.js
|
relativeTime: https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/relativeTime.min.js
|
||||||
localizedFormat: https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/localizedFormat.min.js
|
localizedFormat: https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/localizedFormat.min.js
|
||||||
|
|
||||||
magnific-popup:
|
glightbox:
|
||||||
css: https://cdn.jsdelivr.net/npm/magnific-popup@1.1.0/dist/magnific-popup.min.css
|
css: https://cdn.jsdelivr.net/npm/glightbox@3.3.0/dist/css/glightbox.min.css
|
||||||
js: https://cdn.jsdelivr.net/npm/magnific-popup@1.1.0/dist/jquery.magnific-popup.min.js
|
js: https://cdn.jsdelivr.net/npm/glightbox@3.3.0/dist/js/glightbox.min.js
|
||||||
|
|
||||||
lazy-polyfill:
|
lazy-polyfill:
|
||||||
css: https://cdn.jsdelivr.net/npm/loading-attribute-polyfill@2.1.1/dist/loading-attribute-polyfill.min.css
|
css: https://cdn.jsdelivr.net/npm/loading-attribute-polyfill@2.1.1/dist/loading-attribute-polyfill.min.css
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
{ threshold: [0] }
|
{ threshold: [0] }
|
||||||
);
|
);
|
||||||
|
|
||||||
disqus_observer.observe(document.querySelector('#disqus_thread'));
|
disqus_observer.observe(document.getElementById('disqus_thread'));
|
||||||
|
|
||||||
/* Auto switch theme */
|
/* Auto switch theme */
|
||||||
function reloadDisqus() {
|
function reloadDisqus() {
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.querySelector('.mode-toggle')) {
|
if (document.getElementById('mode-toggle')) {
|
||||||
window.addEventListener('message', reloadDisqus);
|
window.addEventListener('message', reloadDisqus);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
(function () {
|
(function () {
|
||||||
const origin = 'https://giscus.app';
|
const origin = 'https://giscus.app';
|
||||||
const iframe = 'iframe.giscus-frame';
|
|
||||||
const lightTheme = 'light';
|
const lightTheme = 'light';
|
||||||
const darkTheme = 'dark_dimmed';
|
const darkTheme = 'dark_dimmed';
|
||||||
|
|
||||||
|
@ -58,7 +57,7 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const giscus = document.querySelector(iframe).contentWindow;
|
const giscus = document.getElementsByClassName('giscus-frame')[0].contentWindow;
|
||||||
giscus.postMessage({ giscus: message }, origin);
|
giscus.postMessage({ giscus: message }, origin);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
(function () {
|
(function () {
|
||||||
const origin = 'https://utteranc.es';
|
const origin = 'https://utteranc.es';
|
||||||
const iframe = 'iframe.utterances-frame';
|
|
||||||
const lightTheme = 'github-light';
|
const lightTheme = 'github-light';
|
||||||
const darkTheme = 'github-dark';
|
const darkTheme = 'github-dark';
|
||||||
let initTheme = lightTheme;
|
let initTheme = lightTheme;
|
||||||
|
@ -43,7 +42,7 @@
|
||||||
theme: theme
|
theme: theme
|
||||||
};
|
};
|
||||||
|
|
||||||
const utterances = document.querySelector(iframe).contentWindow;
|
const utterances = document.getElementsByClassName('utterances-frame')[0].contentWindow;
|
||||||
utterances.postMessage(message, origin);
|
utterances.postMessage(message, origin);
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -97,8 +97,8 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if page.layout == 'page' or page.layout == 'post' %}
|
{% if page.layout == 'page' or page.layout == 'post' %}
|
||||||
<!-- Manific Popup -->
|
<!-- Image Popup -->
|
||||||
<link rel="stylesheet" href="{{ site.data.origin[type].magnific-popup.css | relative_url }}">
|
<link rel="stylesheet" href="{{ site.data.origin[type].glightbox.css | relative_url }}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<!-- JavaScript -->
|
<!-- JavaScript -->
|
||||||
|
|
|
@ -2,12 +2,9 @@
|
||||||
|
|
||||||
<!-- commons -->
|
<!-- commons -->
|
||||||
|
|
||||||
{% assign urls = site.data.origin[type].jquery.js
|
{%- capture urls -%}
|
||||||
| append: ','
|
{{ site.data.origin[type].bootstrap.js }},{{ site.data.origin[type].search.js }}
|
||||||
| append: site.data.origin[type].bootstrap.js
|
{%- endcapture -%}
|
||||||
| append: ','
|
|
||||||
| append: site.data.origin[type].search.js
|
|
||||||
%}
|
|
||||||
|
|
||||||
<!-- layout specified -->
|
<!-- layout specified -->
|
||||||
|
|
||||||
|
@ -20,7 +17,7 @@
|
||||||
<!-- image lazy-loading & popup & clipboard -->
|
<!-- image lazy-loading & popup & clipboard -->
|
||||||
{% assign urls = urls
|
{% assign urls = urls
|
||||||
| append: ','
|
| append: ','
|
||||||
| append: site.data.origin[type]['magnific-popup'].js
|
| append: site.data.origin[type].glightbox.js
|
||||||
| append: ','
|
| append: ','
|
||||||
| append: site.data.origin[type].clipboard.js
|
| append: site.data.origin[type].clipboard.js
|
||||||
%}
|
%}
|
||||||
|
|
|
@ -1,29 +1,31 @@
|
||||||
<!-- mermaid-js loader -->
|
<!-- mermaid-js loader -->
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
(function () {
|
function updateMermaid(event) {
|
||||||
function updateMermaid(event) {
|
if (event.source === window && event.data && event.data.direction === ModeToggle.ID) {
|
||||||
if (event.source === window && event.data && event.data.direction === ModeToggle.ID) {
|
const mode = event.data.message;
|
||||||
const mode = event.data.message;
|
|
||||||
|
|
||||||
if (typeof mermaid === 'undefined') {
|
if (typeof mermaid === 'undefined') {
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
let expectedTheme = mode === ModeToggle.DARK_MODE ? 'dark' : 'default';
|
|
||||||
let config = { theme: expectedTheme };
|
|
||||||
|
|
||||||
/* Re-render the SVG › <https://github.com/mermaid-js/mermaid/issues/311#issuecomment-332557344> */
|
|
||||||
$('.mermaid').each(function () {
|
|
||||||
let svgCode = $(this).prev().children().html();
|
|
||||||
$(this).removeAttr('data-processed');
|
|
||||||
$(this).html(svgCode);
|
|
||||||
});
|
|
||||||
|
|
||||||
mermaid.initialize(config);
|
|
||||||
mermaid.init(undefined, '.mermaid');
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
let expectedTheme = mode === ModeToggle.DARK_MODE ? 'dark' : 'default';
|
||||||
|
let config = { theme: expectedTheme };
|
||||||
|
|
||||||
|
/* Re-render the SVG › <https://github.com/mermaid-js/mermaid/issues/311#issuecomment-332557344> */
|
||||||
|
const mermaidList = document.getElementsByClassName('mermaid');
|
||||||
|
|
||||||
|
[...mermaidList].forEach((elem) => {
|
||||||
|
const svgCode = elem.previousSibling.children.item(0).innerHTML;
|
||||||
|
elem.innerHTML = svgCode;
|
||||||
|
elem.removeAttribute('data-processed');
|
||||||
|
});
|
||||||
|
|
||||||
|
mermaid.initialize(config);
|
||||||
|
mermaid.init(undefined, '.mermaid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(function () {
|
||||||
let initTheme = 'default';
|
let initTheme = 'default';
|
||||||
const html = document.documentElement;
|
const html = document.documentElement;
|
||||||
|
|
||||||
|
@ -35,11 +37,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
let mermaidConf = {
|
let mermaidConf = {
|
||||||
theme: initTheme /* <default|dark|forest|neutral> */
|
theme: initTheme /* <default | dark | forest | neutral> */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Create mermaid tag */
|
/* Create mermaid tag */
|
||||||
document.querySelectorAll('pre>code.language-mermaid').forEach((elem) => {
|
const basicList = document.getElementsByClassName('language-mermaid');
|
||||||
|
[...basicList].forEach((elem) => {
|
||||||
const svgCode = elem.textContent;
|
const svgCode = elem.textContent;
|
||||||
const backup = elem.parentElement;
|
const backup = elem.parentElement;
|
||||||
backup.classList.add('unloaded');
|
backup.classList.add('unloaded');
|
||||||
|
@ -52,7 +55,6 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
mermaid.initialize(mermaidConf);
|
mermaid.initialize(mermaidConf);
|
||||||
|
|
||||||
window.addEventListener('message', updateMermaid);
|
window.addEventListener('message', updateMermaid);
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
|
|
||||||
<div class="sidebar-bottom d-flex flex-wrap align-items-center w-100">
|
<div class="sidebar-bottom d-flex flex-wrap align-items-center w-100">
|
||||||
{% unless site.theme_mode %}
|
{% unless site.theme_mode %}
|
||||||
<button type="button" class="mode-toggle btn" aria-label="Switch Mode">
|
<button type="button" class="btn" aria-label="Switch Mode" id="mode-toggle">
|
||||||
<i class="fas fa-adjust"></i>
|
<i class="fas fa-adjust"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
<i class="fas fa-search fa-fw"></i>
|
<i class="fas fa-search fa-fw"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<search class="align-items-center ms-3 ms-lg-0">
|
<search id="search" class="align-items-center ms-3 ms-lg-0">
|
||||||
<i class="fas fa-search fa-fw"></i>
|
<i class="fas fa-search fa-fw"></i>
|
||||||
<input
|
<input
|
||||||
class="form-control"
|
class="form-control"
|
||||||
|
|
|
@ -3,18 +3,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function back2top() {
|
export function back2top() {
|
||||||
const $window = $(window);
|
const btn = document.getElementById('back-to-top');
|
||||||
const $btn = $('#back-to-top');
|
|
||||||
|
|
||||||
$window.on('scroll', () => {
|
window.addEventListener('scroll', () => {
|
||||||
if ($window.scrollTop() > 50) {
|
if (window.scrollY > 50) {
|
||||||
$btn.fadeIn();
|
btn.classList.add('show');
|
||||||
} else {
|
} else {
|
||||||
$btn.fadeOut();
|
btn.classList.remove('show');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$btn.on('click', () => {
|
btn.addEventListener('click', () => {
|
||||||
$window.scrollTop(0);
|
window.scrollTo({ top: 0 });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,34 +3,31 @@
|
||||||
*/
|
*/
|
||||||
const childPrefix = 'l_';
|
const childPrefix = 'l_';
|
||||||
const parentPrefix = 'h_';
|
const parentPrefix = 'h_';
|
||||||
const collapse = $('.collapse');
|
const children = document.getElementsByClassName('collapse');
|
||||||
|
|
||||||
export function categoryCollapse() {
|
export function categoryCollapse() {
|
||||||
/* close up top-category */
|
[...children].forEach((elem) => {
|
||||||
collapse.on('hide.bs.collapse', function () {
|
const id = parentPrefix + elem.id.substring(childPrefix.length);
|
||||||
/* Bootstrap collapse events. */ const parentId =
|
const parent = document.getElementById(id);
|
||||||
parentPrefix + $(this).attr('id').substring(childPrefix.length);
|
|
||||||
if (parentId) {
|
|
||||||
$(`#${parentId} .far.fa-folder-open`).attr(
|
|
||||||
'class',
|
|
||||||
'far fa-folder fa-fw'
|
|
||||||
);
|
|
||||||
$(`#${parentId} i.fas`).addClass('rotate');
|
|
||||||
$(`#${parentId}`).removeClass('hide-border-bottom');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/* expand the top category */
|
// collapse sub-categories
|
||||||
collapse.on('show.bs.collapse', function () {
|
elem.addEventListener('hide.bs.collapse', () => {
|
||||||
const parentId =
|
if (parent) {
|
||||||
parentPrefix + $(this).attr('id').substring(childPrefix.length);
|
parent.querySelector('.far.fa-folder-open').className =
|
||||||
if (parentId) {
|
'far fa-folder fa-fw';
|
||||||
$(`#${parentId} .far.fa-folder`).attr(
|
parent.querySelector('.fas.fa-angle-down').classList.add('rotate');
|
||||||
'class',
|
parent.classList.remove('hide-border-bottom');
|
||||||
'far fa-folder-open fa-fw'
|
}
|
||||||
);
|
});
|
||||||
$(`#${parentId} i.fas`).removeClass('rotate');
|
|
||||||
$(`#${parentId}`).addClass('hide-border-bottom');
|
// expand sub-categories
|
||||||
}
|
elem.addEventListener('show.bs.collapse', () => {
|
||||||
|
if (parent) {
|
||||||
|
parent.querySelector('.far.fa-folder').className =
|
||||||
|
'far fa-folder-open fa-fw';
|
||||||
|
parent.querySelector('.fas.fa-angle-down').classList.remove('rotate');
|
||||||
|
parent.classList.add('hide-border-bottom');
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,102 +7,103 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const clipboardSelector = '.code-header>button';
|
const clipboardSelector = '.code-header>button';
|
||||||
|
|
||||||
|
const ICON_DEFAULT = 'far fa-clipboard';
|
||||||
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-bs-original-title';
|
const ATTR_TITLE_ORIGIN = 'data-bs-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.hasAttribute(ATTR_TIMEOUT)) {
|
||||||
let timeout = $(node).attr(ATTR_TIMEOUT);
|
let timeout = node.getAttribute(ATTR_TIMEOUT);
|
||||||
if (Number(timeout) > Date.now()) {
|
if (Number(timeout) > Date.now()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function lock(node) {
|
function lock(node) {
|
||||||
$(node).attr(ATTR_TIMEOUT, Date.now() + TIMEOUT);
|
node.setAttribute(ATTR_TIMEOUT, Date.now() + TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
function unlock(node) {
|
function unlock(node) {
|
||||||
$(node).removeAttr(ATTR_TIMEOUT);
|
node.removeAttribute(ATTR_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getIcon(btn) {
|
|
||||||
let iconNode = $(btn).children();
|
|
||||||
return iconNode.attr('class');
|
|
||||||
}
|
|
||||||
|
|
||||||
const ICON_DEFAULT = getIcon(clipboardSelector);
|
|
||||||
|
|
||||||
function showTooltip(btn) {
|
function showTooltip(btn) {
|
||||||
const succeedTitle = $(btn).attr(ATTR_TITLE_SUCCEED);
|
const succeedTitle = btn.getAttribute(ATTR_TITLE_SUCCEED);
|
||||||
$(btn).attr(ATTR_TITLE_ORIGIN, succeedTitle).tooltip('show');
|
btn.setAttribute(ATTR_TITLE_ORIGIN, succeedTitle);
|
||||||
|
bootstrap.Tooltip.getInstance(btn).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideTooltip(btn) {
|
function hideTooltip(btn) {
|
||||||
$(btn).tooltip('hide').removeAttr(ATTR_TITLE_ORIGIN);
|
bootstrap.Tooltip.getInstance(btn).hide();
|
||||||
|
btn.removeAttribute(ATTR_TITLE_ORIGIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSuccessIcon(btn) {
|
function setSuccessIcon(btn) {
|
||||||
let btnNode = $(btn);
|
const icon = btn.children[0];
|
||||||
let iconNode = btnNode.children();
|
icon.setAttribute('class', ICON_SUCCESS);
|
||||||
iconNode.attr('class', ICON_SUCCESS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resumeIcon(btn) {
|
function resumeIcon(btn) {
|
||||||
let btnNode = $(btn);
|
const icon = btn.children[0];
|
||||||
let iconNode = btnNode.children();
|
icon.setAttribute('class', ICON_DEFAULT);
|
||||||
iconNode.attr('class', ICON_DEFAULT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initClipboard() {
|
export function initClipboard() {
|
||||||
// Initial the clipboard.js object
|
const clipboardList = document.querySelectorAll(clipboardSelector);
|
||||||
if ($(clipboardSelector).length) {
|
|
||||||
const clipboard = new ClipboardJS(clipboardSelector, {
|
|
||||||
target(trigger) {
|
|
||||||
let codeBlock = trigger.parentNode.nextElementSibling;
|
|
||||||
return codeBlock.querySelector('code .rouge-code');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const clipboardList = document.querySelectorAll(clipboardSelector);
|
if (clipboardList.length === 0) {
|
||||||
[...clipboardList].map(
|
return;
|
||||||
(elem) =>
|
|
||||||
new bootstrap.Tooltip(elem, {
|
|
||||||
placement: 'left'
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initial the clipboard.js object
|
||||||
|
const clipboard = new ClipboardJS(clipboardSelector, {
|
||||||
|
target: (trigger) => {
|
||||||
|
const codeBlock = trigger.parentNode.nextElementSibling;
|
||||||
|
return codeBlock.querySelector('code .rouge-code');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
[...clipboardList].map(
|
||||||
|
(elem) =>
|
||||||
|
new bootstrap.Tooltip(elem, {
|
||||||
|
placement: 'left'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
clipboard.on('success', (e) => {
|
||||||
|
const trigger = e.trigger;
|
||||||
|
|
||||||
|
e.clearSelection();
|
||||||
|
|
||||||
|
if (isLocked(trigger)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSuccessIcon(trigger);
|
||||||
|
showTooltip(trigger);
|
||||||
|
lock(trigger);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
hideTooltip(trigger);
|
||||||
|
resumeIcon(trigger);
|
||||||
|
unlock(trigger);
|
||||||
|
}, TIMEOUT);
|
||||||
|
});
|
||||||
|
|
||||||
/* --- Post link sharing --- */
|
/* --- Post link sharing --- */
|
||||||
|
|
||||||
const btnCopyLink = $('#copy-link');
|
const btnCopyLink = document.getElementById('copy-link');
|
||||||
|
|
||||||
btnCopyLink.on('click', (e) => {
|
btnCopyLink.addEventListener('click', (e) => {
|
||||||
let target = $(e.target);
|
const target = e.target;
|
||||||
|
|
||||||
if (isLocked(target)) {
|
if (isLocked(target)) {
|
||||||
return;
|
return;
|
||||||
|
@ -110,21 +111,23 @@ export function initClipboard() {
|
||||||
|
|
||||||
// Copy URL to clipboard
|
// Copy URL to clipboard
|
||||||
navigator.clipboard.writeText(window.location.href).then(() => {
|
navigator.clipboard.writeText(window.location.href).then(() => {
|
||||||
const defaultTitle = target.attr(ATTR_TITLE_ORIGIN);
|
const defaultTitle = target.getAttribute(ATTR_TITLE_ORIGIN);
|
||||||
const succeedTitle = target.attr(ATTR_TITLE_SUCCEED);
|
const succeedTitle = target.getAttribute(ATTR_TITLE_SUCCEED);
|
||||||
|
|
||||||
// Switch tooltip title
|
// Switch tooltip title
|
||||||
target.attr(ATTR_TITLE_ORIGIN, succeedTitle).tooltip('show');
|
target.setAttribute(ATTR_TITLE_ORIGIN, succeedTitle);
|
||||||
|
bootstrap.Tooltip.getInstance(target).show();
|
||||||
|
|
||||||
lock(target);
|
lock(target);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
target.attr(ATTR_TITLE_ORIGIN, defaultTitle);
|
target.setAttribute(ATTR_TITLE_ORIGIN, defaultTitle);
|
||||||
unlock(target);
|
unlock(target);
|
||||||
}, TIMEOUT);
|
}, TIMEOUT);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
btnCopyLink.on('mouseleave', function (e) {
|
btnCopyLink.addEventListener('mouseleave', (e) => {
|
||||||
const target = $(e.target);
|
bootstrap.Tooltip.getInstance(e.target).hide();
|
||||||
target.tooltip('hide');
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ const cover = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function removeCover(clzss) {
|
function removeCover(clzss) {
|
||||||
$(this).parent().removeClass(clzss);
|
this.parentElement.classList.remove(clzss);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleImage() {
|
function handleImage() {
|
||||||
|
@ -30,32 +30,38 @@ function handleImage() {
|
||||||
* Switches the LQIP with the real image URL.
|
* Switches the LQIP with the real image URL.
|
||||||
*/
|
*/
|
||||||
function switchLQIP() {
|
function switchLQIP() {
|
||||||
const $img = $(this);
|
const src = this.getAttribute(ATTR_DATA_SRC);
|
||||||
const src = $img.attr(ATTR_DATA_SRC);
|
this.setAttribute('src', encodeURI(src));
|
||||||
|
this.removeAttribute(ATTR_DATA_SRC);
|
||||||
$img.attr('src', encodeURI(src));
|
|
||||||
$img.removeAttr(ATTR_DATA_SRC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadImg() {
|
export function loadImg() {
|
||||||
const $images = $('article img');
|
const images = document.querySelectorAll('article img');
|
||||||
|
|
||||||
if ($images.length) {
|
if (images.length === 0) {
|
||||||
$images.on('load', handleImage);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
images.forEach((img) => {
|
||||||
|
img.addEventListener('load', handleImage);
|
||||||
|
});
|
||||||
|
|
||||||
// Images loaded from the browser cache do not trigger the 'load' event
|
// Images loaded from the browser cache do not trigger the 'load' event
|
||||||
$('article img[loading="lazy"]').each(function () {
|
document.querySelectorAll('article img[loading="lazy"]').forEach((img) => {
|
||||||
if (this.complete) {
|
if (img.complete) {
|
||||||
removeCover.call(this, cover.SHIMMER);
|
removeCover.call(img, cover.SHIMMER);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// LQIPs set by the data URI or WebP will not trigger the 'load' event,
|
// LQIPs set by the data URI or WebP will not trigger the 'load' event,
|
||||||
// so manually convert the URI to the URL of a high-resolution image.
|
// so manually convert the URI to the URL of a high-resolution image.
|
||||||
const $lqips = $(`article img[${ATTR_DATA_LQIP}="true"]`);
|
const lqips = document.querySelectorAll(
|
||||||
|
`article img[${ATTR_DATA_LQIP}="true"]`
|
||||||
|
);
|
||||||
|
|
||||||
if ($lqips.length) {
|
if (lqips.length) {
|
||||||
$lqips.each(switchLQIP);
|
lqips.forEach((lqip) => {
|
||||||
|
switchLQIP.call(lqip);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,15 @@
|
||||||
/**
|
/**
|
||||||
* Set up image popup
|
* Set up image popup
|
||||||
*
|
*
|
||||||
* See: https://github.com/dimsemenov/Magnific-Popup
|
* Dependencies: https://github.com/biati-digital/glightbox
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const IMG_CLASS = 'popup';
|
||||||
|
|
||||||
export function imgPopup() {
|
export function imgPopup() {
|
||||||
if ($('.popup') <= 0) {
|
if (document.getElementsByClassName(IMG_CLASS).length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('.popup').magnificPopup({
|
GLightbox({ selector: `.${IMG_CLASS}` });
|
||||||
type: 'image',
|
|
||||||
closeOnContentClick: true,
|
|
||||||
showCloseBtn: false,
|
|
||||||
zoom: {
|
|
||||||
enabled: true,
|
|
||||||
duration: 300,
|
|
||||||
easing: 'ease-in-out'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,15 +15,15 @@ class LocaleHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
static get locale() {
|
static get locale() {
|
||||||
return $('html').attr('lang').substring(0, 2);
|
return document.documentElement.getAttribute('lang').substring(0, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getTimestamp(elem) {
|
static getTimestamp(elem) {
|
||||||
return Number(elem.attr(LocaleHelper.attrTimestamp)); // unix timestamp
|
return Number(elem.getAttribute(this.attrTimestamp)); // unix timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDateFormat(elem) {
|
static getDateFormat(elem) {
|
||||||
return elem.attr(LocaleHelper.attrDateFormat);
|
return elem.getAttribute(this.attrDateFormat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,21 +31,23 @@ export function initLocaleDatetime() {
|
||||||
dayjs.locale(LocaleHelper.locale);
|
dayjs.locale(LocaleHelper.locale);
|
||||||
dayjs.extend(window.dayjs_plugin_localizedFormat);
|
dayjs.extend(window.dayjs_plugin_localizedFormat);
|
||||||
|
|
||||||
$(`[${LocaleHelper.attrTimestamp}]`).each(function () {
|
document
|
||||||
const date = dayjs.unix(LocaleHelper.getTimestamp($(this)));
|
.querySelectorAll(`[${LocaleHelper.attrTimestamp}]`)
|
||||||
const text = date.format(LocaleHelper.getDateFormat($(this)));
|
.forEach((elem) => {
|
||||||
$(this).text(text);
|
const date = dayjs.unix(LocaleHelper.getTimestamp(elem));
|
||||||
$(this).removeAttr(LocaleHelper.attrTimestamp);
|
const text = date.format(LocaleHelper.getDateFormat(elem));
|
||||||
$(this).removeAttr(LocaleHelper.attrDateFormat);
|
elem.textContent = text;
|
||||||
|
elem.removeAttribute(LocaleHelper.attrTimestamp);
|
||||||
|
elem.removeAttribute(LocaleHelper.attrDateFormat);
|
||||||
|
|
||||||
// setup tooltips
|
// setup tooltips
|
||||||
const tooltip = $(this).attr('data-bs-toggle');
|
if (
|
||||||
if (typeof tooltip === 'undefined' || tooltip !== 'tooltip') {
|
elem.hasAttribute('data-bs-toggle') &&
|
||||||
return;
|
elem.getAttribute('data-bs-toggle') === 'tooltip'
|
||||||
}
|
) {
|
||||||
|
// 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
|
const tooltipText = date.format('llll');
|
||||||
$(this).attr('data-bs-title', tooltipText);
|
elem.setAttribute('data-bs-title', tooltipText);
|
||||||
new bootstrap.Tooltip($(this));
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,14 @@
|
||||||
/**
|
/**
|
||||||
* Add listener for theme mode toggle
|
* Add listener for theme mode toggle
|
||||||
*/
|
*/
|
||||||
const $toggleElem = $('.mode-toggle');
|
const toggle = document.getElementById('mode-toggle');
|
||||||
|
|
||||||
export function modeWatcher() {
|
export function modeWatcher() {
|
||||||
if ($toggleElem.length === 0) {
|
if (!toggle) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$toggleElem.off().on('click', (e) => {
|
toggle.addEventListener('click', () => {
|
||||||
const $target = $(e.target);
|
modeToggle.flipMode();
|
||||||
let $btn =
|
|
||||||
$target.prop('tagName') === 'button'.toUpperCase()
|
|
||||||
? $target
|
|
||||||
: $target.parent();
|
|
||||||
|
|
||||||
modeToggle.flipMode(); // modeToggle: `_includes/mode-toggle.html`
|
|
||||||
$btn.trigger('blur'); // remove the clicking outline
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,121 +1,109 @@
|
||||||
/**
|
/**
|
||||||
* This script make #search-result-wrapper switch to unloaded or shown automatically.
|
* This script make #search-result-wrapper switch to unloaded or shown automatically.
|
||||||
*/
|
*/
|
||||||
const $btnSbTrigger = $('#sidebar-trigger');
|
|
||||||
const $btnSearchTrigger = $('#search-trigger');
|
|
||||||
const $btnCancel = $('#search-cancel');
|
|
||||||
const $content = $('#main-wrapper>.container>.row');
|
|
||||||
const $topbarTitle = $('#topbar-title');
|
|
||||||
const $search = $('search');
|
|
||||||
const $resultWrapper = $('#search-result-wrapper');
|
|
||||||
const $results = $('#search-results');
|
|
||||||
const $input = $('#search-input');
|
|
||||||
const $hints = $('#search-hints');
|
|
||||||
const $viewport = $('html,body');
|
|
||||||
|
|
||||||
// class names
|
const btnSbTrigger = document.getElementById('sidebar-trigger');
|
||||||
const C_LOADED = 'loaded';
|
const btnSearchTrigger = document.getElementById('search-trigger');
|
||||||
const C_UNLOADED = 'unloaded';
|
const btnCancel = document.getElementById('search-cancel');
|
||||||
const C_FOCUS = 'input-focus';
|
const content = document.querySelectorAll('#main-wrapper>.container>.row');
|
||||||
const C_FLEX = 'd-flex';
|
const topbarTitle = document.getElementById('topbar-title');
|
||||||
|
const search = document.getElementById('search');
|
||||||
|
const resultWrapper = document.getElementById('search-result-wrapper');
|
||||||
|
const results = document.getElementById('search-results');
|
||||||
|
const input = document.getElementById('search-input');
|
||||||
|
const hints = document.getElementById('search-hints');
|
||||||
|
|
||||||
class ScrollBlocker {
|
// CSS class names
|
||||||
static offset = 0;
|
const LOADED = 'loaded';
|
||||||
static resultVisible = false;
|
const UNLOADED = 'unloaded';
|
||||||
|
const FOCUS = 'input-focus';
|
||||||
|
const FLEX = 'd-flex';
|
||||||
|
|
||||||
static on() {
|
/* Actions in mobile screens (Sidebar hidden) */
|
||||||
ScrollBlocker.offset = window.scrollY;
|
|
||||||
$viewport.scrollTop(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static off() {
|
|
||||||
$viewport.scrollTop(ScrollBlocker.offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*--- Actions in mobile screens (Sidebar hidden) ---*/
|
|
||||||
class MobileSearchBar {
|
class MobileSearchBar {
|
||||||
static on() {
|
static on() {
|
||||||
$btnSbTrigger.addClass(C_UNLOADED);
|
btnSbTrigger.classList.add(UNLOADED);
|
||||||
$topbarTitle.addClass(C_UNLOADED);
|
topbarTitle.classList.add(UNLOADED);
|
||||||
$btnSearchTrigger.addClass(C_UNLOADED);
|
btnSearchTrigger.classList.add(UNLOADED);
|
||||||
$search.addClass(C_FLEX);
|
search.classList.add(FLEX);
|
||||||
$btnCancel.addClass(C_LOADED);
|
btnCancel.classList.add(LOADED);
|
||||||
}
|
}
|
||||||
|
|
||||||
static off() {
|
static off() {
|
||||||
$btnCancel.removeClass(C_LOADED);
|
btnCancel.classList.remove(LOADED);
|
||||||
$search.removeClass(C_FLEX);
|
search.classList.remove(FLEX);
|
||||||
$btnSbTrigger.removeClass(C_UNLOADED);
|
btnSbTrigger.classList.remove(UNLOADED);
|
||||||
$topbarTitle.removeClass(C_UNLOADED);
|
topbarTitle.classList.remove(UNLOADED);
|
||||||
$btnSearchTrigger.removeClass(C_UNLOADED);
|
btnSearchTrigger.classList.remove(UNLOADED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ResultSwitch {
|
class ResultSwitch {
|
||||||
|
static resultVisible = false;
|
||||||
|
|
||||||
static on() {
|
static on() {
|
||||||
if (!ScrollBlocker.resultVisible) {
|
if (!this.resultVisible) {
|
||||||
// the block method must be called before $(#main-wrapper>.container) unloaded.
|
resultWrapper.classList.remove(UNLOADED);
|
||||||
ScrollBlocker.on();
|
content.forEach((el) => {
|
||||||
$resultWrapper.removeClass(C_UNLOADED);
|
el.classList.add(UNLOADED);
|
||||||
$content.addClass(C_UNLOADED);
|
});
|
||||||
ScrollBlocker.resultVisible = true;
|
this.resultVisible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static off() {
|
static off() {
|
||||||
if (ScrollBlocker.resultVisible) {
|
if (this.resultVisible) {
|
||||||
$results.empty();
|
results.innerHTML = '';
|
||||||
if ($hints.hasClass(C_UNLOADED)) {
|
|
||||||
$hints.removeClass(C_UNLOADED);
|
if (hints.classList.contains(UNLOADED)) {
|
||||||
|
hints.classList.remove(UNLOADED);
|
||||||
}
|
}
|
||||||
$resultWrapper.addClass(C_UNLOADED);
|
|
||||||
$content.removeClass(C_UNLOADED);
|
|
||||||
|
|
||||||
// now the release method must be called after $(#main-wrapper>.container) display
|
resultWrapper.classList.add(UNLOADED);
|
||||||
ScrollBlocker.off();
|
content.forEach((el) => {
|
||||||
|
el.classList.remove(UNLOADED);
|
||||||
$input.val('');
|
});
|
||||||
ScrollBlocker.resultVisible = false;
|
input.textContent = '';
|
||||||
|
this.resultVisible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isMobileView() {
|
function isMobileView() {
|
||||||
return $btnCancel.hasClass(C_LOADED);
|
return btnCancel.classList.contains(LOADED);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function displaySearch() {
|
export function displaySearch() {
|
||||||
$btnSearchTrigger.on('click', function () {
|
btnSearchTrigger.addEventListener('click', () => {
|
||||||
MobileSearchBar.on();
|
MobileSearchBar.on();
|
||||||
ResultSwitch.on();
|
ResultSwitch.on();
|
||||||
$input.trigger('focus');
|
input.focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
$btnCancel.on('click', function () {
|
btnCancel.addEventListener('click', () => {
|
||||||
MobileSearchBar.off();
|
MobileSearchBar.off();
|
||||||
ResultSwitch.off();
|
ResultSwitch.off();
|
||||||
});
|
});
|
||||||
|
|
||||||
$input.on('focus', function () {
|
input.addEventListener('focus', () => {
|
||||||
$search.addClass(C_FOCUS);
|
search.classList.add(FOCUS);
|
||||||
});
|
});
|
||||||
|
|
||||||
$input.on('focusout', function () {
|
input.addEventListener('focusout', () => {
|
||||||
$search.removeClass(C_FOCUS);
|
search.classList.remove(FOCUS);
|
||||||
});
|
});
|
||||||
|
|
||||||
$input.on('input', () => {
|
input.addEventListener('input', () => {
|
||||||
if ($input.val() === '') {
|
if (input.value === '') {
|
||||||
if (isMobileView()) {
|
if (isMobileView()) {
|
||||||
$hints.removeClass(C_UNLOADED);
|
hints.classList.remove(UNLOADED);
|
||||||
} else {
|
} else {
|
||||||
ResultSwitch.off();
|
ResultSwitch.off();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ResultSwitch.on();
|
ResultSwitch.on();
|
||||||
if (isMobileView()) {
|
if (isMobileView()) {
|
||||||
$hints.addClass(C_UNLOADED);
|
hints.classList.add(UNLOADED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
* Expand or close the sidebar in mobile screens.
|
* Expand or close the sidebar in mobile screens.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const $body = $('body');
|
|
||||||
const ATTR_DISPLAY = 'sidebar-display';
|
const ATTR_DISPLAY = 'sidebar-display';
|
||||||
|
|
||||||
class SidebarUtil {
|
class SidebarUtil {
|
||||||
|
@ -10,9 +9,9 @@ class SidebarUtil {
|
||||||
|
|
||||||
static toggle() {
|
static toggle() {
|
||||||
if (SidebarUtil.isExpanded === false) {
|
if (SidebarUtil.isExpanded === false) {
|
||||||
$body.attr(ATTR_DISPLAY, '');
|
document.body.setAttribute(ATTR_DISPLAY, '');
|
||||||
} else {
|
} else {
|
||||||
$body.removeAttr(ATTR_DISPLAY);
|
document.body.removeAttribute(ATTR_DISPLAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
SidebarUtil.isExpanded = !SidebarUtil.isExpanded;
|
SidebarUtil.isExpanded = !SidebarUtil.isExpanded;
|
||||||
|
@ -20,6 +19,9 @@ class SidebarUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sidebarExpand() {
|
export function sidebarExpand() {
|
||||||
$('#sidebar-trigger').on('click', SidebarUtil.toggle);
|
document
|
||||||
$('#mask').on('click', SidebarUtil.toggle);
|
.getElementById('sidebar-trigger')
|
||||||
|
.addEventListener('click', SidebarUtil.toggle);
|
||||||
|
|
||||||
|
document.getElementById('mask').addEventListener('click', SidebarUtil.toggle);
|
||||||
}
|
}
|
||||||
|
|
|
@ -672,18 +672,6 @@ main {
|
||||||
|
|
||||||
/* --- Overriding --- */
|
/* --- Overriding --- */
|
||||||
|
|
||||||
/* magnific-popup */
|
|
||||||
|
|
||||||
figure .mfp-title {
|
|
||||||
text-align: center;
|
|
||||||
padding-right: 0;
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mfp-img {
|
|
||||||
transition: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* mermaid */
|
/* mermaid */
|
||||||
.mermaid {
|
.mermaid {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -876,7 +864,7 @@ $btn-mb: 0.5rem;
|
||||||
line-height: $btn-size;
|
line-height: $btn-size;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mode-toggle {
|
#mode-toggle {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
|
|
||||||
|
@ -1158,7 +1146,8 @@ search {
|
||||||
/* --- button back-to-top --- */
|
/* --- button back-to-top --- */
|
||||||
|
|
||||||
#back-to-top {
|
#back-to-top {
|
||||||
display: none;
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@ -1171,8 +1160,7 @@ search {
|
||||||
height: $back2top-size;
|
height: $back2top-size;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 1px solid var(--btn-backtotop-border-color);
|
border: 1px solid var(--btn-backtotop-border-color);
|
||||||
transition: transform 0.2s ease-out;
|
transition: opacity 0.5s ease-in-out, transform 0.2s ease-out;
|
||||||
-webkit-transition: transform 0.2s ease-out;
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
transform: translate3d(0, -5px, 0);
|
transform: translate3d(0, -5px, 0);
|
||||||
|
@ -1184,6 +1172,11 @@ search {
|
||||||
position: relative;
|
position: relative;
|
||||||
bottom: 2px;
|
bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.show {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#notification {
|
#notification {
|
||||||
|
|
|
@ -8,30 +8,32 @@ if ('serviceWorker' in navigator) {
|
||||||
|
|
||||||
if (isEnabled) {
|
if (isEnabled) {
|
||||||
const swUrl = '{{ '/sw.min.js' | relative_url }}';
|
const swUrl = '{{ '/sw.min.js' | relative_url }}';
|
||||||
const $notification = $('#notification');
|
const notification = document.getElementById('notification');
|
||||||
const $btnRefresh = $('#notification .toast-body>button');
|
const btnRefresh = notification.querySelector('.toast-body>button');
|
||||||
|
const popupWindow = bootstrap.Toast.getOrCreateInstance(notification);
|
||||||
|
|
||||||
navigator.serviceWorker.register(swUrl).then((registration) => {
|
navigator.serviceWorker.register(swUrl).then((registration) => {
|
||||||
{% comment %}In case the user ignores the notification{% endcomment %}
|
{% comment %}In case the user ignores the notification{% endcomment %}
|
||||||
if (registration.waiting) {
|
if (registration.waiting) {
|
||||||
$notification.toast('show');
|
popupWindow.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
registration.addEventListener('updatefound', () => {
|
registration.addEventListener('updatefound', () => {
|
||||||
registration.installing.addEventListener('statechange', () => {
|
registration.installing.addEventListener('statechange', () => {
|
||||||
if (registration.waiting) {
|
if (registration.waiting) {
|
||||||
if (navigator.serviceWorker.controller) {
|
if (navigator.serviceWorker.controller) {
|
||||||
$notification.toast('show');
|
popupWindow.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$btnRefresh.on('click', () => {
|
btnRefresh.addEventListener('click', () => {
|
||||||
if (registration.waiting) {
|
if (registration.waiting) {
|
||||||
registration.waiting.postMessage('SKIP_WAITING');
|
registration.waiting.postMessage('SKIP_WAITING');
|
||||||
}
|
}
|
||||||
$notification.toast('hide');
|
|
||||||
|
popupWindow.hide();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 7bc0d86b6af83d7acfc63db50f29a5975cec2513
|
Subproject commit 29d7021f45317d02df9a3297d3c52a30bd4ae795
|
Loading…
Reference in a new issue