stokarenko/dynamic-fields-for

View on GitHub
src/js.coffee

Summary

Maintainability
Test Coverage
$.fn.extend
  find2: (selector) ->
    @.filter(selector).add(@.find(selector))

class DynamicFields
  @_counter = 0

  @add: (element) ->
    lazy_dynamic_fields = @_find($(element).data('dynamicFieldsAdd'))
    lazy_dynamic_fields.add() if lazy_dynamic_fields

  @remove: (element) ->
    lazy_dynamic_fields = @_find($(element).data('dynamicFieldsRemove'))
    lazy_dynamic_fields.remove($(element)) if lazy_dynamic_fields

  @_find: (fields_id) ->
    $fields_begin = @_$anchor('begin', fields_id)
    fields_begin = $fields_begin.get(0)
    return null unless fields_begin
    fields_begin.lazy_dynamic_fields ||= new @ $fields_begin

  @_anchor: (postfix, fields_id) ->
    "[data-dynamic-fields-#{postfix}=#{fields_id}]"

  @_$anchor: (postfix, fields_id) ->
    $(@_anchor(postfix, fields_id))

  constructor: ($fields_begin) ->
    @fields_id = $fields_begin.data('dynamicFieldsBegin')

    @$fields_begin = $fields_begin
    @$fields_end = @_$anchor('end')

    @$fields_parent = @$fields_begin.parent()

  add: ->
    fields_add = $($.parseHTML(@_render_insertion('Add'), null, true))

    @$fields_parent.trigger('dynamic-fields:before-add-into')
    @$fields_end.before(fields_add)
    fields_add.not('script').trigger('dynamic-fields:after-add')
    @$fields_parent.trigger('dynamic-fields:after-add-into')

  remove: ($element) ->
    node = $element
    while (fields_item_begin = node.prevAll("#{@_anchor('item-begin')}:first"); fields_item_begin.length == 0)
      node = node.parent()
      break if node.length == 0

    return if fields_item_begin.length == 0

    if (object_id = $element.data('dynamicFieldsRemoveId'))
      destroy_inputs = @_render_insertion('Remove').replace(
        /(["])dynamic_fields_object_id(["])/g,
        "$1#{object_id}$2"
      )
      @$fields_begin.after(destroy_inputs)

    all_fields = @$fields_parent.contents()
    index_begin = all_fields.index(fields_item_begin)
    index_end = all_fields.index(fields_item_begin.nextAll("#{@_anchor('item-begin')}, #{@_anchor('end')}").first())
    fields_remove = all_fields.slice(index_begin, index_end)

    @$fields_parent.trigger('dynamic-fields:before-remove-from')
    fields_remove.not('script').trigger('dynamic-fields:before-remove')
    fields_remove.remove()
    @$fields_parent.trigger('dynamic-fields:after-remove-from')

  _anchor: (postfix) ->
    @constructor._anchor(postfix, @fields_id)

  _$anchor: (postfix) ->
    @constructor._$anchor(postfix, @fields_id)

  _render_insertion: (template_type) ->
    res = template = @$fields_begin.data("dynamicFields#{template_type}Template")

    regex = /data-dynamic-fields-begin="(\d+([^"]+))/g
    while ( match = regex.exec(template) )
      res = res.replace(new RegExp(match[1], 'g'), "#{ @._next_index() }#{ match[2] }")

    res.replace(
      /(["';][^"';]*?)dynamic_fields_index/g,
      "$1#{@_next_index()}"
    )

  _next_index: ->
    new Date().getTime() + @constructor._counter++

$(document).on 'click', '[data-dynamic-fields-remove]', (event) ->
  event.preventDefault()
  DynamicFields.remove(@)

$(document).on 'click', '[data-dynamic-fields-add]', (event) ->
  event.preventDefault()
  DynamicFields.add(@)