angular.module('NgDrag', [])

.factory 'DragHolder', ->
  object = { scope: '', dragging: '', context: '' }

  object.set = (scope,dragging,context) ->
    this.scope    = scope
    this.dragging = dragging
    this.context  = context

  object.get = ->
      return this

  return object
.directive 'ngPositionable', (DragHolder) ->
  link: (scope, element, attributes, ngModelCtrl) ->
    el = element[0]
    element.css('transition', '0.3s all')
    el.draggable = true
    el.addEventListener 'dragstart', (e) ->
      this.classList.add('drag')
      DragHolder.set scope,attributes.ngPositionable,attributes.ngContext
      e.stopPropagation()
    el.addEventListener 'dragend',(e) ->
      this.classList.remove('drag')
    el.droppable = true
    el.addEventListener 'dragover', (e) ->
      e.dataTransfer.dropEffect = 'move'
      e.preventDefault()
      e.stopPropagation()
      this.classList.add('over')
    el.addEventListener 'dragenter', (e) ->
      e.preventDefault()
      e.stopPropagation()
      this.classList.add('over')
    el.addEventListener 'dragleave', (e) ->
      this.classList.remove('over')
      e.preventDefault()
    el.addEventListener 'drop', (e) ->
      e.stopPropagation() if (e.stopPropagation)
      this.classList.remove('over')
      raw_factory = attributes.ngPositionable.split('_')
      factory=[]
      for word in raw_factory
        factory.push(word.charAt(0).toUpperCase() + word.slice(1))
      factory = factory.join('')
      list_name = attributes.ngParent || factory
      dragged = DragHolder.get()
      dragging = dragged.scope[dragged.dragging]
      if dragged.context
        draggingContext = dragged.scope[dragged.context]
        droppingContext = scope[attributes.ngContext]
        if draggingContext != droppingContext
          dragged.scope[list_name].drop(dragging)
          scope[factory].push(dragging)
      dropping = scope[attributes.ngPositionable]
      if attributes.ngDisabled
        disabled = attributes.ngDisabled.split('!')
        disabled[0] = '!' if disabled.length > 1
        bool = scope.$parent[disabled.pop()]
        bool = !bool if disabled.length > 0
      else
        bool = false
      unless bool
        scope.setPosition(dragging,dropping,draggingContext,droppingContext,attributes.ngContext,factory,list_name)
  controller: ($scope, $element, $filter, $injector,$parse) ->
    $scope.setPosition = (dragging,dropping,draggingContext,droppingContext,context,factory,list_name) ->
      unless dragging == dropping
        orderedArray = $filter('orderBy')($parse(list_name)($scope), 'position')
        index = orderedArray.indexOf(dropping)
        unless dragging == orderedArray[index - 1]
          unless index == 0
            dragging.position = parseFloat(dropping.position) - (parseFloat(dropping.position) - parseFloat(orderedArray[index - 1].position)) / 2
          else
            dragging.position = parseFloat(dropping.position) / 2
          object = {id: dragging.id, position: dragging.position}
          object[context + '_id'] = droppingContext.id if context
          list = $injector.get(factory)
          list.update object, (returnData) ->
            dragging = returnData

.directive 'ngDrag', ->
  link: (scope, element, attributes, ngModelCtrl) ->
    console.warn 'ngDrag is deprecated. Please consider using mrDrag in its stead.'
    callFunction = attributes.ngDrag
    el = element[0]
    el.draggable = true
    el.addEventListener 'dragstart', (e) ->
      e.dataTransfer.setData('html',el)
      this.classList.add('drag')
      scope.$eval(callFunction)
    el.addEventListener 'dragend',(e) ->
      this.classList.remove('drag')

.directive 'ngDrop', ->
  link: (scope, element, attributes, ngModelCtrl) ->
    console.warn 'ngDrop is deprecated. Please consider using mrDrop in its stead.'
    callFunction = attributes.ngDrop
    el = element[0]
    el.droppable = true
    el.addEventListener 'dragover', (e) ->
      e.dataTransfer.dropEffect = 'move'
      e.preventDefault()
      this.classList.add('over')
    el.addEventListener 'dragenter', (e) ->
      e.preventDefault()
      this.classList.add('over')
    el.addEventListener 'dragleave', (e) ->
      this.classList.remove('over')
      e.preventDefault()
    el.addEventListener 'drop', (e) ->
      e.stopPropagation() if (e.stopPropagation)
      this.classList.remove('over')
      scope.$eval(callFunction)

.directive 'ngChangeDebounce', ($timeout) ->
  restrict: 'A',
  require: 'ngModel',
  link: (scope, element, attr, ngModelCtrl) ->
    return if (attr.type == 'radio' || attr.type == 'checkbox')
    callFunction = attr.ngChangeDebounce
    element.bind 'keyup', ->
      $timeout.cancel(scope.debounce)
      scope.debounce = $timeout ->
        scope.$eval(callFunction)
      ,750