diff --git a/admin/site/themes/afp/static/js/scroll-spy.js b/admin/site/themes/afp/static/js/scroll-spy.js --- a/admin/site/themes/afp/static/js/scroll-spy.js +++ b/admin/site/themes/afp/static/js/scroll-spy.js @@ -1,147 +1,138 @@ /** * Scrollspy, inspired from bootstrap. Original license: * -------------------------------------------------------------------------- * Bootstrap (v5.1.3): scrollspy.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * * -------------------------------------------------------------------------- */ const EVENT_SPY_ACTIVATE = 'activate.spy' const CLASS_ACTIVE = 'active' const CLASS_SPY_LINK = 'spy-link' /** * Class definition */ class ScrollSpy { constructor(element, target, target_suffix = "", offset = 0.5) { ScrollSpy.instance = this this._element = element this._offset = offset this._offsets = [] this._link_ids = [] this._active_id = null this._scrollHeight = 0 this._target = target - this._eps = 0.01 this._target_suffix = target_suffix window.onscroll = () => this._process() this.refresh() this._process() } - scroll_to(id) { - const elem = document.getElementById(id + this._target_suffix) - if (elem) { - const offset = -window.innerHeight * (this._offset - this._eps) - window.scroll(0, elem.offsetTop + offset) - } - } - refresh() { this._clear() this._offsets = [] this._link_ids = [] this._scrollHeight = this._get_scroll_height() const targets = [] for (const link of document.getElementById(this._target).getElementsByClassName(CLASS_SPY_LINK)) { // visible and has id if (link.id && !is_collapsed(link)) { const target = document.getElementById(this._get_target_id(link)) if (target) { const targetBCR = target.getBoundingClientRect() if (targetBCR.width || targetBCR.height) { targets.push([targetBCR.top + window.pageYOffset, link.id]) } } } } for (const item of targets.sort((a, b) => a[0] - b[0])) { this._offsets.push(item[0]) this._link_ids.push(item[1]) } } // Private _get_target_id(link) { return link.getAttribute('href').slice(1) + this._target_suffix } _get_scroll_height() { return window.scrollHeight || Math.max( document.body.scrollHeight, document.documentElement.scrollHeight ) } _process() { const scroll_top = window.pageYOffset + this._offset * window.innerHeight const scroll_height = this._get_scroll_height() const max_scroll = this._offset * window.innerHeight + scroll_height - window.innerHeight if (this._scrollHeight !== scroll_height) { this.refresh() } if (scroll_top >= max_scroll) { const target_id = this._link_ids[this._link_ids.length - 1] if (this._active_id !== target_id) { this._activate(target_id) } return } if (this._active_id && scroll_top < this._offsets[0] && this._offsets[0] > 0) { this._clear() return } for (let i = this._offsets.length; i--;) { const isActiveTarget = this._active_id !== this._link_ids[i] && scroll_top >= this._offsets[i] && (typeof this._offsets[i + 1] === 'undefined' || scroll_top < this._offsets[i + 1]) if (isActiveTarget) { this._activate(this._link_ids[i]) } } } _activate(link_id) { this._clear() this._active_id = link_id const link = document.getElementById(link_id) if (link) { const elem = document.getElementById(this._get_target_id(link)) link.classList.add(CLASS_ACTIVE) elem.classList.add(CLASS_ACTIVE) const event = new Event(EVENT_SPY_ACTIVATE) event.relatedTarget = link window.dispatchEvent(event) } } _clear() { if (this._active_id) { const link = document.getElementById(this._active_id) if (link) { const elem = document.getElementById(this._get_target_id(link)) if (link.classList.contains(CLASS_ACTIVE)) link.classList.remove(CLASS_ACTIVE) if (elem.classList.contains(CLASS_ACTIVE)) elem.classList.remove(CLASS_ACTIVE) } } } static instance } \ No newline at end of file diff --git a/admin/site/themes/afp/static/js/theory.js b/admin/site/themes/afp/static/js/theory.js --- a/admin/site/themes/afp/static/js/theory.js +++ b/admin/site/themes/afp/static/js/theory.js @@ -1,279 +1,287 @@ /* constants */ const ID_THEORY_LIST = 'theories' const CLASS_LOADER = 'loader' const CLASS_ANIMATION = 'animation' const ATTRIBUTE_THEORY_SRC = 'theory-src' const CLASS_NAVBAR_TYPE = 'theory-navbar-type' const CLASS_THY_NAV = 'thy-nav' const PARAM_NAVBAR_TYPE = 'theory-navbar-type' const ID_NAVBAR_TYPE_SELECTOR = 'navbar-type-selector' const ID_NAVBAR = 'theory-navbar' const NAVBAR_TYPES = ['fact', 'type', 'const'] /* routing */ function target(base_href, rel_href) { const href_parts = rel_href.split('/') if (href_parts.length === 1) return `#${href_parts[0]}` else if (href_parts.length === 3 && href_parts[0] === '..' && href_parts[1] !== '..') { return `../${href_parts[1].toLowerCase()}/#${href_parts[2]}` } else return `${base_href}/../${rel_href}` } -function to_id(thy_name, ref) { +function to_ref(thy_name, ref) { if (ref) return `${thy_name}.html#${ref}` else return `${thy_name}.html` } -const to_fresh_id = (id) => `${id}#` +const to_id = (id) => `${id}#` const to_svg_id = (id) => `${id}#svg` const to_container_id = (id) => `${id}#container` const to_collapsible_id = (id) => `${id}#collapsible` const to_spinner_id = (id) => `${id}#spinner` const to_nav_id = (id) => `${id}#nav` const to_ul_id = (id) => `${id}#ul` const of_ul_id = (id) => id.split('#').slice(0, -1).join('#') const to_a_id = (id) => `${id}#a` /* document translation */ function translate(base_href, thy_name, thy_body) { const thy_refs = [...thy_body.getElementsByTagName('span')].map((span) => { let ref = span.getAttribute('id') if (ref) { - span.setAttribute('id', to_fresh_id(to_id(thy_name, ref))) + span.setAttribute('id', to_id(to_ref(thy_name, ref))) } return ref }).filter(e => e) for (const link of thy_body.getElementsByTagName('a')) { const rel_href = link.getAttribute('href') link.setAttribute('href', target(base_href, rel_href)) } return thy_refs } /* theory lazy-loading */ async function fetch_theory_body(href) { const html_str = await fetch(href).then((http_res) => { if (http_res.status !== 200) return Promise.resolve(`
${http_res.statusText}`) else return http_res.text() }).catch((_) => { console.log(`Could not load theory at '${href}'. Redirecting...`) window.location.replace(href) }) const parser = new DOMParser() const html = parser.parseFromString(html_str, 'text/html') return html.getElementsByTagName('body')[0] } async function load_theory(thy_name, href) { const thy_body = await fetch_theory_body(href) const refs = translate(href, thy_name, thy_body) const collapse = document.getElementById(to_collapsible_id(thy_name)) collapse.append(...Array(...thy_body.children).slice(1)) return refs } async function open_theory(thy_name) { const container = document.getElementById(to_container_id(thy_name)) if (container) { if (document.getElementById(to_collapsible_id(thy_name))) open(container) else { const collapsible = parse_elem(`