<commit_before>Backbone.NestedModel = Backbone.Model.extend({
  
  constructor: function(attrs, opts){
    Backbone.Model.prototype.constructor.apply( this, arguments );
  },

  get: function(attrStrOrPath){
    var attrPath = Backbone.NestedModel.attrPath(attrStrOrPath),
      childAttr = attrPath[0],
      result = Backbone.Model.prototype.get.call(this, childAttr);
    
    if (result instanceof Backbone.Collection){
      if (attrPath.length > 1){
        var otherAttrs = _.rest(attrPath);
        result = result.getNested(otherAttrs);
        
      } else {
        result = result.toJSON();

        if (window.console){
          window.console.log("Square bracket notation is preferred for accesing values of attribute '" + attrStrOrPath + "'.");
        }
      }

    } else if (result instanceof Backbone.Model){
      if (attrPath.length > 1){
        var otherAttrs = _.rest(attrPath);
        result = result.get(otherAttrs);

      } else {
        result = result.toJSON();

        if (window.console){
          window.console.log("Dot notation is preferred for accesing values of attribute '" + attrStrOrPath + "'.");
        }
      }
    }
    // else it's a leaf

    return result;
  },

  set: function(attrs, opts){
    for (var attrStr in attrs){
      var attrPath = Backbone.NestedModel.attrPath(attrStr),
        childAttr = attrPath[0],
        attrObj = Backbone.NestedModel.createAttrObj(attrPath, attrs[attrStr]),
        val = attrObj[childAttr];

      if (_.isArray(val)){
        var nestedCollection = this.ensureNestedCollection(childAttr),
          model, collOpts;

        for (var i = 0; i < val.length; i++){
          model = nestedCollection.at(i);
          if (model){
            // nested model already exists in collection
            model.set(val[i], opts);
          } else {
            collOpts = _.defaults({at: i}, opts);
            nestedCollection.add(val[i], collOpts);
          }
        }

      } else if (typeof val === 'object'){
        // nested attributes
        var nestedModel = this.ensureNestedModel(childAttr);
        nestedModel.set(val, opts);

      } else {
        // leaf
        var setOpts = {};
        setOpts[childAttr] = val;
        Backbone.Model.prototype.set.call(this, setOpts, opts);
      }
    }
  },

  toJSON: function(){
    var json = Backbone.Model.prototype.toJSON.call(this),
      val;
    
    for (var attr in json){
      val = json[attr];

      // convert nested attributes to JSON
      if (val instanceof Backbone.Model || val instanceof Backbone.Collection){
        json[attr] = val.toJSON();
      }
    }

    return json;
  },

  ensureNestedModel: function(attr){
    var nestedModel = Backbone.Model.prototype.get.call(this, attr);

    // TODO handle overwrite vs. modification?
    if (!(nestedModel instanceof Backbone.NestedModel)){ // !(childAttr in this.attributes)
      // create nested model
      nestedModel = new Backbone.NestedModel();

      var setOpts = {};
      setOpts[attr] = nestedModel;
      Backbone.Model.prototype.set.call(this, setOpts, {silent: true});

      nestedModel.bind('all', function(evt){
        this.onNestedModelChange(attr, evt);
      }, this);
    }

    return nestedModel;
  },

  onNestedModelChange: function(attr, evt){
    var matchData = evt.match(/^change(:(.+))?$/);
    if (matchData){
      // change event
      var childAttr = matchData[2];
      if (childAttr){ // 'change:nested'
        this.trigger('change:' + attr + '.' + childAttr, this, {});
      } else { // 'change'
        this.trigger('change:' + attr, this);
        this.change();
      }
    }
  },

  ensureNestedCollection: function(attr){
    var nestedCollection = Backbone.Model.prototype.get.call(this, attr);

    // TODO handle overwrite vs. modification?
    if (!(nestedCollection instanceof Backbone.NestedCollection)){ // !(childAttr in this.attributes)
      // create nested collection
      nestedCollection = new Backbone.NestedCollection();
      var setOpts = {};
      setOpts[attr] = nestedCollection;
      Backbone.Model.prototype.set.call(this, setOpts, {silent: true});

      nestedCollection.bind('all', function(evt, model){
        this.onNestedCollectionChange(model, attr, evt);
      }, this);
    }

    return nestedCollection;
  },

  onNestedCollectionChange: function(model, attr, evt){
    var coll = this.attributes[attr],
      index = coll.indexOf(model),
      matchData = evt.match(/^change(:(.+))?$/);
    
    if (matchData){
      // change event
      var childAttr = matchData[2];
      if (childAttr){ // 'change:nested'
        this.trigger('change:' + attr + '[' + index + '].' + childAttr, this, {});
      } else { // 'change'
        this.trigger('change:' + attr + '[' + index + ']', this);
        this.trigger('change:' + attr, this);
        this.change();
      }
    }
  }

}, {
  // class methods

  attrPath: function(attrStrOrPath){
    var path;
    <commit_msg>refactored onNestedCollectionChange<commit_after>Backbone.NestedModel = Backbone.Model.extend({
  
  constructor: function(attrs, opts){
    Backbone.Model.prototype.constructor.apply( this, arguments );
  },

  get: function(attrStrOrPath){
    var attrPath = Backbone.NestedModel.attrPath(attrStrOrPath),
      childAttr = attrPath[0],
      result = Backbone.Model.prototype.get.call(this, childAttr);
    
    if (result instanceof Backbone.Collection){
      if (attrPath.length > 1){
        var otherAttrs = _.rest(attrPath);
        result = result.getNested(otherAttrs);
        
      } else {
        result = result.toJSON();

        if (window.console){
          window.console.log("Square bracket notation is preferred for accesing values of attribute '" + attrStrOrPath + "'.");
        }
      }

    } else if (result instanceof Backbone.Model){
      if (attrPath.length > 1){
        var otherAttrs = _.rest(attrPath);
        result = result.get(otherAttrs);

      } else {
        result = result.toJSON();

        if (window.console){
          window.console.log("Dot notation is preferred for accesing values of attribute '" + attrStrOrPath + "'.");
        }
      }
    }
    // else it's a leaf

    return result;
  },

  set: function(attrs, opts){
    for (var attrStr in attrs){
      var attrPath = Backbone.NestedModel.attrPath(attrStr),
        childAttr = attrPath[0],
        attrObj = Backbone.NestedModel.createAttrObj(attrPath, attrs[attrStr]),
        val = attrObj[childAttr];

      if (_.isArray(val)){
        var nestedCollection = this.ensureNestedCollection(childAttr),
          model, collOpts;

        for (var i = 0; i < val.length; i++){
          model = nestedCollection.at(i);
          if (model){
            // nested model already exists in collection
            model.set(val[i], opts);
          } else {
            collOpts = _.defaults({at: i}, opts);
            nestedCollection.add(val[i], collOpts);
          }
        }

      } else if (typeof val === 'object'){
        // nested attributes
        var nestedModel = this.ensureNestedModel(childAttr);
        nestedModel.set(val, opts);

      } else {
        // leaf
        var setOpts = {};
        setOpts[childAttr] = val;
        Backbone.Model.prototype.set.call(this, setOpts, opts);
      }
    }
  },

  toJSON: function(){
    var json = Backbone.Model.prototype.toJSON.call(this),
      val;
    
    for (var attr in json){
      val = json[attr];

      // convert nested attributes to JSON
      if (val instanceof Backbone.Model || val instanceof Backbone.Collection){
        json[attr] = val.toJSON();
      }
    }

    return json;
  },

  ensureNestedModel: function(attr){
    var nestedModel = Backbone.Model.prototype.get.call(this, attr);

    // TODO handle overwrite vs. modification?
    if (!(nestedModel instanceof Backbone.NestedModel)){ // !(childAttr in this.attributes)
      // create nested model
      nestedModel = new Backbone.NestedModel();

      var setOpts = {};
      setOpts[attr] = nestedModel;
      Backbone.Model.prototype.set.call(this, setOpts, {silent: true});

      nestedModel.bind('all', function(evt){
        this.onNestedAttrChange(attr, evt);
      }, this);
    }

    return nestedModel;
  },

  onNestedAttrChange: function(attr, evt){
    var matchData = evt.match(/^change(:(.+))?$/);
    if (matchData){
      // change event
      var childAttr = matchData[2];
      if (childAttr){ // 'change:nested'
        this.trigger('change:' + attr + '.' + childAttr, this, {});
      } else { // 'change'
        this.trigger('change:' + attr, this);
        this.change();
      }
    }
  },

  ensureNestedCollection: function(attr){
    var nestedCollection = Backbone.Model.prototype.get.call(this, attr);

    // TODO handle overwrite vs. modification?
    if (!(nestedCollection instanceof Backbone.NestedCollection)){ // !(childAttr in this.attributes)
      // create nested collection
      nestedCollection = new Backbone.NestedCollection();
      var setOpts = {};
      setOpts[attr] = nestedCollection;
      Backbone.Model.prototype.set.call(this, setOpts, {silent: true});

      nestedCollection.bind('all', function(evt, model){
        this.onNestedCollectionChange(model, attr, evt);
      }, this);
    }

    return nestedCollection;
  },

  onNestedCollectionChange: function(model, attr, evt){
    var coll = this.attributes[attr],
      index = coll.indexOf(model),
      fullAttr = attr + '[' + index + ']';
    
    this.onNestedAttrChange(fullAttr, evt);
    this.onNestedAttrChange(attr, evt);
  }

}, {
  // class methods

  attrPath: function(attrStrOrPath){
    var path;
    
{'fraction_non_alphanumeric': 0.13389164598842018, 'fraction_numerical': 0.0013440860215053765, 'mean_word_length': 2.433794817181399, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, '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': '3467885', 'n_tokens_mistral': 2866, 'n_tokens_neox': 2841, 'n_words': 639}