<commit_before>/* global window */

import { createSelector } from 'reselect';
import { get_chunk_text_nodes } from '../component';
import { demagicize_rect, demagicize_rects } from 'lib/Util';

function get_word_chunk_position(word_index, subs_chunks) {
    const chunk_index = subs_chunks.chunk_index_by_word_index[word_index];
    const chunk = subs_chunks[chunk_index];
    const icco = subs_chunks.icco_by_word_index[word_index];
    const chunk_text_nodes = get_chunk_text_nodes();
    const text_node = chunk_text_nodes ? chunk_text_nodes[chunk_index] : null;
    return {
        chunk,
        icco,
        text_node,
    };
}

const range = window.document.createRange();
export const get_word_rectangles = (words, subs, subs_chunks) => {
    let rects = [];
    if (words && words.length > 0) {
        const last_word = words[words.length - 1];
        const { text_node: start_word_el, icco: start_word_icco } = get_word_chunk_position(words[0] .i, subs_chunks);
        const { text_node:   end_word_el, icco:   end_word_icco } = get_word_chunk_position(last_word.i, subs_chunks);
        if (start_word_el && end_word_el) {
            range.setStart(start_word_el, start_word_icco);
            range.setEnd  (  end_word_el,   end_word_icco);
            rects = demagicize_rects(range.getClientRects(), window.scrollX, window.scrollY);
        }
    }
    return rects;
};

export const get_subs   = (state) => state.track_detail.subs;
const get_current_time  = (state) => state.track_detail.current_time;
const get_selection_boundaries
                        = (state) => ({
                            start: {
                                chunk_index: state.track_detail.selection_start_chunk_index,
                                icco:        state.track_detail.selection_start_icco,
                            },
                            end: {
                                chunk_index: state.track_detail.selection_end_chunk_index,
                                icco:        state.track_detail.selection_end_icco,
                            },
                        });
export const get_subs_chunks = createSelector(
    [get_subs],
    (subs) => {
        const chunks = [];
        const wbuf = [];
        const chunk_index_by_word_index  = [];
        const icco_by_word_index = [];
        let chunk_index = -1;
        let char_offset = 0;
        let word_offset = 0;
        let is_now_humanic = null;
        let wbuf_str_length = 0;
        const flush = function () {
            const str = wbuf.concat('').join(' ');
            chunks.push({
                is_humanic: is_now_humanic,
                str,
                char_offset,
                word_offset,
            });
            char_offset += str.length;
            word_offset += wbuf.length;
            wbuf.length = 0;
            wbuf_str_length = 0;
        };
        subs.forEach((sub, word_index) => {
            const subhum = !!sub.humanic;
            if (subhum !== is_now_humanic) {
                flush();
                is_now_humanic = subhum;
                chunk_index++;
            }
            chunk_index_by_word_index[word_index] = chunk_index;
            icco_by_word_index[word_index] = wbuf_str_length;
            wbuf.push(sub.occurrence);
            wbuf_str_length += sub.occurrence.length + 1;
        });
        flush();
        chunks.shift();
        return {
            chunks,
            chunk_index_by_word_index,
            icco_by_word_index,
        };
    },
);
export const get_word_timestamps = createSelector(
    [get_subs],
    (subs) => subs.map((sub, i) => sub.timestamp).concat(Infinity),
);
const NULL_CURRENT_WORD = {
    is_null: true,
    occurrence: '',
    rects: [],
    start_offset: null,
    end_offset: null,
};
let current_word;
export const get_current_word = createSelector(
    [get_word_timestamps, get_current_time, get_subs, get_subs_chunks],
    (word_timestamps, current_time, subs, subs_chunks) => {
        if (subs.length === 0) {
            return NULL_CURRENT_WORD;
        }
        let i = current_word ? current_word.i : 0;
        while (word_timestamps[i + 1] <= current_time) i++;
        while (word_timestamps[i]     >  current_time) i--;
        if (i < 0) i = 0;
        const sub = subs[i];
        let start_offset = null;
        let end_offset   = null;
        let rects = [];
        if (sub && subs_chunks) {
            const { text_node, icco } = get_word_chunk_position(i, subs_chunks);
            if (text_node) {
                start_offset = icco;
                end_offset   = icco + sub.occurrence.length;
                if (text_node.length < end_offset) {
                    return NULL_CURRENT_WORD;
                }
                range.setStart(text_node, start_offset);
                range.setEnd  (text_node,   end_offset);
                rects = demagicize_rects(range.getClientRects(), window.scrollX, window.scrollY);
            }
        }<commit_msg>current word is now 20ms ahead of time<commit_after>/* global window */

import { createSelector } from 'reselect';
import { get_chunk_text_nodes } from '../component';
import { demagicize_rect, demagicize_rects } from 'lib/Util';

function get_word_chunk_position(word_index, subs_chunks) {
    const chunk_index = subs_chunks.chunk_index_by_word_index[word_index];
    const chunk = subs_chunks[chunk_index];
    const icco = subs_chunks.icco_by_word_index[word_index];
    const chunk_text_nodes = get_chunk_text_nodes();
    const text_node = chunk_text_nodes ? chunk_text_nodes[chunk_index] : null;
    return {
        chunk,
        icco,
        text_node,
    };
}

const range = window.document.createRange();
export const get_word_rectangles = (words, subs, subs_chunks) => {
    let rects = [];
    if (words && words.length > 0) {
        const last_word = words[words.length - 1];
        const { text_node: start_word_el, icco: start_word_icco } = get_word_chunk_position(words[0] .i, subs_chunks);
        const { text_node:   end_word_el, icco:   end_word_icco } = get_word_chunk_position(last_word.i, subs_chunks);
        if (start_word_el && end_word_el) {
            range.setStart(start_word_el, start_word_icco);
            range.setEnd  (  end_word_el,   end_word_icco);
            rects = demagicize_rects(range.getClientRects(), window.scrollX, window.scrollY);
        }
    }
    return rects;
};

export const get_subs   = (state) => state.track_detail.subs;
const get_current_time  = (state) => state.track_detail.current_time;
const get_selection_boundaries
                        = (state) => ({
                            start: {
                                chunk_index: state.track_detail.selection_start_chunk_index,
                                icco:        state.track_detail.selection_start_icco,
                            },
                            end: {
                                chunk_index: state.track_detail.selection_end_chunk_index,
                                icco:        state.track_detail.selection_end_icco,
                            },
                        });
export const get_subs_chunks = createSelector(
    [get_subs],
    (subs) => {
        const chunks = [];
        const wbuf = [];
        const chunk_index_by_word_index  = [];
        const icco_by_word_index = [];
        let chunk_index = -1;
        let char_offset = 0;
        let word_offset = 0;
        let is_now_humanic = null;
        let wbuf_str_length = 0;
        const flush = function () {
            const str = wbuf.concat('').join(' ');
            chunks.push({
                is_humanic: is_now_humanic,
                str,
                char_offset,
                word_offset,
            });
            char_offset += str.length;
            word_offset += wbuf.length;
            wbuf.length = 0;
            wbuf_str_length = 0;
        };
        subs.forEach((sub, word_index) => {
            const subhum = !!sub.humanic;
            if (subhum !== is_now_humanic) {
                flush();
                is_now_humanic = subhum;
                chunk_index++;
            }
            chunk_index_by_word_index[word_index] = chunk_index;
            icco_by_word_index[word_index] = wbuf_str_length;
            wbuf.push(sub.occurrence);
            wbuf_str_length += sub.occurrence.length + 1;
        });
        flush();
        chunks.shift();
        return {
            chunks,
            chunk_index_by_word_index,
            icco_by_word_index,
        };
    },
);
export const get_word_timestamps = createSelector(
    [get_subs],
    (subs) => subs.map((sub, i) => sub.timestamp).concat(Infinity),
);
const NULL_CURRENT_WORD = {
    is_null: true,
    occurrence: '',
    rects: [],
    start_offset: null,
    end_offset: null,
};
let current_word;
export const get_current_word = createSelector(
    [get_word_timestamps, get_current_time, get_subs, get_subs_chunks],
    (word_timestamps, current_time, subs, subs_chunks) => {
        // pretend we're a bit ahead of time
        // to get the next word if we're just about to hit it
        const time_ahead = current_time + 0.02;
        if (subs.length === 0) {
            return NULL_CURRENT_WORD;
        }
        let i = current_word ? current_word.i : 0;
        while (word_timestamps[i + 1] <= time_ahead) i++;
        while (word_timestamps[i]     >  time_ahead) i--;
        if (i < 0) i = 0;
        const sub = subs[i];
        let start_offset = null;
        let end_offset   = null;
        let rects = [];
        if (sub && subs_chunks) {
            const { text_node, icco } = get_word_chunk_position(i, subs_chunks);
            if (text_node) {
                start_offset = icco;
                end_offset   = icco + sub.occurrence.length;
                if (text_node.length < end_offset) {
                    return NULL_CURRENT_WORD;
                }
                range.setStart(text_node, start_offset);
                range.setEnd  (text_node,   end_offset);
                rects = demagicize_rects(range.getClientRects(), window.scrollX, window.scrollY);
            }
        }
{'fraction_non_alphanumeric': 0.10011778563015312, 'fraction_numerical': 0.003435414212799372, 'mean_word_length': 1.8880385487528344, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 25, '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': '3737179', 'n_tokens_mistral': 3198, 'n_tokens_neox': 3113, 'n_words': 594}