first
This commit is contained in:
26
node_modules/@11ty/eleventy-navigation/.eleventy.js
generated
vendored
Normal file
26
node_modules/@11ty/eleventy-navigation/.eleventy.js
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
const pkg = require("./package.json");
|
||||
const EleventyNavigation = require("./eleventy-navigation");
|
||||
|
||||
// export the configuration function for plugin
|
||||
module.exports = function(eleventyConfig) {
|
||||
try {
|
||||
eleventyConfig.versionCheck(pkg["11ty"].compatibility);
|
||||
} catch(e) {
|
||||
console.log( `WARN: Eleventy Plugin (${pkg.name}) Compatibility: ${e.message}` );
|
||||
}
|
||||
|
||||
eleventyConfig.addFilter("eleventyNavigation", EleventyNavigation.findNavigationEntries);
|
||||
eleventyConfig.addFilter("eleventyNavigationBreadcrumb", EleventyNavigation.findBreadcrumbEntries);
|
||||
eleventyConfig.addFilter("eleventyNavigationToHtml", function(pages, options) {
|
||||
return EleventyNavigation.toHtml.call(eleventyConfig, pages, options);
|
||||
});
|
||||
eleventyConfig.addFilter("eleventyNavigationToMarkdown", function(pages, options) {
|
||||
return EleventyNavigation.toMarkdown.call(eleventyConfig, pages, options);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.navigation = {
|
||||
find: EleventyNavigation.findNavigationEntries,
|
||||
findBreadcrumbs: EleventyNavigation.findBreadcrumbEntries,
|
||||
getDependencyGraph: EleventyNavigation.getDependencyGraph,
|
||||
};
|
||||
22
node_modules/@11ty/eleventy-navigation/.github/workflows/ci.yml
generated
vendored
Normal file
22
node_modules/@11ty/eleventy-navigation/.github/workflows/ci.yml
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
name: Node Unit Tests
|
||||
on: [push, pull_request]
|
||||
permissions: read-all
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
|
||||
node: ["18", "20", "22", "24"]
|
||||
name: Node.js ${{ matrix.node }} on ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # 4.1.7
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # 4.0.3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
# cache: npm
|
||||
- run: npm install
|
||||
- run: npm test
|
||||
env:
|
||||
YARN_GPG: no
|
||||
25
node_modules/@11ty/eleventy-navigation/.github/workflows/release.yml
generated
vendored
Normal file
25
node_modules/@11ty/eleventy-navigation/.github/workflows/release.yml
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
name: Publish Release to npm
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
permissions: read-all
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
environment: GitHub Publish
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # 4.1.7
|
||||
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # 4.0.3
|
||||
with:
|
||||
node-version: "22"
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
- run: npm install -g npm@latest
|
||||
- run: npm ci
|
||||
- run: npm test
|
||||
- if: ${{ github.event.release.tag_name != '' && env.NPM_PUBLISH_TAG != '' }}
|
||||
run: npm publish --provenance --access=public --tag=${{ env.NPM_PUBLISH_TAG }}
|
||||
env:
|
||||
NPM_PUBLISH_TAG: ${{ contains(github.event.release.tag_name, '-beta.') && 'beta' || 'latest' }}
|
||||
21
node_modules/@11ty/eleventy-navigation/LICENSE
generated
vendored
Normal file
21
node_modules/@11ty/eleventy-navigation/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019–2025 Zach Leatherman @zachleat
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
9
node_modules/@11ty/eleventy-navigation/README.md
generated
vendored
Normal file
9
node_modules/@11ty/eleventy-navigation/README.md
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
<p align="center"><img src="https://www.11ty.dev/img/logo-github.svg" width="200" height="200" alt="11ty Logo"></p>
|
||||
|
||||
# eleventy-navigation 🕚⚡️🎈🐀
|
||||
|
||||
A plugin for creating hierarchical navigation in Eleventy projects. Supports breadcrumbs too!
|
||||
|
||||
Used [in production on 11ty.dev](https://www.11ty.dev/docs/)!
|
||||
|
||||
## Read the [Full Documentation on 11ty.dev](https://www.11ty.dev/docs/plugins/navigation/)
|
||||
277
node_modules/@11ty/eleventy-navigation/eleventy-navigation.js
generated
vendored
Normal file
277
node_modules/@11ty/eleventy-navigation/eleventy-navigation.js
generated
vendored
Normal file
@ -0,0 +1,277 @@
|
||||
const DepGraph = require("dependency-graph").DepGraph;
|
||||
|
||||
function findNavigationEntries(nodes = [], key = "") {
|
||||
let keys = key.split(",").filter(k => Boolean(k));
|
||||
let pages = {};
|
||||
for(let entry of nodes) {
|
||||
let data = entry?.data || {};
|
||||
if(data?.eleventyNavigation) {
|
||||
let {eleventyNavigation} = data || {};
|
||||
|
||||
let pageKey;
|
||||
if(!key && !eleventyNavigation.parent) { // top level (no parents)
|
||||
pageKey = "__default";
|
||||
} else if(keys.includes(eleventyNavigation.parent)) {
|
||||
pageKey = eleventyNavigation.parent;
|
||||
}
|
||||
|
||||
if(pageKey) {
|
||||
if(!pages[pageKey]) {
|
||||
pages[pageKey] = [];
|
||||
}
|
||||
let url = eleventyNavigation.url ?? data?.page?.url;
|
||||
|
||||
pages[pageKey].push(Object.assign({ data }, eleventyNavigation, {
|
||||
...(url ? { url } : {}),
|
||||
pluginType: "eleventy-navigation",
|
||||
...(keys.length > 0 ? { parentKey: eleventyNavigation.parent } : {}),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Object.values(pages).flat().sort(function(a, b) {
|
||||
if(a.pinned && b.pinned) {
|
||||
return (a.order || 0) - (b.order || 0);
|
||||
}
|
||||
|
||||
let order = [a.order, b.order];
|
||||
if(a.pinned) {
|
||||
order[0] = -Infinity;
|
||||
}
|
||||
if(b.pinned) {
|
||||
order[1] = -Infinity;
|
||||
}
|
||||
|
||||
if(order[0] === undefined && order[1] === undefined) {
|
||||
return 0;
|
||||
}
|
||||
if(order[1] === undefined) {
|
||||
return -1;
|
||||
}
|
||||
if(order[0] === undefined) {
|
||||
return 1;
|
||||
}
|
||||
return order[0] - order[1];
|
||||
}).map(function(entry) {
|
||||
if(!entry.title) {
|
||||
entry.title = entry.key;
|
||||
}
|
||||
if(entry.key) {
|
||||
entry.children = findNavigationEntries(nodes, entry.key);
|
||||
}
|
||||
return entry;
|
||||
});
|
||||
}
|
||||
|
||||
function findDependencies(pages, depGraph, parentKey) {
|
||||
for( let page of pages ) {
|
||||
depGraph.addNode(page.key, page);
|
||||
if(parentKey) {
|
||||
depGraph.addDependency(page.key, parentKey);
|
||||
}
|
||||
if(page.children) {
|
||||
findDependencies(page.children, depGraph, page.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getDependencyGraph(nodes) {
|
||||
let pages = findNavigationEntries(nodes);
|
||||
let graph = new DepGraph();
|
||||
findDependencies(pages, graph);
|
||||
return graph;
|
||||
}
|
||||
|
||||
function isOptionMatch(options, name) {
|
||||
// Liquid.js issue #35
|
||||
if(Array.isArray(options)) {
|
||||
return options[options.indexOf(name)]
|
||||
}
|
||||
return options[name];
|
||||
}
|
||||
|
||||
function findBreadcrumbEntries(nodes, activeKey, options = {}) {
|
||||
let graph = getDependencyGraph(nodes);
|
||||
if (isOptionMatch(options, "allowMissing") && !graph.hasNode(activeKey)) {
|
||||
// Fail gracefully if the key isn't in the graph
|
||||
return [];
|
||||
}
|
||||
let deps = graph.dependenciesOf(activeKey);
|
||||
if(isOptionMatch(options, "includeSelf")) {
|
||||
deps.push(activeKey);
|
||||
}
|
||||
|
||||
return activeKey ? deps.map(key => {
|
||||
let data = Object.assign({}, graph.getNodeData(key));
|
||||
delete data.children;
|
||||
data._isBreadcrumb = true;
|
||||
return data;
|
||||
}) : [];
|
||||
}
|
||||
|
||||
function getUrlFilter(eleventyConfig) {
|
||||
// eleventyConfig.pathPrefix was first available in Eleventy 2.0.0-canary.15
|
||||
// And in Eleventy 2.0.0-canary.15 we recommend the a built-in transform for pathPrefix
|
||||
if(eleventyConfig.pathPrefix !== undefined) {
|
||||
return function(url) {
|
||||
return url;
|
||||
};
|
||||
}
|
||||
|
||||
if("getFilter" in eleventyConfig) {
|
||||
// v0.10.0 and above
|
||||
return eleventyConfig.getFilter("url");
|
||||
} else if("nunjucksFilters" in eleventyConfig) {
|
||||
// backwards compat, hardcoded key
|
||||
return eleventyConfig.nunjucksFilters.url;
|
||||
} else {
|
||||
// Theoretically we could just move on here with a `url => url` but then `pathPrefix`
|
||||
// would not work and it wouldn’t be obvious why—so let’s fail loudly to avoid that.
|
||||
throw new Error("Could not find a `url` filter for the eleventy-navigation plugin in eleventyNavigationToHtml filter.");
|
||||
}
|
||||
}
|
||||
|
||||
function buildHtmlAttr(name, values) {
|
||||
// values could be array or string
|
||||
if (!values || !values.length) {
|
||||
return '';
|
||||
}
|
||||
const valueStr = Array.isArray(values) ? values.join(" ") : values;
|
||||
return ` ${name}="${valueStr}"`;
|
||||
}
|
||||
|
||||
function buildAllHtmlAttrs(attrs) {
|
||||
return attrs.reduce((acc, { name, values }) => acc + buildHtmlAttr(name, values), '');
|
||||
}
|
||||
|
||||
function navigationToHtml(pages, options = {}) {
|
||||
options = Object.assign({
|
||||
listElement: "ul",
|
||||
listItemElement: "li",
|
||||
listClass: "",
|
||||
listItemClass: "",
|
||||
listItemHasChildrenClass: "",
|
||||
activeKey: "",
|
||||
activeListItemClass: "",
|
||||
anchorClass: "",
|
||||
activeAnchorClass: "",
|
||||
useAriaCurrentAttr: false,
|
||||
showExcerpt: false,
|
||||
isChildList: false,
|
||||
useTopLevelDetails: false,
|
||||
anchorElementWithoutHref: "a", // default, better to use span
|
||||
}, options);
|
||||
|
||||
let isChildList = !!options.isChildList;
|
||||
options.isChildList = true;
|
||||
|
||||
let urlFilter;
|
||||
|
||||
if(pages.length && pages[0].pluginType !== "eleventy-navigation") {
|
||||
throw new Error("Incorrect argument passed to eleventyNavigationToHtml filter. You must call `eleventyNavigation` or `eleventyNavigationBreadcrumb` first, like: `collection.all | eleventyNavigation | eleventyNavigationToHtml | safe`");
|
||||
}
|
||||
|
||||
return pages.length ? `<${options.listElement}${!isChildList && options.listClass ? ` class="${options.listClass}"` : ''}>${pages.map(entry => {
|
||||
let liClass = [];
|
||||
let aClass = [];
|
||||
let aAttrs = [];
|
||||
if(options.listItemClass) {
|
||||
liClass.push(options.listItemClass);
|
||||
}
|
||||
if(options.anchorClass) {
|
||||
aClass.push(options.anchorClass);
|
||||
}
|
||||
if(entry.url) {
|
||||
if(!urlFilter) {
|
||||
// don’t get if not used
|
||||
urlFilter = getUrlFilter(this);
|
||||
}
|
||||
aAttrs.push({name: "href", values: urlFilter(entry.url)})
|
||||
}
|
||||
if(options.activeKey === entry.key) {
|
||||
if(options.activeListItemClass) {
|
||||
liClass.push(options.activeListItemClass);
|
||||
}
|
||||
if(options.activeAnchorClass) {
|
||||
aClass.push(options.activeAnchorClass);
|
||||
}
|
||||
if(options.useAriaCurrentAttr) {
|
||||
aAttrs.push({ name: "aria-current", values: "page" });
|
||||
}
|
||||
}
|
||||
if(options.listItemHasChildrenClass && entry.children && entry.children.length) {
|
||||
liClass.push(options.listItemHasChildrenClass);
|
||||
}
|
||||
if(aClass.length) {
|
||||
aAttrs.push({ name: "class", values: aClass });
|
||||
}
|
||||
|
||||
let postfix = "";
|
||||
|
||||
// Helper to show pin/order in text:
|
||||
// let hasOrder = entry.order || entry.order === 0;
|
||||
// if(process.env.ELEVENTY_RUN_MODE === "serve" && (hasOrder || entry.pinned)) {
|
||||
// postfix = ` (${entry.pinned ? "📌" : ""}${entry.order ?? ""})`;
|
||||
// }
|
||||
|
||||
let aAttrsStr = buildAllHtmlAttrs(aAttrs);
|
||||
let hasLink = aAttrs.find(entry => entry.name === "href");
|
||||
let itemTitle = entry.title + postfix;
|
||||
|
||||
let titleHtmlStart = `<a${aAttrsStr}>${itemTitle}</a>`;
|
||||
|
||||
// purely defensive use of `useTopLevelDetails` here
|
||||
if(options.anchorElementWithoutHref && !hasLink) {
|
||||
titleHtmlStart = `<${options.anchorElementWithoutHref}>${itemTitle}</${options.anchorElementWithoutHref}>`;
|
||||
}
|
||||
|
||||
let titleHtmlEnd = "";
|
||||
if(options.useTopLevelDetails && !isChildList && entry.children) {
|
||||
if(hasLink) {
|
||||
// `<a>` must be sibling: no other interactive elements in <summary>
|
||||
titleHtmlStart = `${titleHtmlStart}<details><summary>${itemTitle}</summary>`;
|
||||
} else {
|
||||
titleHtmlStart = `<details><summary>${itemTitle}</summary>`;
|
||||
}
|
||||
titleHtmlEnd = "</details>";
|
||||
}
|
||||
|
||||
let childContentStr = entry.children ? navigationToHtml.call(this, entry.children, options) : "";
|
||||
|
||||
return `<${options.listItemElement}${buildHtmlAttr("class", liClass)}>${titleHtmlStart}${options.showExcerpt && entry.excerpt ? `: ${entry.excerpt}` : ""}${childContentStr}${titleHtmlEnd}</${options.listItemElement}>`;
|
||||
}).join("\n")}</${options.listElement}>` : "";
|
||||
}
|
||||
|
||||
function navigationToMarkdown(pages, options = {}) {
|
||||
options = Object.assign({
|
||||
showExcerpt: false,
|
||||
childDepth: 0
|
||||
}, options);
|
||||
|
||||
let childDepth = 1 + options.childDepth;
|
||||
options.childDepth++;
|
||||
|
||||
let urlFilter;
|
||||
|
||||
if(pages.length && pages[0].pluginType !== "eleventy-navigation") {
|
||||
throw new Error("Incorrect argument passed to eleventyNavigationToMarkdown filter. You must call `eleventyNavigation` or `eleventyNavigationBreadcrumb` first, like: `collection.all | eleventyNavigation | eleventyNavigationToMarkdown | safe`");
|
||||
}
|
||||
|
||||
let indent = (new Array(childDepth)).join(" ") || "";
|
||||
return pages.length ? `${pages.map(entry => {
|
||||
if(entry.url && !urlFilter) {
|
||||
// don’t get if not used
|
||||
urlFilter = getUrlFilter(this);
|
||||
}
|
||||
return `${indent}* ${entry.url ? `[` : ""}${entry.title}${entry.url ? `](${urlFilter(entry.url)})` : ""}${options.showExcerpt && entry.excerpt ? `: ${entry.excerpt}` : ""}\n${entry.children ? navigationToMarkdown.call(this, entry.children, options) : ""}`;
|
||||
}).join("")}` : "";
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getDependencyGraph,
|
||||
findNavigationEntries,
|
||||
findBreadcrumbEntries,
|
||||
toHtml: navigationToHtml,
|
||||
toMarkdown: navigationToMarkdown
|
||||
};
|
||||
49
node_modules/@11ty/eleventy-navigation/package.json
generated
vendored
Normal file
49
node_modules/@11ty/eleventy-navigation/package.json
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "@11ty/eleventy-navigation",
|
||||
"version": "1.0.5",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"description": "A plugin for creating hierarchical navigation in Eleventy projects. Supports breadcrumbs too!",
|
||||
"main": ".eleventy.js",
|
||||
"scripts": {
|
||||
"test": "npx ava",
|
||||
"sample": "npx @11ty/eleventy --input=sample --output=sample/_site --config=sample/.eleventy.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/11ty/eleventy-navigation.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/11ty/eleventy-navigation/issues"
|
||||
},
|
||||
"homepage": "https://www.11ty.dev/docs/plugins/navigation/",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/11ty"
|
||||
},
|
||||
"keywords": [
|
||||
"eleventy",
|
||||
"eleventy-plugin"
|
||||
],
|
||||
"author": {
|
||||
"name": "Zach Leatherman",
|
||||
"email": "zachleatherman@gmail.com",
|
||||
"url": "https://zachleat.com/"
|
||||
},
|
||||
"license": "MIT",
|
||||
"11ty": {
|
||||
"compatibility": ">=0.7 || >=1.0.0-canary"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ava": "^6.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"dependency-graph": "^1.0.0"
|
||||
},
|
||||
"ava": {
|
||||
"files": [
|
||||
"./test/*.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
9
node_modules/@11ty/eleventy-navigation/sample/.eleventy.js
generated
vendored
Normal file
9
node_modules/@11ty/eleventy-navigation/sample/.eleventy.js
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
const EleventyNavigationPlugin = require("../");
|
||||
|
||||
module.exports = function(eleventyConfig) {
|
||||
eleventyConfig.addPlugin(EleventyNavigationPlugin);
|
||||
|
||||
return {
|
||||
pathPrefix: "/sdljfkaldsjlfka/"
|
||||
}
|
||||
};
|
||||
5
node_modules/@11ty/eleventy-navigation/sample/bats.md
generated
vendored
Normal file
5
node_modules/@11ty/eleventy-navigation/sample/bats.md
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
eleventyNavigation:
|
||||
key: Bats
|
||||
parent: Mammals
|
||||
---
|
||||
5
node_modules/@11ty/eleventy-navigation/sample/humans.md
generated
vendored
Normal file
5
node_modules/@11ty/eleventy-navigation/sample/humans.md
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
eleventyNavigation:
|
||||
key: Humans
|
||||
parent: Mammals
|
||||
---
|
||||
19
node_modules/@11ty/eleventy-navigation/sample/liquid/index.liquid
generated
vendored
Normal file
19
node_modules/@11ty/eleventy-navigation/sample/liquid/index.liquid
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<h2>Full List</h2>
|
||||
|
||||
{{ collections.all | eleventyNavigation | dump(2) }}
|
||||
|
||||
{{ collections.all | eleventyNavigation | eleventyNavigationToHtml }}
|
||||
|
||||
<h2>Breadcrumb for Bats</h2>
|
||||
|
||||
{{
|
||||
collections.all
|
||||
| eleventyNavigationBreadcrumb: "Bats"
|
||||
| dump(2)
|
||||
}}
|
||||
|
||||
{{
|
||||
collections.all
|
||||
| eleventyNavigationBreadcrumb: "Bats"
|
||||
| eleventyNavigationToHtml
|
||||
}}
|
||||
4
node_modules/@11ty/eleventy-navigation/sample/mammals.md
generated
vendored
Normal file
4
node_modules/@11ty/eleventy-navigation/sample/mammals.md
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
eleventyNavigation:
|
||||
key: Mammals
|
||||
---
|
||||
4
node_modules/@11ty/eleventy-navigation/sample/no-key.md
generated
vendored
Normal file
4
node_modules/@11ty/eleventy-navigation/sample/no-key.md
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
eleventyNavigation:
|
||||
---
|
||||
This page has no key.
|
||||
3
node_modules/@11ty/eleventy-navigation/sample/no-nav.md
generated
vendored
Normal file
3
node_modules/@11ty/eleventy-navigation/sample/no-nav.md
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
---
|
||||
---
|
||||
This page has no navigation.
|
||||
11
node_modules/@11ty/eleventy-navigation/sample/nunjucks/index.njk
generated
vendored
Normal file
11
node_modules/@11ty/eleventy-navigation/sample/nunjucks/index.njk
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
<h2>Full List</h2>
|
||||
|
||||
{{ collections.all | eleventyNavigation | dump(2) | safe }}
|
||||
|
||||
{{ collections.all | eleventyNavigation | eleventyNavigationToHtml | safe }}
|
||||
|
||||
<h2>Breadcrumb for Bats</h2>
|
||||
|
||||
{{ collections.all | eleventyNavigationBreadcrumb("Bats") | dump(2) | safe }}
|
||||
|
||||
{{ collections.all | eleventyNavigationBreadcrumb("Bats") | eleventyNavigationToHtml | safe }}
|
||||
747
node_modules/@11ty/eleventy-navigation/test/navigationTest.js
generated
vendored
Normal file
747
node_modules/@11ty/eleventy-navigation/test/navigationTest.js
generated
vendored
Normal file
@ -0,0 +1,747 @@
|
||||
const test = require("ava");
|
||||
const EleventyNavigation = require("../eleventy-navigation");
|
||||
|
||||
test("Empty navigation", t => {
|
||||
t.deepEqual(EleventyNavigation.findNavigationEntries(), []);
|
||||
});
|
||||
|
||||
test("One root page navigation", t => {
|
||||
let obj = EleventyNavigation.findNavigationEntries([
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1"
|
||||
},
|
||||
page: {
|
||||
url: "root1.html"
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
t.is(obj[0].key, "root1");
|
||||
t.is(obj[0].pluginType, "eleventy-navigation");
|
||||
|
||||
// Warning, title must be preserved per the public API
|
||||
t.is(obj[0].title, "root1");
|
||||
// Warning, url must be preserved per the public API
|
||||
t.is(obj[0].url, "root1.html");
|
||||
|
||||
t.is(obj[0].children.length, 0);
|
||||
});
|
||||
|
||||
test("One root page navigation with separate title", t => {
|
||||
let obj = EleventyNavigation.findNavigationEntries([
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1",
|
||||
title: "Another title"
|
||||
},
|
||||
page: {
|
||||
url: "root1.html"
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
t.is(obj[0].key, "root1");
|
||||
t.is(obj[0].pluginType, "eleventy-navigation");
|
||||
t.is(obj[0].title, "Another title");
|
||||
t.is(obj[0].children.length, 0);
|
||||
});
|
||||
|
||||
test("One root, one child page navigation", t => {
|
||||
let obj = EleventyNavigation.findNavigationEntries([
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1"
|
||||
},
|
||||
page: {
|
||||
url: "root1.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child1"
|
||||
},
|
||||
page: {
|
||||
url: "child1.html"
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
t.is(obj[0].key, "root1");
|
||||
t.is(obj[0].children.length, 1);
|
||||
t.is(obj[0].children[0].parent, "root1");
|
||||
t.is(obj[0].children[0].key, "child1");
|
||||
});
|
||||
|
||||
test("Three layers deep navigation", t => {
|
||||
let obj = EleventyNavigation.findNavigationEntries([
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1"
|
||||
},
|
||||
page: {
|
||||
url: "root1.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child1"
|
||||
},
|
||||
page: {
|
||||
url: "child1.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "child1",
|
||||
key: "grandchild1"
|
||||
},
|
||||
page: {
|
||||
url: "grandchild1.html"
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
t.is(obj[0].key, "root1");
|
||||
t.is(obj[0].children.length, 1);
|
||||
t.is(obj[0].children[0].parent, "root1");
|
||||
t.is(obj[0].children[0].key, "child1");
|
||||
t.is(obj[0].children[0].children[0].parent, "child1");
|
||||
t.is(obj[0].children[0].children[0].key, "grandchild1");
|
||||
});
|
||||
|
||||
test("One root, three child navigation (order)", t => {
|
||||
let obj = EleventyNavigation.findNavigationEntries([
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1"
|
||||
},
|
||||
page: {
|
||||
url: "root1.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child1",
|
||||
order: 3
|
||||
},
|
||||
page: {
|
||||
url: "child1.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child2",
|
||||
order: 1
|
||||
},
|
||||
page: {
|
||||
url: "child2.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child3",
|
||||
order: 2
|
||||
},
|
||||
page: {
|
||||
url: "child3.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child4"
|
||||
},
|
||||
page: {
|
||||
url: "child4.html"
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
t.is(obj[0].key, "root1");
|
||||
t.is(obj[0].children.length, 4);
|
||||
t.is(obj[0].children.map(e => e.key).join(","), "child2,child3,child1,child4");
|
||||
});
|
||||
|
||||
test("One root, three child navigation, one with 0 (order)", t => {
|
||||
let obj = EleventyNavigation.findNavigationEntries([
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1"
|
||||
},
|
||||
page: {
|
||||
url: "root1.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child1",
|
||||
order: 3
|
||||
},
|
||||
page: {
|
||||
url: "child1.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child2",
|
||||
order: 0
|
||||
},
|
||||
page: {
|
||||
url: "child2.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child3",
|
||||
order: 2
|
||||
},
|
||||
page: {
|
||||
url: "child3.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child4"
|
||||
},
|
||||
page: {
|
||||
url: "child4.html"
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
t.is(obj[0].key, "root1");
|
||||
t.is(obj[0].children.length, 4);
|
||||
t.is(obj[0].children.map(e => e.key).join(","), "child2,child3,child1,child4");
|
||||
});
|
||||
|
||||
test("One root, three child navigation (implied order)", t => {
|
||||
let obj = EleventyNavigation.findNavigationEntries([
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1"
|
||||
},
|
||||
page: {
|
||||
url: "root1.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child1",
|
||||
order: 3
|
||||
},
|
||||
page: {
|
||||
url: "child1.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child2"
|
||||
},
|
||||
page: {
|
||||
url: "child2.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child3",
|
||||
order: -1
|
||||
},
|
||||
page: {
|
||||
url: "child3.html"
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
t.is(obj[0].key, "root1");
|
||||
t.is(obj[0].children.length, 3);
|
||||
t.is(obj[0].children.map(e => e.key).join(","), "child3,child1,child2");
|
||||
});
|
||||
|
||||
test("Show throw an error without a config", t => {
|
||||
let obj = EleventyNavigation.findNavigationEntries([
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1"
|
||||
},
|
||||
page: {
|
||||
url: "root1.html"
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
t.throws(() => {
|
||||
EleventyNavigation.toHtml(obj);
|
||||
});
|
||||
});
|
||||
|
||||
let fakeConfig = {
|
||||
nunjucksFilters: {
|
||||
url: url => url
|
||||
}
|
||||
};
|
||||
|
||||
let fakeNavigationEntries = [
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1"
|
||||
},
|
||||
page: {
|
||||
url: "root1.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child1"
|
||||
},
|
||||
page: {
|
||||
url: "child1.html"
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
let fakeNavigationEntriesEmptyUrl = [
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1"
|
||||
},
|
||||
page: {
|
||||
url: "root1.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child1"
|
||||
},
|
||||
page: {
|
||||
url: false
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
test("Checking active class on output HTML", t => {
|
||||
let obj = EleventyNavigation.findNavigationEntries(fakeNavigationEntries);
|
||||
|
||||
let html = EleventyNavigation.toHtml.call(fakeConfig, obj);
|
||||
t.is(html, `<ul><li><a href="root1.html">root1</a><ul><li><a href="child1.html">child1</a></li></ul></li></ul>`);
|
||||
|
||||
let activeHtmlItem = EleventyNavigation.toHtml.call(fakeConfig, obj, {
|
||||
activeKey: "child1",
|
||||
activeListItemClass: "this-is-the-active-item"
|
||||
});
|
||||
t.is(activeHtmlItem, `<ul><li><a href="root1.html">root1</a><ul><li class="this-is-the-active-item"><a href="child1.html">child1</a></li></ul></li></ul>`);
|
||||
|
||||
let activeHtmlAnchor = EleventyNavigation.toHtml.call(fakeConfig, obj, {
|
||||
activeKey: "child1",
|
||||
activeAnchorClass: "this-is-the-active-anchor"
|
||||
});
|
||||
t.is(activeHtmlAnchor, `<ul><li><a href="root1.html">root1</a><ul><li><a href="child1.html" class="this-is-the-active-anchor">child1</a></li></ul></li></ul>`);
|
||||
|
||||
let activeHtmlItemAndAnchor = EleventyNavigation.toHtml.call(fakeConfig, obj, {
|
||||
activeKey: "child1",
|
||||
activeListItemClass: "this-is-the-active-item",
|
||||
activeAnchorClass: "this-is-the-active-anchor"
|
||||
});
|
||||
t.is(activeHtmlItemAndAnchor, `<ul><li><a href="root1.html">root1</a><ul><li class="this-is-the-active-item"><a href="child1.html" class="this-is-the-active-anchor">child1</a></li></ul></li></ul>`);
|
||||
});
|
||||
|
||||
test("Checking aria-current option on output HTML", t => {
|
||||
let obj = EleventyNavigation.findNavigationEntries(fakeNavigationEntries);
|
||||
|
||||
let html = EleventyNavigation.toHtml.call(fakeConfig, obj);
|
||||
t.true(html.indexOf(`<li><a href="child1.html">child1</a></li>`) > -1);
|
||||
|
||||
let activeHtmlAnchor = EleventyNavigation.toHtml.call(fakeConfig, obj, {
|
||||
activeKey: "child1",
|
||||
useAriaCurrentAttr: true
|
||||
});
|
||||
t.true(activeHtmlAnchor.indexOf(`<li><a href="child1.html" aria-current="page">child1</a></li>`) > -1);
|
||||
});
|
||||
|
||||
test("Checking has children class on output HTML", t => {
|
||||
let obj = EleventyNavigation.findNavigationEntries(fakeNavigationEntries);
|
||||
|
||||
let activeHtml = EleventyNavigation.toHtml.call(fakeConfig, obj, {
|
||||
listItemHasChildrenClass: "item-has-children"
|
||||
});
|
||||
t.true(activeHtml.indexOf(`<li class="item-has-children"><a href="root1.html">root1</a>`) > -1);
|
||||
t.true(activeHtml.indexOf(`<li><a href="child1.html">child1</a></li>`) > -1);
|
||||
});
|
||||
|
||||
test("URL override", t => {
|
||||
let obj = EleventyNavigation.findNavigationEntries([
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1",
|
||||
url: "https://www.zachleat.com/"
|
||||
},
|
||||
page: {
|
||||
url: "root1.html"
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
t.is(obj[0].url, "https://www.zachleat.com/");
|
||||
});
|
||||
|
||||
test("Breadcrumbs", t => {
|
||||
let obj = EleventyNavigation.findBreadcrumbEntries([
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1"
|
||||
},
|
||||
page: {
|
||||
url: "root1.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child1"
|
||||
},
|
||||
page: {
|
||||
url: "child1.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "child1",
|
||||
key: "grandchild1"
|
||||
},
|
||||
page: {
|
||||
url: "grandchild1.html"
|
||||
}
|
||||
}
|
||||
}
|
||||
], "grandchild1");
|
||||
|
||||
t.is(obj.length, 2);
|
||||
t.is(obj[0].key, "root1");
|
||||
t.is(obj[1].key, "child1");
|
||||
});
|
||||
|
||||
test("Breadcrumbs (include self)", t => {
|
||||
let obj = EleventyNavigation.findBreadcrumbEntries([
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1"
|
||||
},
|
||||
page: {
|
||||
url: "root1.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child1"
|
||||
},
|
||||
page: {
|
||||
url: "child1.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "child1",
|
||||
key: "grandchild1"
|
||||
},
|
||||
page: {
|
||||
url: "grandchild1.html"
|
||||
}
|
||||
}
|
||||
}
|
||||
], "grandchild1", {
|
||||
includeSelf: true
|
||||
});
|
||||
|
||||
t.is(obj.length, 3);
|
||||
t.is(obj[0].key, "root1");
|
||||
t.is(obj[1].key, "child1");
|
||||
t.is(obj[2].key, "grandchild1");
|
||||
});
|
||||
|
||||
test("Breadcrumbs (options.allowMissing)", t => {
|
||||
const entries = EleventyNavigation.findBreadcrumbEntries(
|
||||
[],
|
||||
"orphan",
|
||||
{allowMissing: true}
|
||||
);
|
||||
t.is(entries.length, 0);
|
||||
});
|
||||
|
||||
test("Output markdown", t => {
|
||||
let obj = EleventyNavigation.findNavigationEntries([
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1"
|
||||
},
|
||||
page: {
|
||||
url: "root1.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child1"
|
||||
},
|
||||
page: {
|
||||
url: "child1.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "child1",
|
||||
key: "grandchild1"
|
||||
},
|
||||
page: {
|
||||
url: "grandchild1.html"
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
let html = EleventyNavigation.toMarkdown.call(fakeConfig, obj);
|
||||
t.is(html, `* [root1](root1.html)
|
||||
* [child1](child1.html)
|
||||
* [grandchild1](grandchild1.html)
|
||||
`);
|
||||
});
|
||||
|
||||
test("Navigation entry contains page data", t => {
|
||||
let obj = EleventyNavigation.findNavigationEntries([
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1"
|
||||
},
|
||||
page: {
|
||||
url: "root1.html"
|
||||
},
|
||||
tags: [
|
||||
"robot",
|
||||
"lesbian"
|
||||
],
|
||||
collections: {}
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
// Page data like tags should be included in the obj
|
||||
t.deepEqual(obj[0].data.tags, ["robot", "lesbian"]);
|
||||
});
|
||||
|
||||
test("Missing url", t => {
|
||||
let obj = EleventyNavigation.findNavigationEntries([
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1",
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
t.is(EleventyNavigation.toHtml.call(fakeConfig, obj), `<ul><li><a>root1</a></li></ul>`);
|
||||
});
|
||||
|
||||
test("Multiple roots", t => {
|
||||
let obj = EleventyNavigation.findNavigationEntries([
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child1",
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root2"
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root2",
|
||||
key: "child3",
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
t.deepEqual(obj.map(e => e.key), ["root1", "root2"]);
|
||||
t.deepEqual(obj[0].children.map(e => e.key), ["child1"]);
|
||||
t.deepEqual(obj[1].children.map(e => e.key), ["child3"]);
|
||||
});
|
||||
|
||||
test("Multiple roots, multiple keys", t => {
|
||||
let obj = EleventyNavigation.findNavigationEntries([
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root1",
|
||||
key: "child1",
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root2"
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
parent: "root2",
|
||||
key: "child3",
|
||||
}
|
||||
}
|
||||
}
|
||||
], "root1,root2");
|
||||
|
||||
t.deepEqual(obj.map(e => e.key), ["child1", "child3"]);
|
||||
t.deepEqual(obj[0].children.map(e => e.key), []);
|
||||
t.deepEqual(obj[1].children.map(e => e.key), []);
|
||||
});
|
||||
|
||||
test("Breadcrumb include self", t => {
|
||||
let nodes = [
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1",
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
let obj = EleventyNavigation.findBreadcrumbEntries(nodes, "root1", { includeSelf: true });
|
||||
|
||||
t.is(EleventyNavigation.toHtml(obj), `<ul><li><a>root1</a></li></ul>`);
|
||||
});
|
||||
|
||||
test("Breadcrumb include self (Liquid.js #35)", t => {
|
||||
let nodes = [
|
||||
{
|
||||
data: {
|
||||
eleventyNavigation: {
|
||||
key: "root1",
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
let obj = EleventyNavigation.findBreadcrumbEntries(nodes, "root1", [ "includeSelf", true ]);
|
||||
|
||||
t.is(EleventyNavigation.toHtml(obj), `<ul><li><a>root1</a></li></ul>`);
|
||||
});
|
||||
|
||||
test("Use top level details", t => {
|
||||
let obj = EleventyNavigation.findNavigationEntries(fakeNavigationEntries);
|
||||
|
||||
let html = EleventyNavigation.toHtml.call(fakeConfig, obj, {
|
||||
useTopLevelDetails: true
|
||||
});
|
||||
t.is(html, `<ul><li><a href="root1.html">root1</a><details><summary>root1</summary><ul><li><a href="child1.html">child1</a></li></ul></details></li></ul>`);
|
||||
});
|
||||
|
||||
test("Use top level details with empty url", t => {
|
||||
let obj = EleventyNavigation.findNavigationEntries(fakeNavigationEntriesEmptyUrl);
|
||||
|
||||
let html = EleventyNavigation.toHtml.call(fakeConfig, obj, {
|
||||
useTopLevelDetails: true,
|
||||
anchorElementWithoutHref: "span",
|
||||
});
|
||||
t.is(html, `<ul><li><a href="root1.html">root1</a><details><summary>root1</summary><ul><li><span>child1</span></li></ul></details></li></ul>`);
|
||||
});
|
||||
Reference in New Issue
Block a user