1
0
mirror of https://github.com/cotes2020/jekyll-theme-chirpy.git synced 2025-12-19 06:06:54 +00:00

refactor(build): modularize JS code

- replace gulp with rollup
- remove JS output from repo
This commit is contained in:
Cotes Chung
2023-03-13 20:20:59 +08:00
parent 002f02533d
commit b69d3d7edd
55 changed files with 1267 additions and 1247 deletions

View File

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

View File

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

View File

@@ -0,0 +1,118 @@
/**
* Clipboard functions
*
* Dependencies:
* - popper.js (https://github.com/popperjs/popper-core)
* - clipboard.js (https://github.com/zenorocha/clipboard.js)
*/
const btnSelector = '.code-header>button';
const ICON_SUCCESS = 'fas fa-check';
const ATTR_TIMEOUT = 'timeout';
const ATTR_TITLE_SUCCEED = 'data-title-succeed';
const ATTR_TITLE_ORIGIN = 'data-original-title';
const TIMEOUT = 2000; // in milliseconds
function isLocked(node) {
if ($(node)[0].hasAttribute(ATTR_TIMEOUT)) {
let timeout = $(node).attr(ATTR_TIMEOUT);
if (Number(timeout) > Date.now()) {
return true;
}
}
return false;
}
function lock(node) {
$(node).attr(ATTR_TIMEOUT, Date.now() + TIMEOUT);
}
function unlock(node) {
$(node).removeAttr(ATTR_TIMEOUT);
}
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);
}
export function initClipboard() {
// 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'
});
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').on('click', (e) => {
let target = $(e.target);
if (isLocked(target)) {
return;
}
// Copy URL to clipboard
navigator.clipboard.writeText(window.location.href).then(() => {
const defaultTitle = target.attr(ATTR_TITLE_ORIGIN);
const succeedTitle = target.attr(ATTR_TITLE_SUCCEED);
// Switch tooltip title
target.attr(ATTR_TITLE_ORIGIN, succeedTitle).tooltip('show');
lock(target);
setTimeout(() => {
target.attr(ATTR_TITLE_ORIGIN, defaultTitle);
unlock(target);
}, TIMEOUT);
});
});
}

View File

@@ -0,0 +1,68 @@
/**
* Top bar title auto change while scrolling up/down in mobile screens.
*/
const titleSelector = 'div.post>h1:first-of-type';
const $pageTitle = $(titleSelector);
const $topbarTitle = $('#topbar-title');
const defaultTitleText = $topbarTitle.text().trim();
export function convertTitle() {
if (
$pageTitle.length === 0 /* on Home page */ ||
$pageTitle.hasClass('dynamic-title') ||
$topbarTitle.is(':hidden')
) {
/* not in mobile views */
return;
}
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();
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.on('click', function () {
$('body,html').animate({ scrollTop: 0 }, 800);
});
}

View File

@@ -0,0 +1,27 @@
/**
* Set up image stuff
*/
export function imgExtra() {
if ($('#core-wrapper img[data-src]') <= 0) {
return;
}
/* See: <https://github.com/dimsemenov/Magnific-Popup> */
$('.popup').magnificPopup({
type: 'image',
closeOnContentClick: true,
showCloseBtn: false,
zoom: {
enabled: true,
duration: 300,
easing: 'ease-in-out'
}
});
/* Stop shimmer when image loaded */
document.addEventListener('lazyloaded', function (e) {
const $img = $(e.target);
$img.parent().removeClass('shimmer');
});
}

View File

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

View File

@@ -0,0 +1,21 @@
/**
* Add listener for theme mode toggle
*/
const $toggleElem = $('.mode-toggle');
export function modeWatcher() {
if ($toggleElem.length === 0) {
return;
}
$toggleElem.off().on('click', (e) => {
const $target = $(e.target);
let $btn =
$target.prop('tagName') === 'button'.toUpperCase()
? $target
: $target.parent();
modeToggle.flipMode(); // modeToggle: `_includes/mode-toggle.html`
$btn.trigger('blur'); // remove the clicking outline
});
}

View File

@@ -0,0 +1,254 @@
/**
* Count page views form GA or local cache file.
*
* Dependencies:
* - jQuery
* - countUp.js <https://github.com/inorganik/countUp.js>
*/
const getInitStatus = (function () {
let hasInit = false;
return () => {
let ret = hasInit;
if (!hasInit) {
hasInit = true;
}
return ret;
};
})();
const PvOpts = (function () {
function getContent(selector) {
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]');
}
};
})();
const PvStorage = (function () {
const Keys = {
KEY_PV: 'pv',
KEY_PV_SRC: 'pv_src',
KEY_CREATION: 'pv_created_date'
};
const Source = {
LOCAL: 'same-origin',
PROXY: 'cors'
};
function get(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;
}
}
}
};
})(); /* PvStorage */
function countUp(min, max, destId) {
if (min < max) {
let numAnim = new CountUp(destId, min, max);
if (!numAnim.error) {
numAnim.start();
} else {
console.error(numAnim.error);
}
}
}
function countPV(path, rows) {
let count = 0;
if (typeof rows !== 'undefined') {
for (let i = 0; i < rows.length; ++i) {
const gaPath = rows[parseInt(i, 10)][0];
if (gaPath === path) {
/* path format see: site.permalink */
count += parseInt(rows[parseInt(i, 10)][1], 10);
break;
}
}
}
return count;
}
function tacklePV(rows, path, elem, hasInit) {
let count = countPV(path, rows);
count = count === 0 ? 1 : count;
if (!hasInit) {
elem.text(new Intl.NumberFormat().format(count));
} else {
const initCount = parseInt(elem.text().replace(/,/g, ''), 10);
if (count > initCount) {
countUp(initCount, count, elem.attr('id'));
}
}
}
function displayPageviews(data) {
if (typeof data === 'undefined') {
return;
}
let hasInit = getInitStatus();
const rows = data.rows; /* could be undefined */
if ($('#post-list').length > 0) {
/* the Home page */
$('.post-preview').each(function () {
const path = $(this).find('a').attr('href');
tacklePV(rows, path, $(this).find('.pageviews'), hasInit);
});
} else if ($('.post').length > 0) {
/* the post */
const path = window.location.pathname;
tacklePV(rows, path, $('#pv'), hasInit);
}
}
function fetchProxyPageviews() {
if (PvOpts.hasProxyMeta()) {
$.ajax({
type: 'GET',
url: PvOpts.getProxyMeta(),
dataType: 'jsonp',
jsonpCallback: 'displayPageviews',
success: (data) => {
PvStorage.saveProxyCache(JSON.stringify(data));
},
error: (jqXHR, textStatus, errorThrown) => {
console.log(
'Failed to load pageviews from proxy server: ' + errorThrown
);
}
});
}
}
function fetchLocalPageviews(hasCache = false) {
return fetch(PvOpts.getLocalMeta())
.then((response) => response.json())
.then((data) => {
if (hasCache) {
// The cache from the proxy will sometimes be more recent than the local one
if (PvStorage.isFromProxy() && PvStorage.newerThan(data)) {
return;
}
}
displayPageviews(data);
PvStorage.saveLocalCache(JSON.stringify(data));
});
}
export function initPageviews() {
if ($('.pageviews').length <= 0) {
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
if (PvOpts.hasLocalMeta()) {
fetchLocalPageviews().then(fetchProxyPageviews);
} else {
fetchProxyPageviews();
}
}
}

View File

@@ -0,0 +1,122 @@
/**
* 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 $main = $('#main');
const $topbarTitle = $('#topbar-title');
const $searchWrapper = $('#search-wrapper');
const $resultWrapper = $('#search-result-wrapper');
const $results = $('#search-results');
const $input = $('#search-input');
const $hints = $('#search-hints');
const $viewport = $('html,body');
// class names
const C_LOADED = 'loaded';
const C_UNLOADED = 'unloaded';
const C_FOCUS = 'input-focus';
const C_FLEX = 'd-flex';
class ScrollBlocker {
static offset = 0;
static resultVisible = false;
static on() {
ScrollBlocker.offset = window.scrollY;
$viewport.scrollTop(0);
}
static off() {
$viewport.scrollTop(ScrollBlocker.offset);
}
}
/*--- Actions in mobile screens (Sidebar hidden) ---*/
class MobileSearchBar {
static on() {
$btnSbTrigger.addClass(C_UNLOADED);
$topbarTitle.addClass(C_UNLOADED);
$btnSearchTrigger.addClass(C_UNLOADED);
$searchWrapper.addClass(C_FLEX);
$btnCancel.addClass(C_LOADED);
}
static off() {
$btnCancel.removeClass(C_LOADED);
$searchWrapper.removeClass(C_FLEX);
$btnSbTrigger.removeClass(C_UNLOADED);
$topbarTitle.removeClass(C_UNLOADED);
$btnSearchTrigger.removeClass(C_UNLOADED);
}
}
class ResultSwitch {
static on() {
if (!ScrollBlocker.resultVisible) {
// the block method must be called before $(#main) unloaded.
ScrollBlocker.on();
$resultWrapper.removeClass(C_UNLOADED);
$main.addClass(C_UNLOADED);
ScrollBlocker.resultVisible = true;
}
}
static off() {
if (ScrollBlocker.resultVisible) {
$results.empty();
if ($hints.hasClass(C_UNLOADED)) {
$hints.removeClass(C_UNLOADED);
}
$resultWrapper.addClass(C_UNLOADED);
$main.removeClass(C_UNLOADED);
// now the release method must be called after $(#main) display
ScrollBlocker.off();
$input.val('');
ScrollBlocker.resultVisible = false;
}
}
}
function isMobileView() {
return $btnCancel.hasClass(C_LOADED);
}
export function displaySearch() {
$btnSearchTrigger.on('click', function () {
MobileSearchBar.on();
ResultSwitch.on();
$input.trigger('focus');
});
$btnCancel.on('click', function () {
MobileSearchBar.off();
ResultSwitch.off();
});
$input.on('focus', function () {
$searchWrapper.addClass(C_FOCUS);
});
$input.on('focusout', function () {
$searchWrapper.removeClass(C_FOCUS);
});
$input.on('input', () => {
if ($input.val() === '') {
if (isMobileView()) {
$hints.removeClass(C_UNLOADED);
} else {
ResultSwitch.off();
}
} else {
ResultSwitch.on();
if (isMobileView()) {
$hints.addClass(C_UNLOADED);
}
}
});
}

View File

@@ -0,0 +1,25 @@
/**
* Expand or close the sidebar in mobile screens.
*/
const $body = $('body');
const ATTR_DISPLAY = 'sidebar-display';
class SidebarUtil {
static isExpanded = false;
static toggle() {
if (SidebarUtil.isExpanded === false) {
$body.attr(ATTR_DISPLAY, '');
} else {
$body.removeAttr(ATTR_DISPLAY);
}
SidebarUtil.isExpanded = !SidebarUtil.isExpanded;
}
}
export function sidebarExpand() {
$('#sidebar-trigger').on('click', SidebarUtil.toggle);
$('#mask').on('click', SidebarUtil.toggle);
}

View File

@@ -0,0 +1,109 @@
/**
Safari doesn't support CSS `scroll-behavior: smooth`,
so here is a compatible solution for all browser to 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.
*/
import ScrollHelper from './utils/scroll-helper';
export function smoothScroll() {
const $topbarTitle = $('#topbar-title');
const REM = 16; // in pixels
const ATTR_SCROLL_FOCUS = 'scroll-focus';
const SCOPE = "a[href*='#']:not([href='#']):not([href='#0'])";
$(SCOPE).on('click', function (event) {
if (
this.pathname.replace(/^\//, '') !== location.pathname.replace(/^\//, '')
) {
return;
}
if (location.hostname !== this.hostname) {
return;
}
const hash = decodeURI(this.hash);
let toFootnoteRef = RegExp(/^#fnref:/).test(hash);
let toFootnote = toFootnoteRef ? false : RegExp(/^#fn:/).test(hash);
let selector = '#' + $.escapeSelector(hash.substring(1));
let $target = $(selector);
let isMobileViews = $topbarTitle.is(':visible');
let isPortrait = $(window).width() < $(window).height();
if (typeof $target === 'undefined') {
return;
}
event.preventDefault();
if (history.pushState) {
/* add hash to URL */
history.pushState(null, null, hash);
}
let curOffset = $(window).scrollTop();
let destOffset = ($target.offset().top -= REM / 2);
if (destOffset < curOffset) {
// scroll up
ScrollHelper.hideTopbar();
ScrollHelper.addScrollUpTask();
if (isMobileViews && isPortrait) {
destOffset -= ScrollHelper.getTopbarHeight();
}
} else {
// scroll down
if (isMobileViews && isPortrait) {
destOffset -= ScrollHelper.getTopbarHeight();
}
}
$('html').animate(
{
scrollTop: destOffset
},
500,
() => {
$target.trigger('focus');
/* clean up old scroll mark */
const $scroll_focus = $(`[${ATTR_SCROLL_FOCUS}=true]`);
if ($scroll_focus.length) {
$scroll_focus.attr(ATTR_SCROLL_FOCUS, 'false');
}
/* Clean :target links */
const $target_links = $(':target');
if ($target_links.length) {
/* element that visited by the URL with hash */
$target_links.attr(ATTR_SCROLL_FOCUS, 'false');
}
/* set scroll mark to footnotes */
if (toFootnote || toFootnoteRef) {
$target.attr(ATTR_SCROLL_FOCUS, 'true');
}
if ($target.is(':focus')) {
/* Checking if the target was focused */
return false;
} else {
$target.attr(
'tabindex',
'-1'
); /* Adding tabindex for elements not focusable */
$target.trigger('focus'); /* Set focus again */
}
if (ScrollHelper.hasScrollUpTask()) {
ScrollHelper.popScrollUpTask();
}
}
);
}); /* click() */
}

View File

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

View File

@@ -0,0 +1,93 @@
/**
* Hide Header on scroll down
*/
import ScrollHelper from './utils/scroll-helper';
const $searchInput = $('#search-input');
const delta = ScrollHelper.getTopbarHeight();
let didScroll;
let lastScrollTop = 0;
function hasScrolled() {
let st = $(window).scrollTop();
/* Make sure they scroll more than delta */
if (Math.abs(lastScrollTop - st) <= delta) {
return;
}
if (st > lastScrollTop) {
/* Scroll down */
ScrollHelper.hideTopbar();
if ($searchInput.is(':focus')) {
$searchInput.trigger('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
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();
}
export function switchTopbar() {
const orientation = screen.orientation;
if (orientation) {
orientation.onchange = () => {
const type = orientation.type;
if (type === 'landscape-primary' || type === 'landscape-secondary') {
handleLandscape();
}
};
} 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).on('scroll', () => {
if (didScroll) {
return;
}
didScroll = true;
});
setInterval(() => {
if (didScroll) {
hasScrolled();
didScroll = false;
}
}, 250);
}

View File

@@ -0,0 +1,64 @@
/**
* A tool for smooth scrolling and topbar switcher
*/
const ATTR_TOPBAR_VISIBLE = 'data-topbar-visible';
const $body = $('body');
const $topbarWrapper = $('#topbar-wrapper');
export default class ScrollHelper {
static scrollUpCount = 0; // the number of times the scroll up was triggered by ToC or anchor
static topbarIsLocked = false;
static orientationIsLocked = false;
static hideTopbar() {
$body.attr(ATTR_TOPBAR_VISIBLE, 'false');
}
static showTopbar() {
$body.attr(ATTR_TOPBAR_VISIBLE, 'true');
}
// scroll up
static addScrollUpTask() {
ScrollHelper.scrollUpCount += 1;
if (!ScrollHelper.topbarIsLocked) {
ScrollHelper.topbarIsLocked = true;
}
}
static popScrollUpTask() {
ScrollHelper.scrollUpCount -= 1;
}
static hasScrollUpTask() {
return ScrollHelper.scrollUpCount > 0;
}
static topbarLocked() {
return ScrollHelper.topbarIsLocked === true;
}
static unlockTopbar() {
ScrollHelper.topbarIsLocked = false;
}
static getTopbarHeight() {
return $topbarWrapper.outerHeight();
}
// orientation change
static orientationLocked() {
return ScrollHelper.orientationIsLocked === true;
}
static lockOrientation() {
ScrollHelper.orientationIsLocked = true;
}
static unLockOrientation() {
ScrollHelper.orientationIsLocked = false;
}
}

View File

@@ -0,0 +1,3 @@
export { basic } from './layouts/basic';
export { initSidebar } from './layouts/sidebar';
export { initTopbar } from './layouts/topbar';

View File

@@ -0,0 +1,7 @@
import { back2top } from '../components/back-to-top';
import { loadTooptip } from '../components/tooltip-loader';
export function basic() {
back2top();
loadTooptip();
}

View File

@@ -0,0 +1,7 @@
import { modeWatcher } from '../components/mode-watcher';
import { sidebarExpand } from '../components/sidebar';
export function initSidebar() {
modeWatcher();
sidebarExpand();
}

View File

@@ -0,0 +1,9 @@
import { convertTitle } from '../components/convert-title';
import { displaySearch } from '../components/search-display';
import { switchTopbar } from '../components/topbar-switcher';
export function initTopbar() {
convertTitle();
displaySearch();
switchTopbar();
}

View File

@@ -0,0 +1,6 @@
export { categoryCollapse } from './components/category-collapse';
export { initClipboard } from './components/clipboard';
export { imgExtra } from './components/img-extra';
export { initLocaleDatetime } from './components/locale-datetime';
export { initPageviews } from './components/pageviews';
export { smoothScroll } from './components/smooth-scroll';