-
- {{- range .Params.theories }}
-
- - {{ . }} - - {{- end -}} -
{{ . }}
+ {{- end -}}
- ${nav_tree_rec(thy_name, [...path, key], key1, by_key[key1], type)} `)
+ return `
+ ${res}
+ `
+ } else return res
+}
+
+function nav_tree(thy_name, refs, type) {
+ let trees = Object.entries(group_by(refs)).map(([key, parts]) =>
+ `${nav_tree_rec(thy_name, [thy_name], key, parts, type)} `)
+
+ return parse_elem(`
+ `)
}
-/* fragment controls */
+const cached_refs = {}
+const load_theory_nav = (thy_name, refs) => {
+ let selected = get_query(PARAM_NAVBAR_TYPE) ? get_query(PARAM_NAVBAR_TYPE) : DEFAULT_NAVBAR_TYPE
-function follow_hash(hash) {
- if (hash !== '') {
- const elem = document.getElementById(hash)
- if (elem) {
- console.log("Scrolling into " + hash)
- elem.scrollIntoView()
- }
+ let by_type = group_by(refs.filter(ref => ref.includes('|')).map((id) => id.split('|').reverse()))
+ let type_selector = document.getElementById(ID_NAVBAR_TYPE_SELECTOR)
+ let options = [...type_selector.options].map(e => e.value)
+
+ for (let [type, elems] of Object.entries(by_type)) {
+ if (!options.includes(type)) type_selector.appendChild(parse_elem(``))
+
+ let parts_by_thy = group_by(elems.map((s) => s[0].split('.')))
+ if (!cached_refs[type]) cached_refs[type] = {}
+ cached_refs[type][thy_name] = parts_by_thy[thy_name]
+ }
+
+ let tree = nav_tree(thy_name, cached_refs[selected][thy_name], selected)
+ document.getElementById(to_nav_id(thy_name)).appendChild(tree)
+
+ ScrollSpy.instance.refresh()
+}
+
+
+/* state */
+
+let navbar_last_opened = []
+
+
+/* controls */
+
+const follow_theory_hash = async () => {
+ let hash = window.location.hash
+
+ if (hash.length > 1) {
+ const id = hash.slice(1)
+
+ const thy_name = strip_suffix(id.split('#')[0], '.html')
+ await open_theory(thy_name)
+
+ const elem = document.getElementById(id)
+ if (elem) elem.scrollIntoView()
}
}
-const follow_theory_hash = async function () {
- const hash = window.location.hash
- if (hash.length > 1) {
- const hashes = hash.split('#')
- const thy_name = hashes[1]
- await open_theory(thy_name)
- follow_hash(hashes.slice(1).join('#'))
+const toggle_theory = async (thy_name) => {
+ const hash = `#${to_id(thy_name)}`
+ const collapsible = document.getElementById(to_collapsible_id(thy_name))
+ if (collapsible) {
+ if (!close(collapsible)) {
+ if (window.location.hash === hash) open(collapsible)
+ else window.location.hash = hash
+ }
+ } else window.location.hash = hash
+}
+
+const change_selector = (type) => {
+ let old_type = get_query(PARAM_NAVBAR_TYPE)
+ if (!old_type || old_type !== type) {
+ set_query(PARAM_NAVBAR_TYPE, type)
+
+ for (const elem of document.getElementsByClassName(CLASS_NAVBAR_TYPE)) {
+ let thy_name = of_ul_id(elem.id)
+ elem.replaceWith(nav_tree(thy_name, cached_refs[type][thy_name], type))
+ }
+
+ ScrollSpy.instance.refresh()
}
}
+const open_tree = (elem) => {
+ if (elem.classList.contains(CLASS_COLLAPSIBLE)) {
+ if (open(elem)) navbar_last_opened.push(elem)
+ }
+ if (elem.parentElement) open_tree(elem.parentElement)
+}
+
+const sync_navbar = (link) => {
+ for (const elem of navbar_last_opened){
+ close(elem)
+ }
+
+ open_tree(link.parentElement)
+
+ link.scrollIntoView()
+}
+
/* setup */
-const init = async function () {
- const theory_list = document.getElementById('html-theories')
+const init = async () => {
+ const theory_list = document.getElementById(ID_THEORY_LIST)
+ const thy_names = []
+
+
if (theory_list) {
for (const theory of theory_list.children) {
- const thy_link = theory.firstElementChild
+ thy_names.push(theory.id)
- const href = thy_link.getAttribute('href')
- const thy_name = thy_link.innerHTML
+ const href = theory.getAttribute('href')
+ const thy_name = theory.id
const thy_collapsible = parse_elem(`
-
+ const container = document.getElementById(to_container_id(thy_name))
+
+ if (container) {
+ const collapsible = parse_elem(`
+
+
`)
- elem.appendChild(content)
- await load_theory(thy_name, elem.getAttribute('datasrc'))
- const spinner = document.getElementById(thy_name + "#spinner")
+ container.appendChild(collapsible)
+ let refs = await load_theory(thy_name, container.getAttribute(ATTRIBUTE_THEORY_SRC))
+ await load_theory_nav(thy_name, refs)
+ const spinner = document.getElementById(to_spinner_id(thy_name))
spinner.parentNode.removeChild(spinner)
}
}
}
-const toggle_theory = async function (thy_name) {
- const content = theory_content(thy_name)
- if (content && content.style.display === 'block') content.style.display = 'none'
- else {
- const hash = `#${thy_name}`
- if (window.location.hash === hash) await open_theory(thy_name)
- else window.location.hash = hash
- }
+function nav_tree_rec(thy_name, path, key, ref_parts, type) {
+ const rec_ref = ref_parts.filter(e => e.length > 0)
+ const id = to_id(thy_name, `${path.join('.')}.${key}|${type}`)
+ let res
+ if (rec_ref.length < ref_parts.length) {
+ res = `${key}`
+ } else res = `${key}`
+
+ if (rec_ref.length > 1) {
+ const by_key = group_by(rec_ref)
+ const children = Object.keys(by_key).map((key1) => `
+
-
+
+ `))
+ navbar.append(...thy_names.map((thy_name) => parse_elem(`
+
+
+ ${thy_name}
+
+ `)), parse_elem('
')) + + new ScrollSpy(document.body, 'theory-navbar') + await follow_theory_hash() } -window.onload = init -window.onhashchange = follow_theory_hash +document.addEventListener('DOMContentLoaded', init) +window.addEventListener(EVENT_SPY_ACTIVATE, (e) => sync_navbar(e.relatedTarget)) + +window.onhashchange = follow_theory_hash \ No newline at end of file diff --git a/admin/site/themes/afp/static/js/util.js b/admin/site/themes/afp/static/js/util.js new file mode 100644 --- /dev/null +++ b/admin/site/themes/afp/static/js/util.js @@ -0,0 +1,41 @@ +/* utilities */ + +const strip_suffix = (str, suffix) => { + if (str.endsWith(suffix)) return str.slice(0, -suffix.length) + else return str +} + +const group_by = (elems) => { + return elems.reduce((ks, kv) => { + if (kv.isEmpty) return ks + else { + const k = kv[0] + const vs = kv.slice(1) + if (ks[k]) ks[k].push(vs) + else ks[k] = [vs] + return ks + } + }, {}) +} + +const parse_elem = (html_str) => { + const template = document.createElement('template') + template.innerHTML = html_str + return template.content +} + +const open = (collapsible) => { + if (collapsible.style.display === 'none') { + collapsible.style.display = 'block' + return true + } + else return false +} + +const close = (collapsible) => { + if (collapsible.style.display === 'block') { + collapsible.style.display = 'none' + return true + } + else return false +}
-
+ ${thy_name}
-
+
`)
theory.replaceWith(thy_collapsible)
}
}
+
+ const navbar = document.getElementById(ID_NAVBAR)
+ const type = get_query(PARAM_NAVBAR_TYPE) ? get_query(PARAM_NAVBAR_TYPE) : DEFAULT_NAVBAR_TYPE
+ navbar.appendChild(parse_elem(`
+ + ${thy_name} +
')) + + new ScrollSpy(document.body, 'theory-navbar') + await follow_theory_hash() } -window.onload = init -window.onhashchange = follow_theory_hash +document.addEventListener('DOMContentLoaded', init) +window.addEventListener(EVENT_SPY_ACTIVATE, (e) => sync_navbar(e.relatedTarget)) + +window.onhashchange = follow_theory_hash \ No newline at end of file diff --git a/admin/site/themes/afp/static/js/util.js b/admin/site/themes/afp/static/js/util.js new file mode 100644 --- /dev/null +++ b/admin/site/themes/afp/static/js/util.js @@ -0,0 +1,41 @@ +/* utilities */ + +const strip_suffix = (str, suffix) => { + if (str.endsWith(suffix)) return str.slice(0, -suffix.length) + else return str +} + +const group_by = (elems) => { + return elems.reduce((ks, kv) => { + if (kv.isEmpty) return ks + else { + const k = kv[0] + const vs = kv.slice(1) + if (ks[k]) ks[k].push(vs) + else ks[k] = [vs] + return ks + } + }, {}) +} + +const parse_elem = (html_str) => { + const template = document.createElement('template') + template.innerHTML = html_str + return template.content +} + +const open = (collapsible) => { + if (collapsible.style.display === 'none') { + collapsible.style.display = 'block' + return true + } + else return false +} + +const close = (collapsible) => { + if (collapsible.style.display === 'block') { + collapsible.style.display = 'none' + return true + } + else return false +}