• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1const { ref } = Vue;
2
3// site management
4
5let myPath = window.location.pathname.slice(1) || "index.html";
6if (!/\.html/.test(myPath)) {
7  myPath = `${myPath}.html`; // cloudflare likes to drop the .html
8}
9
10/** replace a/b/c.md with a/b */
11function path2dir(p) {
12  const dir = p.split("/").slice(0, -1).join("/");
13  return dir;
14}
15
16/** replace a/b/c.md with a/b/c.html */
17function md2html(p) {
18  return p.replace(/\.md$/, ".html");
19}
20
21/** replace a/b/c with to /a/b/c, also '' => '/' */
22function path2url(p) {
23  if (p === "index") {
24    return "/";
25  }
26  return `/${p}`;
27}
28
29/** replace a/b/c.html with a/b/c.md */
30function html2md(p) {
31  return p.replace(/\.html$/, ".md");
32}
33
34/** replace a/b/c.md with a/b/c */
35function dropmd(p) {
36  return p.replace(/\.md$/, "");
37}
38
39/** replace a/b/c.html with a/b/c */
40function drophtml(p) {
41  return p.replace(/\.html$/, "").replace(/\/$/, "");
42}
43
44/** load and cook the site data */
45async function siteData() {
46  // load the json
47  const d = await fetch("/assets/json/tree.json");
48  const j = await d.json();
49  const { usermap } = j;
50
51  return j;
52}
53
54const AncestorPages = {
55  props: ["ancestorPages"],
56  setup() {},
57  template: `
58       <span class="ancestor" v-for="ancestor of ancestorPages" :key="ancestor.href">
59         <a class="uplink" v-bind:href="ancestor.href">{{ ancestor.title }}</a><span class="crumb">❱</span>
60       </span>
61`,
62};
63
64const SubPagesPopup = {
65  props: {
66    children: {
67      type: Object,
68      required: true,
69    },
70  },
71  emits: ["hide"], // when user clicks hide
72  setup() {},
73  methods: {
74    hide() {
75      this.$emit("hide");
76    },
77  },
78  template: `
79          <div class="subpages">
80          <span class="hamburger" @click="hide()">✕</span>
81          <ul class="subpages" >
82              <li v-for="subpage of children" :key="subpage.path">
83                  <a v-bind:href="subpage.href">
84                      {{ subpage.title }}
85                       <span class="hamburger" v-if="subpage.children">❱</span>
86                  </a>
87              </li>
88          </ul>
89        </div>
90`,
91};
92
93const SubMap = {
94  name: "SubMap",
95  props: {
96    usermap: {
97      type: Object,
98      required: true,
99    },
100    path: String,
101  },
102  setup() {},
103  computed: {
104    title() {
105      return this.usermap[this.path].title;
106    },
107    children() {
108      return this.usermap[this.path].children || [];
109    },
110    href() {
111      return path2url(this.path);
112    },
113  },
114  template: `
115    <div class="submap">
116      <a v-bind:href="href" v-bind:title="path">{{title}}</a>
117      <SubMap v-for="child in children" :key="child" :usermap="usermap" :path="child" />
118    </div>
119  `,
120};
121
122const SiteMap = {
123  props: {
124    tree: {
125      type: Object,
126      required: true,
127    },
128  },
129  components: {
130    SubMap,
131  },
132  emits: ["hide"],
133  setup() {},
134  methods: {
135    hide() {
136      this.$emit("hide");
137    },
138  },
139  template: `
140    <div class="sitemap">
141          <span class="hamburger" @click="hide()">✕</span>
142          <b>Site Map</b><hr/>
143          <SubMap :usermap="tree.value.usermap" path="index"/>
144    </div>
145  `,
146};
147
148const app = Vue.createApp(
149  {
150    components: {
151      AncestorPages,
152      SubPagesPopup,
153      SiteMap,
154    },
155    setup(props) {
156      // the tree.json data
157      const tree = ref({});
158      // loading status for tree.json
159      const status = ref(null);
160      // is the popup menu shown?
161      const popup = ref(false);
162      // is the site map shown?
163      const showmap = ref(false);
164
165      return {
166        tree,
167        status,
168        popup,
169        showmap,
170      };
171    },
172    mounted() {
173      const t = this;
174      siteData().then(
175        (d) => (t.tree.value = d),
176        (e) => (t.status = e)
177      );
178    },
179    props: {
180      path: String,
181    },
182    computed: {
183      /** base path:  'index' or 'downloads/cldr-33' */
184      base() {
185        if (this.path) {
186          return drophtml(this.path);
187        } else {
188          return "index"; // '' => 'index'
189        }
190        return null;
191      },
192      ourTitle() {
193        if (this.tree?.value) {
194          if (this.path === "") return this.rootTitle;
195          return this?.tree?.value?.usermap[this.base]?.title;
196        }
197      },
198      // title of root
199      rootTitle() {
200        const usermap = this?.tree?.value?.usermap ?? {};
201        return usermap?.index?.title ?? "CLDR";
202      },
203      children() {
204        const usermap = this?.tree?.value?.usermap;
205        if (!usermap) return []; // no children
206        const entry = usermap[this.base];
207        const children = entry?.children;
208        if (!children || !children.length) return [];
209        return children.map((path) => ({
210          path,
211          href: path2url(path),
212          title: usermap[path]?.title || path,
213          children: (usermap[path].children ?? []).length > 0,
214        }));
215      },
216      ancestorPages() {
217        const pages = [];
218        // if we are not loaded, or if we're at the root, then exit
219        const usermap = this?.tree?.value?.usermap;
220        if (
221          !usermap ||
222          !this.path ||
223          this.path == "index.html" ||
224          this.map == "index"
225        ) {
226          return [];
227        }
228        // traverse
229        let path = drophtml(this.path); // can't be null, empty, or index (see above). Map a/b/c.html to a/b/c
230        do {
231          // calculate the immediate ancestor
232          const nextParentPath = usermap[path]?.parent;
233          if (!nextParentPath) break;
234          if (nextParentPath == path) {
235            console.error("Loop detected!");
236            break;
237          }
238          const nextParent = usermap[nextParentPath];
239          if (!nextParent) break;
240          const href = path2url(nextParentPath);
241          const { title } = nextParent || nextParentPath;
242          // prepend
243          pages.push({
244            href,
245            title,
246            path: nextParentPath,
247          });
248          path = nextParentPath;
249        } while (path); // we iterate over 'path' until it returns null
250        pages.reverse();
251        return pages;
252      },
253    },
254    template: `
255    <div>
256       <div class='status' v-if="status">{{ status }}</div>
257       <div class='status' v-if="!tree">Loading…</div>
258       <a class="icon" href="http://www.unicode.org/"> <img border="0"
259					src="/assets/img/logo60s2.gif"  alt="[Unicode]" width="34"
260					height="33"></a>&nbsp;&nbsp;
261
262       <AncestorPages :ancestorPages="ancestorPages"/>
263
264       <div v-if="!children || !children.length" class="title"> {{ ourTitle }} </div>
265
266       <div v-else class="title"  @mouseover="popup = true"><span class="hamburger" @click="showmap=false,popup = !popup">≡</span>
267
268            {{ ourTitle }}
269
270        <SubPagesPopup v-if="popup && !showmap" @hide="popup = false" :children="children"/>
271
272      </div>
273        <div class="showmap" v-if="!showmap" @click="popup=false,showmap = true">Site Map</div>
274        <SiteMap v-if="tree && showmap" :tree="tree" @hide="showmap = popup = false"  />
275
276    </div>`,
277  },
278  {
279    // path of / goes to /index.html
280    path: myPath,
281  }
282);
283
284app.mount("#nav");
285
286// load anchor.js
287anchors.add("h1, h2, h3, h4, h5, h6, caption, dfn");
288