<commit_before>const isVisible = require('./common_functions').isVisible;
const Url       = require('./url');

/*
    adds anchor links to elements with data-anchor attribute
    created anchors work similarly to native anchors, but rely on URL params instead

    HOW TO USE:
        <h1 data-anchor='some string'>Some title</h1>

        --> an anchor link is inserted into the element
        string passed to data-anchor doesn't have to be unique

    IMPLEMENTATION:
        string passed to data-anchor is converted into UNIQUE ID,
        attr value is replaced with this ID
        URL param value is this ID
        e.g.

        <h1 data-anchor='Hello world'>Hello</h1>
        <h1 data-anchor='Hello world'>Hello</h1>

        will become

        <h1 data-anchor='hello-world'>Hello</h1>    // ?anchor=hello-world
        <h1 data-anchor='hello-world-2'>Hello</h1>  // ?anchor=hello-world-2
*/

const ScrollToAnchor = (() => {
    let id_duplicate_count = {};

    const init = () => {
        addAnchorsToElements();
        scrollToAnchorInQuery();
    };

    const encode = (str) => {
        const prep = str.toLowerCase().replace(/\s/g, '-');
        let appendix = '';
        if (typeof id_duplicate_count[prep] === 'number') {
            id_duplicate_count[prep]++;
            appendix = `-${id_duplicate_count[prep] + 1}`;
        } else {
            id_duplicate_count[prep] = 0;
        }
        return encodeURI(`${prep}${appendix}`);
    };

    const makeAnchorLink = (id) => {
        const anchor_link = document.createElement('a');
        const url = new URL(window.location);
        url.search = `anchor=${id}`;
        anchor_link.href = url.href;
        anchor_link.classList.add('data-anchor-link');
        return anchor_link;
    };

    const addAnchorsToElements = () => {
        const els = document.querySelectorAll('[data-anchor]');
        els.forEach(el => {
            const title = el.dataset.anchor;
            const id = encode(title);
            el.dataset.anchor = id;
            const anchor_link = makeAnchorLink(id);
            el.appendChild(anchor_link);
            anchor_link.addEventListener('click', (e) => {
                e.preventDefault();
                $.scrollTo(el, 500);
                Url.updateParamsWithoutReload({
                    anchor: id,
                }, true);
            });
        });
    };

    const scrollToAnchorInQuery = () => {
        const params = Url.paramsHash();
        const id = params.anchor;
        if (!id) return;
        const candidates = document.querySelectorAll(`[data-anchor="${id}"]`);
        const el = Array.from(candidates).find(isVisible);
        if (!el) return;
        window.setTimeout(() => {
            $.scrollTo(el, 500);
        }, 200);
    };

    const cleanup = () => {
        id_duplicate_count = {};
        Url.updateParamsWithoutReload({
            anchor: null,
        }, true);
    };

    return {
        init,
        cleanup,
    };
})();

module.exports = ScrollToAnchor;<commit_msg>add scroll offset<commit_after>const isVisible = require('./common_functions').isVisible;
const Url       = require('./url');

/*
    adds anchor links to elements with data-anchor attribute
    created anchors work similarly to native anchors, but rely on URL params instead

    HOW TO USE:
        <h1 data-anchor='some string'>Some title</h1>

        --> an anchor link is inserted into the element
        string passed to data-anchor doesn't have to be unique

    IMPLEMENTATION:
        string passed to data-anchor is converted into UNIQUE ID,
        attr value is replaced with this ID
        URL param value is this ID
        e.g.

        <h1 data-anchor='Hello world'>Hello</h1>
        <h1 data-anchor='Hello world'>Hello</h1>

        will become

        <h1 data-anchor='hello-world'>Hello</h1>    // ?anchor=hello-world
        <h1 data-anchor='hello-world-2'>Hello</h1>  // ?anchor=hello-world-2
*/

const ScrollToAnchor = (() => {
    let id_duplicate_count = {};

    const init = () => {
        addAnchorsToElements();
        scrollToAnchorInQuery();
    };

    const encode = (str) => {
        const prep = str.toLowerCase().replace(/\s/g, '-');
        let appendix = '';
        if (typeof id_duplicate_count[prep] === 'number') {
            id_duplicate_count[prep]++;
            appendix = `-${id_duplicate_count[prep] + 1}`;
        } else {
            id_duplicate_count[prep] = 0;
        }
        return encodeURI(`${prep}${appendix}`);
    };

    const makeAnchorLink = (id) => {
        const anchor_link = document.createElement('a');
        const url = new URL(window.location);
        url.search = `anchor=${id}`;
        anchor_link.href = url.href;
        anchor_link.classList.add('data-anchor-link');
        return anchor_link;
    };

    const addAnchorsToElements = () => {
        const els = document.querySelectorAll('[data-anchor]');
        els.forEach(el => {
            const title = el.dataset.anchor;
            const id = encode(title);
            el.dataset.anchor = id;
            const anchor_link = makeAnchorLink(id);
            el.appendChild(anchor_link);
            anchor_link.addEventListener('click', (e) => {
                e.preventDefault();
                scrollToEl(el);
                Url.updateParamsWithoutReload({
                    anchor: id,
                }, true);
            });
        });
    };

    const scrollToEl = (el) => {
        $.scrollTo(el, 500, { offset: -10 });
    }

    const scrollToAnchorInQuery = () => {
        const params = Url.paramsHash();
        const id = params.anchor;
        if (!id) return;
        const candidates = document.querySelectorAll(`[data-anchor="${id}"]`);
        const el = Array.from(candidates).find(isVisible);
        if (!el) return;
        window.setTimeout(() => {
            scrollToEl(el);
        }, 200);
    };

    const cleanup = () => {
        id_duplicate_count = {};
        Url.updateParamsWithoutReload({
            anchor: null,
        }, true);
    };

    return {
        init,
        cleanup,
    };
})();

module.exports = ScrollToAnchor;
{'fraction_non_alphanumeric': 0.13536269430051814, 'fraction_numerical': 0.007286269430051813, 'mean_word_length': 1.988388969521045, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 46, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, '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': '4141669', 'n_tokens_mistral': 1811, 'n_tokens_neox': 1744, 'n_words': 402}