<commit_before>import elementName from "./ElementName"
import Page from "./Page/page";

export default function(content, rules, done, DELAY) {

  let state = {
    path: [], // Stack representing which element we're currently inside
    pages: [],
    getNewPage: () => { // Gross hack to allow rules to advance to next page
      return makeNextPage();
    }
  }

  let throttle = (func) => {
    if (DELAY > 0) setTimeout(func, DELAY);
    else func();
  }

  let beforeAddRules = (elmt) => {
    rules.forEach( (rule) => {
      if (elmt.matches(rule.selector) && rule.beforeAdd) {

        let backupPg = state.currentPage.clone();
        let backupElmt = elmt.cloneNode(true);
        rule.beforeAdd(elmt, state);

        if (state.currentPage.hasOverflowed()) {
          // restore from backup
          elmt.innerHTML = backupElmt.innerHTML; // TODO: make less hacky

          let idx = state.pages.indexOf(state.currentPage);
          state.pages[idx] = backupPg;
          state.currentPage = backupPg;

          state.currentPage = makeNextPage();

          rule.beforeAdd(elmt, state);
        }
      }
    });
  }
  let afterAddRules = (elmt) => {
    rules.forEach( (rule) => {
      if (elmt.matches(rule.selector) && rule.afterAdd) {
        rule.afterAdd(elmt, state);
      }
    });
  }
  let newPageRules = (pg) => {
    rules.forEach( (rule) => {
      if (rule.newPage) rule.newPage(pg, state);
    });
  }
  let afterBindRules = (pages) => {
    rules.forEach( (rule) => {
      if (rule.afterBind) {
        pages.forEach((pg, i) => {
          rule.afterBind(pg, i);
        });
      }
    });
  }

  // Creates clones for ever level of tag
  // we were in when we overflowed the last page
  let makeNextPage = () => {
    state.path = clonePath(state.path);
    let newPage = new Page();
    newPageRules(newPage);
    state.pages.push(newPage);
    state.currentPage = newPage; // TODO redundant
    if (state.path[0]) {
      newPage.flowContent.appendChild(state.path[0]);
    }
    return newPage;
  };

  let moveNodeToNextPage = (nodeToMove) => {
    state.path.pop();

    let fn = state.currentPage.footer.lastChild; // <--
    state.currentPage = makeNextPage();
    if (fn) state.currentPage.footer.appendChild(fn); // <-- move footnote to new page

    last(state.path).appendChild(nodeToMove);
    state.path.push(nodeToMove);
  }

  // Adds an text node by binary searching amount of
  // words until it just barely doesnt overflow
  let addTextNode = (node, doneCallback, abortCallback) => {

    last(state.path).appendChild(node);

    let textNode = node;
    let origText = textNode.nodeValue;

    let lastPos = 0;
    let pos = origText.length/2;;

    let step = () => {

      let dist = Math.abs(lastPos - pos);

      if (pos > origText.length - 1) {
        throttle(doneCallback);
        return;
      }
      textNode.nodeValue = origText.substr(0, pos);

      if (dist < 1) { // Is done

        // Back out to word boundary
        while(origText.charAt(pos) !== " " && pos > -1) pos--;

        if (pos < 1 && origText.trim().length > 0) {
          // console.error(`Bindery: Aborted adding "${origText.substr(0,25)}"`);
          textNode.nodeValue = origText;
          abortCallback();
          return;
        }

        textNode.nodeValue = origText.substr(0, pos);

        origText = origText.substr(pos);
        pos = 0;

        // Start on new page
        state.currentPage = makeNextPage();
        textNode = document.createTextNode(origText);
        last(state.path).appendChild(textNode);

        // If the remainder fits there, we're done
        if (!state.currentPage.hasOverflowed()) {
          throttle(doneCallback);
          return;
        }
      }
      lastPos = pos;

      let hasOverflowed = state.currentPage.hasOverflowed();
      pos = pos + (hasOverflowed ? -dist : dist) / 2;

      throttle(step);
    }

    if (state.currentPage.hasOverflowed()) {
      step(); // find breakpoint
    }
    else throttle(doneCallback); // add in one go
  }


  // Adds an element node by clearing its childNodes, then inserting them
  // one by one recursively until thet overflow the page
  let addElementNode = (node, doneCallback) => {

    // Add this node to the current page or context
    if (state.path.length == 0) {
      state.currentPage.flowContent.appendChild(node);
    }
    else {
      last(state.path).appendChild(node);
    }
    state.path.push(node);

    // Clear this node, before re-adding its children
    let childNodes = [...node.childNodes];
    node.innerHTML = '';

    let index = 0;
    let addNextChild = () => {
      if (!(index < childNodes.length)) {
        doneCallback();
        return;
      }
      let child = childNodes[index];
      index += 1;
      switch (child.nodeType) {
        case Node.TEXT_NODE:
          let cancel = () => {
            moveNodeToNextPage(node);
            addTextNode(child, addNextChild, cancel);<commit_msg>Workaround for elements larger than the flow container<commit_after>import elementName from "./ElementName"
import Page from "./Page/page";

export default function(content, rules, done, DELAY) {

  let state = {
    path: [], // Stack representing which element we're currently inside
    pages: [],
    getNewPage: () => { // Gross hack to allow rules to advance to next page
      return makeNextPage();
    }
  }

  let throttle = (func) => {
    if (DELAY > 0) setTimeout(func, DELAY);
    else func();
  }

  let beforeAddRules = (elmt) => {
    rules.forEach( (rule) => {
      if (elmt.matches(rule.selector) && rule.beforeAdd) {

        let backupPg = state.currentPage.clone();
        let backupElmt = elmt.cloneNode(true);
        rule.beforeAdd(elmt, state);

        if (state.currentPage.hasOverflowed()) {
          // restore from backup
          elmt.innerHTML = backupElmt.innerHTML; // TODO: make less hacky

          let idx = state.pages.indexOf(state.currentPage);
          state.pages[idx] = backupPg;
          state.currentPage = backupPg;

          state.currentPage = makeNextPage();

          rule.beforeAdd(elmt, state);
        }
      }
    });
  }
  let afterAddRules = (elmt) => {
    rules.forEach( (rule) => {
      if (elmt.matches(rule.selector) && rule.afterAdd) {
        rule.afterAdd(elmt, state);
      }
    });
  }
  let newPageRules = (pg) => {
    rules.forEach( (rule) => {
      if (rule.newPage) rule.newPage(pg, state);
    });
  }
  let afterBindRules = (pages) => {
    rules.forEach( (rule) => {
      if (rule.afterBind) {
        pages.forEach((pg, i) => {
          rule.afterBind(pg, i);
        });
      }
    });
  }

  // Creates clones for ever level of tag
  // we were in when we overflowed the last page
  let makeNextPage = () => {
    state.path = clonePath(state.path);
    let newPage = new Page();
    newPageRules(newPage);
    state.pages.push(newPage);

    state.currentPage = newPage; // TODO redundant
    if (state.path[0]) {
      newPage.flowContent.appendChild(state.path[0]);
    }

    // make sure the cloned page is valid.
    // this catches elements with an explicitly set height greater than the
    // flow area, which will never be split and cause an infinite loop
    if (newPage.hasOverflowed()) {
      let suspect = last(state.path)
      console.error(`Bindery: NextPage already overflowing, probably due to a style set on ${elementName(suspect)}. Since we aren't sure where to break ${elementName(suspect)}, it may be overflowing.`);
      suspect.parentNode.removeChild(suspect);
    }

    return newPage;
  };

  let moveNodeToNextPage = (nodeToMove) => {
    state.path.pop();

    let fn = state.currentPage.footer.lastChild; // <--
    state.currentPage = makeNextPage();
    if (fn) state.currentPage.footer.appendChild(fn); // <-- move footnote to new page

    last(state.path).appendChild(nodeToMove);
    state.path.push(nodeToMove);
  }

  // Adds an text node by binary searching amount of
  // words until it just barely doesnt overflow
  let addTextNode = (node, doneCallback, abortCallback) => {

    last(state.path).appendChild(node);

    let textNode = node;
    let origText = textNode.nodeValue;

    let lastPos = 0;
    let pos = origText.length/2;;

    let step = () => {

      let dist = Math.abs(lastPos - pos);

      if (pos > origText.length - 1) {
        throttle(doneCallback);
        return;
      }
      textNode.nodeValue = origText.substr(0, pos);

      if (dist < 1) { // Is done

        // Back out to word boundary
        while(origText.charAt(pos) !== " " && pos > -1) pos--;

        if (pos < 1 && origText.trim().length > 0) {
          // console.error(`Bindery: Aborted adding "${origText.substr(0,25)}"`);
          textNode.nodeValue = origText;
          abortCallback();
          return;
        }

        textNode.nodeValue = origText.substr(0, pos);

        origText = origText.substr(pos);
        pos = 0;

        // Start on new page
        state.currentPage = makeNextPage();

        textNode = document.createTextNode(origText);
        last(state.path).appendChild(textNode);

        // If the remainder fits there, we're done
        if (!state.currentPage.hasOverflowed()) {
          throttle(doneCallback);
          return;
        }
      }
      lastPos = pos;

      let hasOverflowed = state.currentPage.hasOverflowed();
      pos = pos + (hasOverflowed ? -dist : dist) / 2;

      throttle(step);
    }

    if (state.currentPage.hasOverflowed()) {
      step(); // find breakpoint
    }
    else throttle(doneCallback); // add in one go
  }


  // Adds an element node by clearing its childNodes, then inserting them
  // one by one recursively until thet overflow the page
  let addElementNode = (node, doneCallback) => {

    // Add this node to the current page or context
    if (state.path.length == 0) {
      state.currentPage.flowContent.appendChild(node);
    }
    else {
      last(state.path).appendChild(node);
    }
    state.path.push(node);

    // 1. Cache the children
    let childNodes = [...node.childNodes];
    // 2. Clear this node
    node.innerHTML = '';


    // 3. Try adding each child one by one
    let index = 0;
    let addNextChild = () => {
      if (!(index < childNodes.length)) {
        doneCallback();
        return;
      }
      let child = childNodes[index];
      index += 1;

      switch (child.nodeType) {
        case Node.TEXT_NODE:
          let cancel = () => {
            moveNodeToNextPage(node);
            addTextNode(child, addNextChild, cancel);
{'fraction_non_alphanumeric': 0.13120802194683567, 'fraction_numerical': 0.004067732475640904, 'mean_word_length': 2.2619561863622337, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 47, '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': '7344227', 'n_tokens_mistral': 3280, 'n_tokens_neox': 3154, 'n_words': 819}