<commit_before>import { Blot } from './abstract/blot';
import ContainerBlot from './abstract/container';
import LinkedList from '../collection/linked-list';
import * as Registry from '../registry';


const OBSERVER_CONFIG = {
  attributes: true,
  characterData: true,
  childList: true,
  subtree: true
};

const MAX_OPTIMIZE_ITERATIONS = 100;


class ScrollBlot extends ContainerBlot {
  static blotName = 'scroll';
  static defaultChild = 'block';
  static scope = Registry.Scope.BLOCK_BLOT;
  static tagName = 'DIV';

  observer: MutationObserver;

  constructor(node: HTMLDivElement) {
    super(node);
    this.parent = null;
    this.observer = new MutationObserver((mutations: MutationRecord[]) => {
      this.update(mutations);
    });
    this.observer.observe(this.domNode, OBSERVER_CONFIG);
  }

  detach() {
    super.detach();
    this.observer.disconnect();
  }

  deleteAt(index: number, length: number): void {
    this.update();
    if (index === 0 && length === this.length()) {
      this.children.forEach(function(child) {
        child.remove();
      });
    } else {
      super.deleteAt(index, length)
    }
    this.optimize();
  }

  formatAt(index: number, length: number, name: string, value: any): void {
    this.update();
    super.formatAt(index, length, name, value);
    this.optimize();
  }

  insertAt(index: number, value: string, def?: any): void {
    this.update();
    super.insertAt(index, value, def);
    this.optimize();
  }

  optimize(mutations: MutationRecord[] = []): void {
    super.optimize();
    mutations.push.apply(mutations, this.observer.takeRecords());
    // TODO use WeakMap
    let mark = (blot: Blot) => {
      if (blot == null || blot === this) return;
      if (blot.domNode.parentNode == null) return;
      if (blot.domNode[Registry.DATA_KEY].mutations == null) {
        blot.domNode[Registry.DATA_KEY].mutations = [];
      }
      mark(blot.parent);
    }
    let optimize = function(blot: Blot) {  // Post-order traversal
      if (blot instanceof ContainerBlot) {
        blot.children.forEach(function(child) {
          if (!child.domNode[Registry.DATA_KEY] || child.domNode[Registry.DATA_KEY].mutations == null) return;
          optimize(child);
        });
      }
      blot.optimize();
    }
    let remaining = mutations;
    for (let i = 0; remaining.length > 0; i += 1) {
      if (i >= MAX_OPTIMIZE_ITERATIONS) {
        throw new Error('[Parchment] Maximum optimize iterations reached');
      }
      remaining.forEach(function(mutation) {
        let blot = Registry.find(mutation.target, true);
        if (blot != null && blot.domNode === mutation.target) {
          if (mutation.type === 'childList') {
            mark(Registry.find(mutation.previousSibling, false));
            [].forEach.call(mutation.addedNodes, function(node) {
              let child = Registry.find(node, false);
              mark(child);<commit_msg>fix scroll always optimizing direct children<commit_after>import { Blot } from './abstract/blot';
import ContainerBlot from './abstract/container';
import LinkedList from '../collection/linked-list';
import * as Registry from '../registry';


const OBSERVER_CONFIG = {
  attributes: true,
  characterData: true,
  childList: true,
  subtree: true
};

const MAX_OPTIMIZE_ITERATIONS = 100;


class ScrollBlot extends ContainerBlot {
  static blotName = 'scroll';
  static defaultChild = 'block';
  static scope = Registry.Scope.BLOCK_BLOT;
  static tagName = 'DIV';

  observer: MutationObserver;

  constructor(node: HTMLDivElement) {
    super(node);
    this.parent = null;
    this.observer = new MutationObserver((mutations: MutationRecord[]) => {
      this.update(mutations);
    });
    this.observer.observe(this.domNode, OBSERVER_CONFIG);
  }

  detach() {
    super.detach();
    this.observer.disconnect();
  }

  deleteAt(index: number, length: number): void {
    this.update();
    if (index === 0 && length === this.length()) {
      this.children.forEach(function(child) {
        child.remove();
      });
    } else {
      super.deleteAt(index, length)
    }
    this.optimize();
  }

  formatAt(index: number, length: number, name: string, value: any): void {
    this.update();
    super.formatAt(index, length, name, value);
    this.optimize();
  }

  insertAt(index: number, value: string, def?: any): void {
    this.update();
    super.insertAt(index, value, def);
    this.optimize();
  }

  optimize(mutations: MutationRecord[] = []): void {
    super.optimize();
    mutations.push.apply(mutations, this.observer.takeRecords());
    // TODO use WeakMap
    let mark = (blot: Blot) => {
      if (blot == null || blot === this) return;
      if (blot.domNode.parentNode == null) return;
      if (blot.domNode[Registry.DATA_KEY].mutations == null) {
        blot.domNode[Registry.DATA_KEY].mutations = [];
      }
      mark(blot.parent);
    }
    let optimize = function(blot: Blot) {  // Post-order traversal
      if (blot.domNode[Registry.DATA_KEY] == null || blot.domNode[Registry.DATA_KEY].mutations == null) {
        return;
      }
      if (blot instanceof ContainerBlot) {
        blot.children.forEach(optimize);
      }
      blot.optimize();
    }
    let remaining = mutations;
    for (let i = 0; remaining.length > 0; i += 1) {
      if (i >= MAX_OPTIMIZE_ITERATIONS) {
        throw new Error('[Parchment] Maximum optimize iterations reached');
      }
      remaining.forEach(function(mutation) {
        let blot = Registry.find(mutation.target, true);
        if (blot != null && blot.domNode === mutation.target) {
          if (mutation.type === 'childList') {
            mark(Registry.find(mutation.previousSibling, false));
            [].forEach.call(mutation.addedNodes, function(node) {
              let child = Registry.find(node, false);
              mark(child);
{'fraction_non_alphanumeric': 0.1433949349760438, 'fraction_numerical': 0.0023956194387405884, 'mean_word_length': 2.936026936026936, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 11, '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': '2713016', 'n_tokens_mistral': 1873, 'n_tokens_neox': 1794, 'n_words': 396}