'use strict';

describe('uiSortable', function() {

  beforeEach(module(function($compileProvider) {
    if (typeof $compileProvider.debugInfoEnabled === 'function') {
      $compileProvider.debugInfoEnabled(false);
    }
  }));

  // Ensure the sortable angular module is loaded
  beforeEach(module('ui.sortable'));
  beforeEach(module('ui.sortable.testHelper'));
  
  var EXTRA_DY_PERCENTAGE, listInnerContent, simulateElementDrag;

  beforeEach(inject(function (sortableTestHelper) {
    EXTRA_DY_PERCENTAGE = sortableTestHelper.EXTRA_DY_PERCENTAGE;
    listInnerContent = sortableTestHelper.listInnerContent;
    simulateElementDrag = sortableTestHelper.simulateElementDrag;
  }));

  describe('Nested sortables related', function() {

    var host;

    beforeEach(inject(function() {
      host = $('<div id="test-host"></div>');
      $('body').append(host);
    }));

    afterEach(function() {
      host.remove();
      host = null;
    });

    it('should update model when sorting between nested sortables', function() {
      inject(function($compile, $rootScope) {
        var elementTree, li1, li2;

        elementTree = $compile(''.concat(
          '<ul ui-sortable="sortableOptions" ng-model="items" class="nested-sortable outterList" style="float: left;margin-left: 10px;padding-bottom: 10px;">',
            '<li ng-repeat="item in items">',
              '<div>',
                '<span class="itemContent lvl1ItemContent">{{item.text}}</span>',
                '<ul ui-sortable="sortableOptions" ng-model="item.items" class="nested-sortable innerList" style="margin-left: 10px;padding-bottom: 10px;">',
                  '<li ng-repeat="i in item.items">',
                    '<span class="itemContent lvl2ItemContent">{{i.text}}</span>',
                  '</li>',
                '</ul>',
              '</div>',
            '</li>',
          '</ul>',
          '<div style="clear: both;"></div>'))($rootScope);

        $rootScope.$apply(function() {
          $rootScope.items = [
            {
              text: 'Item 1',
              items: []
            },
            {
              text: 'Item 2',
              items: [
                { text: 'Item 2.1', items: [] },
                { text: 'Item 2.2', items: [] }
              ]
            }
          ];
          
          $rootScope.sortableOptions = {
            connectWith: '.nested-sortable'
          };
        });

        host.append(elementTree);

        // this should drag the item out of the list and
        // the item should return back to its original position
        li1 = elementTree.find('.innerList:last').find('li:last');
        li1.simulate('drag', { dx: -200, moves: 30 });
        expect($rootScope.items.map(function(x){ return x.text; }))
          .toEqual(['Item 1', 'Item 2']);
        expect($rootScope.items.map(function(x){ return x.text; }))
          .toEqual(listInnerContent(elementTree, '.lvl1ItemContent'));
        expect($rootScope.items[0].items.map(function(x){ return x.text; }))
          .toEqual([]);
        expect($rootScope.items[0].items.map(function(x){ return x.text; }))
          .toEqual(listInnerContent(elementTree.find('.innerList:eq(0)'), '.lvl2ItemContent'));
        expect($rootScope.items[1].items.map(function(x){ return x.text; }))
          .toEqual(['Item 2.1', 'Item 2.2']);
        expect($rootScope.items[1].items.map(function(x){ return x.text; }))
          .toEqual(listInnerContent(elementTree.find('.innerList:eq(1)'), '.lvl2ItemContent'));

        // this should drag the item from the outter list and
        // drop it to the inner list
        li1 = elementTree.find('> li:first');
        li2 = elementTree.find('.innerList:last').find('li:last');
        simulateElementDrag(li1, li2, { place: 'above', extradx: 10, extrady: -5 });
        expect($rootScope.items.map(function(x){ return x.text; }))
          .toEqual(['Item 2']);
        expect($rootScope.items.map(function(x){ return x.text; }))
          .toEqual(listInnerContent(elementTree, '.lvl1ItemContent'));
        expect($rootScope.items[0].items.map(function(x){ return x.text; }))
          .toEqual(['Item 2.1', 'Item 1', 'Item 2.2']);
        expect($rootScope.items[0].items.map(function(x){ return x.text; }))
          .toEqual(listInnerContent(elementTree.find('.innerList:eq(0)'), '.lvl2ItemContent'));

        // this should drag the item from the inner list and
        // drop it to the outter list
        li1 = elementTree.find('.innerList:last').find('li:last');
        li2 = elementTree.find('> li:first');
        simulateElementDrag(li1, li2, { place: 'above', extradx: -10, extrady: -6 });
        expect($rootScope.items.map(function(x){ return x.text; }))
          .toEqual(['Item 2.2', 'Item 2']);
        expect($rootScope.items.map(function(x){ return x.text; }))
          .toEqual(listInnerContent(elementTree, '.lvl1ItemContent'));
        expect($rootScope.items[0].items.map(function(x){ return x.text; }))
          .toEqual([]);
        expect($rootScope.items[0].items.map(function(x){ return x.text; }))
          .toEqual(listInnerContent(elementTree.find('.innerList:eq(0)'), '.lvl2ItemContent'));
        expect($rootScope.items[1].items.map(function(x){ return x.text; }))
          .toEqual(['Item 2.1', 'Item 1']);
        expect($rootScope.items[1].items.map(function(x){ return x.text; }))
          .toEqual(listInnerContent(elementTree.find('.innerList:eq(1)'), '.lvl2ItemContent'));

        $(elementTree).remove();
      });
    });

    it('should update model when sorting between drectly nested sortables', function() {
      inject(function($compile, $rootScope) {
        var elementTree, li1, li2;

        elementTree = $compile(''.concat(
          '<div ui-sortable="sortableOptions" ng-model="items" class="nested-sortable outterList" style="float: left;margin-left: 10px;padding-bottom: 10px;">',
            '<div ui-sortable="innerSortableOptions" ng-model="item.items" class="nested-sortable innerList" ng-repeat="item in items">',
              '<div class="itemContent lvl1ItemContent">{{item.text}}</div>',
              '<div ng-repeat="i in item.items" style="margin-left: 10px;padding-bottom: 10px;">',
                '<div class="itemContent lvl2ItemContent">{{i.text}}</div>',
              '</div>',
            '</div>',
          '</div>',
          '<div style="clear: both;"></div>'))($rootScope);

        $rootScope.$apply(function() {
          $rootScope.items = [
            {
              text: 'Item 1',
              items: [
                { text: 'Item 1.1', items: [] },
                { text: 'Item 1.2', items: [] }
              ]
            },
            {
              text: 'Item 2',
              items: [
                { text: 'Item 2.1', items: [] },
                { text: 'Item 2.2', items: [] }
              ]
            }
          ];
          
          $rootScope.sortableOptions = {};
          $rootScope.innerSortableOptions = {
            connectWith: '.nested-sortable'
          };
        });

        host.append(elementTree);

        li1 = elementTree.find('.innerList:last');
        li2 = elementTree.find('.innerList:first');
        simulateElementDrag(li1, li2, { place: 'above', extradx: -10, extrady: -6 });
        expect($rootScope.items.map(function(x){ return x.text; }))
          .toEqual(['Item 2', 'Item 1']);
        expect($rootScope.items.map(function(x){ return x.text; }))
          .toEqual(listInnerContent(elementTree, '.lvl1ItemContent'));

        $(elementTree).remove();
      });
    });

  });

});