astro-accelerator-utils 0.3.7 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +19 -19
  3. package/index.d.mts +4 -4
  4. package/index.mjs +9 -9
  5. package/lib/postFiltering.d.mts +48 -48
  6. package/lib/postFiltering.mjs +116 -116
  7. package/lib/postOrdering.d.mts +32 -32
  8. package/lib/postOrdering.mjs +51 -51
  9. package/lib/v1/accelerator.d.mts +36 -36
  10. package/lib/v1/accelerator.mjs +70 -70
  11. package/lib/v1/authors.d.mts +28 -28
  12. package/lib/v1/authors.mjs +71 -71
  13. package/lib/v1/cache.d.mts +43 -43
  14. package/lib/v1/cache.mjs +99 -99
  15. package/lib/v1/dates.d.mts +19 -19
  16. package/lib/v1/dates.mjs +26 -26
  17. package/lib/v1/markdown.d.mts +20 -20
  18. package/lib/v1/markdown.mjs +45 -45
  19. package/lib/v1/navigation.d.mts +113 -111
  20. package/lib/v1/navigation.mjs +446 -427
  21. package/lib/v1/paging.d.mts +15 -15
  22. package/lib/v1/paging.mjs +77 -77
  23. package/lib/v1/posts.d.mts +26 -26
  24. package/lib/v1/posts.mjs +47 -47
  25. package/lib/v1/statistics-stub.d.mts +5 -5
  26. package/lib/v1/statistics-stub.mjs +9 -9
  27. package/lib/v1/statistics.d.mts +39 -39
  28. package/lib/v1/statistics.mjs +78 -78
  29. package/lib/v1/taxonomy.d.mts +62 -62
  30. package/lib/v1/taxonomy.mjs +144 -144
  31. package/lib/v1/urls.d.mts +40 -40
  32. package/lib/v1/urls.mjs +104 -104
  33. package/package.json +3 -3
  34. package/types/Astro.d.ts +19 -19
  35. package/types/AuthorList.d.ts +8 -8
  36. package/types/BannerImage.d.ts +4 -4
  37. package/types/Frontmatter.d.ts +35 -30
  38. package/types/Link.d.ts +6 -6
  39. package/types/NavPage.d.ts +22 -22
  40. package/types/PagePredicate.d.ts +2 -2
  41. package/types/Site.d.ts +43 -43
  42. package/types/Taxonomy.d.ts +15 -15
@@ -1,428 +1,447 @@
1
- import * as PostFiltering from '../postFiltering.mjs';
2
-
3
- /**
4
- * @typedef { import("./posts.mjs").Posts } Posts
5
- * @typedef { import("./taxonomy.mjs").Taxonomy } Taxonomy
6
- * @typedef { import("./urls.mjs").UrlFormatter } UrlFormatter
7
- * @typedef { import("../../types/Astro").MarkdownInstance } MarkdownInstance
8
- * @typedef { import("../../types/NavPage").NavPage } NavPage
9
- * @typedef { import("../../types/NavPage").MenuItem } MenuItem
10
- */
11
-
12
- const defaults = {
13
- url: '',
14
- ariaCurrent: false,
15
- isOpen: false,
16
- section: '',
17
- children: []
18
- }
19
-
20
- export class Navigation {
21
- /**
22
- * Constructor
23
- * @param {Posts} posts
24
- * @param {UrlFormatter} urlFormatter
25
- * @param {Taxonomy} taxonomy
26
- */
27
- constructor(posts, urlFormatter, taxonomy) {
28
- this.posts = posts;
29
- this.urlFormatter = urlFormatter;
30
- this.taxonomy = taxonomy;
31
- }
32
-
33
- /**
34
- * Returns a list of breadcrumbs
35
- * @param {URL} currentUrl
36
- * @param {string} subfolder
37
- * @param {number} customCount
38
- * @returns {NavPage[]}
39
- */
40
- breadcrumbs(currentUrl, subfolder, customCount) {
41
- const allPages = this.posts.all();
42
-
43
- const pathParts = currentUrl.pathname.split('/');
44
-
45
- if (subfolder.length > 0) {
46
- // Running in a subfolder
47
- pathParts.shift();
48
- }
49
-
50
- /** @type {NavPage[]} */
51
- const navPages = [];
52
- let path = '';
53
-
54
- pathParts.forEach((part) => {
55
- path += part.length > 0 ? '/' + part : '';
56
- const match = this.popMatchingPage(allPages, path);
57
-
58
- if (match) {
59
- navPages.push(this.mapNavPage(match));
60
- }
61
- });
62
-
63
- if (customCount === 0) {
64
- navPages[navPages.length -1].url = currentUrl.pathname;
65
- }
66
-
67
- this.setCurrentPage(navPages, currentUrl);
68
-
69
- return navPages;
70
- }
71
-
72
- /**
73
- *
74
- * @param {URL} currentUrl
75
- * @param {string} subfolder
76
- * @param {(MenuItem | 'auto')[]} menu
77
- * @returns {NavPage[]}
78
- */
79
- menu(currentUrl, subfolder, menu) {
80
- const pages = [];
81
- for (let i = 0; i < menu.length; i++) {
82
- const item = menu[i];
83
- if (this.isNavPage(item)) {
84
- pages.push({ ...defaults, ...item });
85
- } else {
86
- const p = this.autoMenu(subfolder);
87
- for (let j = 0; j < p.length; j++) {
88
- pages.push(p[j]);
89
- }
90
- }
91
- }
92
-
93
- this.setCurrentPage(pages, currentUrl);
94
-
95
- return pages;
96
- }
97
-
98
- /**
99
- *
100
- * @param {NavPage} page
101
- * @param {NavPage[]} pageList
102
- */
103
- getChildren(page, pageList) {
104
- const children = pageList
105
- .filter((mp) =>
106
- page.url != '/'
107
- && mp.url != page.url
108
- && mp.url.startsWith(page.url)
109
- && mp.url.split('/').length == (page.url.split('/').length + 1)
110
- )
111
- .sort((mp) => mp.order);
112
-
113
- for (let child of children) {
114
- child.children = this.getChildren(child, pageList);
115
- }
116
-
117
- if (children.length > 0) {
118
- // Add the item to itself as the first item
119
- const ownChild = structuredClone(page);
120
- ownChild.order = -1;
121
- ownChild.children = [];
122
- children.push(ownChild);
123
- }
124
-
125
- return children;
126
- }
127
-
128
- /**
129
- *
130
- * @param {string} subfolder
131
- * @returns {NavPage[]}
132
- */
133
- autoMenu(subfolder) {
134
- const allPages = this.posts
135
- .all()
136
- .filter(PostFiltering.showInMenu);
137
-
138
- const topLevelPages = this.posts
139
- .root(subfolder)
140
- .filter(PostFiltering.showInMenu);
141
-
142
- const pageHierarchy = topLevelPages
143
- .map(p => this.mapNavPage(p))
144
- .sort((a, b) => parseInt(a.order, 10) - parseInt(b.order, 10));
145
-
146
- /** @type {NavPage[]} */
147
- const pageList = allPages.map(p => this.mapNavPage(p));
148
-
149
- for (let i = 0; i < pageHierarchy.length; i++) {
150
- const page = pageHierarchy[i];
151
-
152
- if (i > 0) {
153
- // Don't add children to first link (Home)
154
- page.children = this.getChildren(page, pageList);
155
- }
156
- }
157
-
158
- return pageHierarchy;
159
- }
160
-
161
- /**
162
- *
163
- * @param {URL} currentUrl
164
- * @param {TranslationProvider} _
165
- * @param {any} translations
166
- * @param {string} subfolder
167
- * @param {(MenuItem | 'categories' | 'tags' | 'toptags')[]} menu
168
- * @returns {NavPage[]}
169
- */
170
- footer(currentUrl, _, translations, subfolder, menu) {
171
-
172
- // const cache = new Cache(site.cacheMaxAge);
173
- // const posts = new Posts(cache);
174
- // const urlFormatter = new UrlFormatter(site.url);
175
- // const taxonomy = new Taxonomy(cache, posts, urlFormatter);
176
- // const navigation = new Navigation(posts, urlFormatter);
177
- const links = this.taxonomy.links(translations, _, subfolder);
178
- const entries = this.taxonomy.getTaxonomy();
179
-
180
- /** @type {NavPage[]} */
181
- let pages = [];
182
-
183
- for (let i = 0; i < menu.length; i++) {
184
- const item = menu[i];
185
- if (this.isNavPage(item)) {
186
- pages.push({ ...defaults, ...item });
187
- } else {
188
- switch (item) {
189
- case 'tags':
190
- const tags = this.getTags(links, _, translations, subfolder, entries);
191
- for (let j = 0; j < tags.length; j++) {
192
- pages.push(tags[j]);
193
- }
194
- break;
195
- case 'toptags':
196
- const toptags = this.getTopTags(links, _, translations, subfolder, entries);
197
- for (let j = 0; j < toptags.length; j++) {
198
- pages.push(toptags[j]);
199
- }
200
- break;
201
- case 'categories':
202
- const categories = this.getCategories(links, _, translations, subfolder, entries);
203
- for (let j = 0; j < categories.length; j++) {
204
- pages.push(categories[j]);
205
- }
206
- break;
207
- }
208
- }
209
- }
210
-
211
- this.setCurrentPage(pages, currentUrl);
212
-
213
- return pages;
214
- }
215
-
216
- /**
217
- *
218
- * @param {TaxonomyLinks} links
219
- * @param {TranslationProvider} _
220
- * @param {any} translations
221
- * @param {string} subfolder
222
- * @param {TaxonomyList} entries
223
- * @returns {NavPage[]}
224
- */
225
- getCategories(links, _, translations, subfolder, entries) {
226
-
227
- const category = _(translations.articles.category) ?? 'category';
228
- const categoryTitle = _(translations.articles.category_title) ?? 'Categories';
229
- const categoryLink = `${subfolder}/${category}/`;
230
-
231
- let order = 0;
232
-
233
- /** @type {NavPage[]} */
234
- const pageHierarchy = [{
235
- title: categoryTitle,
236
- url: categoryLink,
237
- ariaCurrent: false,
238
- isOpen: false,
239
- order: 1,
240
- children: entries.categories.map(item => {
241
- return {
242
- title: item.title,
243
- url: links.getCategoryLink(item.title),
244
- ariaCurrent: false,
245
- isOpen: false,
246
- order: ++order,
247
- children: []
248
- };
249
- })
250
- }];
251
-
252
- return pageHierarchy;
253
- }
254
-
255
- /**
256
- *
257
- * @param {TaxonomyLinks} links
258
- * @param {TranslationProvider} _
259
- * @param {any} translations
260
- * @param {string} subfolder
261
- * @param {TaxonomyList} entries
262
- * @returns {NavPage[]}
263
- */
264
- getTags(links, _, translations, subfolder, entries) {
265
-
266
- const tag = _(translations.articles.tag) ?? 'tag';
267
- const tagTitle = _(translations.articles.tag_title) ?? 'Tags';
268
- const tagLink = `${subfolder}/${tag}/`;
269
-
270
- let order = 0;
271
-
272
- /** @type {NavPage[]} */
273
- const pageHierarchy = [{
274
- title: tagTitle,
275
- url: tagLink,
276
- ariaCurrent: false,
277
- isOpen: false,
278
- order: 1,
279
- children: entries.tags.map(item => {
280
- return {
281
- title: item.title,
282
- url: links.getTagLink(item.title),
283
- ariaCurrent: false,
284
- isOpen: false,
285
- order: ++order,
286
- children: []
287
- };
288
- })
289
- }];
290
-
291
- return pageHierarchy;
292
- }
293
-
294
- /**
295
- *
296
- * @param {TaxonomyLinks} links
297
- * @param {TranslationProvider} _
298
- * @param {any} translations
299
- * @param {string} subfolder
300
- * @param {TaxonomyList} entries
301
- * @returns {NavPage[]}
302
- */
303
- getTopTags(links, _, translations, subfolder, entries) {
304
-
305
- const tag = _(translations.articles.tag) ?? 'tag';
306
- const tagTitle = _(translations.articles.tag_title) ?? 'Tags';
307
- const tagLink = `${subfolder}/${tag}/`;
308
-
309
- let order = 0;
310
-
311
- /** @type {NavPage[]} */
312
- const pageHierarchy = [{
313
- title: tagTitle,
314
- url: tagLink,
315
- ariaCurrent: false,
316
- isOpen: false,
317
- order: 1,
318
- children: entries.topTags.map(item => {
319
- return {
320
- title: item.title,
321
- url: links.getTagLink(item.title),
322
- ariaCurrent: false,
323
- isOpen: false,
324
- order: ++order,
325
- children: []
326
- };
327
- })
328
- }];
329
-
330
- return pageHierarchy;
331
- }
332
-
333
-
334
- /**
335
- * Walks a NavPage tree to set current page
336
- * @param {NavPage[]} pages
337
- * @param {URL} currentUrl
338
- */
339
- setCurrentPage(pages, currentUrl) {
340
- pages.forEach(p => {
341
- p.isOpen = currentUrl.pathname.startsWith(p.url);
342
- p.ariaCurrent = p.url == currentUrl.pathname
343
- ? 'page'
344
- : false;
345
-
346
- if (p.children) {
347
- this.setCurrentPage(p.children, currentUrl);
348
- }
349
- });
350
- }
351
-
352
- /**
353
- * Converts a MarkdownInstance into a NavPage
354
- * @param {MarkdownInstance} page
355
- * @returns {NavPage}
356
- */
357
- mapNavPage(page) {
358
- let url = page.url == null || (page.url ?? '').length == 0
359
- ? '/'
360
- : page.url;
361
-
362
- // Send visitors straight to the first page
363
- if (page.frontmatter.paged) {
364
- url += '/1/';
365
- }
366
-
367
- url = this.urlFormatter.addSlashToAddress(url);
368
-
369
- if (page.frontmatter.layout == 'src/layouts/Redirect.astro') {
370
- // Skips past the redirect
371
- url = page.frontmatter.redirect;
372
- }
373
-
374
- /** @type {NavPage} */
375
- const entry = {
376
- fullTitle: page.frontmatter.title,
377
- section: page.frontmatter.navSection ?? page.frontmatter.navTitle ?? page.frontmatter.title,
378
- title: page.frontmatter.navTitle ?? page.frontmatter.title,
379
- url: url,
380
- order: page.frontmatter.navOrder ?? Number.MAX_SAFE_INTEGER,
381
- children: [],
382
- // These are later set to the correct value, but not now as we want to cache
383
- isOpen: false,
384
- ariaCurrent: false
385
- }
386
-
387
- return entry;
388
- }
389
-
390
-
391
- /**
392
- * Checks whether the item is a NavPage
393
- * @param {NavPage | 'auto' | 'tags' | 'toptags' | 'categories'} item
394
- * @returns {item is NavPage}
395
- */
396
- isNavPage(item) {
397
- if (typeof item === 'string' && ['auto', 'tags', 'toptags', 'categories'].includes(item)) {
398
- return false;
399
- }
400
-
401
- return true;
402
- }
403
-
404
- /**
405
- * Pops matching page from array
406
- * @param {MarkdownInstance[]} allPages
407
- * @param {string} search
408
- * @returns
409
- */
410
- popMatchingPage(allPages, search) {
411
- const numberToRemove = 1;
412
- let indexToRemove = -1;
413
- let match = null;
414
-
415
- for (let i = 0; i < allPages.length; i++) {
416
- if (allPages[i].url == search) {
417
- indexToRemove = i;
418
- match = allPages[i];
419
- }
420
- }
421
-
422
- if (match) {
423
- allPages.splice(indexToRemove, numberToRemove);
424
- }
425
-
426
- return match;
427
- }
1
+ import * as PostFiltering from '../postFiltering.mjs';
2
+
3
+ /**
4
+ * @typedef { import("./posts.mjs").Posts } Posts
5
+ * @typedef { import("./taxonomy.mjs").Taxonomy } Taxonomy
6
+ * @typedef { import("./urls.mjs").UrlFormatter } UrlFormatter
7
+ * @typedef { import("../../types/Astro").MarkdownInstance } MarkdownInstance
8
+ * @typedef { import("../../types/NavPage").NavPage } NavPage
9
+ * @typedef { import("../../types/NavPage").MenuItem } MenuItem
10
+ */
11
+
12
+ const defaults = {
13
+ url: '',
14
+ ariaCurrent: false,
15
+ isOpen: false,
16
+ section: '',
17
+ children: []
18
+ }
19
+
20
+ export class Navigation {
21
+ /**
22
+ * Constructor
23
+ * @param {Posts} posts
24
+ * @param {UrlFormatter} urlFormatter
25
+ * @param {Taxonomy} taxonomy
26
+ */
27
+ constructor(posts, urlFormatter, taxonomy) {
28
+ this.posts = posts;
29
+ this.urlFormatter = urlFormatter;
30
+ this.taxonomy = taxonomy;
31
+ }
32
+
33
+ /**
34
+ * Returns a list of breadcrumbs
35
+ * @param {URL} currentUrl
36
+ * @param {string} subfolder
37
+ * @param {number} customCount
38
+ * @returns {NavPage[]}
39
+ */
40
+ breadcrumbs(currentUrl, subfolder, customCount) {
41
+ const allPages = this.posts.all();
42
+
43
+ const pathParts = currentUrl.pathname.split('/');
44
+
45
+ if (subfolder.length > 0) {
46
+ // Running in a subfolder
47
+ pathParts.shift();
48
+ }
49
+
50
+ /** @type {NavPage[]} */
51
+ const navPages = [];
52
+ let path = '';
53
+
54
+ pathParts.forEach((part) => {
55
+ path += part.length > 0 ? '/' + part : '';
56
+ const match = this.popMatchingPage(allPages, path);
57
+
58
+ if (match) {
59
+ navPages.push(this.mapNavPage(match));
60
+ }
61
+ });
62
+
63
+ if (customCount === 0) {
64
+ navPages[navPages.length -1].url = currentUrl.pathname;
65
+ }
66
+
67
+ this.setCurrentPage(navPages, currentUrl);
68
+
69
+ return navPages;
70
+ }
71
+
72
+ /**
73
+ *
74
+ * @param {URL} currentUrl
75
+ * @param {string} subfolder
76
+ * @param {(MenuItem | 'auto')[]} menu
77
+ * @returns {NavPage[]}
78
+ */
79
+ menu(currentUrl, subfolder, menu) {
80
+ const pages = [];
81
+ for (let i = 0; i < menu.length; i++) {
82
+ const item = menu[i];
83
+ this.addMenuItem(pages, item, subfolder);
84
+ }
85
+
86
+ this.setCurrentPage(pages, currentUrl);
87
+
88
+ return pages;
89
+ }
90
+
91
+ addMenuItem(pages, item, subfolder) {
92
+ if (this.isNavPage(item)) {
93
+ // Expand defaults with custom values
94
+ const result = { ...defaults, ...item };
95
+
96
+ // Recursively add children
97
+ const children = [];
98
+ for (const child of result.children) {
99
+ this.addMenuItem(children, child, subfolder);
100
+ }
101
+ result.children = children;
102
+
103
+ pages.push(result);
104
+ } else {
105
+ const p = this.autoMenu(subfolder);
106
+ for (let j = 0; j < p.length; j++) {
107
+ pages.push(p[j]);
108
+ }
109
+ }
110
+ }
111
+
112
+ /**
113
+ *
114
+ * @param {NavPage} page
115
+ * @param {NavPage[]} pageList
116
+ */
117
+ getChildren(page, pageList) {
118
+ const children = pageList
119
+ .filter((mp) =>
120
+ page.url != '/'
121
+ && mp.url != page.url
122
+ && mp.url.startsWith(page.url)
123
+ && mp.url.split('/').length == (page.url.split('/').length + 1)
124
+ )
125
+ .sort((mp) => mp.order);
126
+
127
+ for (let child of children) {
128
+ child.children = this.getChildren(child, pageList);
129
+ }
130
+
131
+ if (children.length > 0) {
132
+ // Add the item to itself as the first item
133
+ const ownChild = structuredClone(page);
134
+ ownChild.order = -1;
135
+ ownChild.children = [];
136
+ children.push(ownChild);
137
+ }
138
+
139
+ return children;
140
+ }
141
+
142
+ /**
143
+ *
144
+ * @param {string} subfolder
145
+ * @returns {NavPage[]}
146
+ */
147
+ autoMenu(subfolder) {
148
+ const allPages = this.posts
149
+ .all()
150
+ .filter(PostFiltering.showInMenu);
151
+
152
+ const topLevelPages = this.posts
153
+ .root(subfolder)
154
+ .filter(PostFiltering.showInMenu);
155
+
156
+ const pageHierarchy = topLevelPages
157
+ .map(p => this.mapNavPage(p))
158
+ .sort((a, b) => parseInt(a.order, 10) - parseInt(b.order, 10));
159
+
160
+ /** @type {NavPage[]} */
161
+ const pageList = allPages.map(p => this.mapNavPage(p));
162
+
163
+ for (let i = 0; i < pageHierarchy.length; i++) {
164
+ const page = pageHierarchy[i];
165
+
166
+ if (i > 0) {
167
+ // Don't add children to first link (Home)
168
+ page.children = this.getChildren(page, pageList);
169
+ }
170
+ }
171
+
172
+ return pageHierarchy;
173
+ }
174
+
175
+ /**
176
+ *
177
+ * @param {URL} currentUrl
178
+ * @param {TranslationProvider} _
179
+ * @param {any} translations
180
+ * @param {string} subfolder
181
+ * @param {(MenuItem | 'categories' | 'tags' | 'toptags')[]} menu
182
+ * @returns {NavPage[]}
183
+ */
184
+ footer(currentUrl, _, translations, subfolder, menu) {
185
+
186
+ // const cache = new Cache(site.cacheMaxAge);
187
+ // const posts = new Posts(cache);
188
+ // const urlFormatter = new UrlFormatter(site.url);
189
+ // const taxonomy = new Taxonomy(cache, posts, urlFormatter);
190
+ // const navigation = new Navigation(posts, urlFormatter);
191
+ const links = this.taxonomy.links(translations, _, subfolder);
192
+ const entries = this.taxonomy.getTaxonomy();
193
+
194
+ /** @type {NavPage[]} */
195
+ let pages = [];
196
+
197
+ for (let i = 0; i < menu.length; i++) {
198
+ const item = menu[i];
199
+ this.addFooterItem(pages, item, links, _, translations, subfolder, entries);
200
+ }
201
+
202
+ this.setCurrentPage(pages, currentUrl);
203
+
204
+ return pages;
205
+ }
206
+
207
+ addFooterItem(pages, item, links, _, translations, subfolder, entries) {
208
+ if (this.isNavPage(item)) {
209
+ const result = {...defaults, ...item};
210
+ pages.push(result)
211
+ } else {
212
+ switch (item) {
213
+ case 'tags':
214
+ const tags = this.getTags(links, _, translations, subfolder, entries);
215
+ for (let j = 0; j < tags.length; j++) {
216
+ pages.push(tags[j]);
217
+ }
218
+ break;
219
+ case 'toptags':
220
+ const toptags = this.getTopTags(links, _, translations, subfolder, entries);
221
+ for (let j = 0; j < toptags.length; j++) {
222
+ pages.push(toptags[j]);
223
+ }
224
+ break;
225
+ case 'categories':
226
+ const categories = this.getCategories(links, _, translations, subfolder, entries);
227
+ for (let j = 0; j < categories.length; j++) {
228
+ pages.push(categories[j]);
229
+ }
230
+ break;
231
+ }
232
+ }
233
+ }
234
+
235
+ /**
236
+ *
237
+ * @param {TaxonomyLinks} links
238
+ * @param {TranslationProvider} _
239
+ * @param {any} translations
240
+ * @param {string} subfolder
241
+ * @param {TaxonomyList} entries
242
+ * @returns {NavPage[]}
243
+ */
244
+ getCategories(links, _, translations, subfolder, entries) {
245
+
246
+ const category = _(translations.articles.category) ?? 'category';
247
+ const categoryTitle = _(translations.articles.category_title) ?? 'Categories';
248
+ const categoryLink = `${subfolder}/${category}/`;
249
+
250
+ let order = 0;
251
+
252
+ /** @type {NavPage[]} */
253
+ const pageHierarchy = [{
254
+ title: categoryTitle,
255
+ url: categoryLink,
256
+ ariaCurrent: false,
257
+ isOpen: false,
258
+ order: 1,
259
+ children: entries.categories.map(item => {
260
+ return {
261
+ title: item.title,
262
+ url: links.getCategoryLink(item.title),
263
+ ariaCurrent: false,
264
+ isOpen: false,
265
+ order: ++order,
266
+ children: []
267
+ };
268
+ })
269
+ }];
270
+
271
+ return pageHierarchy;
272
+ }
273
+
274
+ /**
275
+ *
276
+ * @param {TaxonomyLinks} links
277
+ * @param {TranslationProvider} _
278
+ * @param {any} translations
279
+ * @param {string} subfolder
280
+ * @param {TaxonomyList} entries
281
+ * @returns {NavPage[]}
282
+ */
283
+ getTags(links, _, translations, subfolder, entries) {
284
+
285
+ const tag = _(translations.articles.tag) ?? 'tag';
286
+ const tagTitle = _(translations.articles.tag_title) ?? 'Tags';
287
+ const tagLink = `${subfolder}/${tag}/`;
288
+
289
+ let order = 0;
290
+
291
+ /** @type {NavPage[]} */
292
+ const pageHierarchy = [{
293
+ title: tagTitle,
294
+ url: tagLink,
295
+ ariaCurrent: false,
296
+ isOpen: false,
297
+ order: 1,
298
+ children: entries.tags.map(item => {
299
+ return {
300
+ title: item.title,
301
+ url: links.getTagLink(item.title),
302
+ ariaCurrent: false,
303
+ isOpen: false,
304
+ order: ++order,
305
+ children: []
306
+ };
307
+ })
308
+ }];
309
+
310
+ return pageHierarchy;
311
+ }
312
+
313
+ /**
314
+ *
315
+ * @param {TaxonomyLinks} links
316
+ * @param {TranslationProvider} _
317
+ * @param {any} translations
318
+ * @param {string} subfolder
319
+ * @param {TaxonomyList} entries
320
+ * @returns {NavPage[]}
321
+ */
322
+ getTopTags(links, _, translations, subfolder, entries) {
323
+
324
+ const tag = _(translations.articles.tag) ?? 'tag';
325
+ const tagTitle = _(translations.articles.tag_title) ?? 'Tags';
326
+ const tagLink = `${subfolder}/${tag}/`;
327
+
328
+ let order = 0;
329
+
330
+ /** @type {NavPage[]} */
331
+ const pageHierarchy = [{
332
+ title: tagTitle,
333
+ url: tagLink,
334
+ ariaCurrent: false,
335
+ isOpen: false,
336
+ order: 1,
337
+ children: entries.topTags.map(item => {
338
+ return {
339
+ title: item.title,
340
+ url: links.getTagLink(item.title),
341
+ ariaCurrent: false,
342
+ isOpen: false,
343
+ order: ++order,
344
+ children: []
345
+ };
346
+ })
347
+ }];
348
+
349
+ return pageHierarchy;
350
+ }
351
+
352
+
353
+ /**
354
+ * Walks a NavPage tree to set current page
355
+ * @param {NavPage[]} pages
356
+ * @param {URL} currentUrl
357
+ */
358
+ setCurrentPage(pages, currentUrl) {
359
+ pages.forEach(p => {
360
+ p.isOpen = currentUrl.pathname.startsWith(p.url);
361
+ p.ariaCurrent = p.url == currentUrl.pathname
362
+ ? 'page'
363
+ : false;
364
+
365
+ if (p.children) {
366
+ this.setCurrentPage(p.children, currentUrl);
367
+ }
368
+ });
369
+ }
370
+
371
+ /**
372
+ * Converts a MarkdownInstance into a NavPage
373
+ * @param {MarkdownInstance} page
374
+ * @returns {NavPage}
375
+ */
376
+ mapNavPage(page) {
377
+ let url = page.url == null || (page.url ?? '').length == 0
378
+ ? '/'
379
+ : page.url;
380
+
381
+ // Send visitors straight to the first page
382
+ if (page.frontmatter.paged) {
383
+ url += '/1/';
384
+ }
385
+
386
+ url = this.urlFormatter.addSlashToAddress(url);
387
+
388
+ if (page.frontmatter.layout == 'src/layouts/Redirect.astro') {
389
+ // Skips past the redirect
390
+ url = page.frontmatter.redirect;
391
+ }
392
+
393
+ /** @type {NavPage} */
394
+ const entry = {
395
+ fullTitle: page.frontmatter.title,
396
+ section: page.frontmatter.navSection ?? page.frontmatter.navTitle ?? page.frontmatter.title,
397
+ title: page.frontmatter.navTitle ?? page.frontmatter.title,
398
+ url: url,
399
+ order: page.frontmatter.navOrder ?? Number.MAX_SAFE_INTEGER,
400
+ children: [],
401
+ // These are later set to the correct value, but not now as we want to cache
402
+ isOpen: false,
403
+ ariaCurrent: false
404
+ }
405
+
406
+ return entry;
407
+ }
408
+
409
+
410
+ /**
411
+ * Checks whether the item is a NavPage
412
+ * @param {NavPage | 'auto' | 'tags' | 'toptags' | 'categories'} item
413
+ * @returns {item is NavPage}
414
+ */
415
+ isNavPage(item) {
416
+ if (typeof item === 'string' && ['auto', 'tags', 'toptags', 'categories'].includes(item)) {
417
+ return false;
418
+ }
419
+
420
+ return true;
421
+ }
422
+
423
+ /**
424
+ * Pops matching page from array
425
+ * @param {MarkdownInstance[]} allPages
426
+ * @param {string} search
427
+ * @returns
428
+ */
429
+ popMatchingPage(allPages, search) {
430
+ const numberToRemove = 1;
431
+ let indexToRemove = -1;
432
+ let match = null;
433
+
434
+ for (let i = 0; i < allPages.length; i++) {
435
+ if (allPages[i].url == search) {
436
+ indexToRemove = i;
437
+ match = allPages[i];
438
+ }
439
+ }
440
+
441
+ if (match) {
442
+ allPages.splice(indexToRemove, numberToRemove);
443
+ }
444
+
445
+ return match;
446
+ }
428
447
  }