describe('datepicker directive', function() {
  var $rootScope, $compile, $templateCache, element;
  beforeEach(module('ui.bootstrap.datepicker'));
  beforeEach(module('template/datepicker/datepicker.html'));
  beforeEach(module('template/datepicker/day.html'));
  beforeEach(module('template/datepicker/month.html'));
  beforeEach(module('template/datepicker/year.html'));
  beforeEach(module('template/datepicker/popup.html'));
  beforeEach(module(function($compileProvider) {
    $compileProvider.directive('dateModel', function() {
      return {
        restrict: 'A',
        require: 'ngModel',
        link: function(scope, element, attrs, modelController) {
          modelController.$formatters.push(function(object) {
            return new Date(object.date);
          });

          modelController.$parsers.push(function(date) {
            return {
              type: 'date',
              date: date.toUTCString()
            };
          });
        }
      };
    });
  }));

  function getTitleButton() {
    return element.find('th').eq(1).find('button').first();
  }

  function getTitle() {
    return getTitleButton().text();
  }

  function clickTitleButton() {
    getTitleButton().click();
  }

  function clickPreviousButton(times) {
    var el = element.find('th').eq(0).find('button').eq(0);
    for (var i = 0, n = times || 1; i < n; i++) {
      el.click();
    }
  }

  function clickNextButton() {
    element.find('th').eq(2).find('button').eq(0).click();
  }

  function getLabelsRow() {
    return element.find('thead').find('tr').eq(1);
  }

  function getLabels(dayMode) {
    var els = getLabelsRow().find('th'),
        labels = [];
    for (var i = dayMode ? 1 : 0, n = els.length; i < n; i++) {
      labels.push(els.eq(i).text());
    }
    return labels;
  }

  function getWeeks() {
    var rows = element.find('tbody').find('tr'),
        weeks = [];
    for (var i = 0, n = rows.length; i < n; i++) {
      weeks.push(rows.eq(i).find('td').eq(0).first().text());
    }
    return weeks;
  }

  function getOptions(dayMode) {
    var tr = element.find('tbody').find('tr');
    var rows = [];

    for (var j = 0, numRows = tr.length; j < numRows; j++) {
      var cols = tr.eq(j).find('td'), days = [];
      for (var i = dayMode ? 1 : 0, n = cols.length; i < n; i++) {
        days.push(cols.eq(i).find('button').text());
      }
      rows.push(days);
    }
    return rows;
  }

  function clickOption(index) {
    getAllOptionsEl().eq(index).click();
  }

  function getAllOptionsEl(dayMode) {
    return element.find('tbody').find('button');
  }

  function selectedElementIndex() {
    var buttons = getAllOptionsEl();
    for (var i = 0; i < buttons.length; i++) {
      if (angular.element(buttons[i]).hasClass('btn-info')) {
        return i;
      }
    }
  }

  function expectSelectedElement(index) {
    var buttons = getAllOptionsEl();
    angular.forEach( buttons, function(button, idx) {
      expect(angular.element(button).hasClass('btn-info')).toBe(idx === index);
    });
  }

  function triggerKeyDown(element, key, ctrl) {
    var keyCodes = {
      'enter': 13,
      'space': 32,
      'pageup': 33,
      'pagedown': 34,
      'end': 35,
      'home': 36,
      'left': 37,
      'up': 38,
      'right': 39,
      'down': 40,
      'esc': 27
    };
    var e = $.Event('keydown');
    e.which = keyCodes[key];
    if (ctrl) {
      e.ctrlKey = true;
    }
    element.trigger(e);
  }

  describe('$datepickerSuppressError', function() {
    var $compile,
        $log,
        $scope;

    it('should not suppress log error message for ng-model date error by default', function() {
      inject(function(_$log_, _$rootScope_, _$compile_) {
        $log = _$log_;
        $scope = _$rootScope_.$new();
        $compile = _$compile_;
      });

      spyOn($log, 'error');
      element = $compile('<uib-datepicker ng-model="locals.date"></uib-datepicker>')($scope);

      $scope.locals = {
        date: 'lalala'
      };
      $scope.$digest();
      expect($log.error).toHaveBeenCalled();
    });

    it('should not suppress log error message for ng-model date error when false', function() {
      module(function($provide) {
        $provide.value('$datepickerSuppressError', false);
      });

      inject(function(_$log_, _$rootScope_, _$compile_) {
        $log = _$log_;
        $scope = _$rootScope_.$new();
        $compile = _$compile_;
      });

      spyOn($log, 'error');
      element = $compile('<uib-datepicker ng-model="locals.date"></uib-datepicker>')($scope);

      $scope.locals = {
        date: 'lalala'
      };
      $scope.$digest();
      expect($log.error).toHaveBeenCalled();
    });

    it('should suppress log error message for ng-model date error when true', function() {
      module(function($provide) {
        $provide.value('$datepickerSuppressError', true);
      });

      inject(function(_$log_, _$rootScope_, _$compile_) {
        $log = _$log_;
        $scope = _$rootScope_.$new();
        $compile = _$compile_;
      });
      spyOn($log, 'error');

      element = $compile('<uib-datepicker ng-model="locals.date"></uib-datepicker>')($scope);

      $scope.locals = {
        date: 'lalala'
      };
      $scope.$digest();
      expect($log.error).not.toHaveBeenCalled();
    });
  });

  describe('', function() {
    beforeEach(inject(function(_$compile_, _$rootScope_, _$templateCache_) {
      $compile = _$compile_;
      $rootScope = _$rootScope_;
      $rootScope.date = new Date('September 30, 2010 15:30:00');
      $templateCache = _$templateCache_;
    }));


    describe('', function() {
      beforeEach(function() {
        element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
        $rootScope.$digest();
      });

      it('is has a `<table>` element', function() {
        expect(element.find('table').length).toBe(1);
      });

      it('shows the correct title', function() {
        expect(getTitle()).toBe('September 2010');
      });

      it('shows the label row & the correct day labels', function() {
        expect(getLabelsRow().css('display')).not.toBe('none');
        expect(getLabels(true)).toEqual(['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']);
      });

      it('renders the calendar days correctly', function() {
        expect(getOptions(true)).toEqual([
          ['29', '30', '31', '01', '02', '03', '04'],
          ['05', '06', '07', '08', '09', '10', '11'],
          ['12', '13', '14', '15', '16', '17', '18'],
          ['19', '20', '21', '22', '23', '24', '25'],
          ['26', '27', '28', '29', '30', '01', '02'],
          ['03', '04', '05', '06', '07', '08', '09']
        ]);
      });

      it('renders the week numbers based on ISO 8601', function() {
        expect(getWeeks()).toEqual(['35', '36', '37', '38', '39', '40']);
      });

      it('value is correct', function() {
        expect($rootScope.date).toEqual(new Date('September 30, 2010 15:30:00'));
      });

      it('has `selected` only the correct day', function() {
        expectSelectedElement(32);
      });

      it('has no `selected` day when model is cleared', function() {
        $rootScope.date = null;
        $rootScope.$digest();

        expect($rootScope.date).toBe(null);
        expectSelectedElement(null);
      });

      it('does not change current view when model is cleared', function() {
        $rootScope.date = null;
        $rootScope.$digest();

        expect($rootScope.date).toBe(null);
        expect(getTitle()).toBe('September 2010');
      });

      it('`disables` visible dates from other months', function() {
        var buttons = getAllOptionsEl();
        angular.forEach(buttons, function(button, index) {
          expect(angular.element(button).find('span').hasClass('text-muted')).toBe( index < 3 || index > 32 );
        });
      });

      it('updates the model when a day is clicked', function() {
        clickOption(17);
        expect($rootScope.date).toEqual(new Date('September 15, 2010 15:30:00'));
      });

      it('moves to the previous month & renders correctly when `previous` button is clicked', function() {
        clickPreviousButton();

        expect(getTitle()).toBe('August 2010');
        expect(getLabels(true)).toEqual(['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']);
        expect(getOptions(true)).toEqual([
          ['01', '02', '03', '04', '05', '06', '07'],
          ['08', '09', '10', '11', '12', '13', '14'],
          ['15', '16', '17', '18', '19', '20', '21'],
          ['22', '23', '24', '25', '26', '27', '28'],
          ['29', '30', '31', '01', '02', '03', '04'],
          ['05', '06', '07', '08', '09', '10', '11']
        ]);

        expectSelectedElement(null, null);
      });

      it('updates the model only when a day is clicked in the `previous` month', function() {
        clickPreviousButton();
        expect($rootScope.date).toEqual(new Date('September 30, 2010 15:30:00'));

        clickOption(17);
        expect($rootScope.date).toEqual(new Date('August 18, 2010 15:30:00'));
      });

      it('moves to the next month & renders correctly when `next` button is clicked', function() {
        clickNextButton();

        expect(getTitle()).toBe('October 2010');
        expect(getLabels(true)).toEqual(['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']);
        expect(getOptions(true)).toEqual([
          ['26', '27', '28', '29', '30', '01', '02'],
          ['03', '04', '05', '06', '07', '08', '09'],
          ['10', '11', '12', '13', '14', '15', '16'],
          ['17', '18', '19', '20', '21', '22', '23'],
          ['24', '25', '26', '27', '28', '29', '30'],
          ['31', '01', '02', '03', '04', '05', '06']
        ]);

        expectSelectedElement(4);
      });

      it('updates the model only when a day is clicked in the `next` month', function() {
        clickNextButton();
        expect($rootScope.date).toEqual(new Date('September 30, 2010 15:30:00'));

        clickOption(17);
        expect($rootScope.date).toEqual(new Date('October 13, 2010 15:30:00'));
      });

      it('updates the calendar when a day of another month is selected', function() {
        clickOption(33);
        expect($rootScope.date).toEqual(new Date('October 01, 2010 15:30:00'));
        expect(getTitle()).toBe('October 2010');
        expect(getLabels(true)).toEqual(['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']);
        expect(getOptions(true)).toEqual([
          ['26', '27', '28', '29', '30', '01', '02'],
          ['03', '04', '05', '06', '07', '08', '09'],
          ['10', '11', '12', '13', '14', '15', '16'],
          ['17', '18', '19', '20', '21', '22', '23'],
          ['24', '25', '26', '27', '28', '29', '30'],
          ['31', '01', '02', '03', '04', '05', '06']
        ]);

        expectSelectedElement(5);
      });

      // issue #1697
      it('should not "jump" months', function() {
        $rootScope.date = new Date('January 30, 2014');
        $rootScope.$digest();
        clickNextButton();
        expect(getTitle()).toBe('February 2014');
        clickPreviousButton();
        expect(getTitle()).toBe('January 2014');
      });

      it('should support custom templates', function() {
        $templateCache.put('foo/bar.html', '<div>baz</div>');

        element = $compile('<uib-datepicker ng-model="date" template-url="foo/bar.html"></uib-datepicker>')($rootScope);
        $rootScope.$digest();

        expect(element.html()).toBe('baz');
      });

      it('should support custom day, month and year templates', function() {
        $templateCache.put('foo/day.html', '<div>day</div>');
        $templateCache.put('foo/month.html', '<div>month</div>');
        $templateCache.put('foo/year.html', '<div>year</div>');

        $templateCache.put('foo/bar.html', '<div>' +
          '<uib-daypicker template-url="foo/day.html"></uib-daypicker>' +
          '<uib-monthpicker template-url="foo/month.html"></uib-monthpicker>' +
          '<uib-yearpicker template-url="foo/year.html"></uib-yearpicker>' +
        '</div>');

        element = $compile('<uib-datepicker ng-model="date" template-url="foo/bar.html"></uib-datepicker>')($rootScope);
        $rootScope.$digest();

        var expectedHtml = '<div template-url="foo/day.html">day</div><div template-url="foo/month.html">month</div><div template-url="foo/year.html">year</div>';

        expect(element.html()).toBe(expectedHtml);
      });

      it('should expose the controller in the template', function() {
        $templateCache.put('template/datepicker/datepicker.html', '<div>{{datepicker.text}}</div>');

        element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
        $rootScope.$digest();

        var ctrl = element.controller('uib-datepicker');
        expect(ctrl).toBeDefined();
        expect(element.html()).toBe('');

        ctrl.text = 'baz';
        $rootScope.$digest();

        expect(element.html()).toBe('baz');
      });

      describe('when `model` changes', function() {
        function testCalendar() {
          expect(getTitle()).toBe('November 2005');
          expect(getOptions(true)).toEqual([
            ['30', '31', '01', '02', '03', '04', '05'],
            ['06', '07', '08', '09', '10', '11', '12'],
            ['13', '14', '15', '16', '17', '18', '19'],
            ['20', '21', '22', '23', '24', '25', '26'],
            ['27', '28', '29', '30', '01', '02', '03'],
            ['04', '05', '06', '07', '08', '09', '10']
          ]);

          expectSelectedElement(8);
        }

        describe('to a Date object', function() {
          it('updates', function() {
            $rootScope.date = new Date('November 7, 2005 23:30:00');
            $rootScope.$digest();
            testCalendar();
            expect(angular.isDate($rootScope.date)).toBe(true);
          });

          it('to a date that is invalid, it doesn\`t update', function() {
            $rootScope.date = new Date('pizza');
            $rootScope.$digest();
            expect(getTitle()).toBe('September 2010');
            expect(angular.isDate($rootScope.date)).toBe(true);
            expect(isNaN($rootScope.date)).toBe(true);
          });
        });

        describe('not to a Date object', function() {
          it('to a Number, it updates calendar', function() {
            $rootScope.date = parseInt((new Date('November 7, 2005 23:30:00')).getTime(), 10);
            $rootScope.$digest();
            testCalendar();
            expect(angular.isNumber($rootScope.date)).toBe(true);
          });

          it('to a string that can be parsed by Date, it updates calendar', function() {
            $rootScope.date = 'November 7, 2005 23:30:00';
            $rootScope.$digest();
            testCalendar();
            expect(angular.isString($rootScope.date)).toBe(true);
          });

          it('to a string that cannot be parsed by Date, it doesn\'t update', function() {
            $rootScope.date = 'pizza';
            $rootScope.$digest();
            expect(getTitle()).toBe('September 2010');
            expect($rootScope.date).toBe('pizza');
          });
        });
      });

      it('does not loop between after max mode', function() {
        expect(getTitle()).toBe('September 2010');

        clickTitleButton();
        expect(getTitle()).toBe('2010');

        clickTitleButton();
        expect(getTitle()).toBe('2001 - 2020');

        clickTitleButton();
        expect(getTitle()).toBe('2001 - 2020');
      });

      describe('month selection mode', function() {
        beforeEach(function() {
          clickTitleButton();
        });

        it('shows the year as title', function() {
          expect(getTitle()).toBe('2010');
        });

        it('shows months as options', function() {
          expect(getOptions()).toEqual([
            ['January', 'February', 'March'],
            ['April', 'May', 'June'],
            ['July', 'August', 'September'],
            ['October', 'November', 'December']
          ]);
        });

        it('does not change the model', function() {
          expect($rootScope.date).toEqual(new Date('September 30, 2010 15:30:00'));
        });

        it('has `selected` only the correct month', function() {
          expectSelectedElement(8);
        });

        it('moves to the previous year when `previous` button is clicked', function() {
          clickPreviousButton();

          expect(getTitle()).toBe('2009');
          expect(getOptions()).toEqual([
            ['January', 'February', 'March'],
            ['April', 'May', 'June'],
            ['July', 'August', 'September'],
            ['October', 'November', 'December']
          ]);

          expectSelectedElement(null);
        });

        it('moves to the next year when `next` button is clicked', function() {
          clickNextButton();

          expect(getTitle()).toBe('2011');
          expect(getOptions()).toEqual([
            ['January', 'February', 'March'],
            ['April', 'May', 'June'],
            ['July', 'August', 'September'],
            ['October', 'November', 'December']
          ]);

          expectSelectedElement(null);
        });

        it('renders correctly when a month is clicked', function() {
          clickPreviousButton(5);
          expect(getTitle()).toBe('2005');

          clickOption(10);
          expect($rootScope.date).toEqual(new Date('September 30, 2010 15:30:00'));
          expect(getTitle()).toBe('November 2005');
          expect(getOptions(true)).toEqual([
            ['30', '31', '01', '02', '03', '04', '05'],
            ['06', '07', '08', '09', '10', '11', '12'],
            ['13', '14', '15', '16', '17', '18', '19'],
            ['20', '21', '22', '23', '24', '25', '26'],
            ['27', '28', '29', '30', '01', '02', '03'],
            ['04', '05', '06', '07', '08', '09', '10']
          ]);

          clickOption(17);
          expect($rootScope.date).toEqual(new Date('November 16, 2005 15:30:00'));
        });
      });

      describe('year selection mode', function() {
        beforeEach(function() {
          clickTitleButton();
          clickTitleButton();
        });

        it('shows the year range as title', function() {
          expect(getTitle()).toBe('2001 - 2020');
        });

        it('shows years as options', function() {
          expect(getOptions()).toEqual([
            ['2001', '2002', '2003', '2004', '2005'],
            ['2006', '2007', '2008', '2009', '2010'],
            ['2011', '2012', '2013', '2014', '2015'],
            ['2016', '2017', '2018', '2019', '2020']
          ]);
        });

        it('does not change the model', function() {
          expect($rootScope.date).toEqual(new Date('September 30, 2010 15:30:00'));
        });

        it('has `selected` only the selected year', function() {
          expectSelectedElement(9);
        });

        it('moves to the previous year set when `previous` button is clicked', function() {
          clickPreviousButton();

          expect(getTitle()).toBe('1981 - 2000');
          expect(getOptions()).toEqual([
            ['1981', '1982', '1983', '1984', '1985'],
            ['1986', '1987', '1988', '1989', '1990'],
            ['1991', '1992', '1993', '1994', '1995'],
            ['1996', '1997', '1998', '1999', '2000']
          ]);
          expectSelectedElement(null);
        });

        it('moves to the next year set when `next` button is clicked', function() {
          clickNextButton();

          expect(getTitle()).toBe('2021 - 2040');
          expect(getOptions()).toEqual([
            ['2021', '2022', '2023', '2024', '2025'],
            ['2026', '2027', '2028', '2029', '2030'],
            ['2031', '2032', '2033', '2034', '2035'],
            ['2036', '2037', '2038', '2039', '2040']
          ]);

          expectSelectedElement(null);
        });
      });

      describe('keyboard navigation', function() {
        function getActiveLabel() {
          return element.find('.active').eq(0).text();
        }

        describe('day mode', function() {
          it('will be able to activate previous day', function() {
            triggerKeyDown(element, 'left');
            expect(getActiveLabel()).toBe('29');
          });

          it('will be able to select with enter', function() {
            triggerKeyDown(element, 'left');
            triggerKeyDown(element, 'enter');
            expect($rootScope.date).toEqual(new Date('September 29, 2010 15:30:00'));
          });

          it('will be able to select with space', function() {
            triggerKeyDown(element, 'left');
            triggerKeyDown(element, 'space');
            expect($rootScope.date).toEqual(new Date('September 29, 2010 15:30:00'));
          });

          it('will be able to activate next day', function() {
            triggerKeyDown(element, 'right');
            expect(getActiveLabel()).toBe('01');
            expect(getTitle()).toBe('October 2010');
          });

          it('will be able to activate same day in previous week', function() {
            triggerKeyDown(element, 'up');
            expect(getActiveLabel()).toBe('23');
          });

          it('will be able to activate same day in next week', function() {
            triggerKeyDown(element, 'down');
            expect(getActiveLabel()).toBe('07');
            expect(getTitle()).toBe('October 2010');
          });

          it('will be able to activate same date in previous month', function() {
            triggerKeyDown(element, 'pageup');
            expect(getActiveLabel()).toBe('30');
            expect(getTitle()).toBe('August 2010');
          });

          it('will be able to activate same date in next month', function() {
            triggerKeyDown(element, 'pagedown');
            expect(getActiveLabel()).toBe('30');
            expect(getTitle()).toBe('October 2010');
          });

          it('will be able to activate first day of the month', function() {
            triggerKeyDown(element, 'home');
            expect(getActiveLabel()).toBe('01');
            expect(getTitle()).toBe('September 2010');
          });

          it('will be able to activate last day of the month', function() {
            $rootScope.date = new Date('September 1, 2010 15:30:00');
            $rootScope.$digest();

            triggerKeyDown(element, 'end');
            expect(getActiveLabel()).toBe('30');
            expect(getTitle()).toBe('September 2010');
          });

          it('will be able to move to month mode', function() {
            triggerKeyDown(element, 'up', true);
            expect(getActiveLabel()).toBe('September');
            expect(getTitle()).toBe('2010');
          });

          it('will not respond when trying to move to lower mode', function() {
            triggerKeyDown(element, 'down', true);
            expect(getActiveLabel()).toBe('30');
            expect(getTitle()).toBe('September 2010');
          });
        });

        describe('month mode', function() {
          beforeEach(function() {
            triggerKeyDown(element, 'up', true);
          });

          it('will be able to activate previous month', function() {
            triggerKeyDown(element, 'left');
            expect(getActiveLabel()).toBe('August');
          });

          it('will be able to activate next month', function() {
            triggerKeyDown(element, 'right');
            expect(getActiveLabel()).toBe('October');
          });

          it('will be able to activate same month in previous row', function() {
            triggerKeyDown(element, 'up');
            expect(getActiveLabel()).toBe('June');
          });

          it('will be able to activate same month in next row', function() {
            triggerKeyDown(element, 'down');
            expect(getActiveLabel()).toBe('December');
          });

          it('will be able to activate same date in previous year', function() {
            triggerKeyDown(element, 'pageup');
            expect(getActiveLabel()).toBe('September');
            expect(getTitle()).toBe('2009');
          });

          it('will be able to activate same date in next year', function() {
            triggerKeyDown(element, 'pagedown');
            expect(getActiveLabel()).toBe('September');
            expect(getTitle()).toBe('2011');
          });

          it('will be able to activate first month of the year', function() {
            triggerKeyDown(element, 'home');
            expect(getActiveLabel()).toBe('January');
            expect(getTitle()).toBe('2010');
          });

          it('will be able to activate last month of the year', function() {
            triggerKeyDown(element, 'end');
            expect(getActiveLabel()).toBe('December');
            expect(getTitle()).toBe('2010');
          });

          it('will be able to move to year mode', function() {
            triggerKeyDown(element, 'up', true);
            expect(getActiveLabel()).toBe('2010');
            expect(getTitle()).toBe('2001 - 2020');
          });

          it('will be able to move to day mode', function() {
            triggerKeyDown(element, 'down', true);
            expect(getActiveLabel()).toBe('30');
            expect(getTitle()).toBe('September 2010');
          });

          it('will move to day mode when selecting', function() {
            triggerKeyDown(element, 'left', true);
            triggerKeyDown(element, 'enter', true);
            expect(getActiveLabel()).toBe('30');
            expect(getTitle()).toBe('August 2010');
            expect($rootScope.date).toEqual(new Date('September 30, 2010 15:30:00'));
          });
        });

        describe('year mode', function() {
          beforeEach(function() {
            triggerKeyDown(element, 'up', true);
            triggerKeyDown(element, 'up', true);
          });

          it('will be able to activate previous year', function() {
            triggerKeyDown(element, 'left');
            expect(getActiveLabel()).toBe('2009');
          });

          it('will be able to activate next year', function() {
            triggerKeyDown(element, 'right');
            expect(getActiveLabel()).toBe('2011');
          });

          it('will be able to activate same year in previous row', function() {
            triggerKeyDown(element, 'up');
            expect(getActiveLabel()).toBe('2005');
          });

          it('will be able to activate same year in next row', function() {
            triggerKeyDown(element, 'down');
            expect(getActiveLabel()).toBe('2015');
          });

          it('will be able to activate same date in previous view', function() {
            triggerKeyDown(element, 'pageup');
            expect(getActiveLabel()).toBe('1990');
          });

          it('will be able to activate same date in next view', function() {
            triggerKeyDown(element, 'pagedown');
            expect(getActiveLabel()).toBe('2030');
          });

          it('will be able to activate first year of the year', function() {
            triggerKeyDown(element, 'home');
            expect(getActiveLabel()).toBe('2001');
          });

          it('will be able to activate last year of the year', function() {
            triggerKeyDown(element, 'end');
            expect(getActiveLabel()).toBe('2020');
          });

          it('will not respond when trying to move to upper mode', function() {
            triggerKeyDown(element, 'up', true);
            expect(getTitle()).toBe('2001 - 2020');
          });

          it('will be able to move to month mode', function() {
            triggerKeyDown(element, 'down', true);
            expect(getActiveLabel()).toBe('September');
            expect(getTitle()).toBe('2010');
          });

          it('will move to month mode when selecting', function() {
            triggerKeyDown(element, 'left', true);
            triggerKeyDown(element, 'enter', true);
            expect(getActiveLabel()).toBe('September');
            expect(getTitle()).toBe('2009');
            expect($rootScope.date).toEqual(new Date('September 30, 2010 15:30:00'));
          });
        });

        describe('`aria-activedescendant`', function() {
          function checkActivedescendant() {
            var activeId = element.find('table').attr('aria-activedescendant');
            expect(element.find('#' + activeId + ' > button')).toHaveClass('active');
          }

          it('updates correctly', function() {
            triggerKeyDown(element, 'left');
            checkActivedescendant();

            triggerKeyDown(element, 'down');
            checkActivedescendant();

            triggerKeyDown(element, 'up', true);
            checkActivedescendant();

            triggerKeyDown(element, 'up', true);
            checkActivedescendant();
          });
        });
      });
    });

    describe('attribute `starting-day`', function () {
      beforeEach(function() {
        $rootScope.startingDay = 1;
        element = $compile('<uib-datepicker ng-model="date" starting-day="startingDay"></uib-datepicker>')($rootScope);
        $rootScope.$digest();
      });

      it('shows the day labels rotated', function() {
        expect(getLabels(true)).toEqual(['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']);
      });

      it('renders the calendar days correctly', function() {
        expect(getOptions(true)).toEqual([
          ['30', '31', '01', '02', '03', '04', '05'],
          ['06', '07', '08', '09', '10', '11', '12'],
          ['13', '14', '15', '16', '17', '18', '19'],
          ['20', '21', '22', '23', '24', '25', '26'],
          ['27', '28', '29', '30', '01', '02', '03'],
          ['04', '05', '06', '07', '08', '09', '10']
        ]);
      });

      it('renders the week numbers correctly', function() {
        expect(getWeeks()).toEqual(['35', '36', '37', '38', '39', '40']);
      });
    });

    describe('attribute `show-weeks`', function() {
      beforeEach(function() {
        $rootScope.showWeeks = false;
        element = $compile('<uib-datepicker ng-model="date" show-weeks="showWeeks"></uib-datepicker>')($rootScope);
        $rootScope.$digest();
      });

      it('hides week numbers based on variable', function() {
        expect(getLabelsRow().find('th').length).toEqual(7);
        var tr = element.find('tbody').find('tr');
        for (var i = 0; i < 5; i++) {
          expect(tr.eq(i).find('td').length).toEqual(7);
        }
      });
    });

    describe('`min-date` attribute', function () {
      beforeEach(function() {
        $rootScope.mindate = new Date('September 12, 2010');
        element = $compile('<uib-datepicker ng-model="date" min-date="mindate"></uib-datepicker>')($rootScope);
        $rootScope.$digest();
      });

      it('disables appropriate days in current month', function() {
        var buttons = getAllOptionsEl();
        angular.forEach(buttons, function(button, index) {
          expect(angular.element(button).prop('disabled')).toBe(index < 14);
        });
      });

      it('disables appropriate days when min date changes', function() {
        $rootScope.mindate = new Date('September 5, 2010');
        $rootScope.$digest();

        var buttons = getAllOptionsEl();
        angular.forEach(buttons, function(button, index) {
          expect(angular.element(button).prop('disabled')).toBe(index < 7);
        });
      });

      it('invalidates when model is a disabled date', function() {
        $rootScope.mindate = new Date('September 5, 2010');
        $rootScope.date = new Date('September 2, 2010');
        $rootScope.$digest();
        expect(element.hasClass('ng-invalid')).toBeTruthy();
        expect(element.hasClass('ng-invalid-date-disabled')).toBeTruthy();
      });

      it('disables all days in previous month', function() {
        clickPreviousButton();
        var buttons = getAllOptionsEl();
        angular.forEach(buttons, function(button, index) {
          expect(angular.element(button).prop('disabled')).toBe(true);
        });
      });

      it('disables no days in next month', function() {
        clickNextButton();
        var buttons = getAllOptionsEl();
        angular.forEach(buttons, function(button, index) {
          expect(angular.element(button).prop('disabled')).toBe(false);
        });
      });

      it('disables appropriate months in current year', function() {
        clickTitleButton();
        var buttons = getAllOptionsEl();
        angular.forEach(buttons, function(button, index) {
          expect(angular.element(button).prop('disabled')).toBe(index < 8);
        });
      });

      it('disables all months in previous year', function() {
        clickTitleButton();
        clickPreviousButton();
        var buttons = getAllOptionsEl();
        angular.forEach(buttons, function(button, index) {
          expect(angular.element(button).prop('disabled')).toBe(true);
        });
      });

      it('disables no months in next year', function() {
        clickTitleButton();
        clickNextButton();
        var buttons = getAllOptionsEl();
        angular.forEach(buttons, function(button, index) {
          expect(angular.element(button).prop('disabled')).toBe(false);
        });
      });

      it('enables everything before if it is cleared', function() {
        $rootScope.mindate = null;
        $rootScope.date = new Date('December 20, 1949');
        $rootScope.$digest();

        clickTitleButton();
        var buttons = getAllOptionsEl();
        angular.forEach(buttons, function(button, index) {
          expect(angular.element(button).prop('disabled')).toBe(false);
        });
      });

    });

    describe('`max-date` attribute', function() {
      beforeEach(function() {
        $rootScope.maxdate = new Date('September 25, 2010');
        element = $compile('<uib-datepicker ng-model="date" max-date="maxdate"></uib-datepicker>')($rootScope);
        $rootScope.$digest();
      });

      it('disables appropriate days in current month', function() {
        var buttons = getAllOptionsEl();
        angular.forEach(buttons, function(button, index) {
          expect(angular.element(button).prop('disabled')).toBe(index > 27);
        });
      });

      it('disables appropriate days when max date changes', function() {
        $rootScope.maxdate = new Date('September 18, 2010');
        $rootScope.$digest();

        var buttons = getAllOptionsEl();
        angular.forEach(buttons, function(button, index) {
          expect(angular.element(button).prop('disabled')).toBe(index > 20);
        });
      });

      it('invalidates when model is a disabled date', function() {
        $rootScope.maxdate = new Date('September 18, 2010');
        $rootScope.$digest();
        expect(element.hasClass('ng-invalid')).toBeTruthy();
        expect(element.hasClass('ng-invalid-date-disabled')).toBeTruthy();
      });

      it('disables no days in previous month', function() {
        clickPreviousButton();
        var buttons = getAllOptionsEl();
        angular.forEach(buttons, function(button, index) {
          expect(angular.element(button).prop('disabled')).toBe(false);
        });
      });

      it('disables all days in next month', function() {
        clickNextButton();
        var buttons = getAllOptionsEl();
        angular.forEach(buttons, function(button, index) {
          expect(angular.element(button).prop('disabled')).toBe(true);
        });
      });

      it('disables appropriate months in current year', function() {
        clickTitleButton();
        var buttons = getAllOptionsEl();
        angular.forEach(buttons, function(button, index) {
          expect(angular.element(button).prop('disabled')).toBe(index > 8);
        });
      });

      it('disables no months in previous year', function() {
        clickTitleButton();
        clickPreviousButton();
        var buttons = getAllOptionsEl();
        angular.forEach(buttons, function(button, index) {
          expect(angular.element(button).prop('disabled')).toBe(false);
        });
      });

      it('disables all months in next year', function() {
        clickTitleButton();
        clickNextButton();
        var buttons = getAllOptionsEl();
        angular.forEach(buttons, function(button, index) {
          expect(angular.element(button).prop('disabled')).toBe(true);
        });
      });

      it('enables everything after if it is cleared', function() {
        $rootScope.maxdate = null;
        $rootScope.$digest();
        var buttons = getAllOptionsEl();
        angular.forEach(buttons, function(button, index) {
          expect(angular.element(button).prop('disabled')).toBe(false);
        });
      });
    });

    describe('date-disabled expression', function () {
      beforeEach(function() {
        $rootScope.dateDisabledHandler = jasmine.createSpy('dateDisabledHandler');
        element = $compile('<uib-datepicker ng-model="date" date-disabled="dateDisabledHandler(date, mode)"></uib-datepicker>')($rootScope);
        $rootScope.$digest();
      });

      it('executes the dateDisabled expression for each visible day plus one for validation', function() {
        expect($rootScope.dateDisabledHandler.calls.count()).toEqual(42 + 1);
      });

      it('executes the dateDisabled expression for each visible month plus one for validation', function() {
        $rootScope.dateDisabledHandler.calls.reset();
        clickTitleButton();
        expect($rootScope.dateDisabledHandler.calls.count()).toEqual(12 + 1);
      });

      it('executes the dateDisabled expression for each visible year plus one for validation', function() {
        clickTitleButton();
        $rootScope.dateDisabledHandler.calls.reset();
        clickTitleButton();
        expect($rootScope.dateDisabledHandler.calls.count()).toEqual(20 + 1);
      });
    });

    describe('custom-class expression', function() {
      beforeEach(function() {
        $rootScope.customClassHandler = jasmine.createSpy('customClassHandler');
        element = $compile('<uib-datepicker ng-model="date" custom-class="customClassHandler(date, mode)"></uib-datepicker>')($rootScope);
        $rootScope.$digest();
      });

      it('executes the customClass expression for each visible day plus one for validation', function() {
        expect($rootScope.customClassHandler.calls.count()).toEqual(42);
      });

      it('executes the customClass expression for each visible month plus one for validation', function() {
        $rootScope.customClassHandler.calls.reset();
        clickTitleButton();
        expect($rootScope.customClassHandler.calls.count()).toEqual(12);
      });

      it('executes the customClass expression for each visible year plus one for validation', function() {
        clickTitleButton();
        $rootScope.customClassHandler.calls.reset();
        clickTitleButton();
        expect($rootScope.customClassHandler.calls.count()).toEqual(20);
      });
    });

    describe('formatting', function() {
      beforeEach(function() {
        $rootScope.dayTitle = 'MMMM, yy';
        element = $compile('<uib-datepicker ng-model="date"' +
          'format-day="d"' +
          'format-day-header="EEEE"' +
          'format-day-title="{{dayTitle}}"' +
          'format-month="MMM"' +
          'format-month-title="yy"' +
          'format-year="yy"' +
          'year-range="10"></uib-datepicker>')($rootScope);
        $rootScope.$digest();
      });

      it('changes the title format in `day` mode', function() {
        expect(getTitle()).toBe('September, 10');
      });

      it('changes the title & months format in `month` mode', function() {
        clickTitleButton();

        expect(getTitle()).toBe('10');
        expect(getOptions()).toEqual([
          ['Jan', 'Feb', 'Mar'],
          ['Apr', 'May', 'Jun'],
          ['Jul', 'Aug', 'Sep'],
          ['Oct', 'Nov', 'Dec']
        ]);
      });

      it('changes the title, year format & range in `year` mode', function() {
        clickTitleButton();
        clickTitleButton();

        expect(getTitle()).toBe('01 - 10');
        expect(getOptions()).toEqual([
          ['01', '02', '03', '04', '05'],
          ['06', '07', '08', '09', '10']
        ]);
      });

      it('shows day labels', function() {
        expect(getLabels(true)).toEqual(['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']);
      });

      it('changes the day format', function() {
        expect(getOptions(true)).toEqual([
          ['29', '30', '31', '1', '2', '3', '4'],
          ['5', '6', '7', '8', '9', '10', '11'],
          ['12', '13', '14', '15', '16', '17', '18'],
          ['19', '20', '21', '22', '23', '24', '25'],
          ['26', '27', '28', '29', '30', '1', '2'],
          ['3', '4', '5', '6', '7', '8', '9']
        ]);
      });
    });

    describe('setting datepickerConfig', function() {
      var originalConfig = {};
      beforeEach(inject(function(uibDatepickerConfig) {
        angular.extend(originalConfig, uibDatepickerConfig);
        uibDatepickerConfig.formatDay = 'd';
        uibDatepickerConfig.formatMonth = 'MMM';
        uibDatepickerConfig.formatYear = 'yy';
        uibDatepickerConfig.formatDayHeader = 'EEEE';
        uibDatepickerConfig.formatDayTitle = 'MMM, yy';
        uibDatepickerConfig.formatMonthTitle = 'yy';
        uibDatepickerConfig.showWeeks = false;
        uibDatepickerConfig.yearRange = 10;
        uibDatepickerConfig.startingDay = 6;

        element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
        $rootScope.$digest();
      }));
      afterEach(inject(function(uibDatepickerConfig) {
        // return it to the original state
        angular.extend(uibDatepickerConfig, originalConfig);
      }));

      it('changes the title format in `day` mode', function() {
        expect(getTitle()).toBe('Sep, 10');
      });

      it('changes the title & months format in `month` mode', function() {
        clickTitleButton();

        expect(getTitle()).toBe('10');
        expect(getOptions()).toEqual([
          ['Jan', 'Feb', 'Mar'],
          ['Apr', 'May', 'Jun'],
          ['Jul', 'Aug', 'Sep'],
          ['Oct', 'Nov', 'Dec']
        ]);
      });

      it('changes the title, year format & range in `year` mode', function() {
        clickTitleButton();
        clickTitleButton();

        expect(getTitle()).toBe('01 - 10');
        expect(getOptions()).toEqual([
          ['01', '02', '03', '04', '05'],
          ['06', '07', '08', '09', '10']
        ]);
      });

      it('changes the `starting-day` & day headers & format', function() {
        expect(getLabels()).toEqual(['Saturday', 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']);
        expect(getOptions(false)).toEqual([
          ['28', '29', '30', '31', '1', '2', '3'],
          ['4', '5', '6', '7', '8', '9', '10'],
          ['11', '12', '13', '14', '15', '16', '17'],
          ['18', '19', '20', '21', '22', '23', '24'],
          ['25', '26', '27', '28', '29', '30', '1'],
          ['2', '3', '4', '5', '6', '7', '8']
        ]);
      });

      it('changes initial visibility for weeks', function() {
        expect(getLabelsRow().find('th').length).toEqual(7);
        var tr = element.find('tbody').find('tr');
        for (var i = 0; i < 5; i++) {
          expect(tr.eq(i).find('td').length).toEqual(7);
        }
      });

    });

    describe('setting datepickerPopupConfig', function() {
      var originalConfig = {};
      beforeEach(inject(function(uibDatepickerPopupConfig) {
        angular.extend(originalConfig, uibDatepickerPopupConfig);
        uibDatepickerPopupConfig.datepickerPopup = 'MM-dd-yyyy';

        element = $compile('<input ng-model="date" uib-datepicker-popup>')($rootScope);
        $rootScope.$digest();
      }));
      afterEach(inject(function(uibDatepickerPopupConfig) {
        // return it to the original state
        angular.extend(uibDatepickerPopupConfig, originalConfig);
      }));

      it('changes date format', function() {
        expect(element.val()).toEqual('09-30-2010');
      });

    });

    describe('setting datepickerPopupConfig inside ng-if', function() {
      var originalConfig = {};
      beforeEach(inject(function (uibDatepickerPopupConfig) {
        angular.extend(originalConfig, uibDatepickerPopupConfig);
        uibDatepickerPopupConfig.datepickerPopup = 'MM-dd-yyyy';

        element = $compile('<div><div ng-if="true"><input ng-model="date" uib-datepicker-popup></div></div>')($rootScope);
        $rootScope.$digest();
      }));
      afterEach(inject(function (uibDatepickerPopupConfig) {
        // return it to the original state
        angular.extend(uibDatepickerPopupConfig, originalConfig);
      }));

      it('changes date format', function () {
        expect(element.find('input').val()).toEqual('09-30-2010');
      });
    });

    describe('as popup', function () {
      var inputEl, dropdownEl, $document, $sniffer, $timeout;

      function assignElements(wrapElement) {
        inputEl = wrapElement.find('input');
        dropdownEl = wrapElement.find('ul');
        element = dropdownEl.find('table');
      }

      function changeInputValueTo(el, value) {
        el.val(value);
        el.trigger($sniffer.hasEvent('input') ? 'input' : 'change');
        $rootScope.$digest();
      }

      describe('initially', function () {
        beforeEach(inject(function(_$document_, _$sniffer_) {
          $document = _$document_;
          $sniffer = _$sniffer_;
          $rootScope.isopen = true;
          $rootScope.date = new Date('September 30, 2010 15:30:00');
          var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup><div>')($rootScope);
          $rootScope.$digest();
          assignElements(wrapElement);
        }));

        it('does not to display datepicker initially', function() {
          expect(dropdownEl.length).toBe(0);
        });

        it('to display the correct value in input', function() {
          expect(inputEl.val()).toBe('2010-09-30');
        });
      });

      describe('initially opened', function() {
        var wrapElement;

        beforeEach(inject(function(_$document_, _$sniffer_, _$timeout_) {
          $document = _$document_;
          $sniffer = _$sniffer_;
          $timeout = _$timeout_;
          $rootScope.isopen = true;
          $rootScope.date = new Date('September 30, 2010 15:30:00');
          wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup is-open="isopen"><div>')($rootScope);
          $rootScope.$digest();
          assignElements(wrapElement);
        }));

        it('datepicker is displayed', function() {
          expect(dropdownEl.length).toBe(1);
        });

        it('renders the calendar correctly', function() {
          expect(getLabelsRow().css('display')).not.toBe('none');
          expect(getLabels(true)).toEqual(['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']);
          expect(getOptions(true)).toEqual([
            ['29', '30', '31', '01', '02', '03', '04'],
            ['05', '06', '07', '08', '09', '10', '11'],
            ['12', '13', '14', '15', '16', '17', '18'],
            ['19', '20', '21', '22', '23', '24', '25'],
            ['26', '27', '28', '29', '30', '01', '02'],
            ['03', '04', '05', '06', '07', '08', '09']
          ]);
        });

        it('updates the input when a day is clicked', function() {
          clickOption(17);
          expect(inputEl.val()).toBe('2010-09-15');
          expect($rootScope.date).toEqual(new Date('September 15, 2010 15:30:00'));
        });

        it('should mark the input field dirty when a day is clicked', function() {
          expect(inputEl).toHaveClass('ng-pristine');
          clickOption(17);
          expect(inputEl).toHaveClass('ng-dirty');
        });

        it('updates the input correctly when model changes', function() {
          $rootScope.date = new Date('January 10, 1983 10:00:00');
          $rootScope.$digest();
          expect(inputEl.val()).toBe('1983-01-10');
        });

        it('closes the dropdown when a day is clicked', function() {
          expect(dropdownEl.length).toBe(1);

          clickOption(17);
          assignElements(wrapElement);
          expect(dropdownEl.length).toBe(0);
        });

        it('updates the model & calendar when input value changes', function() {
          changeInputValueTo(inputEl, '2010-09-15');

          expect($rootScope.date.getFullYear()).toEqual(2010);
          expect($rootScope.date.getMonth()).toEqual(8);
          expect($rootScope.date.getDate()).toEqual(15);

          expect(getOptions(true)).toEqual([
            ['29', '30', '31', '01', '02', '03', '04'],
            ['05', '06', '07', '08', '09', '10', '11'],
            ['12', '13', '14', '15', '16', '17', '18'],
            ['19', '20', '21', '22', '23', '24', '25'],
            ['26', '27', '28', '29', '30', '01', '02'],
            ['03', '04', '05', '06', '07', '08', '09']
          ]);
          expectSelectedElement(17);
        });

        it('closes when click outside of calendar', function() {
          expect(dropdownEl.length).toBe(1);

          $timeout.flush(0);
          $document.find('body').click();
          assignElements(wrapElement);
          expect(dropdownEl.length).toBe(0);
        });

        it('sets `ng-invalid` for invalid input', function() {
          changeInputValueTo(inputEl, 'pizza');

          expect(inputEl).toHaveClass('ng-invalid');
          expect(inputEl).toHaveClass('ng-invalid-date');
          expect($rootScope.date).toBeUndefined();
          expect(inputEl.val()).toBe('pizza');
        });

        it('unsets `ng-invalid` for valid input', function() {
          changeInputValueTo(inputEl, 'pizza');
          expect(inputEl).toHaveClass('ng-invalid-date');

          $rootScope.date = new Date('August 11, 2013');
          $rootScope.$digest();
          expect(inputEl).not.toHaveClass('ng-invalid');
          expect(inputEl).not.toHaveClass('ng-invalid-date');
        });

        describe('focus', function () {
          beforeEach(function() {
            var body = $document.find('body');
            body.append(inputEl);
            body.append(dropdownEl);
          });

          afterEach(function() {
            inputEl.remove();
            dropdownEl.remove();
          });

          it('returns to the input when ESC key is pressed in the popup and closes', function() {
            expect(dropdownEl.length).toBe(1);

            dropdownEl.find('button').eq(0).focus();
            expect(document.activeElement.tagName).toBe('BUTTON');

            triggerKeyDown(dropdownEl, 'esc');
            assignElements(wrapElement);
            expect(dropdownEl.length).toBe(0);
            expect(document.activeElement.tagName).toBe('INPUT');
          });

          it('returns to the input when ESC key is pressed in the input and closes', function() {
            expect(dropdownEl.length).toBe(1);

            dropdownEl.find('button').eq(0).focus();
            expect(document.activeElement.tagName).toBe('BUTTON');

            triggerKeyDown(inputEl, 'esc');
            $rootScope.$digest();
            assignElements(wrapElement);
            expect(dropdownEl.length).toBe(0);
            expect(document.activeElement.tagName).toBe('INPUT');
          });

          it('stops the ESC key from propagating if the dropdown is open, but not when closed', function() {
            var documentKey = -1;
            var getKey = function(evt) { documentKey = evt.which; };
            $document.bind('keydown', getKey);

            triggerKeyDown(inputEl, 'esc');
            expect(documentKey).toBe(-1);

            triggerKeyDown(inputEl, 'esc');
            expect(documentKey).toBe(27);

            $document.unbind('keydown', getKey);
          });
        });

        describe('works with ngModelOptions', function () {
          var $timeout;

          beforeEach(inject(function(_$document_, _$sniffer_, _$timeout_) {
            $document = _$document_;
            $timeout = _$timeout_;
            $rootScope.isopen = true;
            $rootScope.date = new Date('September 30, 2010 15:30:00');
            var wrapElement = $compile('<div><input ng-model="date" ' +
              'ng-model-options="{ debounce: 10000 }" ' +
              'uib-datepicker-popup is-open="isopen"><div>')($rootScope);
            $rootScope.$digest();
            assignElements(wrapElement);
          }));

          it('should change model and update calendar after debounce timeout', function() {
            changeInputValueTo(inputEl, '1980-03-05');

            expect($rootScope.date.getFullYear()).toEqual(2010);
            expect($rootScope.date.getMonth()).toEqual(9 - 1);
            expect($rootScope.date.getDate()).toEqual(30);

            expect(getOptions(true)).toEqual([
              ['29', '30', '31', '01', '02', '03', '04'],
              ['05', '06', '07', '08', '09', '10', '11'],
              ['12', '13', '14', '15', '16', '17', '18'],
              ['19', '20', '21', '22', '23', '24', '25'],
              ['26', '27', '28', '29', '30', '01', '02'],
              ['03', '04', '05', '06', '07', '08', '09']
            ]);

            // No changes yet
            $timeout.flush(2000);
            expect($rootScope.date.getFullYear()).toEqual(2010);
            expect($rootScope.date.getMonth()).toEqual(9 - 1);
            expect($rootScope.date.getDate()).toEqual(30);

            expect(getOptions(true)).toEqual([
              ['29', '30', '31', '01', '02', '03', '04'],
              ['05', '06', '07', '08', '09', '10', '11'],
              ['12', '13', '14', '15', '16', '17', '18'],
              ['19', '20', '21', '22', '23', '24', '25'],
              ['26', '27', '28', '29', '30', '01', '02'],
              ['03', '04', '05', '06', '07', '08', '09']
            ]);

            $timeout.flush(10000);
            expect($rootScope.date.getFullYear()).toEqual(1980);
            expect($rootScope.date.getMonth()).toEqual(2);
            expect($rootScope.date.getDate()).toEqual(5);

            expect(getOptions(true)).toEqual([
              ['24', '25', '26', '27', '28', '29', '01'],
              ['02', '03', '04', '05', '06', '07', '08'],
              ['09', '10', '11', '12', '13', '14', '15'],
              ['16', '17', '18', '19', '20', '21', '22'],
              ['23', '24', '25', '26', '27', '28', '29'],
              ['30', '31', '01', '02', '03', '04', '05']
            ]);
            expectSelectedElement( 10 );
          });
        });

        describe('works with HTML5 date input types', function() {
          var date2 = new Date('October 1, 2010 12:34:56.789');
          beforeEach(inject(function(_$document_) {
            $document = _$document_;
            $rootScope.isopen = true;
            $rootScope.date = new Date('September 30, 2010 15:30:00');
          }));

          it('works as date', function() {
            setupInputWithType('date');
            expect(dropdownEl.length).toBe(1);
            expect(inputEl.val()).toBe('2010-09-30');

            changeInputValueTo(inputEl, '1980-03-05');

            expect($rootScope.date.getFullYear()).toEqual(1980);
            expect($rootScope.date.getMonth()).toEqual(2);
            expect($rootScope.date.getDate()).toEqual(5);

            expect(getOptions(true)).toEqual([
              ['24', '25', '26', '27', '28', '29', '01'],
              ['02', '03', '04', '05', '06', '07', '08'],
              ['09', '10', '11', '12', '13', '14', '15'],
              ['16', '17', '18', '19', '20', '21', '22'],
              ['23', '24', '25', '26', '27', '28', '29'],
              ['30', '31', '01', '02', '03', '04', '05']
            ]);
            expect(selectedElementIndex()).toEqual(10);
          });

          it('works as datetime-local', function() {
            setupInputWithType('datetime-local');
            expect(inputEl.val()).toBe('2010-09-30T15:30:00.000');

            changeInputValueTo(inputEl, '1980-03-05T12:34:56.000');

            expect($rootScope.date.getFullYear()).toEqual(1980);
            expect($rootScope.date.getMonth()).toEqual(2);
            expect($rootScope.date.getDate()).toEqual(5);

            expect(getOptions(true)).toEqual([
              ['24', '25', '26', '27', '28', '29', '01'],
              ['02', '03', '04', '05', '06', '07', '08'],
              ['09', '10', '11', '12', '13', '14', '15'],
              ['16', '17', '18', '19', '20', '21', '22'],
              ['23', '24', '25', '26', '27', '28', '29'],
              ['30', '31', '01', '02', '03', '04', '05']
            ]);
            expect(selectedElementIndex()).toEqual(10);
          });

          it('works as month', function() {
            setupInputWithType('month');
            expect(inputEl.val()).toBe('2010-09');

            changeInputValueTo(inputEl, '1980-03');

            expect($rootScope.date.getFullYear()).toEqual(1980);
            expect($rootScope.date.getMonth()).toEqual(2);
            expect($rootScope.date.getDate()).toEqual(30);

            expect(getOptions()).toEqual([
              ['January', 'February', 'March'],
              ['April', 'May', 'June'],
              ['July', 'August', 'September'],
              ['October', 'November', 'December']
            ]);
            expect(selectedElementIndex()).toEqual(2);
          });

          function setupInputWithType(type) {
            var wrapElement = $compile('<div><input type="' +
              type + '" ng-model="date" uib-datepicker-popup is-open="isopen"><div>')($rootScope);
            $rootScope.$digest();
            assignElements(wrapElement);
          }
        });

      });

      describe('attribute `datepickerOptions`', function() {
        describe('show-weeks', function() {
          beforeEach(function() {
            $rootScope.opts = {
              'show-weeks': false
            };
            var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup datepicker-options="opts" is-open="true"></div>')($rootScope);
            $rootScope.$digest();
            assignElements(wrapElement);
          });

          it('hides week numbers based on variable', function() {
            expect(getLabelsRow().find('th').length).toEqual(7);
            var tr = element.find('tbody').find('tr');
            for (var i = 0; i < 5; i++) {
              expect(tr.eq(i).find('td').length).toEqual(7);
            }
          });
        });

        describe('init-date', function(){
          beforeEach(function() {
            $rootScope.date = null;
            $rootScope.opts = {
              'initDate': new Date('November 9, 1980')
            };
            var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup datepicker-options="opts" is-open="true"></div>')($rootScope);
            $rootScope.$digest();
            assignElements(wrapElement);
          });

          it('does not alter the model', function() {
            expect($rootScope.date).toBe(null);
          });

          it('shows the correct title', function() {
            expect(getTitle()).toBe('November 1980');
          });
        });
      });

      describe('attribute `init-date`', function() {
        beforeEach(function() {
          $rootScope.date = null;
          $rootScope.initDate = new Date('November 9, 1980');
        });

        describe('when initially set', function() {
          beforeEach(function() {
            var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup init-date="initDate" is-open="true"></div>')($rootScope);
            $rootScope.$digest();
            assignElements(wrapElement);
          });

          it('does not alter the model', function() {
            expect($rootScope.date).toBe(null);
          });

          it('shows the correct title', function() {
            expect(getTitle()).toBe('November 1980');
          });
        });

        describe('when modified before date selected.', function() {
          beforeEach(function() {
            var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup init-date="initDate" is-open="true"></div>')($rootScope);
            $rootScope.$digest();
            assignElements(wrapElement);

            $rootScope.initDate = new Date('December 20, 1981');
            $rootScope.$digest();
          });

          it('does not alter the model', function() {
            expect($rootScope.date).toBe(null);
          });

          it('shows the correct title', function() {
            expect(getTitle()).toBe('December 1981');
          });
        });

        describe('when modified after date selected.', function() {
          beforeEach(function() {
            var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup init-date="initDate" is-open="true"></div>')($rootScope);
            $rootScope.$digest();
            assignElements(wrapElement);
            $rootScope.date = new Date('April 1, 1982');
            $rootScope.initDate = new Date('December 20, 1981');
            $rootScope.$digest();
          });

          it('does not alter the model', function() {
            expect($rootScope.date).toEqual(new Date('April 1, 1982'));
          });

          it('shows the correct title', function() {
            expect(getTitle()).toBe('April 1982');
          });
        });
      });

      describe('toggles programatically by `open` attribute', function() {
        var wrapElement;

        beforeEach(inject(function() {
          $rootScope.open = true;
          wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup is-open="open"><div>')($rootScope);
          $rootScope.$digest();
          assignElements(wrapElement);
        }));

        it('to display initially', function() {
          expect(dropdownEl.length).toBe(1);
        });

        it('to close / open from scope variable', function() {
          expect(dropdownEl.length).toBe(1);
          $rootScope.open = false;
          $rootScope.$digest();
          assignElements(wrapElement);
          expect(dropdownEl.length).toBe(0);

          $rootScope.open = true;
          $rootScope.$digest();
          assignElements(wrapElement);
          expect(dropdownEl.length).toBe(1);
        });
      });

      describe('custom format', function() {
        beforeEach(inject(function() {
          var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup="dd-MMMM-yyyy" is-open="true"><div>')($rootScope);
          $rootScope.$digest();
          assignElements(wrapElement);
        }));

        it('to display the correct value in input', function() {
          expect(inputEl.val()).toBe('30-September-2010');
        });

        it('updates the input when a day is clicked', function() {
          clickOption(17);
          expect(inputEl.val()).toBe('15-September-2010');
          expect($rootScope.date).toEqual(new Date('September 15, 2010 15:30:00'));
        });

        it('updates the input correctly when model changes', function() {
          $rootScope.date = new Date('January 10, 1983 10:00:00');
          $rootScope.$digest();
          expect(inputEl.val()).toBe('10-January-1983');
        });
      });

      describe('dynamic custom format', function() {
        beforeEach(inject(function() {
          $rootScope.format = 'dd-MMMM-yyyy';
          var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup="{{format}}" is-open="true"><div>')($rootScope);
          $rootScope.$digest();
          assignElements(wrapElement);
        }));

        it('to display the correct value in input', function() {
          expect(inputEl.val()).toBe('30-September-2010');
        });

        it('updates the input when a day is clicked', function() {
          clickOption(17);
          expect(inputEl.val()).toBe('15-September-2010');
          expect($rootScope.date).toEqual(new Date('September 15, 2010 15:30:00'));
        });

        it('updates the input correctly when model changes', function() {
          $rootScope.date = new Date('August 11, 2013 09:09:00');
          $rootScope.$digest();
          expect(inputEl.val()).toBe('11-August-2013');
        });

        it('updates the input correctly when format changes', function() {
          $rootScope.format = 'dd/MM/yyyy';
          $rootScope.$digest();
          expect(inputEl.val()).toBe('30/09/2010');
        });
      });

      describe('european format', function() {
        it('dd.MM.yyyy', function() {
          var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup="dd.MM.yyyy"><div>')($rootScope);
          $rootScope.$digest();
          assignElements(wrapElement);

          changeInputValueTo(inputEl, '11.08.2013');
          expect($rootScope.date.getFullYear()).toEqual(2013);
          expect($rootScope.date.getMonth()).toEqual(7);
          expect($rootScope.date.getDate()).toEqual(11);
        });
      });

      describe('`close-on-date-selection` attribute', function() {
        var wrapElement;
        beforeEach(inject(function() {
          $rootScope.close = false;
          wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup close-on-date-selection="close" is-open="true"><div>')($rootScope);
          $rootScope.$digest();
          assignElements(wrapElement);
        }));

        it('does not close the dropdown when a day is clicked', function() {
          clickOption(17);
          assignElements(wrapElement);
          expect(dropdownEl.length).toBe(1);
        });
      });

      describe('button bar', function() {
        var buttons, buttonBarElement;

        function assignButtonBar() {
          buttonBarElement = dropdownEl.find('li').eq(-1);
          buttons = buttonBarElement.find('button');
        }

        describe('', function() {
          var wrapElement;

          beforeEach(inject(function() {
            $rootScope.isopen = true;
            wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup is-open="isopen"><div>')($rootScope);
            $rootScope.$digest();
            assignElements(wrapElement);
            assignButtonBar();
          }));

          it('should exist', function() {
            expect(dropdownEl.length).toBe(1);
            expect(dropdownEl.find('li').length).toBe(2);
          });

          it('should have three buttons', function() {
            expect(buttons.length).toBe(3);

            expect(buttons.eq(0).text()).toBe('Today');
            expect(buttons.eq(1).text()).toBe('Clear');
            expect(buttons.eq(2).text()).toBe('Done');
          });

          it('should have a button to set today date without altering time part', function() {
            var today = new Date();
            buttons.eq(0).click();
            expect($rootScope.date.getFullYear()).toBe(today.getFullYear());
            expect($rootScope.date.getMonth()).toBe(today.getMonth());
            expect($rootScope.date.getDate()).toBe(today.getDate());

            expect($rootScope.date.getHours()).toBe(15);
            expect($rootScope.date.getMinutes()).toBe(30);
            expect($rootScope.date.getSeconds()).toBe(0);
          });

          it('should have a button to set today date if blank', function() {
            $rootScope.date = null;
            $rootScope.$digest();

            var today = new Date();
            buttons.eq(0).click();
            expect($rootScope.date.getFullYear()).toBe(today.getFullYear());
            expect($rootScope.date.getMonth()).toBe(today.getMonth());
            expect($rootScope.date.getDate()).toBe(today.getDate());

            expect($rootScope.date.getHours()).toBe(0);
            expect($rootScope.date.getMinutes()).toBe(0);
            expect($rootScope.date.getSeconds()).toBe(0);
          });

          it('should have a button to clear value', function() {
            buttons.eq(1).click();
            expect($rootScope.date).toBe(null);
          });

          it('should have a button to close calendar', function() {
            buttons.eq(2).click();
            assignElements(wrapElement);
            expect(dropdownEl.length).toBe(0);
          });
        });

        describe('customization', function() {
          it('should change text from attributes', function() {
            $rootScope.clearText = 'Null it!';
            $rootScope.close = 'Close';
            var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup current-text="Now" clear-text="{{clearText}}" close-text="{{close}}ME" is-open="true"><div>')($rootScope);
            $rootScope.$digest();
            assignElements(wrapElement);
            assignButtonBar();

            expect(buttons.eq(0).text()).toBe('Now');
            expect(buttons.eq(1).text()).toBe('Null it!');
            expect(buttons.eq(2).text()).toBe('CloseME');
          });

          it('should disable today button if before min date', function() {
            $rootScope.minDate = new Date().setDate(new Date().getDate() + 1);
            var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup min-date="minDate" is-open="true"><div>')($rootScope);
            $rootScope.$digest();
            assignElements(wrapElement);
            assignButtonBar();

            expect(buttons.eq(0).prop('disabled')).toBe(true);
          });

          it('should disable today button if after max date', function() {
            $rootScope.maxDate = new Date().setDate(new Date().getDate() - 2);
            var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup max-date="maxDate" is-open="true"><div>')($rootScope);
            $rootScope.$digest();
            assignElements(wrapElement);
            assignButtonBar();

            expect(buttons.eq(0).prop('disabled')).toBe(true);
          });

          it('should remove bar', function() {
            $rootScope.showBar = false;
            var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup show-button-bar="showBar" is-open="true"><div>')($rootScope);
            $rootScope.$digest();
            assignElements(wrapElement);
            expect(dropdownEl.find('li').length).toBe(1);
          });

          it('should hide weeks column on popup', function() {
            var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup show-weeks="false" is-open="true"><div>')($rootScope);
            $rootScope.$digest();
            assignElements(wrapElement);

            expect(getLabelsRow().find('th').length).toEqual(7);
            var tr = element.find('tbody').find('tr');
            for (var i = 0; i < 5; i++) {
              expect(tr.eq(i).find('td').length).toEqual(7);
            }
          });

          it('should show weeks column on popup', function() {
            var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup show-weeks="true" is-open="true"><div>')($rootScope);
            $rootScope.$digest();
            assignElements(wrapElement);

            expect(getLabelsRow().find('th').eq(0)).not.toBeHidden();
            var tr = element.find('tbody').find('tr');
            for (var i = 0; i < 5; i++) {
              expect(tr.eq(i).find('td').eq(0)).not.toBeHidden();
            }
          });
        });

        describe('`ng-change`', function() {
          beforeEach(inject(function() {
            $rootScope.changeHandler = jasmine.createSpy('changeHandler');
            var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup ng-change="changeHandler()" is-open="true"><div>')($rootScope);
            $rootScope.$digest();
            assignElements(wrapElement);
            assignButtonBar();
          }));

          it('should be called when `today` is clicked', function() {
            buttons.eq(0).click();
            expect($rootScope.changeHandler).toHaveBeenCalled();
          });

          it('should be called when `clear` is clicked', function() {
            buttons.eq(1).click();
            expect($rootScope.changeHandler).toHaveBeenCalled();
          });

          it('should not be called when `close` is clicked', function() {
            buttons.eq(2).click();
            expect($rootScope.changeHandler).not.toHaveBeenCalled();
          });
        });
      });

      describe('use with `ng-required` directive', function() {
        describe('`ng-required is true`', function() {
          beforeEach(inject(function() {
            $rootScope.date = '';
            var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup ng-required="true"><div>')($rootScope);
            $rootScope.$digest();
            assignElements(wrapElement);
          }));

          it('should be invalid initially and when no date', function() {
            expect(inputEl.hasClass('ng-invalid')).toBeTruthy();
          });

          it('should be valid if model has been specified', function() {
            $rootScope.date = new Date();
            $rootScope.$digest();
            expect(inputEl.hasClass('ng-valid')).toBeTruthy();
          });

          it('should be valid if model value is a valid timestamp', function() {
            $rootScope.date = Date.now();
            $rootScope.$digest();
            expect(inputEl.hasClass('ng-valid')).toBeTruthy();
          });
        });
        describe('`ng-required is false`', function() {
          beforeEach(inject(function() {
            $rootScope.date = '';
            var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup ng-required="false"><div>')($rootScope);
            $rootScope.$digest();
            assignElements(wrapElement);
          }));

          it('should be valid initially and when no date', function() {
            expect(inputEl.hasClass('ng-valid')).toBeTruthy();
          });
        });
      });

      describe('use with `ng-change` directive', function() {
        beforeEach(inject(function() {
          $rootScope.changeHandler = jasmine.createSpy('changeHandler');
          $rootScope.date = new Date('09/16/2010');
          var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup ng-required="true" ng-change="changeHandler()" is-open="true"><div>')($rootScope);
          $rootScope.$digest();
          assignElements(wrapElement);
        }));

        it('should not be called initially', function() {
          expect($rootScope.changeHandler).not.toHaveBeenCalled();
        });

        it('should be called when a day is clicked', function() {
          clickOption(17);
          expect($rootScope.changeHandler).toHaveBeenCalled();
        });

        it('should not be called when model changes programatically', function() {
          $rootScope.date = new Date();
          $rootScope.$digest();
          expect($rootScope.changeHandler).not.toHaveBeenCalled();
        });
      });

      describe('with datepicker-popup-template-url', function() {
        beforeEach(function() {
          $rootScope.date = new Date();
        });

        afterEach(function () {
          $document.find('body').find('.dropdown-menu').remove();
        });

        it('should allow custom templates for the popup', function() {
          $templateCache.put('foo/bar.html', '<div>baz</div>');

          var elm = angular.element('<div><input ng-model="date" uib-datepicker-popup datepicker-popup-template-url="foo/bar.html" is-open="true"></div>');

          $compile(elm)($rootScope);
          $rootScope.$digest();

          expect(elm.children().eq(1).html()).toBe('baz');
        });
      });

      describe('with datepicker-template-url', function() {
        beforeEach(function() {
          $rootScope.date = new Date();
        });

        afterEach(function() {
          $document.find('body').find('.dropdown-menu').remove();
        });

        it('should allow custom templates for the datepicker', function() {
          $templateCache.put('foo/bar.html', '<div>baz</div>');

          var elm = angular.element('<div><input ng-model="date" uib-datepicker-popup datepicker-template-url="foo/bar.html" is-open="true"></div>');

          $compile(elm)($rootScope);
          $rootScope.$digest();

          var datepicker = elm.find('[uib-datepicker]');

          expect(datepicker.html()).toBe('baz');
        });
      });

      describe('with an append-to-body attribute', function() {
        beforeEach(function() {
          $rootScope.date = new Date();
        });

        afterEach(function() {
          $document.find('body').children().remove();
        });

        it('should append to the body', function() {
          var $body = $document.find('body'),
              bodyLength = $body.children().length,
              elm = angular.element(
                '<div><input uib-datepicker-popup ng-model="date" datepicker-append-to-body="true" is-open="true" /></div>'
              );
          $compile(elm)($rootScope);
          $rootScope.$digest();

          expect($body.children().length).toEqual(bodyLength + 1);
          expect(elm.children().length).toEqual(1);
        });
        it('should be removed on scope destroy', function() {
          var $body = $document.find('body'),
              bodyLength = $body.children().length,
              isolatedScope = $rootScope.$new(),
              elm = angular.element(
                '<input uib-datepicker-popup ng-model="date" datepicker-append-to-body="true" is-open="true" />'
              );
          $compile(elm)(isolatedScope);
          isolatedScope.$digest();
          expect($body.children().length).toEqual(bodyLength + 1);
          isolatedScope.$destroy();
          expect($body.children().length).toEqual(bodyLength);
        });
      });

      describe('with setting datepickerConfig.showWeeks to false', function() {
        var originalConfig = {};
        beforeEach(inject(function(uibDatepickerConfig) {
          angular.extend(originalConfig, uibDatepickerConfig);
          uibDatepickerConfig.showWeeks = false;

          var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup is-open="true"><div>')($rootScope);
          $rootScope.$digest();
          assignElements(wrapElement);
        }));
        afterEach(inject(function(uibDatepickerConfig) {
          // return it to the original state
          angular.extend(uibDatepickerConfig, originalConfig);
        }));

        it('changes initial visibility for weeks', function() {
          expect(getLabelsRow().find('th').length).toEqual(7);
          var tr = element.find('tbody').find('tr');
          for (var i = 0; i < 5; i++) {
            expect(tr.eq(i).find('td').length).toEqual(7);
          }
        });
      });

      describe('`datepicker-mode`', function() {
        beforeEach(inject(function() {
          $rootScope.date = new Date('August 11, 2013');
          $rootScope.mode = 'month';
          var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup datepicker-mode="mode" is-open="true"></div>')($rootScope);
          $rootScope.$digest();
          assignElements(wrapElement);
        }));

        it('shows the correct title', function() {
          expect(getTitle()).toBe('2013');
        });

        it('updates binding', function() {
          clickTitleButton();
          expect($rootScope.mode).toBe('year');
        });
      });

      describe('attribute `initDate`', function() {
        var weekHeader, weekElement;
        beforeEach(function() {
          $rootScope.date = null;
          $rootScope.initDate = new Date('November 9, 1980');
          var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup init-date="initDate" is-open="true"></div>')($rootScope);
          $rootScope.$digest();
          assignElements(wrapElement);
        });

        it('should not alter the model', function() {
          expect($rootScope.date).toBe(null);
        });

        it('shows the correct title', function() {
          expect(getTitle()).toBe('November 1980');
        });
      });

      describe('attribute `onOpenFocus`', function() {
        beforeEach(function() {
          $rootScope.date = null;
          $rootScope.isopen = false;
          var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup on-open-focus="false" is-open="isopen"></div>')($rootScope);
          $rootScope.$digest();
          assignElements(wrapElement);
        });

        it('should remain focused on the input', function() {
          var focused = true;
          expect(dropdownEl.length).toBe(0);

          inputEl[0].focus();
          inputEl.on('blur', function() {
            focused = false;
          });
          $rootScope.isopen = true;
          $rootScope.$digest();

          expect(inputEl.parent().find('.dropdown-menu').length).toBe(1);
          expect(focused).toBe(true);
        });
      });
    });

    describe('with empty initial state', function() {
      beforeEach(inject(function() {
        $rootScope.date = null;
        element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
        $rootScope.$digest();
      }));

      it('is has a `<table>` element', function() {
        expect(element.find('table').length).toBe(1);
      });

      it('is shows rows with days', function() {
        expect(element.find('tbody').find('tr').length).toBeGreaterThan(3);
      });

      it('sets default 00:00:00 time for selected date', function() {
        $rootScope.date = new Date('August 1, 2013');
        $rootScope.$digest();
        $rootScope.date = null;
        $rootScope.$digest();

        clickOption(14);
        expect($rootScope.date).toEqual(new Date('August 11, 2013 00:00:00'));
      });
    });

    describe('`init-date`', function() {
      beforeEach(inject(function() {
        $rootScope.date = null;
        $rootScope.initDate = new Date('November 9, 1980');
        element = $compile('<uib-datepicker ng-model="date" init-date="initDate"></uib-datepicker>')($rootScope);
        $rootScope.$digest();
      }));

      it('does not alter the model', function() {
        expect($rootScope.date).toBe(null);
      });

      it('shows the correct title', function() {
        expect(getTitle()).toBe('November 1980');
      });
    });

    describe('`datepicker-mode`', function() {
      beforeEach(inject(function() {
        $rootScope.date = new Date('August 11, 2013');
        $rootScope.mode = 'month';
        element = $compile('<uib-datepicker ng-model="date" datepicker-mode="mode"></uib-datepicker>')($rootScope);
        $rootScope.$digest();
      }));

      it('shows the correct title', function() {
        expect(getTitle()).toBe('2013');
      });

      it('updates binding', function() {
        clickTitleButton();
        expect($rootScope.mode).toBe('year');
      });
    });

    describe('`min-mode`', function() {
      beforeEach(inject(function() {
        $rootScope.date = new Date('August 11, 2013');
        $rootScope.mode = 'month';
        $rootScope.minMode = 'month';
        element = $compile('<uib-datepicker ng-model="date" min-mode="minMode" datepicker-mode="mode"></uib-datepicker>')($rootScope);
        $rootScope.$digest();
      }));

      it('does not move below it', function() {
        expect(getTitle()).toBe('2013');
        clickOption( 5 );
        expect(getTitle()).toBe('2013');
        clickTitleButton();
        expect(getTitle()).toBe('2001 - 2020');
        $rootScope.minMode = 'year';
        $rootScope.$digest();
        clickOption( 5 );
        expect(getTitle()).toBe('2001 - 2020');
      });

      it('updates current mode if necessary', function() {
        expect(getTitle()).toBe('2013');
        $rootScope.minMode = 'year';
        $rootScope.$digest();
        expect(getTitle()).toBe('2001 - 2020');
      });
    });

    describe('`max-mode`', function() {
      beforeEach(inject(function() {
        $rootScope.date = new Date('August 11, 2013');
        $rootScope.maxMode = 'month';
        element = $compile('<uib-datepicker ng-model="date" max-mode="maxMode"></uib-datepicker>')($rootScope);
        $rootScope.$digest();
      }));

      it('does not move above it', function() {
        expect(getTitle()).toBe('August 2013');
        clickTitleButton();
        expect(getTitle()).toBe('2013');
        clickTitleButton();
        expect(getTitle()).toBe('2013');
        clickOption( 10 );
        expect(getTitle()).toBe('November 2013');
        $rootScope.maxMode = 'day';
        $rootScope.$digest();
        clickTitleButton();
        expect(getTitle()).toBe('November 2013');
      });

      it('disables the title button at it', function() {
        expect(getTitleButton().prop('disabled')).toBe(false);
        clickTitleButton();
        expect(getTitleButton().prop('disabled')).toBe(true);
        clickTitleButton();
        expect(getTitleButton().prop('disabled')).toBe(true);
        clickOption( 10 );
        expect(getTitleButton().prop('disabled')).toBe(false);
        $rootScope.maxMode = 'day';
        $rootScope.$digest();
        expect(getTitleButton().prop('disabled')).toBe(true);
      });

      it('updates current mode if necessary', function() {
        expect(getTitle()).toBe('August 2013');
        clickTitleButton();
        expect(getTitle()).toBe('2013');
        $rootScope.maxMode = 'day';
        $rootScope.$digest();
        expect(getTitle()).toBe('August 2013');
      });
    });

    describe('with an ngModelController having formatters and parsers', function() {
      beforeEach(inject(function() {
        // Custom date object.
        $rootScope.date = { type: 'date', date: 'April 1, 2015 00:00:00' };

        // Use dateModel directive to add formatters and parsers to the
        // ngModelController that translate the custom date object.
        element = $compile('<uib-datepicker ng-model="date" date-model></uib-datepicker>')($rootScope);
        $rootScope.$digest();
      }));

      it('updates the view', function() {
        $rootScope.date = { type: 'date', date: 'April 15, 2015 00:00:00' };
        $rootScope.$digest();

        expectSelectedElement(17);
      });

      it('updates the model', function() {
        clickOption(17);

        expect($rootScope.date.type).toEqual('date');
        expect(new Date($rootScope.date.date)).toEqual(new Date('April 15, 2015 00:00:00'));
      });
    });

    describe('thursdays determine week count', function() {
      beforeEach(inject(function() {
        $rootScope.date = new Date('June 07, 2014');
      }));

      it('with the default starting day (sunday)', function() {
        element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
        $rootScope.$digest();

        expect(getWeeks()).toEqual(['23', '24', '25', '26', '27', '28']);
      });

      describe('when starting date', function() {
        it('is monday', function() {
          element = $compile('<uib-datepicker ng-model="date" starting-day="1"></uib-datepicker>')($rootScope);
          $rootScope.$digest();

          expect(getWeeks()).toEqual(['22', '23', '24', '25', '26', '27']);
        });

        it('is thursday', function() {
          element = $compile('<uib-datepicker ng-model="date" starting-day="4"></uib-datepicker>')($rootScope);
          $rootScope.$digest();

          expect(getWeeks()).toEqual(['22', '23', '24', '25', '26', '27']);
        });

        it('is saturday', function() {
          element = $compile('<uib-datepicker ng-model="date" starting-day="6"></uib-datepicker>')($rootScope);
          $rootScope.$digest();

          expect(getWeeks()).toEqual(['23', '24', '25', '26', '27', '28']);
        });
      });

      describe('first week in january', function() {
        it('in current year', function() {
          $rootScope.date = new Date('January 07, 2014');
          element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
          $rootScope.$digest();

          expect(getWeeks()).toEqual(['1', '2', '3', '4', '5', '6']);
        });

        it('in last year', function() {
          $rootScope.date = new Date('January 07, 2010');
          element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
          $rootScope.$digest();

          expect(getWeeks()).toEqual(['53', '1', '2', '3', '4', '5']);
        });
      });

      describe('last week(s) in december', function() {
        beforeEach(inject(function() {
         $rootScope.date = new Date('December 07, 2014');
        }));

        it('in next year', function() {
           element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
          $rootScope.$digest();

          expect(getWeeks()).toEqual(['49', '50', '51', '52', '1', '2']);
        });
      });
    });
  });
});

/* deprecation tests below */

describe('datepicker deprecation', function() {
  beforeEach(module('ui.bootstrap.datepicker'));
  beforeEach(module('template/datepicker/datepicker.html'));
  beforeEach(module('template/datepicker/day.html'));
  beforeEach(module('template/datepicker/month.html'));
  beforeEach(module('template/datepicker/year.html'));
  beforeEach(module('template/datepicker/popup.html'));

  it('should suppress warning', function() {
    module(function($provide) {
      $provide.value('$datepickerSuppressWarning', true);
    });

    inject(function($compile, $log, $rootScope) {
      spyOn($log, 'warn');

      var element = $compile('<datepicker ng-model="date"></datepicker>')($rootScope);
      $rootScope.$digest();
      expect($log.warn.calls.count()).toBe(0);
    });
  });

  it('should give warning by default', inject(function($log) {
    spyOn($log, 'warn');

    inject(function($compile, $templateCache, $rootScope) {
      var datepickerTemplate =
        '<div ng-switch="datepickerMode" role="application" ng-keydown="keydown($event)">' +
        '<daypicker ng-switch-when="day" tabindex="0"></daypicker>' +
        '<monthpicker ng-switch-when="month" tabindex="0"></monthpicker>' +
        '<yearpicker ng-switch-when="year" tabindex="0"></yearpicker> ' +
        '</div>';
      $templateCache.put('template/datepicker/datepicker.html', datepickerTemplate);

      var element = $compile('<datepicker ng-model="date"></datepicker>')($rootScope);
      $rootScope.$digest();

      expect($log.warn.calls.count()).toBe(3);
      expect($log.warn.calls.argsFor(0)).toEqual(['DatepickerController is now deprecated. Use UibDatepickerController instead.']);
      expect($log.warn.calls.argsFor(1)).toEqual(['datepicker is now deprecated. Use uib-datepicker instead.']);
      expect($log.warn.calls.argsFor(2)).toEqual(['daypicker is now deprecated. Use uib-daypicker instead.']);

      $log.warn.calls.reset();

      element = $compile('<datepicker datepicker-mode="\'month\'" ng-model="date"></datepicker>')($rootScope);
      $rootScope.$digest();

      expect($log.warn.calls.count()).toBe(3);
      expect($log.warn.calls.argsFor(0)).toEqual(['DatepickerController is now deprecated. Use UibDatepickerController instead.']);
      expect($log.warn.calls.argsFor(1)).toEqual(['datepicker is now deprecated. Use uib-datepicker instead.']);
      expect($log.warn.calls.argsFor(2)).toEqual(['monthpicker is now deprecated. Use uib-monthpicker instead.']);

      $log.warn.calls.reset();

      element = $compile('<datepicker datepicker-mode="\'year\'" ng-model="date"></datepicker>')($rootScope);
      $rootScope.$digest();

      expect($log.warn.calls.count()).toBe(3);
      expect($log.warn.calls.argsFor(0)).toEqual(['DatepickerController is now deprecated. Use UibDatepickerController instead.']);
      expect($log.warn.calls.argsFor(1)).toEqual(['datepicker is now deprecated. Use uib-datepicker instead.']);
      expect($log.warn.calls.argsFor(2)).toEqual(['yearpicker is now deprecated. Use uib-yearpicker instead.']);

      $log.warn.calls.reset();

      element = $compile('<input type="date" datepicker-popup ng-model="dt" />')($rootScope);
      $rootScope.$digest();

      expect($log.warn.calls.count()).toBe(1);
      expect($log.warn.calls.argsFor(0)).toEqual(['datepicker-popup is now deprecated. Use uib-datepicker-popup instead.']);
    });
  }));
});
