mirror of
https://github.com/andwati/andwati.com.git
synced 2026-11-07 09:42:16 +00:00
switched theme
This commit is contained in:
Binary file not shown.
@@ -1,54 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>50x Borked</title>
|
||||
<meta name="author" content="Reilly Tucker Siemens">
|
||||
<meta name="description" content="50x Borked">
|
||||
<meta property="og:site_name" content="tuckersiemens.com">
|
||||
<meta property="og:locale" content="en-us">
|
||||
<meta property="og:title" content="50x Borked">
|
||||
<meta property="og:description" content="50x Borked">
|
||||
<meta property="og:image" content="https://tuckersiemens.com/self.jpg">
|
||||
<meta property="twitter:card" content="summary">
|
||||
<meta property="twitter:site" content="@TuckerSiemens">
|
||||
<meta property="twitter:title" content="50x Borked">
|
||||
<meta property="twitter:description" content="50x Borked">
|
||||
<meta property="twitter:image" content="https://tuckersiemens.com/self.jpg">
|
||||
<link rel="dns-prefetch" href="https://fonts.googleapis.com/">
|
||||
<link rel="stylesheet" href="/site.css">
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||||
</head>
|
||||
<body>
|
||||
<main id="content">
|
||||
<header>
|
||||
<h1>50[0234] Borked</h1>
|
||||
</header>
|
||||
<figure>
|
||||
<video src="50x.mp4" type="video/mp4" autoplay loop controls></video>
|
||||
<figcaption>
|
||||
Video from <a href="https://www.sakugabooru.com/post/show/174648">sakugabooru</a>
|
||||
</figcaption>
|
||||
</figure>
|
||||
</main>
|
||||
<footer>
|
||||
<nav>
|
||||
<a href="/">Home</a>
|
||||
⬡
|
||||
<a href="/posts">Posts</a>
|
||||
⬡
|
||||
<a href="/tags">Tags</a>
|
||||
⬡
|
||||
<a href="/contact">Contact</a>
|
||||
</nav>
|
||||
<p>
|
||||
©
|
||||
2021 Reilly Tucker Siemens ⬡
|
||||
<a href="https://github.com/reillysiemens/tuckersiemens.com">Source</a>
|
||||
⬡
|
||||
<a href="/rss.xml">Feed</a>
|
||||
</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,66 @@
|
||||
/* Outfit */
|
||||
@font-face {
|
||||
font-family: 'Outfit';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url('../fonts/outfit/outfit-v15-latin-300.woff2') format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Outfit';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('../fonts/outfit/outfit-v15-latin-regular.woff2') format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Outfit';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url('../fonts/outfit/outfit-v15-latin-500.woff2') format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Outfit';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url('../fonts/outfit/outfit-v15-latin-600.woff2') format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Outfit';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url('../fonts/outfit/outfit-v15-latin-700.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* JetBrains Mono (official release — full ligature support) */
|
||||
@font-face {
|
||||
font-family: 'JetBrains Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('../fonts/jetbrains-mono/JetBrainsMono-Regular.woff2') format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'JetBrains Mono';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('../fonts/jetbrains-mono/JetBrainsMono-Italic.woff2') format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'JetBrains Mono';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url('../fonts/jetbrains-mono/JetBrainsMono-Bold.woff2') format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'JetBrains Mono';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url('../fonts/jetbrains-mono/JetBrainsMono-BoldItalic.woff2') format('woff2');
|
||||
}
|
||||
Vendored
+1128
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
<svg height="128" width="128" xmlns="http://www.w3.org/2000/svg">
|
||||
<polygon points="64 4, 124 32, 124 96, 64 124, 4 96, 4 32" fill="#b16286"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 151 B |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
google-site-verification: google52b8421f5e6826b8.html
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 8.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 356 KiB |
@@ -0,0 +1,108 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var body = document.getElementById('term-body');
|
||||
if (!body) return;
|
||||
|
||||
var path = window.location.pathname;
|
||||
var promptUser = body.dataset.prompt || 'user@pwnbox';
|
||||
var hostname = body.dataset.hostname || 'pwnbox';
|
||||
var whoami = body.dataset.whoami || 'l0st_r3ader';
|
||||
|
||||
var links = [
|
||||
{ href: body.dataset.linkHome, label: '~/home' },
|
||||
{ href: body.dataset.linkPosts, label: '~/posts' },
|
||||
{ href: body.dataset.linkTags, label: '~/tags' },
|
||||
];
|
||||
|
||||
var steps = [
|
||||
{ type: 'cmd', text: 'cd ' + path },
|
||||
{ type: 'out-error', text: 'bash: cd: ' + path + ': No such file or directory' },
|
||||
{ type: 'cmd', text: 'whoami' },
|
||||
{ type: 'out', text: whoami },
|
||||
{ type: 'cmd', text: 'ls ~/recovery/' },
|
||||
{ type: 'ls' },
|
||||
];
|
||||
|
||||
var CHAR_DELAY = 28;
|
||||
var LINE_DELAY = 180;
|
||||
|
||||
function ps1() {
|
||||
var s = document.createElement('span');
|
||||
s.className = 'term-ps1';
|
||||
s.textContent = promptUser + '@' + hostname + ':~$ ';
|
||||
return s;
|
||||
}
|
||||
|
||||
function appendCmdLine(text, callback) {
|
||||
var row = document.createElement('div');
|
||||
row.className = 'term-row';
|
||||
row.appendChild(ps1());
|
||||
var input = document.createElement('span');
|
||||
input.className = 'term-input';
|
||||
row.appendChild(input);
|
||||
body.appendChild(row);
|
||||
|
||||
var i = 0;
|
||||
function tick() {
|
||||
if (i < text.length) {
|
||||
input.textContent += text[i++];
|
||||
setTimeout(tick, CHAR_DELAY);
|
||||
} else {
|
||||
setTimeout(callback, LINE_DELAY);
|
||||
}
|
||||
}
|
||||
tick();
|
||||
}
|
||||
|
||||
function appendOut(text, extraClass, callback) {
|
||||
var div = document.createElement('div');
|
||||
div.className = 'term-out' + (extraClass ? ' ' + extraClass : '');
|
||||
div.textContent = text;
|
||||
body.appendChild(div);
|
||||
setTimeout(callback, LINE_DELAY);
|
||||
}
|
||||
|
||||
function appendLs(callback) {
|
||||
var div = document.createElement('div');
|
||||
div.className = 'term-out-ls';
|
||||
links.forEach(function (l) {
|
||||
var a = document.createElement('a');
|
||||
a.href = l.href;
|
||||
a.textContent = l.label;
|
||||
div.appendChild(a);
|
||||
});
|
||||
body.appendChild(div);
|
||||
setTimeout(callback, LINE_DELAY);
|
||||
}
|
||||
|
||||
function appendCursor() {
|
||||
var row = document.createElement('div');
|
||||
row.className = 'term-row';
|
||||
row.appendChild(ps1());
|
||||
var cur = document.createElement('span');
|
||||
cur.className = 'term-cursor';
|
||||
cur.setAttribute('aria-hidden', 'true');
|
||||
row.appendChild(cur);
|
||||
body.appendChild(row);
|
||||
}
|
||||
|
||||
function runSteps(i) {
|
||||
if (i >= steps.length) {
|
||||
appendCursor();
|
||||
return;
|
||||
}
|
||||
var step = steps[i];
|
||||
var next = function () {
|
||||
runSteps(i + 1);
|
||||
};
|
||||
|
||||
if (step.type === 'cmd') appendCmdLine(step.text, next);
|
||||
else if (step.type === 'out-error') appendOut(step.text, 'term-out-error', next);
|
||||
else if (step.type === 'out') appendOut(step.text, 'term-out-muted', next);
|
||||
else if (step.type === 'ls') appendLs(next);
|
||||
else next();
|
||||
}
|
||||
|
||||
runSteps(0);
|
||||
})();
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={757:function(t){t.exports=e}},n={};function r(e){var o=n[e];if(void 0!==o)return o.exports;var i=n[e]={exports:{}};return t[e](i,i.exports,r),i.exports}r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var o={};r.d(o,{default:function(){return p}});var i=r(757),a=r.n(i);const l=function(e,t,n){let r=n,o=0;const i=e.length;for(;r<t.length;){const n=t[r];if(o<=0&&t.slice(r,r+i)===e)return r;"\\"===n?r++:"{"===n?o++:"}"===n&&o--,r++}return-1},s=/^\\begin{/;var d=function(e,t){let n;const r=[],o=new RegExp("("+t.map((e=>e.left.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"))).join("|")+")");for(;n=e.search(o),-1!==n;){n>0&&(r.push({type:"text",data:e.slice(0,n)}),e=e.slice(n));const o=t.findIndex((t=>e.startsWith(t.left)));if(n=l(t[o].right,e,t[o].left.length),-1===n)break;const i=e.slice(0,n+t[o].right.length),a=s.test(i)?i:e.slice(t[o].left.length,n);r.push({type:"math",data:a,rawData:i,display:t[o].display}),e=e.slice(n+t[o].right.length)}return""!==e&&r.push({type:"text",data:e}),r};const c=function(e,t){const n=d(e,t.delimiters);if(1===n.length&&"text"===n[0].type)return null;const r=document.createDocumentFragment();for(let e=0;e<n.length;e++)if("text"===n[e].type)r.appendChild(document.createTextNode(n[e].data));else{const o=document.createElement("span");let i=n[e].data;t.displayMode=n[e].display;try{t.preProcess&&(i=t.preProcess(i)),a().render(i,o,t)}catch(o){if(!(o instanceof a().ParseError))throw o;t.errorCallback("KaTeX auto-render: Failed to parse `"+n[e].data+"` with ",o),r.appendChild(document.createTextNode(n[e].rawData));continue}r.appendChild(o)}return r},f=function(e,t){for(let n=0;n<e.childNodes.length;n++){const r=e.childNodes[n];if(3===r.nodeType){let o=r.textContent,i=r.nextSibling,a=0;for(;i&&i.nodeType===Node.TEXT_NODE;)o+=i.textContent,i=i.nextSibling,a++;const l=c(o,t);if(l){for(let e=0;e<a;e++)r.nextSibling.remove();n+=l.childNodes.length-1,e.replaceChild(l,r)}else n+=a}else if(1===r.nodeType){const e=" "+r.className+" ";-1===t.ignoredTags.indexOf(r.nodeName.toLowerCase())&&t.ignoredClasses.every((t=>-1===e.indexOf(" "+t+" ")))&&f(r,t)}}};var p=function(e,t){if(!e)throw new Error("No element provided to render");const n={};for(const e in t)t.hasOwnProperty(e)&&(n[e]=t[e]);n.delimiters=n.delimiters||[{left:"$$",right:"$$",display:!0},{left:"\\(",right:"\\)",display:!1},{left:"\\begin{equation}",right:"\\end{equation}",display:!0},{left:"\\begin{align}",right:"\\end{align}",display:!0},{left:"\\begin{alignat}",right:"\\end{alignat}",display:!0},{left:"\\begin{gather}",right:"\\end{gather}",display:!0},{left:"\\begin{CD}",right:"\\end{CD}",display:!0},{left:"\\[",right:"\\]",display:!0}],n.ignoredTags=n.ignoredTags||["script","noscript","style","textarea","pre","code","option"],n.ignoredClasses=n.ignoredClasses||[],n.errorCallback=n.errorCallback||console.error,n.macros=n.macros||{},f(e,n)};return o=o.default}()}));
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,68 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Select all <pre> elements that contain code blocks
|
||||
const preBlocks = document.querySelectorAll('pre');
|
||||
|
||||
const copyIcon =
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg>';
|
||||
const checkIcon =
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>';
|
||||
|
||||
preBlocks.forEach((pre) => {
|
||||
// Create the copy button
|
||||
const button = document.createElement('button');
|
||||
button.innerHTML = copyIcon;
|
||||
button.className = 'copy-code-btn';
|
||||
button.setAttribute('aria-label', 'Copy code');
|
||||
button.setAttribute('title', 'Copy code');
|
||||
|
||||
// Add the button to the <pre> block
|
||||
// Assuming pre is position relative in CSS
|
||||
pre.appendChild(button);
|
||||
|
||||
button.addEventListener('click', async () => {
|
||||
// Find the <code> element within the <pre>
|
||||
const code = pre.querySelector('code');
|
||||
if (!code) return;
|
||||
|
||||
// Get the text content, removing any leading/trailing whitespace
|
||||
const text = code.innerText;
|
||||
|
||||
try {
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
// Modern Async Clipboard API
|
||||
await navigator.clipboard.writeText(text);
|
||||
} else {
|
||||
// Fallback for insecure contexts (like local network IPs via HTTP)
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = text;
|
||||
|
||||
// Move outside the screen to make it invisible
|
||||
textArea.style.position = 'absolute';
|
||||
textArea.style.left = '-999999px';
|
||||
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
|
||||
document.execCommand('copy');
|
||||
textArea.remove();
|
||||
}
|
||||
|
||||
// Add visual feedback
|
||||
button.innerHTML = checkIcon;
|
||||
button.classList.add('copied');
|
||||
button.setAttribute('aria-label', 'Copied');
|
||||
button.setAttribute('title', 'Copied!');
|
||||
|
||||
// Revert back after 2 seconds
|
||||
setTimeout(() => {
|
||||
button.innerHTML = copyIcon;
|
||||
button.classList.remove('copied');
|
||||
button.setAttribute('aria-label', 'Copy code');
|
||||
button.setAttribute('title', 'Copy code');
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error('Failed to copy code: ', err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
Vendored
+10
File diff suppressed because one or more lines are too long
@@ -0,0 +1,32 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const refs = document.querySelectorAll('sup.footnote-reference');
|
||||
if (!refs.length) return;
|
||||
|
||||
refs.forEach(function (sup) {
|
||||
const a = sup.querySelector('a');
|
||||
if (!a) return;
|
||||
|
||||
const targetId = a.getAttribute('href').replace(/^#/, '');
|
||||
const refId = 'fnref-' + targetId;
|
||||
sup.id = refId;
|
||||
|
||||
const def = document.getElementById(targetId);
|
||||
if (!def) return;
|
||||
|
||||
// Append ↩ to the last <p> inside the definition
|
||||
const paras = def.querySelectorAll('p');
|
||||
const lastPara = paras[paras.length - 1];
|
||||
if (!lastPara) return;
|
||||
|
||||
const backlink = document.createElement('a');
|
||||
backlink.href = '#' + refId;
|
||||
backlink.className = 'footnote-backref';
|
||||
backlink.setAttribute('aria-label', 'Back to reference');
|
||||
backlink.textContent = '↩';
|
||||
|
||||
lastPara.appendChild(document.createTextNode('\u00a0'));
|
||||
lastPara.appendChild(backlink);
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,13 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
document.querySelectorAll('.post-body h2[id], .post-body h3[id]').forEach(function (heading) {
|
||||
var a = document.createElement('a');
|
||||
a.href = '#' + heading.id;
|
||||
a.className = 'heading-anchor';
|
||||
a.setAttribute('aria-label', 'Link to this section');
|
||||
a.setAttribute('tabindex', '-1');
|
||||
a.textContent = '#';
|
||||
heading.appendChild(a);
|
||||
});
|
||||
})();
|
||||
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
+170
@@ -0,0 +1,170 @@
|
||||
!(function (e, t) {
|
||||
'object' == typeof exports && 'object' == typeof module
|
||||
? (module.exports = t(require('katex')))
|
||||
: 'function' == typeof define && define.amd
|
||||
? define(['katex'], t)
|
||||
: 'object' == typeof exports
|
||||
? (exports.renderMathInElement = t(require('katex')))
|
||||
: (e.renderMathInElement = t(e.katex));
|
||||
})('undefined' != typeof self ? self : this, function (e) {
|
||||
return (function () {
|
||||
'use strict';
|
||||
var t = {
|
||||
771: function (t) {
|
||||
t.exports = e;
|
||||
},
|
||||
},
|
||||
r = {};
|
||||
function n(e) {
|
||||
var i = r[e];
|
||||
if (void 0 !== i) return i.exports;
|
||||
var a = (r[e] = { exports: {} });
|
||||
return (t[e](a, a.exports, n), a.exports);
|
||||
}
|
||||
((n.n = function (e) {
|
||||
var t =
|
||||
e && e.__esModule
|
||||
? function () {
|
||||
return e.default;
|
||||
}
|
||||
: function () {
|
||||
return e;
|
||||
};
|
||||
return (n.d(t, { a: t }), t);
|
||||
}),
|
||||
(n.d = function (e, t) {
|
||||
for (var r in t)
|
||||
n.o(t, r) && !n.o(e, r) && Object.defineProperty(e, r, { enumerable: !0, get: t[r] });
|
||||
}),
|
||||
(n.o = function (e, t) {
|
||||
return Object.prototype.hasOwnProperty.call(e, t);
|
||||
}));
|
||||
var i = {};
|
||||
return (
|
||||
(function () {
|
||||
n.d(i, {
|
||||
default: function () {
|
||||
return s;
|
||||
},
|
||||
});
|
||||
var e = n(771),
|
||||
t = n.n(e),
|
||||
r = function (e, t, r) {
|
||||
for (var n = r, i = 0, a = e.length; n < t.length; ) {
|
||||
var o = t[n];
|
||||
if (i <= 0 && t.slice(n, n + a) === e) return n;
|
||||
('\\' === o ? n++ : '{' === o ? i++ : '}' === o && i--, n++);
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
a = /^\\begin{/,
|
||||
o = function (e, t) {
|
||||
for (
|
||||
var n,
|
||||
i = [],
|
||||
o = new RegExp(
|
||||
'(' +
|
||||
t
|
||||
.map(function (e) {
|
||||
return e.left.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
})
|
||||
.join('|') +
|
||||
')',
|
||||
);
|
||||
-1 !== (n = e.search(o));
|
||||
) {
|
||||
n > 0 && (i.push({ type: 'text', data: e.slice(0, n) }), (e = e.slice(n)));
|
||||
var l = t.findIndex(function (t) {
|
||||
return e.startsWith(t.left);
|
||||
});
|
||||
if (-1 === (n = r(t[l].right, e, t[l].left.length))) break;
|
||||
var d = e.slice(0, n + t[l].right.length),
|
||||
s = a.test(d) ? d : e.slice(t[l].left.length, n);
|
||||
(i.push({ type: 'math', data: s, rawData: d, display: t[l].display }),
|
||||
(e = e.slice(n + t[l].right.length)));
|
||||
}
|
||||
return ('' !== e && i.push({ type: 'text', data: e }), i);
|
||||
},
|
||||
l = function (e, r) {
|
||||
var n = o(e, r.delimiters);
|
||||
if (1 === n.length && 'text' === n[0].type) return null;
|
||||
for (var i = document.createDocumentFragment(), a = 0; a < n.length; a++)
|
||||
if ('text' === n[a].type) i.appendChild(document.createTextNode(n[a].data));
|
||||
else {
|
||||
var l = document.createElement('span'),
|
||||
d = n[a].data;
|
||||
r.displayMode = n[a].display;
|
||||
try {
|
||||
(r.preProcess && (d = r.preProcess(d)), t().render(d, l, r));
|
||||
} catch (e) {
|
||||
if (!(e instanceof t().ParseError)) throw e;
|
||||
(r.errorCallback(
|
||||
'KaTeX auto-render: Failed to parse `' + n[a].data + '` with ',
|
||||
e,
|
||||
),
|
||||
i.appendChild(document.createTextNode(n[a].rawData)));
|
||||
continue;
|
||||
}
|
||||
i.appendChild(l);
|
||||
}
|
||||
return i;
|
||||
},
|
||||
d = function e(t, r) {
|
||||
for (var n = 0; n < t.childNodes.length; n++) {
|
||||
var i = t.childNodes[n];
|
||||
if (3 === i.nodeType) {
|
||||
for (
|
||||
var a = i.textContent, o = i.nextSibling, d = 0;
|
||||
o && o.nodeType === Node.TEXT_NODE;
|
||||
)
|
||||
((a += o.textContent), (o = o.nextSibling), d++);
|
||||
var s = l(a, r);
|
||||
if (s) {
|
||||
for (var f = 0; f < d; f++) i.nextSibling.remove();
|
||||
((n += s.childNodes.length - 1), t.replaceChild(s, i));
|
||||
} else n += d;
|
||||
} else
|
||||
1 === i.nodeType &&
|
||||
(function () {
|
||||
var t = ' ' + i.className + ' ';
|
||||
-1 === r.ignoredTags.indexOf(i.nodeName.toLowerCase()) &&
|
||||
r.ignoredClasses.every(function (e) {
|
||||
return -1 === t.indexOf(' ' + e + ' ');
|
||||
}) &&
|
||||
e(i, r);
|
||||
})();
|
||||
}
|
||||
},
|
||||
s = function (e, t) {
|
||||
if (!e) throw new Error('No element provided to render');
|
||||
var r = {};
|
||||
for (var n in t) t.hasOwnProperty(n) && (r[n] = t[n]);
|
||||
((r.delimiters = r.delimiters || [
|
||||
{ left: '$$', right: '$$', display: !0 },
|
||||
{ left: '\\(', right: '\\)', display: !1 },
|
||||
{ left: '\\begin{equation}', right: '\\end{equation}', display: !0 },
|
||||
{ left: '\\begin{align}', right: '\\end{align}', display: !0 },
|
||||
{ left: '\\begin{alignat}', right: '\\end{alignat}', display: !0 },
|
||||
{ left: '\\begin{gather}', right: '\\end{gather}', display: !0 },
|
||||
{ left: '\\begin{CD}', right: '\\end{CD}', display: !0 },
|
||||
{ left: '\\[', right: '\\]', display: !0 },
|
||||
]),
|
||||
(r.ignoredTags = r.ignoredTags || [
|
||||
'script',
|
||||
'noscript',
|
||||
'style',
|
||||
'textarea',
|
||||
'pre',
|
||||
'code',
|
||||
'option',
|
||||
]),
|
||||
(r.ignoredClasses = r.ignoredClasses || []),
|
||||
(r.errorCallback = r.errorCallback || console.error),
|
||||
(r.macros = r.macros || {}),
|
||||
d(e, r));
|
||||
};
|
||||
})(),
|
||||
(i = i.default)
|
||||
);
|
||||
})();
|
||||
});
|
||||
Vendored
+11562
File diff suppressed because it is too large
Load Diff
Vendored
+192132
File diff suppressed because one or more lines are too long
@@ -0,0 +1,34 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const progressBar = document.getElementById('reading-progress-bar');
|
||||
if (!progressBar) return;
|
||||
|
||||
const updateProgress = () => {
|
||||
// Calculate how far the user has scrolled down the page
|
||||
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
|
||||
|
||||
// Calculate the total scrollable height
|
||||
const scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
|
||||
const clientHeight = document.documentElement.clientHeight || window.innerHeight;
|
||||
|
||||
const windowHeight = scrollHeight - clientHeight;
|
||||
|
||||
// Calculate percentage and update width
|
||||
const scrollPercentage = windowHeight > 0 ? (scrollTop / windowHeight) * 100 : 0;
|
||||
progressBar.style.width = scrollPercentage + '%';
|
||||
};
|
||||
|
||||
// Update initially in case the user refreshes midway down the page
|
||||
updateProgress();
|
||||
|
||||
// Throttle the scroll event listener a tiny bit for performance
|
||||
let isTicking = false;
|
||||
window.addEventListener('scroll', () => {
|
||||
if (!isTicking) {
|
||||
window.requestAnimationFrame(() => {
|
||||
updateProgress();
|
||||
isTicking = false;
|
||||
});
|
||||
isTicking = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const scrollToTopBtn = document.getElementById('scroll-to-top');
|
||||
|
||||
if (!scrollToTopBtn) return;
|
||||
|
||||
// Show button when scrolled down 400px
|
||||
const toggleVisibility = () => {
|
||||
if (window.scrollY > 400) {
|
||||
scrollToTopBtn.classList.add('visible');
|
||||
} else {
|
||||
scrollToTopBtn.classList.remove('visible');
|
||||
}
|
||||
};
|
||||
|
||||
// Smooth scroll to top when clicked
|
||||
scrollToTopBtn.addEventListener('click', () => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
});
|
||||
|
||||
// Throttle the scroll event listener a tiny bit for performance
|
||||
let isTicking = false;
|
||||
window.addEventListener('scroll', () => {
|
||||
if (!isTicking) {
|
||||
window.requestAnimationFrame(() => {
|
||||
toggleVisibility();
|
||||
isTicking = false;
|
||||
});
|
||||
isTicking = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,223 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const searchToggleBtn = document.querySelector('.search-toggle');
|
||||
if (!searchToggleBtn) return;
|
||||
|
||||
// Check if elasticlunr is available
|
||||
if (typeof elasticlunr === 'undefined') {
|
||||
console.warn('Elasticlunr not loaded. Search will not work.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Modal elements
|
||||
const overlay = document.getElementById('search-overlay');
|
||||
const input = document.getElementById('search-input');
|
||||
const resultsContainer = document.getElementById('search-results');
|
||||
|
||||
if (!overlay || !input || !resultsContainer) return;
|
||||
|
||||
let index = null;
|
||||
let isIndexLoading = false;
|
||||
let selectedIndex = -1;
|
||||
let resultItems = [];
|
||||
|
||||
// Initialize search index (loaded lazily on first open)
|
||||
const initSearch = async () => {
|
||||
if (index || isIndexLoading) return;
|
||||
isIndexLoading = true;
|
||||
|
||||
try {
|
||||
// Fetch the Zola-generated index
|
||||
const response = await fetch('/search_index.en.js');
|
||||
const jsText = await response.text();
|
||||
|
||||
// The file defines `window.searchIndex = {...}`
|
||||
// We use a safe evaluation approach if it's purely assignment
|
||||
// Or just append it as a script tag
|
||||
const script = document.createElement('script');
|
||||
script.innerHTML = jsText;
|
||||
document.body.appendChild(script);
|
||||
|
||||
// Wait for it to parse
|
||||
setTimeout(() => {
|
||||
if (window.searchIndex) {
|
||||
index = elasticlunr.Index.load(window.searchIndex);
|
||||
// Search immediately if there's text
|
||||
if (input.value.trim().length > 0) {
|
||||
performSearch();
|
||||
}
|
||||
}
|
||||
}, 50);
|
||||
} catch (e) {
|
||||
console.error('Failed to load search index:', e);
|
||||
}
|
||||
};
|
||||
|
||||
// Open Modal
|
||||
const openSearch = () => {
|
||||
overlay.classList.add('active');
|
||||
|
||||
// Slight delay ensures the browser focuses the input after the display transition starts
|
||||
setTimeout(() => {
|
||||
input.focus();
|
||||
}, 50);
|
||||
|
||||
initSearch();
|
||||
document.body.style.overflow = 'hidden'; // Prevent background scrolling
|
||||
};
|
||||
|
||||
// Close Modal
|
||||
const closeSearch = () => {
|
||||
overlay.classList.remove('active');
|
||||
input.value = '';
|
||||
resultsContainer.innerHTML = '';
|
||||
document.body.style.overflow = '';
|
||||
selectedIndex = -1;
|
||||
resultItems = [];
|
||||
};
|
||||
|
||||
// Bind Toggle Button
|
||||
searchToggleBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
if (overlay.classList.contains('active')) {
|
||||
closeSearch();
|
||||
} else {
|
||||
openSearch();
|
||||
}
|
||||
});
|
||||
|
||||
// Close on overlay click
|
||||
overlay.addEventListener('click', (e) => {
|
||||
if (e.target === overlay) closeSearch();
|
||||
});
|
||||
|
||||
// Handle Keyboard Shortcuts
|
||||
document.addEventListener('keydown', (e) => {
|
||||
// Ctrl+K -> Open Search
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
||||
e.preventDefault();
|
||||
openSearch();
|
||||
}
|
||||
|
||||
if (!overlay.classList.contains('active')) return;
|
||||
|
||||
// Escape -> Close Search
|
||||
if (e.key === 'Escape') {
|
||||
closeSearch();
|
||||
}
|
||||
|
||||
// Arrow Navigation
|
||||
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
if (resultItems.length === 0) return;
|
||||
|
||||
if (e.key === 'ArrowDown') {
|
||||
selectedIndex = (selectedIndex + 1) % resultItems.length;
|
||||
} else {
|
||||
selectedIndex = (selectedIndex - 1 + resultItems.length) % resultItems.length;
|
||||
}
|
||||
|
||||
updateSelection();
|
||||
}
|
||||
|
||||
// Enter -> Navigate to selected
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
if (selectedIndex >= 0 && resultItems[selectedIndex]) {
|
||||
window.location.href = resultItems[selectedIndex].href;
|
||||
} else if (resultItems.length > 0) {
|
||||
// Default to first item if none selected
|
||||
window.location.href = resultItems[0].href;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const updateSelection = () => {
|
||||
resultItems.forEach((el, i) => {
|
||||
if (i === selectedIndex) {
|
||||
el.classList.add('selected');
|
||||
el.scrollIntoView({ block: 'nearest' });
|
||||
} else {
|
||||
el.classList.remove('selected');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Perform the Search
|
||||
const performSearch = () => {
|
||||
if (!index) {
|
||||
resultsContainer.innerHTML = '<div class="no-results">Loading search index...</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
const query = input.value.trim();
|
||||
if (query.length === 0) {
|
||||
resultsContainer.innerHTML = '';
|
||||
selectedIndex = -1;
|
||||
resultItems = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const options = {
|
||||
bool: 'OR',
|
||||
expand: true,
|
||||
};
|
||||
|
||||
const results = index.search(query, options);
|
||||
|
||||
if (results.length === 0) {
|
||||
resultsContainer.innerHTML = `<div class="no-results">No results found for "${query}"</div>`;
|
||||
selectedIndex = -1;
|
||||
resultItems = [];
|
||||
return;
|
||||
}
|
||||
|
||||
// Build HTML
|
||||
let html = '';
|
||||
results.forEach((res) => {
|
||||
// The doc is stored inside the index.documentStore
|
||||
// For Zola + elasticlunr, res.ref IS the URL.
|
||||
const url = res.ref;
|
||||
const doc = index.documentStore.docs[res.ref];
|
||||
|
||||
if (!doc) return;
|
||||
|
||||
// Truncate summary if needed
|
||||
let body = doc.body || '';
|
||||
if (body.length > 150) {
|
||||
body = body.substring(0, 150) + '...';
|
||||
}
|
||||
|
||||
const title = doc.title || url;
|
||||
|
||||
html += `
|
||||
<a href="${url}" class="search-result-item">
|
||||
<div class="result-title">${title}</div>
|
||||
<div class="result-summary">${body}</div>
|
||||
</a>
|
||||
`;
|
||||
});
|
||||
|
||||
resultsContainer.innerHTML = html;
|
||||
|
||||
// Cache DOM elements for keyboard navigation
|
||||
resultItems = Array.from(resultsContainer.querySelectorAll('.search-result-item'));
|
||||
selectedIndex = -1; // Reset selection on new search
|
||||
|
||||
// Mouse hover updates selection index
|
||||
resultItems.forEach((item, i) => {
|
||||
item.addEventListener('mouseenter', () => {
|
||||
selectedIndex = i;
|
||||
updateSelection();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Debounce the input
|
||||
let debounceTimer;
|
||||
input.addEventListener('input', () => {
|
||||
clearTimeout(debounceTimer);
|
||||
debounceTimer = setTimeout(() => {
|
||||
performSearch();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const themeToggleBtn = document.getElementById('theme-toggle');
|
||||
const htmlElement = document.documentElement;
|
||||
|
||||
const applyTheme = (theme) => {
|
||||
if (theme === 'dark') {
|
||||
htmlElement.classList.add('dark');
|
||||
} else {
|
||||
htmlElement.classList.remove('dark');
|
||||
}
|
||||
localStorage.setItem('theme', theme);
|
||||
};
|
||||
|
||||
// Check local storage for theme preference
|
||||
const currentTheme = localStorage.getItem('theme');
|
||||
if (currentTheme === 'dark' || currentTheme === 'light') {
|
||||
applyTheme(currentTheme);
|
||||
} else {
|
||||
// Fallback to system preference
|
||||
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
applyTheme(prefersDarkScheme.matches ? 'dark' : 'light');
|
||||
}
|
||||
|
||||
// Toggle theme on click
|
||||
if (themeToggleBtn) {
|
||||
themeToggleBtn.addEventListener('click', () => {
|
||||
const nextTheme = htmlElement.classList.contains('dark') ? 'light' : 'dark';
|
||||
applyTheme(nextTheme);
|
||||
});
|
||||
}
|
||||
|
||||
// Mobile nav toggle
|
||||
const navToggle = document.getElementById('nav-toggle');
|
||||
const navLinks = document.getElementById('nav-links');
|
||||
|
||||
if (navToggle && navLinks) {
|
||||
navToggle.addEventListener('click', () => {
|
||||
const isOpen = navLinks.classList.toggle('is-open');
|
||||
navToggle.setAttribute('aria-expanded', isOpen);
|
||||
});
|
||||
|
||||
// Close nav when a link is clicked
|
||||
navLinks.querySelectorAll('.nav-item').forEach((link) => {
|
||||
link.addEventListener('click', () => {
|
||||
navLinks.classList.remove('is-open');
|
||||
navToggle.setAttribute('aria-expanded', 'false');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Sync cookieconsent dark mode reactively with the site theme
|
||||
const syncCCTheme = () => {
|
||||
const ccMain = document.getElementById('cc-main');
|
||||
if (!ccMain) return;
|
||||
ccMain.classList.toggle('cc--darkmode', htmlElement.classList.contains('dark'));
|
||||
};
|
||||
new MutationObserver(syncCCTheme).observe(htmlElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class'],
|
||||
});
|
||||
// Initial sync in case banner is already mounted
|
||||
setTimeout(syncCCTheme, 100);
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const sidebar = document.querySelector('.post-toc-sidebar');
|
||||
if (!sidebar) return;
|
||||
|
||||
const links = Array.from(sidebar.querySelectorAll('a[data-toc-id]'));
|
||||
if (!links.length) return;
|
||||
|
||||
const headingIds = links.map((a) => a.dataset.tocId);
|
||||
const headings = headingIds.map((id) => document.getElementById(id)).filter(Boolean);
|
||||
|
||||
let activeId = null;
|
||||
|
||||
function setActive(id) {
|
||||
if (id === activeId) return;
|
||||
activeId = id;
|
||||
links.forEach((a) => {
|
||||
if (a.dataset.tocId === id) {
|
||||
a.classList.add('toc-active');
|
||||
} else {
|
||||
a.classList.remove('toc-active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
// Find the topmost visible heading
|
||||
const visible = headings.filter((h) => {
|
||||
const rect = h.getBoundingClientRect();
|
||||
return rect.top >= 0 && rect.top < window.innerHeight * 0.6;
|
||||
});
|
||||
|
||||
if (visible.length > 0) {
|
||||
setActive(visible[0].id);
|
||||
} else {
|
||||
// If scrolled past all headings, keep last active
|
||||
const above = headings.filter((h) => h.getBoundingClientRect().top < 0);
|
||||
if (above.length > 0) {
|
||||
setActive(above[above.length - 1].id);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
rootMargin: '-10% 0px -60% 0px',
|
||||
threshold: 0,
|
||||
},
|
||||
);
|
||||
|
||||
headings.forEach((h) => observer.observe(h));
|
||||
|
||||
// Set initial active on load
|
||||
const firstVisible = headings.find((h) => {
|
||||
const rect = h.getBoundingClientRect();
|
||||
return rect.top >= 0;
|
||||
});
|
||||
if (firstVisible) setActive(firstVisible.id);
|
||||
})();
|
||||
Vendored
-1
File diff suppressed because one or more lines are too long
+136
@@ -0,0 +1,136 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
|
||||
<xsl:template match="/">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
|
||||
<head>
|
||||
<title><xsl:value-of select="/rss/channel/title"/> - RSS Feed</title>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<style>
|
||||
:root {
|
||||
--bg-color: #f9fafb;
|
||||
--text-color: #1a1a1a;
|
||||
--text-muted: #6b7280;
|
||||
--accent-color: #10b981;
|
||||
--hover-color: #059669; /* deeper emerald */
|
||||
--card-bg: #ffffff;
|
||||
--border-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg-color: #0a0a0a;
|
||||
--text-color: #f9fafb;
|
||||
--text-muted: #a1a1aa;
|
||||
--accent-color: #10b981;
|
||||
--hover-color: #34d399; /* lighter emerald */
|
||||
--card-bg: #141414;
|
||||
--border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
body {
|
||||
font-family: 'Outfit', system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
padding: 2rem;
|
||||
max-width: 800px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
a {
|
||||
color: var(--accent-color);
|
||||
text-decoration: none;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
a:hover {
|
||||
color: var(--hover-color);
|
||||
text-decoration: underline;
|
||||
}
|
||||
.header {
|
||||
margin-bottom: 3rem;
|
||||
padding-bottom: 2rem;
|
||||
border-bottom: 2px solid var(--border-color);
|
||||
}
|
||||
.header h1 {
|
||||
font-size: 2.5rem;
|
||||
margin: 0 0 0.5rem 0;
|
||||
color: var(--accent-color);
|
||||
}
|
||||
.header p {
|
||||
font-size: 1.2rem;
|
||||
margin: 0;
|
||||
opacity: 0.9;
|
||||
}
|
||||
.notice {
|
||||
background-color: var(--card-bg);
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 2rem;
|
||||
border-left: 4px solid var(--accent-color);
|
||||
}
|
||||
.item {
|
||||
background-color: var(--card-bg);
|
||||
padding: 1.5rem;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1.5rem;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
.item h2 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.item .meta {
|
||||
font-size: 0.9rem;
|
||||
opacity: 0.7;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.btn {
|
||||
display: inline-block;
|
||||
background-color: var(--accent-color);
|
||||
color: var(--bg-color);
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 4px;
|
||||
font-weight: 600;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.btn:hover {
|
||||
background-color: var(--hover-color);
|
||||
color: var(--bg-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1><xsl:value-of select="/rss/channel/title"/></h1>
|
||||
<p><xsl:value-of select="/rss/channel/description"/></p>
|
||||
</div>
|
||||
|
||||
<div class="notice">
|
||||
<strong>This is an RSS feed.</strong> Subscribe by copying the URL from your address bar into your news reader app.
|
||||
</div>
|
||||
|
||||
<div class="items">
|
||||
<xsl:for-each select="/rss/channel/item">
|
||||
<div class="item">
|
||||
<h2>
|
||||
<a href="{link}">
|
||||
<xsl:value-of select="title"/>
|
||||
</a>
|
||||
</h2>
|
||||
<div class="meta">
|
||||
Published: <xsl:value-of select="pubDate"/>
|
||||
</div>
|
||||
<div>
|
||||
<xsl:value-of select="substring(description, 1, 250)"/>...
|
||||
</div>
|
||||
<a href="{link}" class="btn">Read article →</a>
|
||||
</div>
|
||||
</xsl:for-each>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
||||
Reference in New Issue
Block a user