-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
302 lines (262 loc) · 12.9 KB
/
index.html
File metadata and controls
302 lines (262 loc) · 12.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
<html>
<head>
<title>Antipodean</title>
<link href="./navbar.css" rel="stylesheet">
<link href="./shared.css" rel="stylesheet">
<link href="./toc.css" rel="stylesheet">
<link href="./content.css" rel="stylesheet">
<link href="./content-mobile.css" rel="stylesheet">
<link rel="icon" href="./favicon.svg" type="image/svg+xml"> <!-- Use SVG as favicon -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
</head>
<body>
<div id="navbar">
<button id="prev-chapter" class="nav-button"><</button>
<div id="book-title"></div>
<button id="next-chapter" class="nav-button">></button>
</div>
<div id="toc" class="hidden">
</div>
<div id="content"></div> <!-- Restore original #content div -->
<div id="hidden-content" style="display: none;"></div> <!-- Hidden div for temporary content -->
<script src="js/smartquotes.min.js" defer></script>
<script defer>
async function isLocalServer() {
try {
const response = await fetch(location.href, { method: 'HEAD' });
return response.headers.get('X-Local-Server') === 'true';
} catch (error) {
console.error('Error checking for local server:', error);
return false;
}
}
(async () => {
const isLocal = await isLocalServer();
const githubPagesBaseUrl = isLocal
? 'https://peterwone.github.io/Antipodean/' // Simulate GitHub Pages base URL when running locally
: location.href.split('/').slice(0, -1).join('/') + '/'; // Use location.href to get the base URL
const repoName = isLocal
? 'Antipodean' // Explicitly set the repository name when running locally
: githubPagesBaseUrl.split('/').slice(-2, -1)[0]; // Extract the repository name from the URL
const username = isLocal
? 'peterwone' // Simulate username when running locally
: location.hostname.split('.')[0]; // Extract the username from the hostname
const screenWidth = window.innerWidth;
const columnWidth = screenWidth - 11.9; // Calculate the scroll distance based on column width and gap
const repoUrl = `https://api.github.com/repos/${username}/${repoName}/contents/`; // Construct the GitHub API URL
const navbar = document.getElementById('navbar');
const navbarHeight = navbar.offsetHeight; // Get the actual navbar height
const vw = navbar.offsetWidth; // should be the same as window.innerWidth
document.documentElement.style.setProperty('--navbar-height', `${navbarHeight}px`); // Update the CSS variable
const toc = document.getElementById('toc');
const content = document.getElementById('content');
const hiddenContent = document.getElementById('hidden-content');
const prevButton = document.getElementById('prev-chapter');
const nextButton = document.getElementById('next-chapter');
const bookTitle = document.getElementById('book-title');
bookTitle.textContent = repoName;
let markdownFiles = [];
let currentIndex = 0;
let chapterScrollPositions = {}; // Object to store scroll positions for each chapter
let baseFontSize = parseFloat(getComputedStyle(content).fontSize) || 16; // Default font size
const savedFontSize = localStorage.getItem('contentFontSize');
if (savedFontSize) {
baseFontSize = parseFloat(savedFontSize);
content.style.fontSize = `${baseFontSize}px`;
}
let lastTouchDistance = null;
content.addEventListener('touchmove', (e) => {
if (e.touches.length === 2) {
const touch1 = e.touches[0];
const touch2 = e.touches[1];
const currentDistance = Math.hypot(
touch2.pageX - touch1.pageX,
touch2.pageY - touch1.pageY
);
if (lastTouchDistance) {
const scale = currentDistance / lastTouchDistance;
baseFontSize = Math.max(16, Math.min(32, baseFontSize * scale));
content.style.fontSize = `${baseFontSize}px`;
localStorage.setItem('contentFontSize', baseFontSize); // Save the font size
}
lastTouchDistance = currentDistance;
}
});
content.addEventListener('touchend', () => {
lastTouchDistance = null; // Reset when touch ends
});
// Detect taps on the left or right quarter of the screen and scroll one column left or right, respectively
content.addEventListener('click', async (e) => {
const tapX = e.clientX;
if (tapX < screenWidth * 0.25) {
await TapLeft(columnWidth);
} else if (tapX > screenWidth * 0.75) {
await TapRight(columnWidth);
}
});
async function TapLeft(scrollDistance) {
console.log(`TapLeft(${scrollDistance})`);
var positionWhenTapped = content.scrollLeft;
const targetScrollLeft = Math.max(0, content.scrollLeft - scrollDistance); // Calculate target scroll position
if (positionWhenTapped < scrollDistance && currentIndex > 0) {
console.log("Loading previous chapter");
content.classList.add('chapter-transition-back'); // Add transition class
await loadContent(currentIndex - 1);
const lastPageOffset = content.scrollWidth - content.clientWidth; // Scroll to the end of the previous chapter
content.scrollLeft = lastPageOffset;
setTimeout(() => content.classList.remove('chapter-transition-back'), 500); // Remove transition class after animation
} else {
content.scrollTo({
left: targetScrollLeft,
behavior: 'smooth',
});
}
}
async function TapRight(scrollDistance) {
console.log(`TapRight(${scrollDistance})`);
var positionWhenTapped = content.scrollLeft;
const targetScrollLeft = Math.min(content.scrollWidth - content.clientWidth, content.scrollLeft + scrollDistance); // Calculate target scroll position
const maxScrollLeft = content.scrollWidth - content.offsetWidth - 16;
if (positionWhenTapped >= maxScrollLeft && currentIndex < markdownFiles.length - 1) {
console.log("Loading next chapter");
content.classList.add('chapter-transition-forward'); // Add transition class
await loadContent(currentIndex + 1);
content.scrollLeft = 0; // Set to the start of the next chapter
setTimeout(() => content.classList.remove('chapter-transition-forward'), 500); // Remove transition class after animation
} else {
content.scrollTo({
left: targetScrollLeft,
behavior: 'smooth',
});
}
}
// Toggle between TOC and content when the title is clicked
bookTitle.addEventListener('click', () => {
toc.classList.toggle('hidden');
content.classList.toggle('hidden');
if (!toc.classList.contains('hidden')) {
scrollToCurrentChapter(); // Ensure the current chapter is visible
}
});
async function fetchMarkdownFiles() {
try {
const response = await fetch(isLocal ? '/api/toc' : repoUrl + 'src/');
const files = await response.json();
markdownFiles = isLocal
? files // Use the local API response directly
: files.filter(file => file.name.endsWith('.md')).sort((a, b) => a.name.localeCompare(b.name));
markdownFiles.forEach((file, index) => {
const paragraph = document.createElement('p'); // Create a <p> element
paragraph.classList.add('toc-item'); // Add a class for styling
paragraph.textContent = file.name
.replace(/\.md$/, '') // Remove extension
.replace(/\.(?=[a-zA-Z])/g, ' ') // Period followed by a letter becomes a space
.replace(/-/g, ' '); // Replace dashes with spaces
paragraph.addEventListener('click', (e) => {
e.preventDefault();
loadContent(index);
localStorage.setItem('lastSelectedPage', file.name); // Save the last selected page
toc.classList.add('hidden'); // Hide TOC
content.classList.remove('hidden'); // Show content
});
toc.appendChild(paragraph); // Append the <p> to the TOC
});
// Load the last selected page or the blurb (first chapter file alphabetically)
const lastSelectedPage = localStorage.getItem('lastSelectedPage');
const lastIndex = markdownFiles.findIndex(file => file.name === lastSelectedPage);
if (lastIndex !== -1) {
loadContent(lastIndex);
} else if (markdownFiles.length > 0) {
loadContent(0);
}
} catch (error) {
console.error("Error fetching markdown files:", error);
}
}
async function loadContent(index) {
try {
// Save the current scroll position for the current chapter
const currentFileName = markdownFiles[currentIndex]?.name;
if (currentFileName) {
chapterScrollPositions[currentFileName] = content.scrollLeft; // Use scrollLeft for horizontal scrolling
}
currentIndex = index;
const fileName = markdownFiles[index].name;
const basePath = isLocal ? './src/' : githubPagesBaseUrl + 'src/'; // Adjust base path to include src
const response = await fetch(basePath + (isLocal ? fileName : fileName.replace(/\.md$/, '.html'))); // Change .md to .html for GitHub
const html = await response.text();
hiddenContent.innerHTML = html.replace(/<img\s+([^>]*?)src=["']\.\/([^"']+)["']/g, '<img $1src="src/$2"'); // Update image paths
const markdownBody = hiddenContent.querySelector('.markdown-body'); // Look for the div with class markdown-body
// GitHub injects an <h1> with an anchor link at the top of the markdown body, remove it
const h1WithAnchor = markdownBody.querySelector('h1');
if (h1WithAnchor) {
markdownBody.removeChild(h1WithAnchor);
}
const chapterTitleText = fileName
.replace(/\.md$/, '') // Remove extension
.replace(/\.(?=[a-zA-Z])/g, ' ') // Period followed by a letter becomes a space
.replace(/-/g, ' '); // Replace dashes with spaces
content.innerHTML = `<h1>${chapterTitleText}</h1>\n${markdownBody.innerHTML}` ?? '<p>Content not found.</p>';
hiddenContent.innerHTML = ''; // Clear the hidden div
// Apply smartquotes to the loaded content
smartquotes(content);
highlightCurrentChapter();
updateNavButtons(); // Update navigation buttons based on current index
// if the toc is visible, hide it and show content
if (!toc.classList.contains('hidden')) {
toc.classList.add('hidden');
content.classList.remove('hidden');
}
} catch (error) {
console.error("Error loading content:", error);
}
}
prevButton.addEventListener('click', async () => {
if (currentIndex > 0) {
await loadContent(currentIndex - 1); // Remove transition argument
content.scrollLeft = content.scrollWidth - content.clientWidth - 1; // Scroll to the end of the previous chapter
}
});
nextButton.addEventListener('click', async () => {
if (currentIndex < markdownFiles.length - 1) {
await loadContent(currentIndex + 1); // Remove transition argument
content.scrollLeft = 0; // Scroll to the start of the next chapter
}
});
function updateNavButtons() {
prevButton.disabled = currentIndex === 0;
nextButton.disabled = currentIndex === markdownFiles.length - 1;
}
function highlightCurrentChapter() {
const tocItems = toc.querySelectorAll('.toc-item'); // Select all TOC items
tocItems.forEach((item, index) => {
item.classList.toggle('current-chapter', index === currentIndex); // Highlight the current chapter
});
}
function scrollToCurrentChapter() {
const currentChapter = toc.querySelector('.current-chapter');
if (currentChapter) {
currentChapter.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}
function AdjustSnap() {
// Adjust the scroll position to snap to the nearest column
const quantisedScrollLeft = Math.round(content.scrollLeft / columnWidth) * columnWidth;
content.scrollTo({
left: quantisedScrollLeft,
behavior: 'smooth',
});
}
// Add an event listener for the scroll event
let isScrolling;
content.addEventListener('scroll', () => {
clearTimeout(isScrolling); // Clear any existing timeout
isScrolling = setTimeout(() => {
AdjustSnap(); // Call AdjustSnap when scrolling settles
}, 100); // Adjust timeout as needed
});
fetchMarkdownFiles();
})();
</script>
</body>
</html>