补充主题静态资源与归档模板
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
* AKSRT Page Loader
|
||||
*
|
||||
* Lightweight top progress bar for page transitions and AJAX requests.
|
||||
* No external dependencies — pure vanilla JS.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var STYLE_ID = 'aksrt-loader-style';
|
||||
var BAR_ID = 'aksrt-loader-bar';
|
||||
var CSS =
|
||||
'#' + BAR_ID + '{' +
|
||||
'position:fixed;top:0;left:0;width:0;height:3px;z-index:99999;' +
|
||||
'background:linear-gradient(90deg,#1890ff,#36cfc9);' +
|
||||
'transition:width 0.3s ease,opacity 0.3s ease;' +
|
||||
'pointer-events:none;opacity:0;' +
|
||||
'}' +
|
||||
'#' + BAR_ID + '.is-active{opacity:1;}' +
|
||||
'#' + BAR_ID + '.is-done{' +
|
||||
'width:100%!important;opacity:0;' +
|
||||
'}';
|
||||
|
||||
var bar = null;
|
||||
var timer = null;
|
||||
var progress = 0;
|
||||
|
||||
function injectStyle() {
|
||||
if (document.getElementById(STYLE_ID)) return;
|
||||
var style = document.createElement('style');
|
||||
style.id = STYLE_ID;
|
||||
style.textContent = CSS;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
function createBar() {
|
||||
if (bar) return;
|
||||
bar = document.createElement('div');
|
||||
bar.id = BAR_ID;
|
||||
document.body.appendChild(bar);
|
||||
}
|
||||
|
||||
function start() {
|
||||
if (!bar) createBar();
|
||||
clearTimeout(timer);
|
||||
bar.classList.remove('is-done');
|
||||
bar.classList.add('is-active');
|
||||
progress = 0;
|
||||
bar.style.width = '0%';
|
||||
trickle();
|
||||
}
|
||||
|
||||
function trickle() {
|
||||
if (progress >= 90) return;
|
||||
var increment = progress < 30 ? 8 : progress < 60 ? 4 : 2;
|
||||
progress = Math.min(90, progress + increment * (0.8 + Math.random() * 0.4));
|
||||
bar.style.width = progress + '%';
|
||||
timer = setTimeout(trickle, 200 + Math.random() * 300);
|
||||
}
|
||||
|
||||
function done() {
|
||||
if (!bar) return;
|
||||
clearTimeout(timer);
|
||||
bar.style.width = '100%';
|
||||
bar.classList.add('is-done');
|
||||
setTimeout(function () {
|
||||
bar.classList.remove('is-active', 'is-done');
|
||||
bar.style.width = '0%';
|
||||
progress = 0;
|
||||
}, 400);
|
||||
}
|
||||
|
||||
// Intercept link clicks for same-origin page transitions
|
||||
function onLinkClick(e) {
|
||||
if (e.defaultPrevented) return;
|
||||
if (e.button !== 0) return; // left click only
|
||||
if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
||||
|
||||
var link = e.target.closest('a');
|
||||
if (!link || !link.href) return;
|
||||
if (link.target === '_blank') return;
|
||||
if (link.hasAttribute('download')) return;
|
||||
if (link.href.indexOf('#') === 0) return;
|
||||
|
||||
// Same-origin only
|
||||
try {
|
||||
var url = new URL(link.href);
|
||||
if (url.origin !== window.location.origin) return;
|
||||
if (url.protocol !== "http:" && url.protocol !== "https:") return;
|
||||
if (url.pathname === window.location.pathname && url.search === window.location.search) return;
|
||||
} catch (ex) { return; }
|
||||
|
||||
start();
|
||||
}
|
||||
|
||||
// Intercept form submissions
|
||||
function onFormSubmit(e) {
|
||||
var form = e.target;
|
||||
if (!form || form.tagName !== 'FORM') return;
|
||||
if (form.target === '_blank') return;
|
||||
start();
|
||||
}
|
||||
|
||||
// Hook into AJAX (fetch) if available
|
||||
function hookFetch() {
|
||||
if (!window.fetch) return;
|
||||
var origFetch = window.fetch;
|
||||
window.fetch = function () {
|
||||
start();
|
||||
return origFetch.apply(this, arguments).then(function (response) {
|
||||
done();
|
||||
return response;
|
||||
}).catch(function (err) {
|
||||
done();
|
||||
throw err;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Hook into XMLHttpRequest
|
||||
function hookXHR() {
|
||||
if (!window.XMLHttpRequest) return;
|
||||
var origOpen = XMLHttpRequest.prototype.open;
|
||||
var origSend = XMLHttpRequest.prototype.send;
|
||||
|
||||
XMLHttpRequest.prototype.open = function () {
|
||||
this._aksrtLoader = true;
|
||||
return origOpen.apply(this, arguments);
|
||||
};
|
||||
|
||||
XMLHttpRequest.prototype.send = function () {
|
||||
if (this._aksrtLoader) {
|
||||
start();
|
||||
var self = this;
|
||||
this.addEventListener('loadend', function () { done(); });
|
||||
}
|
||||
return origSend.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
function init() {
|
||||
injectStyle();
|
||||
createBar();
|
||||
|
||||
document.addEventListener('click', onLinkClick, true);
|
||||
document.addEventListener('submit', onFormSubmit, true);
|
||||
|
||||
// Done on page load (handles bfcache)
|
||||
window.addEventListener('pageshow', function (e) {
|
||||
if (e.persisted) done();
|
||||
});
|
||||
window.addEventListener('load', done);
|
||||
|
||||
hookFetch();
|
||||
hookXHR();
|
||||
}
|
||||
|
||||
// Public API
|
||||
window.AksrtLoader = {
|
||||
start: start,
|
||||
done: done
|
||||
};
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user