<commit_before>- # args: rest_index, shift, init_count

:javascript
  $(document).ready(function(){

    var world = new World({
      count: #{ init_count }, // Int
      restIndex: #{ rest_index }, // Int
      addStr: "#{ escape_javascript( render 'clone_week_fields', rest_index: rest_index, shift_index:-1, shift: shift ) }" // Str
    })

    listenTo(world)
  })

  function listenTo(world){ //RECURSION HOOK
    // input: World
    // output: callback(World)
    
    $(world.addSelector).click(function(){
      addShiftTo(world)
    })

    _.each(world.removeSelectors, function(removeSelector, removeIndex){
      $(removeSelector).click(function(){
        removeShiftFrom( world, removeIndex )
      })
    })
  }

  function addShiftTo(world){
    $( world.restSelector ).append( world.addStr )
    
    world
      .swapIds(-1, world.count )
      .expandSelectors()
      .changeCountBy(1)

    listenTo(world) // RECURSE
  }

  function removeShiftFrom(world, removeIndex){
    console.log('called removeShiftFrom')
    $(world.shiftSelectors[removeIndex]).remove() // delete shift entry

    var remainder = _.last(world.shiftSelectors, world.count - 1 - removeIndex )
    _.each(remainder, function( shiftSelector, remainderIndex ){ // loop through all shift entries with indexes greater than shiftIndex
      var offset = world.count - remainder.length
        , oldShiftIndex = remainderIndex + offset
      world.swapIds(oldShiftIndex, oldShiftIndex -1 )
    })

    world
      .shrinkSelectors()
      .changeCountBy(-1)

    listenTo(world) // RECURSE
  }

  function World(wp){
    var self = this

    //ATTRIBUTES
    this.count = wp.count // Int

    this.restIndex = wp.restIndex // Int
    this.restIndexStr = wp.restIndex.toString() // Str
    this.addStr = wp.addStr // Str

    this.restSelector = "#restaurant_"+this.restIndexStr // Str
    this.addSelector = "#add_shift_restaurant_"+this.restIndexStr // Str 
    this.shiftSelectors = getShiftSelectors()
    this.editSelectors = getEditSelectors()
    this.removeSelectors = getRemoveSelectors()



    //PUBLIC METHODS
    this.listen = function(){ // RECURSION HOOK
      $(this.addSelector).click(function(){
        this.addShift() // triggers recursion
      })

      _.each(this.removeSelectors, function(removeSelector, removeIndex){
        $(removeSelector).click(function(){
          this.removeShiftAt( removeIndex ) // triggers recursion
        })
      })
    }

    function addShift(){
      $( this.restSelector ).append( this.addStr )
      
      this
        .swapIds(-1, this.count )
        .expandSelectors()
        .changeCountBy(1)
        .listen() // RECURSE
    }

    this.removeShiftAt = function( removeIndex ){
      $( this.shiftSelectors[ removeIndex ] ).remove() // delete shift entry

      var remainder = _.last( this.shiftSelectors, this.count - 1 - removeIndex )
      _.each( remainder, function( shiftSelector, remainderIndex ) { // loop through all shift entries with indexes greater than shiftIndex
        var offset = this.count - remainder.length
          , oldShiftIndex = remainderIndex + offset
        this.swapIds( oldShiftIndex, oldShiftIndex -1 )
      })

      this
        .shrinkSelectors()
        .changeCountBy(-1)
        .listen() // RECURSE
    }

    this.swapIds = function(oldShiftIndex, newShiftIndex){
      // input: Int, Int
      // does: converts Ints to Strings, swaps old ids for new ids
      // output: new World
      var osi = oldShiftIndex.toString(), nsi = newShiftIndex.toString(), ri = this.restIndex.toString()
      $(this.restSelector).find("#shift_"+osi).attr( "id", "shift_" + nsi )
      $("#remove_restaurant_"+ri+"_shift_"+osi).attr( "id", "remove_restaurant_"+ri+"_shift_"+nsi )
      return this  
    }

    this.expandSelectors = function(){
      this.shiftSelectors.push("#shift_"+this.count)
      this.removeSelectors.push("#add_shift_restaurant_"+this.count)
      this.editSelectors.push("#edit_shift_restaurant_"+this.restIndexStr+"_shift_"+this.count)
      return this
    }

    this.shrinkSelectors = function(){
      this.shiftSelectors.pop()
      this.removeSelectors.pop()
      this.editSelectors.pop()
      return this
    }

    this.changeCountBy = function(int){
      this.count += int
      return this
    }

    // PRIVATE METHODS

    function getShiftSelectors(){
      var base = "#shift_"
      return selectorsFrom(base)
    }

    function getEditSelectors(){
      var base = "#edit_restaurant_"+self.restIndexStr+"_shift_"
      return selectorsFrom(base)
    }

    function getRemoveSelectors(){
      var base = "#remove_restaurant_"+self.restIndexStr+"_shift_"
      return selectorsFrom(base)
    }

    function selectorsFrom(base){
      return _(self.count).times( function(n) { return base + n.toString() } )
    }
  }


 <commit_msg>Refactor jQuery in #preview_clone_week (add event handler clearing after each recursion)<commit_after>- # args: rest_index, shift, init_count

:javascript
  $(document).ready(function(){

    var world = new World({
      count: #{ init_count }, // Int
      restIndex: #{ rest_index }, // Int
      addStr: "#{ escape_javascript( render 'clone_week_fields', rest_index: rest_index, shift_index:-1, shift: shift ) }" // Str
    })

    listenTo(world)
  })

  function listenTo(world){ //RECURSION HOOK
    // input: World
    // does: mutates World, recurses
    listenForAdd(world, addShift)
    listenForModify(world, world.removeSelectors, removeShift)
    listenForModify(world, world.editSelectors, editShift)
  }

  function listenForAdd(world, callback){
    // input: World, function(World, Callback)
    $(world.addSelector).click(function(){
      addShiftTo(world, listenTo)
    })    
  }

  function listenForModify(world, selectors, callback){
    // input: Arr of Strs, function(World, Int, Callback)
    _.each(selectors, function(selector, i){
      $(selector).click(function(){
        callback( world, i, listenTo )
      })
    })     
  }

  function addShift(world, callback){
    // input: World, function(World)
    $( world.restSelector ).append( world.addStr )
    
    world
      .swapIds(-1, world.count )
      .expandSelectors()
      .changeCountBy(1)
      .clearEventHandlers()

    callback(world) // RECURSE
  }

  function editShift(world, callback){

  }

  function removeShift(world, removeIndex, callback){
    //input: World, Int, function(World)

      console.log('______________________________________')
      console.log('>>> WORLD BEFORE REMOVE')
      console.log(world)

    $(world.shiftSelectors[removeIndex]).remove() // delete shift entry

    var remainder = _.last(world.shiftSelectors, world.count - 1 - removeIndex )
    _.each(remainder, function( shiftSelector, remainderIndex ){ // loop through all shift entries with indexes greater than shiftIndex
      var offset = world.count - remainder.length
        , oldShiftIndex = remainderIndex + offset
      world.swapIds(oldShiftIndex, oldShiftIndex -1 )
    })

    world
      .shrinkSelectors()
      .changeCountBy(-1)
      .clearEventHandlers()

    // _.each(world.removeSelectors, function(selector){ $(selector).off('click') })
      

      console.log('>>> WORLD AFTER REMOVE')
      console.log(world)      
      console.log('______________________________________')


    callback(world) // RECURSE
  }

  function World(wp){
    var self = this

    //ATTRIBUTES
    this.count = wp.count // Int

    this.restIndex = wp.restIndex // Int
    this.restIndexStr = wp.restIndex.toString() // Str
    this.addStr = wp.addStr // Str

    this.restSelector = "#restaurant_"+this.restIndexStr // Str
    this.addSelector = "#add_shift_restaurant_"+this.restIndexStr // Str 
    this.shiftSelectors = getShiftSelectors()
    this.editSelectors = getEditSelectors()
    this.removeSelectors = getRemoveSelectors()

    //PUBLIC METHODS

    this.listen = function(){ // RECURSION HOOK
      $(this.addSelector).click(function(){
        this.addShift() // triggers recursion
      })

      _.each(this.removeSelectors, function(removeSelector, removeIndex){
        $(removeSelector).click(function(){
          this.removeShiftAt( removeIndex ) // triggers recursion
        })
      })
    }

    //mutations
    function addShift(){
      $( this.restSelector ).append( this.addStr )
      
      this
        .swapIds(-1, this.count )
        .expandSelectors()
        .changeCountBy(1)
        .listen() // RECURSE
    }

    this.removeShiftAt = function( removeIndex ){
      $( this.shiftSelectors[ removeIndex ] ).remove() // delete shift entry

      var remainder = _.last( this.shiftSelectors, this.count - 1 - removeIndex )
      _.each( remainder, function( shiftSelector, remainderIndex ) { // loop through all shift entries with indexes greater than shiftIndex
        var offset = this.count - remainder.length
          , oldShiftIndex = remainderIndex + offset
        this.swapIds( oldShiftIndex, oldShiftIndex -1 )
      })

      this
        .shrinkSelectors()
        .changeCountBy(-1)
        .listen() // RECURSE
    }

    this.swapIds = function(oldShiftIndex, newShiftIndex){
      // input: Int, Int
      // does: converts Ints to Strings, swaps old ids for new ids
      // output: new World
      var osi = oldShiftIndex.toString(), nsi = newShiftIndex.toString(), ri = this.restIndex.toString()
      $(this.restSelector).find("#shift_"+osi).attr( "id", "shift_" + nsi )
      $("#remove_restaurant_"+ri+"_shift_"+osi).attr( "id", "remove_restaurant_"+ri+"_shift_"+nsi )
      return this  
    }

    this.expandSelectors = function(){
      this.shiftSelectors.push("#shift_"+this.count)
      this.removeSelectors.push("#remove_restaurant_"+this.restIndexStr+"_shift_"+this.count)
      this.editSelectors.push("#edit_restaurant_"+this.restIndexStr+"_shift_"+this.count)
      return this
    }

    this.shrinkSelectors = function(){
      _.each(lineItemSelectors(), function(selectors){
        selectors.pop()
      })
      return this
    }

    this.changeCountBy = function(int){
      this.count += int
      return this
    }

    this.clearEventHandlers = function(){
      // _.each(this.removeSelectors, function(selector){ $(selector).off('click') })

      _.each(eventHandlingSelectors(), function(selectors){
        _.each(selectors, function(selector){
          $(selector).off('click')
        })
      })
      return this
    }

    // PRIVATE METHODS

    //initializers
    function getShiftSelectors(){
      var base = "#shift_"
      return selectorsFrom(base)
    }

    function getEditSelectors(){
      var base = "#edit_restaurant_"+self.restIndexStr+"_shift_"
      return selectorsFrom(base)
    }

    function getRemoveSelectors(){
      var base = "#remove_restaurant_"+self.restIndexStr+"_shift_"
      return selectorsFrom(base)
    }

    function selectorsFrom(base){
      return _(self.count).times( function(n) { return base + n.toString() } )
    }

    //accessors
    function lineItemSelectors(){
      return [ self.shiftSelectors, self.removeSelectors, self.editSelectors ]
    }

    function eventHandlingSelectors(){
      return [ self.removeSelectors, self.editSelectors, [ self.addSelector ] ]
    }

    function modifySelectors(){
      return [ self.removeSelectors, self.editSelectors ]
    }

  }


 
{'fraction_non_alphanumeric': 0.12454984628897672, 'fraction_numerical': 0.001932367149758454, 'mean_word_length': 2.7814679508468947, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, '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': '7647167', 'n_tokens_mistral': 3423, 'n_tokens_neox': 3276, 'n_words': 689}