<commit_before>/**
 * Copyright 2013 Google, Inc.
 * Copyright 2015 Trim-marks Inc.
 * Copyright 2019 Vivliostyle Foundation
 *
 * Vivliostyle.js is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Vivliostyle.js is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Vivliostyle.js.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
goog.provide('adapt.toc');

goog.require('adapt.vtree');
goog.require('adapt.vgen');
goog.require('adapt.ops');
goog.require('adapt.font');
goog.require('adapt.expr');
goog.require('vivliostyle.counters');

// closed: 25B8
// open: 25BE
// empty: 25B9

/** @const */
adapt.toc.bulletClosed = "\u25B8";
/** @const */
adapt.toc.bulletOpen = "\u25BE";
/** @const */
adapt.toc.bulletEmpty = "\u25B9";

/**
 * @param {adapt.ops.OPSDocStore} store
 * @param {string} url
 * @param {?string} lang
 * @param {adapt.vtree.ClientLayout} clientLayout
 * @param {adapt.font.Mapper} fontMapper
 * @param {adapt.expr.Preferences} pref
 * @param {adapt.vgen.CustomRendererFactory} rendererFactory
 * @param {Object.<string,string>} fallbackMap
 * @param {!adapt.base.DocumentURLTransformer} documentURLTransformer
 * @param {!vivliostyle.counters.CounterStore} counterStore
 * @constructor
 * @implements {adapt.vgen.CustomRendererFactory}
 */
adapt.toc.TOCView = function(store, url, lang, clientLayout, fontMapper, pref,
    rendererFactory, fallbackMap, documentURLTransformer, counterStore) {
    /** @const */ this.store = store;
    /** @const */ this.url = url;
    /** @const */ this.lang = lang;
    /** @const */ this.clientLayout = clientLayout;
    /** @const */ this.fontMapper = fontMapper;
    /** @const */ this.pref = adapt.expr.clonePreferences(pref);
    this.pref.spreadView = false;   // No spred view for TOC box
    /** @const */ this.rendererFactory = rendererFactory;
    /** @const */ this.fallbackMap = fallbackMap;
    /** @const */ this.documentURLTransformer = documentURLTransformer;
    /** @const */ this.counterStore = counterStore;
    /** @type {adapt.vtree.Page} */ this.page = null;
    /** @type {adapt.ops.StyleInstance} */ this.instance = null;
};

/**
 * @param {Element} elem
 * @param {number} depth
 * @return {void}
 */
adapt.toc.TOCView.prototype.setAutoHeight = function(elem, depth) {
    if (depth-- == 0) {
        return;
    }
    for (let c = elem.firstChild; c; c = c.nextSibling) {
        if (c.nodeType == 1) {
            const e = /** @type {Element} */ (c);
            if (adapt.base.getCSSProperty(e, "height", "auto") != "auto") {
                adapt.base.setCSSProperty(e, "height", "auto");
                this.setAutoHeight(e, depth);
            }
            if (adapt.base.getCSSProperty(e, "position", "static") == "absolute") {
                adapt.base.setCSSProperty(e, "position", "relative");
                this.setAutoHeight(e, depth);
            }
        }
    }
};

/**
 * @param {Event} evt
 */
adapt.toc.toggleNodeExpansion = evt => {
    const elem = /** @type {Element} */ (evt.target);
    const open = elem.textContent == adapt.toc.bulletClosed;
    elem.textContent = open ? adapt.toc.bulletOpen : adapt.toc.bulletClosed;
    const tocNodeElem = /** @type {Element} */ (elem.parentNode);
    elem.setAttribute("aria-expanded", open ? "true" : "false");
    tocNodeElem.setAttribute("aria-expanded", open ? "true" : "false");
    let c = tocNodeElem.firstChild;
    while (c) {
        if (c.nodeType === 1) {
            const ce = /** @type {HTMLElement} */ (c);
            const adaptClass = ce.getAttribute("data-adapt-class");
            if (adaptClass === "toc-container") {
                ce.setAttribute("aria-hidden", !open ? "true" : "false");
                if (ce.firstChild) {
                    c = ce.firstChild;
                    continue;
                }
            } else if (adaptClass === "toc-node") {
                ce.style.height = open ? "auto" : "0px";

                // Update enable/disable tab move to the button and anchor.
                if (ce.children.length >= 3) {
                    ce.children[0].tabIndex = open ? 0 : -1;
                }
                if (ce.children.length >= 2) {
                    ce.children[1].tabIndex = open ? 0 : -1;
                }
            }
        }
        while (!c.nextSibling && c.parentNode !== tocNodeElem) {
            c = c.parentNode;
        }
        c = c.nextSibling;
    }
    evt.stopPropagation();
};

/**
 * @override
 */
adapt.toc.TOCView.prototype.makeCustomRenderer = function(xmldoc) {
    const renderer = this.rendererFactory.makeCustomRenderer(xmldoc);
    return (
        /**
         * @param {Element} srcElem
         * @param {Element} viewParent
         * @return {!adapt.task.Result.<Element>}<commit_msg>Fix TOC box keyboard navigation: focus lost when closing a tree item without closing the sub tree items<commit_after>/**
 * Copyright 2013 Google, Inc.
 * Copyright 2015 Trim-marks Inc.
 * Copyright 2019 Vivliostyle Foundation
 *
 * Vivliostyle.js is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Vivliostyle.js is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Vivliostyle.js.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
goog.provide('adapt.toc');

goog.require('adapt.vtree');
goog.require('adapt.vgen');
goog.require('adapt.ops');
goog.require('adapt.font');
goog.require('adapt.expr');
goog.require('vivliostyle.counters');

// closed: 25B8
// open: 25BE
// empty: 25B9

/** @const */
adapt.toc.bulletClosed = "\u25B8";
/** @const */
adapt.toc.bulletOpen = "\u25BE";
/** @const */
adapt.toc.bulletEmpty = "\u25B9";

/**
 * @param {adapt.ops.OPSDocStore} store
 * @param {string} url
 * @param {?string} lang
 * @param {adapt.vtree.ClientLayout} clientLayout
 * @param {adapt.font.Mapper} fontMapper
 * @param {adapt.expr.Preferences} pref
 * @param {adapt.vgen.CustomRendererFactory} rendererFactory
 * @param {Object.<string,string>} fallbackMap
 * @param {!adapt.base.DocumentURLTransformer} documentURLTransformer
 * @param {!vivliostyle.counters.CounterStore} counterStore
 * @constructor
 * @implements {adapt.vgen.CustomRendererFactory}
 */
adapt.toc.TOCView = function(store, url, lang, clientLayout, fontMapper, pref,
    rendererFactory, fallbackMap, documentURLTransformer, counterStore) {
    /** @const */ this.store = store;
    /** @const */ this.url = url;
    /** @const */ this.lang = lang;
    /** @const */ this.clientLayout = clientLayout;
    /** @const */ this.fontMapper = fontMapper;
    /** @const */ this.pref = adapt.expr.clonePreferences(pref);
    this.pref.spreadView = false;   // No spred view for TOC box
    /** @const */ this.rendererFactory = rendererFactory;
    /** @const */ this.fallbackMap = fallbackMap;
    /** @const */ this.documentURLTransformer = documentURLTransformer;
    /** @const */ this.counterStore = counterStore;
    /** @type {adapt.vtree.Page} */ this.page = null;
    /** @type {adapt.ops.StyleInstance} */ this.instance = null;
};

/**
 * @param {Element} elem
 * @param {number} depth
 * @return {void}
 */
adapt.toc.TOCView.prototype.setAutoHeight = function(elem, depth) {
    if (depth-- == 0) {
        return;
    }
    for (let c = elem.firstChild; c; c = c.nextSibling) {
        if (c.nodeType == 1) {
            const e = /** @type {Element} */ (c);
            if (adapt.base.getCSSProperty(e, "height", "auto") != "auto") {
                adapt.base.setCSSProperty(e, "height", "auto");
                this.setAutoHeight(e, depth);
            }
            if (adapt.base.getCSSProperty(e, "position", "static") == "absolute") {
                adapt.base.setCSSProperty(e, "position", "relative");
                this.setAutoHeight(e, depth);
            }
        }
    }
};

/**
 * @param {Event} evt
 */
adapt.toc.toggleNodeExpansion = evt => {
    const elem = /** @type {Element} */ (evt.target);
    const open = elem.textContent == adapt.toc.bulletClosed;
    elem.textContent = open ? adapt.toc.bulletOpen : adapt.toc.bulletClosed;
    const tocNodeElem = /** @type {Element} */ (elem.parentNode);
    elem.setAttribute("aria-expanded", open ? "true" : "false");
    tocNodeElem.setAttribute("aria-expanded", open ? "true" : "false");
    let c = tocNodeElem.firstChild;
    while (c) {
        if (c.nodeType === 1) {
            const ce = /** @type {HTMLElement} */ (c);
            const adaptClass = ce.getAttribute("data-adapt-class");
            if (adaptClass === "toc-container") {
                ce.setAttribute("aria-hidden", !open ? "true" : "false");
                if (ce.firstChild) {
                    c = ce.firstChild;
                    continue;
                }
            } else if (adaptClass === "toc-node") {
                ce.style.height = open ? "auto" : "0px";

                // Update enable/disable tab move to the button and anchor.
                if (ce.children.length >= 2) {
                    ce.children[1].tabIndex = open ? 0 : -1;
                }
                if (ce.children.length >= 3) {
                    ce.children[0].tabIndex = open ? 0 : -1;
                    if (!open) {
                        const elem1 = ce.children[0];
                        if (elem1.textContent == adapt.toc.bulletOpen) {
                            elem1.textContent = adapt.toc.bulletClosed;
                            elem1.setAttribute("aria-expanded", "false");
                            ce.setAttribute("aria-expanded", "false");
                            c = ce.children[2];
                            continue;
                        }
                    }
                }
            }
        }
        while (!c.nextSibling && c.parentNode !== tocNodeElem) {
            c = c.parentNode;
        }
        c = c.nextSibling;
    }
    evt.stopPropagation();
};

/**
 * @override
 */
adapt.toc.TOCView.prototype.makeCustomRenderer = function(xmldoc) {
    const renderer = this.rendererFactory.makeCustomRenderer(xmldoc);
    return (
        /**
         * @param {Element} srcElem
         * @param {Element} viewParent
         * @return {!adapt.task.Result.<Element>}
{'fraction_non_alphanumeric': 0.1465072030443055, 'fraction_numerical': 0.007973181118057443, 'mean_word_length': 2.382776585963837, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2708550', 'n_tokens_mistral': 3452, 'n_tokens_neox': 3278, 'n_words': 809}