<commit_before>'use strict';

var forEach = require('lodash/forEach');
var isEqual = require('lodash/isEqual');
var oo = require('../util/oo');
var TreeIndex = require('../util/TreeIndex');

function PathEventProxy(doc) {
  this.listeners = new TreeIndex.Arrays();
  this._list = [];
  this.doc = doc;
}

PathEventProxy.Prototype = function() {

  this.on = function(path, method, context) {
    this._add(context, path, method);
  };

  // proxy.off(this)
  // proxy.off(this, path)
  // proxy.off(this, path, this.onPropertyUpdate)
  this.off = function(context, path, method) {
    this._remove(context, path, method);
  };

  this.connect = function(listener, path, method) {
    console.warn('DEPRECATED: use proxy.on(path, this.onPropertyChange, this) instead');
    this.on(path, method, listener);
  };

  this.disconnect = function(listener) {
    console.warn('DEPRECATED: use proxy.off(this) instead');
    this.off(listener);
  };

  this.onDocumentChanged = function(change, info, doc) {
    // stop if no listeners registered
    if (this._list.length === 0) {
      return;
    }
    var listeners = this.listeners;
    forEach(change.updated, function(_, pathStr) {
      var scopedListeners = listeners.get(pathStr.split(','));
      forEach(scopedListeners, function(entry) {
        entry.method.call(entry.listener, change, info, doc);
      });
    });
  };

  this._add = function(listener, path, method) {
    if (!method) {
      throw new Error('Invalid argument: expected function but got ' + method);<commit_msg>Traverse PathEventProxy listeners more robustly.<commit_after>'use strict';

var forEach = require('lodash/forEach');
var isEqual = require('lodash/isEqual');
var isArray = require('lodash/isArray');
var oo = require('../util/oo');
var TreeIndex = require('../util/TreeIndex');

function PathEventProxy(doc) {
  this.listeners = new TreeIndex.Arrays();
  this._list = [];
  this.doc = doc;
}

PathEventProxy.Prototype = function() {

  this.on = function(path, method, context) {
    this._add(context, path, method);
  };

  // proxy.off(this)
  // proxy.off(this, path)
  // proxy.off(this, path, this.onPropertyUpdate)
  this.off = function(context, path, method) {
    this._remove(context, path, method);
  };

  this.connect = function(listener, path, method) {
    console.warn('DEPRECATED: use proxy.on(path, this.onPropertyChange, this) instead');
    this.on(path, method, listener);
  };

  this.disconnect = function(listener) {
    console.warn('DEPRECATED: use proxy.off(this) instead');
    this.off(listener);
  };

  this.onDocumentChanged = function(change, info, doc) {
    // stop if no listeners registered
    if (this._list.length === 0) {
      return;
    }
    var listeners = this.listeners;
    forEach(change.updated, function(_, pathStr) {
      var scopedListeners = listeners.get(pathStr.split(','));
      if (isArray(scopedListeners)) scopedListeners = scopedListeners.slice(0);
      forEach(scopedListeners, function(entry) {
        entry.method.call(entry.listener, change, info, doc);
      });
    });
  };

  this._add = function(listener, path, method) {
    if (!method) {
      throw new Error('Invalid argument: expected function but got ' + method);
{'fraction_non_alphanumeric': 0.15429808286951144, 'fraction_numerical': 0.0009276437847866419, 'mean_word_length': 3.6681096681096683, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '5413325', 'n_tokens_mistral': 1022, 'n_tokens_neox': 1011, 'n_words': 224}