text: ;(function (window, $, undefined) { ;(function () {
      var VERSION = '2.2.3',
          pluginName = 'datepicker',
          autoInitSelector = '.datepicker-here',
          $body, $datepickersContainer,
          containerBuilt = false,
          baseTemplate = '' +
              '<div class="datepicker">' +
              '<i class="datepicker--pointer"></i>' +
              '<nav class="datepicker--nav"></nav>' +
              '<div class="datepicker--content"></div>' +
              '</div>',
          defaults = {
              classes: '',
              inline: false,
              language: 'ru',
              startDate: new Date(),
              firstDay: '',
              weekends: [6, 0],
              dateFormat: '',
              altField: '',
              altFieldDateFormat: '@',
              toggleSelected: true,
              keyboardNav: true,
  
              position: 'bottom left',
              offset: 12,
  
              view: 'days',
              minView: 'days',
  
              showOtherMonths: true,
              selectOtherMonths: true,
              moveToOtherMonthsOnSelect: true,
  
              showOtherYears: true,
              selectOtherYears: true,
              moveToOtherYearsOnSelect: true,
  
              minDate: '',
              maxDate: '',
              disableNavWhenOutOfRange: true,
  
              multipleDates: false, // Boolean or Number
              multipleDatesSeparator: ',',
              range: false,
  
              todayButton: false,
              clearButton: false,
  
              showEvent: 'focus',
              autoClose: false,
  
              // navigation
              monthsField: 'monthsShort',
              prevHtml: '<svg><path d="M 17,12 l -5,5 l 5,5"></path></svg>',
              nextHtml: '<svg><path d="M 14,12 l 5,5 l -5,5"></path></svg>',
              navTitles: {
                  days: 'MM, <i>yyyy</i>',
                  months: 'yyyy',
                  years: 'yyyy1 - yyyy2'
              },
  
              // timepicker
              timepicker: false,
              onlyTimepicker: false,
              dateTimeSeparator: ' ',
              timeFormat: '',
              minHours: 0,
              maxHours: 24,
              minMinutes: 0,
              maxMinutes: 59,
              hoursStep: 1,
              minutesStep: 1,
  
              // events
              onSelect: '',
              onShow: '',
              onHide: '',
              onChangeMonth: '',
              onChangeYear: '',
              onChangeDecade: '',
              onChangeView: '',
              onRenderCell: ''
          },
          hotKeys = {
              'ctrlRight': [17, 39],
              'ctrlUp': [17, 38],
              'ctrlLeft': [17, 37],
              'ctrlDown': [17, 40],
              'shiftRight': [16, 39],
              'shiftUp': [16, 38],
              'shiftLeft': [16, 37],
              'shiftDown': [16, 40],
              'altUp': [18, 38],
              'altRight': [18, 39],
              'altLeft': [18, 37],
              'altDown': [18, 40],
              'ctrlShiftUp': [16, 17, 38]
          },
          datepicker;
  
      var Datepicker  = function (el, options) {
          this.el = el;
          this.$el = $(el);
  
          this.opts = $.extend(true, {}, defaults, options, this.$el.data());
  
          if ($body == undefined) {
              $body = $('body');
          }
  
          if (!this.opts.startDate) {
              this.opts.startDate = new Date();
          }
  
          if (this.el.nodeName == 'INPUT') {
              this.elIsInput = true;
          }
  
          if (this.opts.altField) {
              this.$altField = typeof this.opts.altField == 'string' ? $(this.opts.altField) : this.opts.altField;
          }
  
          this.inited = false;
          this.visible = false;
          this.silent = false; // Need to prevent unnecessary rendering
  
          this.currentDate = this.opts.startDate;
          this.currentView = this.opts.view;
          this._createShortCuts();
          this.selectedDates = [];
          this.views = {};
          this.keys = [];
          this.minRange = '';
          this.maxRange = '';
          this._prevOnSelectValue = '';
  
          this.init()
      };
  
      datepicker = Datepicker;
  
      datepicker.prototype = {
          VERSION: VERSION,
          viewIndexes: ['days', 'months', 'years'],
  
          init: function () {
              if (!containerBuilt && !this.opts.inline && this.elIsInput) {
                  this._buildDatepickersContainer();
              }
              this._buildBaseHtml();
              this._defineLocale(this.opts.language);
              this._syncWithMinMaxDates();
  
              if (this.elIsInput) {
                  if (!this.opts.inline) {
                      // Set extra classes for proper transitions
                      this._setPositionClasses(this.opts.position);
                      this._bindEvents()
                  }
                  if (this.opts.keyboardNav && !this.opts.onlyTimepicker) {
                      this._bindKeyboardEvents();
                  }
                  this.$datepicker.on('mousedown', this._onMouseDownDatepicker.bind(this));
                  this.$datepicker.on('mouseup', this._onMouseUpDatepicker.bind(this));
              }
  
              if (this.opts.classes) {
                  this.$datepicker.addClass(this.opts.classes)
              }
  
              if (this.opts.timepicker) {
                  this.timepicker = new $.fn.datepicker.Timepicker(this, this.opts);
                  this._bindTimepickerEvents();
              }
  
              if (this.opts.onlyTimepicker) {
                  this.$datepicker.addClass('-only-timepicker-');
              }
  
              this.views[this.currentView] = new $.fn.datepicker.Body(this, this.currentView, this.opts);
              this.views[this.currentView].show();
              this.nav = new $.fn.datepicker.Navigation(this, this.opts);
              this.view = this.currentView;
  
              this.$el.on('clickCell.adp', this._onClickCell.bind(this));
              this.$datepicker.on('mouseenter', '.datepicker--cell', this._onMouseEnterCell.bind(this));
              this.$datepicker.on('mouseleave', '.datepicker--cell', this._onMouseLeaveCell.bind(this));
  
              this.inited = true;
          },
  
          _createShortCuts: function () {
              this.minDate = this.opts.minDate ? this.opts.minDate : new Date(-8639999913600000);
              this.maxDate = this.opts.maxDate ? this.opts.maxDate : new Date(8639999913600000);
          },
  
          _bindEvents : function () {
              this.$el.on(this.opts.showEvent + '.adp', this._onShowEvent.bind(this));
              this.$el.on('mouseup.adp', this._onMouseUpEl.bind(this));
              this.$el.on('blur.adp', this._onBlur.bind(this));
              this.$el.on('keyup.adp', this._onKeyUpGeneral.bind(this));
              $(window).on('resize.adp', this._onResize.bind(this));
              $('body').on('mouseup.adp', this._onMouseUpBody.bind(this));
          },
  
          _bindKeyboardEvents: function () {
              this.$el.on('keydown.adp', this._onKeyDown.bind(this));
              this.$el.on('keyup.adp', this._onKeyUp.bind(this));
              this.$el.on('hotKey.adp', this._onHotKey.bind(this));
          },
  
          _bindTimepickerEvents: function () {
              this.$el.on('timeChange.adp', this._onTimeChange.bind(this));
          },
  
          isWeekend: function (day) {
              return this.opts.weekends.indexOf(day) !== -1;
          },
  
          _defineLocale: function (lang) {
              if (typeof lang == 'string') {
                  this.loc = $.fn.datepicker.language[lang];
                  if (!this.loc) {
                      console.warn('Can\'t find language "' + lang + '" in Datepicker.language, will use "ru" instead');
                      this.loc = $.extend(true, {}, $.fn.datepicker.language.ru)
                  }
  
                  this.loc = $.extend(true, {}, $.fn.datepicker.language.ru, $.fn.datepicker.language[lang])
              } else {
                  this.loc = $.extend(true, {}, $.fn.datepicker.language.ru, lang)
              }
  
              if (this.opts.dateFormat) {
                  this.loc.dateFormat = this.opts.dateFormat
              }
  
              if (this.opts.timeFormat) {
                  this.loc.timeFormat = this.opts.timeFormat
              }
  
              if (this.opts.firstDay !== '') {
                  this.loc.firstDay = this.opts.firstDay
              }
  
              if (this.opts.timepicker) {
                  this.loc.dateFormat = [this.loc.dateFormat, this.loc.timeFormat].join(this.opts.dateTimeSeparator);
              }
  
              if (this.opts.onlyTimepicker) {
                  this.loc.dateFormat = this.loc.timeFormat;
              }
  
              var boundary = this._getWordBoundaryRegExp;
              if (this.loc.timeFormat.match(boundary('aa')) ||
                  this.loc.timeFormat.match(boundary('AA'))
              ) {
                 this.ampm = true;
              }
          },
  
          _buildDatepickersContainer: function () {
              containerBuilt = true;
              $body.append('<div class="datepickers-container" id="datepickers-container"></div>');
              $datepickersContainer = $('#datepickers-container');
          },
  
          _buildBaseHtml: function () {
              var $appendTarget,
                  $inline = $('<div class="datepicker-inline">');
  
              if(this.el.nodeName == 'INPUT') {
                  if (!this.opts.inline) {
                      $appendTarget = $datepickersContainer;
                  } else {
                      $appendTarget = $inline.insertAfter(this.$el)
                  }
              } else {
                  $appendTarget = $inline.appendTo(this.$el)
              }
  
              this.$datepicker = $(baseTemplate).appendTo($appendTarget);
              this.$content = $('.datepicker--content', this.$datepicker);
              this.$nav = $('.datepicker--nav', this.$datepicker);
          },
  
          _triggerOnChange: function () {
              if (!this.selectedDates.length) {
                  // Prevent from triggering multiple onSelect callback with same argument (empty string) in IE10-11
                  if (this._prevOnSelectValue === '') return;
                  this._prevOnSelectValue = '';
                  return this.opts.onSelect('', '', this);
              }
  
              var selectedDates = this.selectedDates,
                  parsedSelected = datepicker.getParsedDate(selectedDates[0]),
                  formattedDates,
                  _this = this,
                  dates = new Date(
                      parsedSelected.year,
                      parsedSelected.month,
                      parsedSelected.date,
                      parsedSelected.hours,
                      parsedSelected.minutes
                  );
  
                  formattedDates = selectedDates.map(function (date) {
                      return _this.formatDate(_this.loc.dateFormat, date)
                  }).join(this.opts.multipleDatesSeparator);
  
              // Create new dates array, to separate it from original selectedDates
              if (this.opts.multipleDates || this.opts.range) {
                  dates = selectedDates.map(function(date) {
                      var parsedDate = datepicker.getParsedDate(date);
                      return new Date(
                          parsedDate.year,
                          parsedDate.month,
                          parsedDate.date,
                          parsedDate.hours,
                          parsedDate.minutes
                      );
                  })
              }
  
              this._prevOnSelectValue = formattedDates;
              this.opts.onSelect(formattedDates, dates, this);
          },
  
          next: function () {
              var d = this.parsedDate,
                  o = this.opts;
              switch (this.view) {
                  case 'days':
                      this.date = new Date(d.year, d.month + 1, 1);
                      if (o.onChangeMonth) o.onChangeMonth(this.parsedDate.month, this.parsedDate.year);
                      break;
                  case 'months':
                      this.date = new Date(d.year + 1, d.month, 1);
                      if (o.onChangeYear) o.onChangeYear(this.parsedDate.year);
                      break;
                  case 'years':
                      this.date = new Date(d.year + 10, 0, 1);
                      if (o.onChangeDecade) o.onChangeDecade(this.curDecade);
                      break;
              }
          },
  
          prev: function () {
              var d = this.parsedDate,
                  o = this.opts;
              switch (this.view) {
                  case 'days':
                      this.date = new Date(d.year, d.month - 1, 1);
                      if (o.onChangeMonth) o.onChangeMonth(this.parsedDate.month, this.parsedDate.year);
                      break;
                  case 'months':
                      this.date = new Date(d.year - 1, d.month, 1);
                      if (o.onChangeYear) o.onChangeYear(this.parsedDate.year);
                      break;
                  case 'years':
                      this.date = new Date(d.year - 10, 0, 1);
                      if (o.onChangeDecade) o.onChangeDecade(this.curDecade);
                      break;
              }
          },
  
          formatDate: function (string, date) {
              date = date || this.date;
              var result = string,
                  boundary = this._getWordBoundaryRegExp,
                  locale = this.loc,
                  leadingZero = datepicker.getLeadingZeroNum,
                  decade = datepicker.getDecade(date),
                  d = datepicker.getParsedDate(date),
                  fullHours = d.fullHours,
                  hours = d.hours,
                  ampm = string.match(boundary('aa')) || string.match(boundary('AA')),
                  dayPeriod = 'am',
                  replacer = this._replacer,
                  validHours;
  
              if (this.opts.timepicker && this.timepicker && ampm) {
                  validHours = this.timepicker._getValidHoursFromDate(date, ampm);
                  fullHours = leadingZero(validHours.hours);
                  hours = validHours.hours;
                  dayPeriod = validHours.dayPeriod;
              }
  
              switch (true) {
                  case /@/.test(result):
                      result = result.replace(/@/, date.getTime());
                  case /aa/.test(result):
                      result = replacer(result, boundary('aa'), dayPeriod);
                  case /AA/.test(result):
                      result = replacer(result, boundary('AA'), dayPeriod.toUpperCase());
                  case /dd/.test(result):
                      result = replacer(result, boundary('dd'), d.fullDate);
                  case /d/.test(result):
                      result = replacer(result, boundary('d'), d.date);
                  case /DD/.test(result):
                      result = replacer(result, boundary('DD'), locale.days[d.day]);
                  case /D/.test(result):
                      result = replacer(result, boundary('D'), locale.daysShort[d.day]);
                  case /mm/.test(result):
                      result = replacer(result, boundary('mm'), d.fullMonth);
                  case /m/.test(result):
                      result = replacer(result, boundary('m'), d.month + 1);
                  case /MM/.test(result):
                      result = replacer(result, boundary('MM'), this.loc.months[d.month]);
                  case /M/.test(result):
                      result = replacer(result, boundary('M'), locale.monthsShort[d.month]);
                  case /ii/.test(result):
                      result = replacer(result, boundary('ii'), d.fullMinutes);
                  case /i/.test(result):
                      result = replacer(result, boundary('i'), d.minutes);
                  case /hh/.test(result):
                      result = replacer(result, boundary('hh'), fullHours);
                  case /h/.test(result):
                      result = replacer(result, boundary('h'), hours);
                  case /yyyy/.test(result):
                      result = replacer(result, boundary('yyyy'), d.year);
                  case /yyyy1/.test(result):
                      result = replacer(result, boundary('yyyy1'), decade[0]);
                  case /yyyy2/.test(result):
                      result = replacer(result, boundary('yyyy2'), decade[1]);
                  case /yy/.test(result):
                      result = replacer(result, boundary('yy'), d.year.toString().slice(-2));
              }
  
              return result;
          },
  
          _replacer: function (str, reg, data) {
              return str.replace(reg, function (match, p1,p2,p3) {
                  return p1 + data + p3;
              })
          },
  
          _getWordBoundaryRegExp: function (sign) {
              var symbols = '\\s|\\.|-|/|\\\\|,|\\$|\\!|\\?|:|;';
  
              return new RegExp('(^|>|' + symbols + ')(' + sign + ')($|<|' + symbols + ')', 'g');
          },
  
  
          selectDate: function (date) {
              var _this = this,
                  opts = _this.opts,
                  d = _this.parsedDate,
                  selectedDates = _this.selectedDates,
                  len = selectedDates.length,
                  newDate = '';
  
              if (Array.isArray(date)) {
                  date.forEach(function (d) {
                      _this.selectDate(d)
                  });
                  return;
              }
  
              if (!(date instanceof Date)) return;
  
              this.lastSelectedDate = date;
  
              // Set new time values from Date
              if (this.timepicker) {
                  this.timepicker._setTime(date);
              }
  
              // On this step timepicker will set valid values in it's instance
              _this._trigger('selectDate', date);
  
              // Set correct time values after timepicker's validation
              // Prevent from setting hours or minutes which values are lesser then `min` value or
              // greater then `max` value
              if (this.timepicker) {
                  date.setHours(this.timepicker.hours);
                  date.setMinutes(this.timepicker.minutes)
              }
  
              if (_this.view == 'days') {
                  if (date.getMonth() != d.month && opts.moveToOtherMonthsOnSelect) {
                      newDate = new Date(date.getFullYear(), date.getMonth(), 1);
                  }
              }
  
              if (_this.view == 'years') {
                  if (date.getFullYear() != d.year && opts.moveToOtherYearsOnSelect) {
                      newDate = new Date(date.getFullYear(), 0, 1);
                  }
              }
  
              if (newDate) {
                  _this.silent = true;
                  _this.date = newDate;
                  _this.silent = false;
                  _this.nav._render()
              }
  
              if (opts.multipleDates && !opts.range) { // Set priority to range functionality
                  if (len === opts.multipleDates) return;
                  if (!_this._isSelected(date)) {
                      _this.selectedDates.push(date);
                  }
              } else if (opts.range) {
                  if (len == 2) {
                      _this.selectedDates = [date];
                      _this.minRange = date;
                      _this.maxRange = '';
                  } else if (len == 1) {
                      _this.selectedDates.push(date);
                      if (!_this.maxRange){
                          _this.maxRange = date;
                      } else {
                          _this.minRange = date;
                      }
                      // Swap dates if they were selected via dp.selectDate() and second date was smaller then first
                      if (datepicker.bigger(_this.maxRange, _this.minRange)) {
                          _this.maxRange = _this.minRange;
                          _this.minRange = date;
                      }
                      _this.selectedDates = [_this.minRange, _this.maxRange]
  
                  } else {
                      _this.selectedDates = [date];
                      _this.minRange = date;
                  }
              } else {
                  _this.selectedDates = [date];
              }
  
              _this._setInputValue();
  
              if (opts.onSelect) {
                  _this._triggerOnChange();
              }
  
              if (opts.autoClose && !this.timepickerIsActive) {
                  if (!opts.multipleDates && !opts.range) {
                      _this.hide();
                  } else if (opts.range && _this.selectedDates.length == 2) {
                      _this.hide();
                  }
              }
  
              _this.views[this.currentView]._render()
          },
  
          removeDate: function (date) {
              var selected = this.selectedDates,
                  _this = this;
  
              if (!(date instanceof Date)) return;
  
              return selected.some(function (curDate, i) {
                  if (datepicker.isSame(curDate, date)) {
                      selected.splice(i, 1);
  
                      if (!_this.selectedDates.length) {
                          _this.minRange = '';
                          _this.maxRange = '';
                          _this.lastSelectedDate = '';
                      } else {
                          _this.lastSelectedDate = _this.selectedDates[_this.selectedDates.length - 1];
                      }
  
                      _this.views[_this.currentView]._render();
                      _this._setInputValue();
  
                      if (_this.opts.onSelect) {
                          _this._triggerOnChange();
                      }
  
                      return true
                  }
              })
          },
  
          today: function () {
              this.silent = true;
              this.view = this.opts.minView;
              this.silent = false;
              this.date = new Date();
  
              if (this.opts.todayButton instanceof Date) {
                  this.selectDate(this.opts.todayButton)
              }
          },
  
          clear: function () {
              this.selectedDates = [];
              this.minRange = '';
              this.maxRange = '';
              this.views[this.currentView]._render();
              this._setInputValue();
              if (this.opts.onSelect) {
                  this._triggerOnChange()
              }
          },
  
          /**
           * Updates datepicker options
           * @param {String|Object} param - parameter's name to update. If object then it will extend current options
           * @param {String|Number|Object} [value] - new param value
           */
          update: function (param, value) {
              var len = arguments.length,
                  lastSelectedDate = this.lastSelectedDate;
  
              if (len == 2) {
                  this.opts[param] = value;
              } else if (len == 1 && typeof param == 'object') {
                  this.opts = $.extend(true, this.opts, param)
              }
  
              this._createShortCuts();
              this._syncWithMinMaxDates();
              this._defineLocale(this.opts.language);
              this.nav._addButtonsIfNeed();
              if (!this.opts.onlyTimepicker) this.nav._render();
              this.views[this.currentView]._render();
  
              if (this.elIsInput && !this.opts.inline) {
                  this._setPositionClasses(this.opts.position);
                  if (this.visible) {
                      this.setPosition(this.opts.position)
                  }
              }
  
              if (this.opts.classes) {
                  this.$datepicker.addClass(this.opts.classes)
              }
  
              if (this.opts.onlyTimepicker) {
                  this.$datepicker.addClass('-only-timepicker-');
              }
  
              if (this.opts.timepicker) {
                  if (lastSelectedDate) this.timepicker._handleDate(lastSelectedDate);
                  this.timepicker._updateRanges();
                  this.timepicker._updateCurrentTime();
                  // Change hours and minutes if it's values have been changed through min/max hours/minutes
                  if (lastSelectedDate) {
                      lastSelectedDate.setHours(this.timepicker.hours);
                      lastSelectedDate.setMinutes(this.timepicker.minutes);
                  }
              }
  
              this._setInputValue();
  
              return this;
          },
  
          _syncWithMinMaxDates: function () {
              var curTime = this.date.getTime();
              this.silent = true;
              if (this.minTime > curTime) {
                  this.date = this.minDate;
              }
  
              if (this.maxTime < curTime) {
                  this.date = this.maxDate;
              }
              this.silent = false;
          },
  
          _isSelected: function (checkDate, cellType) {
              var res = false;
              this.selectedDates.some(function (date) {
                  if (datepicker.isSame(date, checkDate, cellType)) {
                      res = date;
                      return true;
                  }
              });
              return res;
          },
  
          _setInputValue: function () {
              var _this = this,
                  opts = _this.opts,
                  format = _this.loc.dateFormat,
                  altFormat = opts.altFieldDateFormat,
                  value = _this.selectedDates.map(function (date) {
                      return _this.formatDate(format, date)
                  }),
                  altValues;
  
              if (opts.altField && _this.$altField.length) {
                  altValues = this.selectedDates.map(function (date) {
                      return _this.formatDate(altFormat, date)
                  });
                  altValues = altValues.join(this.opts.multipleDatesSeparator);
                  this.$altField.val(altValues);
              }
  
              value = value.join(this.opts.multipleDatesSeparator);
  
              this.$el.val(value)
          },
  
          /**
           * Check if date is between minDate and maxDate
           * @param date {object} - date object
           * @param type {string} - cell type
           * @returns {boolean}
           * @private
           */
          _isInRange: function (date, type) {
              var time = date.getTime(),
                  d = datepicker.getParsedDate(date),
                  min = datepicker.getParsedDate(this.minDate),
                  max = datepicker.getParsedDate(this.maxDate),
                  dMinTime = new Date(d.year, d.month, min.date).getTime(),
                  dMaxTime = new Date(d.year, d.month, max.date).getTime(),
                  types = {
                      day: time >= this.minTime && time <= this.maxTime,
                      month: dMinTime >= this.minTime && dMaxTime <= this.maxTime,
                      year: d.year >= min.year && d.year <= max.year
                  };
              return type ? types[type] : types.day
          },
  
          _getDimensions: function ($el) {
              var offset = $el.offset();
  
              return {
                  width: $el.outerWidth(),
                  height: $el.outerHeight(),
                  left: offset.left,
                  top: offset.top
              }
          },
  
          _getDateFromCell: function (cell) {
              var curDate = this.parsedDate,
                  year = cell.data('year') || curDate.year,
                  month = cell.data('month') == undefined ? curDate.month : cell.data('month'),
                  date = cell.data('date') || 1;
  
              return new Date(year, month, date);
          },
  
          _setPositionClasses: function (pos) {
              pos = pos.split(' ');
              var main = pos[0],
                  sec = pos[1],
                  classes = 'datepicker -' + main + '-' + sec + '- -from-' + main + '-';
  
              if (this.visible) classes += ' active';
  
              this.$datepicker
                  .removeAttr('class')
                  .addClass(classes);
          },
  
          setPosition: function (position) {
              position = position || this.opts.position;
  
              var dims = this._getDimensions(this.$el),
                  selfDims = this._getDimensions(this.$datepicker),
                  pos = position.split(' '),
                  top, left,
                  offset = this.opts.offset,
                  main = pos[0],
                  secondary = pos[1];
  
              switch (main) {
                  case 'top':
                      top = dims.top - selfDims.height - offset;
                      break;
                  case 'right':
                      left = dims.left + dims.width + offset;
                      break;
                  case 'bottom':
                      top = dims.top + dims.height + offset;
                      break;
                  case 'left':
                      left = dims.left - selfDims.width - offset;
                      break;
              }
  
              switch(secondary) {
                  case 'top':
                      top = dims.top;
                      break;
                  case 'right':
                      left = dims.left + dims.width - selfDims.width;
                      break;
                  case 'bottom':
                      top = dims.top + dims.height - selfDims.height;
                      break;
                  case 'left':
                      left = dims.left;
                      break;
                  case 'center':
                      if (/left|right/.test(main)) {
                          top = dims.top + dims.height/2 - selfDims.height/2;
                      } else {
                          left = dims.left + dims.width/2 - selfDims.width/2;
                      }
              }
  
              this.$datepicker
                  .css({
                      left: left,
                      top: top
                  })
          },
  
          show: function () {
              var onShow = this.opts.onShow;
  
              this.setPosition(this.opts.position);
              this.$datepicker.addClass('active');
              this.visible = true;
  
              if (onShow) {
                  this._bindVisionEvents(onShow)
              }
          },
  
          hide: function () {
              var onHide = this.opts.onHide;
  
              this.$datepicker
                  .removeClass('active')
                  .css({
                      left: '-100000px'
                  });
  
              this.focused = '';
              this.keys = [];
  
              this.inFocus = false;
              this.visible = false;
              this.$el.blur();
  
              if (onHide) {
                  this._bindVisionEvents(onHide)
              }
          },
  
          down: function (date) {
              this._changeView(date, 'down');
          },
  
          up: function (date) {
              this._changeView(date, 'up');
          },
  
          _bindVisionEvents: function (event) {
              this.$datepicker.off('transitionend.dp');
              event(this, false);
              this.$datepicker.one('transitionend.dp', event.bind(this, this, true))
          },
  
          _changeView: function (date, dir) {
              date = date || this.focused || this.date;
  
              var nextView = dir == 'up' ? this.viewIndex + 1 : this.viewIndex - 1;
              if (nextView > 2) nextView = 2;
              if (nextView < 0) nextView = 0;
  
              this.silent = true;
              this.date = new Date(date.getFullYear(), date.getMonth(), 1);
              this.silent = false;
              this.view = this.viewIndexes[nextView];
  
          },
  
          _handleHotKey: function (key) {
              var date = datepicker.getParsedDate(this._getFocusedDate()),
                  focusedParsed,
                  o = this.opts,
                  newDate,
                  totalDaysInNextMonth,
                  monthChanged = false,
                  yearChanged = false,
                  decadeChanged = false,
                  y = date.year,
                  m = date.month,
                  d = date.date;
  
              switch (key) {
                  case 'ctrlRight':
                  case 'ctrlUp':
                      m += 1;
                      monthChanged = true;
                      break;
                  case 'ctrlLeft':
                  case 'ctrlDown':
                      m -= 1;
                      monthChanged = true;
                      break;
                  case 'shiftRight':
                  case 'shiftUp':
                      yearChanged = true;
                      y += 1;
                      break;
                  case 'shiftLeft':
                  case 'shiftDown':
                      yearChanged = true;
                      y -= 1;
                      break;
                  case 'altRight':
                  case 'altUp':
                      decadeChanged = true;
                      y += 10;
                      break;
                  case 'altLeft':
                  case 'altDown':
                      decadeChanged = true;
                      y -= 10;
                      break;
                  case 'ctrlShiftUp':
                      this.up();
                      break;
              }
  
              totalDaysInNextMonth = datepicker.getDaysCount(new Date(y,m));
              newDate = new Date(y,m,d);
  
              // If next month has less days than current, set date to total days in that month
              if (totalDaysInNextMonth < d) d = totalDaysInNextMonth;
  
              // Check if newDate is in valid range
              if (newDate.getTime() < this.minTime) {
                  newDate = this.minDate;
              } else if (newDate.getTime() > this.maxTime) {
                  newDate = this.maxDate;
              }
  
              this.focused = newDate;
  
              focusedParsed = datepicker.getParsedDate(newDate);
              if (monthChanged && o.onChangeMonth) {
                  o.onChangeMonth(focusedParsed.month, focusedParsed.year)
              }
              if (yearChanged && o.onChangeYear) {
                  o.onChangeYear(focusedParsed.year)
              }
              if (decadeChanged && o.onChangeDecade) {
                  o.onChangeDecade(this.curDecade)
              }
          },
  
          _registerKey: function (key) {
              var exists = this.keys.some(function (curKey) {
                  return curKey == key;
              });
  
              if (!exists) {
                  this.keys.push(key)
              }
          },
  
          _unRegisterKey: function (key) {
              var index = this.keys.indexOf(key);
  
              this.keys.splice(index, 1);
          },
  
          _isHotKeyPressed: function () {
              var currentHotKey,
                  found = false,
                  _this = this,
                  pressedKeys = this.keys.sort();
  
              for (var hotKey in hotKeys) {
                  currentHotKey = hotKeys[hotKey];
                  if (pressedKeys.length != currentHotKey.length) continue;
  
                  if (currentHotKey.every(function (key, i) { return key == pressedKeys[i]})) {
                      _this._trigger('hotKey', hotKey);
                      found = true;
                  }
              }
  
              return found;
          },
  
          _trigger: function (event, args) {
              this.$el.trigger(event, args)
          },
  
          _focusNextCell: function (keyCode, type) {
              type = type || this.cellType;
  
              var date = datepicker.getParsedDate(this._getFocusedDate()),
                  y = date.year,
                  m = date.month,
                  d = date.date;
  
              if (this._isHotKeyPressed()){
                  return;
              }
  
              switch(keyCode) {
                  case 37: // left
                      type == 'day' ? (d -= 1) : '';
                      type == 'month' ? (m -= 1) : '';
                      type == 'year' ? (y -= 1) : '';
                      break;
                  case 38: // up
                      type == 'day' ? (d -= 7) : '';
                      type == 'month' ? (m -= 3) : '';
                      type == 'year' ? (y -= 4) : '';
                      break;
                  case 39: // right
                      type == 'day' ? (d += 1) : '';
                      type == 'month' ? (m += 1) : '';
                      type == 'year' ? (y += 1) : '';
                      break;
                  case 40: // down
                      type == 'day' ? (d += 7) : '';
                      type == 'month' ? (m += 3) : '';
                      type == 'year' ? (y += 4) : '';
                      break;
              }
  
              var nd = new Date(y,m,d);
              if (nd.getTime() < this.minTime) {
                  nd = this.minDate;
              } else if (nd.getTime() > this.maxTime) {
                  nd = this.maxDate;
              }
  
              this.focused = nd;
  
          },
  
          _getFocusedDate: function () {
              var focused  = this.focused || this.selectedDates[this.selectedDates.length - 1],
                  d = this.parsedDate;
  
              if (!focused) {
                  switch (this.view) {
                      case 'days':
                          focused = new Date(d.year, d.month, new Date().getDate());
                          break;
                      case 'months':
                          focused = new Date(d.year, d.month, 1);
                          break;
                      case 'years':
                          focused = new Date(d.year, 0, 1);
                          break;
                  }
              }
  
              return focused;
          },
  
          _getCell: function (date, type) {
              type = type || this.cellType;
  
              var d = datepicker.getParsedDate(date),
                  selector = '.datepicker--cell[data-year="' + d.year + '"]',
                  $cell;
  
              switch (type) {
                  case 'month':
                      selector = '[data-month="' + d.month + '"]';
                      break;
                  case 'day':
                      selector += '[data-month="' + d.month + '"][data-date="' + d.date + '"]';
                      break;
              }
              $cell = this.views[this.currentView].$el.find(selector);
  
              return $cell.length ? $cell : $('');
          },
  
          destroy: function () {
              var _this = this;
              _this.$el
                  .off('.adp')
                  .data('datepicker', '');
  
              _this.selectedDates = [];
              _this.focused = '';
              _this.views = {};
              _this.keys = [];
              _this.minRange = '';
              _this.maxRange = '';
  
              if (_this.opts.inline || !_this.elIsInput) {
                  _this.$datepicker.closest('.datepicker-inline').remove();
              } else {
                  _this.$datepicker.remove();
              }
          },
  
          _handleAlreadySelectedDates: function (alreadySelected, selectedDate) {
              if (this.opts.range) {
                  if (!this.opts.toggleSelected) {
                      // Add possibility to select same date when range is true
                      if (this.selectedDates.length != 2) {
                          this._trigger('clickCell', selectedDate);
                      }
                  } else {
                      this.removeDate(selectedDate);
                  }
              } else if (this.opts.toggleSelected){
                  this.removeDate(selectedDate);
              }
  
              // Change last selected date to be able to change time when clicking on this cell
              if (!this.opts.toggleSelected) {
                  this.lastSelectedDate = alreadySelected;
                  if (this.opts.timepicker) {
                      this.timepicker._setTime(alreadySelected);
                      this.timepicker.update();
                  }
              }
          },
  
          _onShowEvent: function (e) {
              if (!this.visible) {
                  this.show();
              }
          },
  
          _onBlur: function () {
              if (!this.inFocus && this.visible) {
                  this.hide();
              }
          },
  
          _onMouseDownDatepicker: function (e) {
              this.inFocus = true;
          },
  
          _onMouseUpDatepicker: function (e) {
              this.inFocus = false;
              e.originalEvent.inFocus = true;
              if (!e.originalEvent.timepickerFocus) this.$el.focus();
          },
  
          _onKeyUpGeneral: function (e) {
              var val = this.$el.val();
  
              if (!val) {
                  this.clear();
              }
          },
  
          _onResize: function () {
              if (this.visible) {
                  this.setPosition();
              }
          },
  
          _onMouseUpBody: function (e) {
              if (e.originalEvent.inFocus) return;
  
              if (this.visible && !this.inFocus) {
                  this.hide();
              }
          },
  
          _onMouseUpEl: function (e) {
              e.originalEvent.inFocus = true;
              setTimeout(this._onKeyUpGeneral.bind(this),4);
          },
  
          _onKeyDown: function (e) {
              var code = e.which;
              this._registerKey(code);
  
              // Arrows
              if (code >= 37 && code <= 40) {
                  e.preventDefault();
                  this._focusNextCell(code);
              }
  
              // Enter
              if (code == 13) {
                  if (this.focused) {
                      if (this._getCell(this.focused).hasClass('-disabled-')) return;
                      if (this.view != this.opts.minView) {
                          this.down()
                      } else {
                          var alreadySelected = this._isSelected(this.focused, this.cellType);
  
                          if (!alreadySelected) {
                              if (this.timepicker) {
                                  this.focused.setHours(this.timepicker.hours);
                                  this.focused.setMinutes(this.timepicker.minutes);
                              }
                              this.selectDate(this.focused);
                              return;
                          }
                          this._handleAlreadySelectedDates(alreadySelected, this.focused)
                      }
                  }
              }
  
              // Esc
              if (code == 27) {
                  this.hide();
              }
          },
  
          _onKeyUp: function (e) {
              var code = e.which;
              this._unRegisterKey(code);
          },
  
          _onHotKey: function (e, hotKey) {
              this._handleHotKey(hotKey);
          },
  
          _onMouseEnterCell: function (e) {
              var $cell = $(e.target).closest('.datepicker--cell'),
                  date = this._getDateFromCell($cell);
  
              // Prevent from unnecessary rendering and setting new currentDate
              this.silent = true;
  
              if (this.focused) {
                  this.focused = ''
              }
  
              $cell.addClass('-focus-');
  
              this.focused = date;
              this.silent = false;
  
              if (this.opts.range && this.selectedDates.length == 1) {
                  this.minRange = this.selectedDates[0];
                  this.maxRange = '';
                  if (datepicker.less(this.minRange, this.focused)) {
                      this.maxRange = this.minRange;
                      this.minRange = '';
                  }
                  this.views[this.currentView]._update();
              }
          },
  
          _onMouseLeaveCell: function (e) {
              var $cell = $(e.target).closest('.datepicker--cell');
  
              $cell.removeClass('-focus-');
  
              this.silent = true;
              this.focused = '';
              this.silent = false;
          },
  
          _onTimeChange: function (e, h, m) {
              var date = new Date(),
                  selectedDates = this.selectedDates,
                  selected = false;
  
              if (selectedDates.length) {
                  selected = true;
                  date = this.lastSelectedDate;
              }
  
              date.setHours(h);
              date.setMinutes(m);
  
              if (!selected && !this._getCell(date).hasClass('-disabled-')) {
                  this.selectDate(date);
              } else {
                  this._setInputValue();
                  if (this.opts.onSelect) {
                      this._triggerOnChange();
                  }
              }
          },
  
          _onClickCell: function (e, date) {
              if (this.timepicker) {
                  date.setHours(this.timepicker.hours);
                  date.setMinutes(this.timepicker.minutes);
              }
              this.selectDate(date);
          },
  
          set focused(val) {
              if (!val && this.focused) {
                  var $cell = this._getCell(this.focused);
  
                  if ($cell.length) {
                      $cell.removeClass('-focus-')
                  }
              }
              this._focused = val;
              if (this.opts.range && this.selectedDates.length == 1) {
                  this.minRange = this.selectedDates[0];
                  this.maxRange = '';
                  if (datepicker.less(this.minRange, this._focused)) {
                      this.maxRange = this.minRange;
                      this.minRange = '';
                  }
              }
              if (this.silent) return;
              this.date = val;
          },
  
          get focused() {
              return this._focused;
          },
  
          get parsedDate() {
              return datepicker.getParsedDate(this.date);
          },
  
          set date (val) {
              if (!(val instanceof Date)) return;
  
              this.currentDate = val;
  
              if (this.inited && !this.silent) {
                  this.views[this.view]._render();
                  this.nav._render();
                  if (this.visible && this.elIsInput) {
                      this.setPosition();
                  }
              }
              return val;
          },
  
          get date () {
              return this.currentDate
          },
  
          set view (val) {
              this.viewIndex = this.viewIndexes.indexOf(val);
  
              if (this.viewIndex < 0) {
                  return;
              }
  
              this.prevView = this.currentView;
              this.currentView = val;
  
              if (this.inited) {
                  if (!this.views[val]) {
                      this.views[val] = new  $.fn.datepicker.Body(this, val, this.opts)
                  } else {
                      this.views[val]._render();
                  }
  
                  this.views[this.prevView].hide();
                  this.views[val].show();
                  this.nav._render();
  
                  if (this.opts.onChangeView) {
                      this.opts.onChangeView(val)
                  }
                  if (this.elIsInput && this.visible) this.setPosition();
              }
  
              return val
          },
  
          get view() {
              return this.currentView;
          },
  
          get cellType() {
              return this.view.substring(0, this.view.length - 1)
          },
  
          get minTime() {
              var min = datepicker.getParsedDate(this.minDate);
              return new Date(min.year, min.month, min.date).getTime()
          },
  
          get maxTime() {
              var max = datepicker.getParsedDate(this.maxDate);
              return new Date(max.year, max.month, max.date).getTime()
          },
  
          get curDecade() {
              return datepicker.getDecade(this.date)
          }
      };
  
      //  Utils
      // -------------------------------------------------
  
      datepicker.getDaysCount = function (date) {
          return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
      };
  
      datepicker.getParsedDate = function (date) {
          return {
              year: date.getFullYear(),
              month: date.getMonth(),
              fullMonth: (date.getMonth() + 1) < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1, // One based
              date: date.getDate(),
              fullDate: date.getDate() < 10 ? '0' + date.getDate() : date.getDate(),
              day: date.getDay(),
              hours: date.getHours(),
              fullHours:  date.getHours() < 10 ? '0' + date.getHours() :  date.getHours() ,
              minutes: date.getMinutes(),
              fullMinutes:  date.getMinutes() < 10 ? '0' + date.getMinutes() :  date.getMinutes()
          }
      };
  
      datepicker.getDecade = function (date) {
          var firstYear = Math.floor(date.getFullYear() / 10) * 10;
  
          return [firstYear, firstYear + 9];
      };
  
      datepicker.template = function (str, data) {
          return str.replace(/#\{([\w]+)\}/g, function (source, match) {
              if (data[match] || data[match] === 0) {
                  return data[match]
              }
          });
      };
  
      datepicker.isSame = function (date1, date2, type) {
          if (!date1 || !date2) return false;
          var d1 = datepicker.getParsedDate(date1),
              d2 = datepicker.getParsedDate(date2),
              _type = type ? type : 'day',
  
              conditions = {
                  day: d1.date == d2.date && d1.month == d2.month && d1.year == d2.year,
                  month: d1.month == d2.month && d1.year == d2.year,
                  year: d1.year == d2.year
              };
  
          return conditions[_type];
      };
  
      datepicker.less = function (dateCompareTo, date, type) {
          if (!dateCompareTo || !date) return false;
          return date.getTime() < dateCompareTo.getTime();
      };
  
      datepicker.bigger = function (dateCompareTo, date, type) {
          if (!dateCompareTo || !date) return false;
          return date.getTime() > dateCompareTo.getTime();
      };
  
      datepicker.getLeadingZeroNum = function (num) {
          return parseInt(num) < 10 ? '0' + num : num;
      };
  
      /**
       * Returns copy of date with hours and minutes equals to 0
       * @param date {Date}
       */
      datepicker.resetTime = function (date) {
          if (typeof date != 'object') return;
          date = datepicker.getParsedDate(date);
          return new Date(date.year, date.month, date.date)
      };
  
      $.fn.datepicker = function ( options ) {
          return this.each(function () {
              if (!$.data(this, pluginName)) {
                  $.data(this,  pluginName,
                      new Datepicker( this, options ));
              } else {
                  var _this = $.data(this, pluginName);
  
                  _this.opts = $.extend(true, _this.opts, options);
                  _this.update();
              }
          });
      };
  
      $.fn.datepicker.Constructor = Datepicker;
  
      $.fn.datepicker.language = {
          ru: {
                days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
      daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
      daysMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
      months: ['January','February','March','April','May','June', 'July','August','September','October','November','December'],
      monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
      today: 'Today',
      clear: 'Clear',
      dateFormat: 'mm/dd/yyyy',
      timeFormat: 'hh:ii aa',
      firstDay: 0
          }
      };
  
      $(function () {
          $(autoInitSelector).datepicker();
      })
  
  })();
  
  ;(function () {
      var templates = {
          days:'' +
          '<div class="datepicker--days datepicker--body">' +
          '<div class="datepicker--days-names"></div>' +
          '<div class="datepicker--cells datepicker--cells-days"></div>' +
          '</div>',
          months: '' +
          '<div class="datepicker--months datepicker--body">' +
          '<div class="datepicker--cells datepicker--cells-months"></div>' +
          '</div>',
          years: '' +
          '<div class="datepicker--years datepicker--body">' +
          '<div class="datepicker--cells datepicker--cells-years"></div>' +
          '</div>'
          },
          datepicker = $.fn.datepicker,
          dp = datepicker.Constructor;
  
      datepicker.Body = function (d, type, opts) {
          this.d = d;
          this.type = type;
          this.opts = opts;
          this.$el = $('');
  
          if (this.opts.onlyTimepicker) return;
          this.init();
      };
  
      datepicker.Body.prototype = {
          init: function () {
              this._buildBaseHtml();
              this._render();
  
              this._bindEvents();
          },
  
          _bindEvents: function () {
              this.$el.on('click', '.datepicker--cell', $.proxy(this._onClickCell, this));
          },
  
          _buildBaseHtml: function () {
              this.$el = $(templates[this.type]).appendTo(this.d.$content);
              this.$names = $('.datepicker--days-names', this.$el);
              this.$cells = $('.datepicker--cells', this.$el);
          },
  
          _getDayNamesHtml: function (firstDay, curDay, html, i) {
              curDay = curDay != undefined ? curDay : firstDay;
              html = html ? html : '';
              i = i != undefined ? i : 0;
  
              if (i > 7) return html;
              if (curDay == 7) return this._getDayNamesHtml(firstDay, 0, html, ++i);
  
              html += '<div class="datepicker--day-name' + (this.d.isWeekend(curDay) ? " -weekend-" : "") + '">' + this.d.loc.daysMin[curDay] + '</div>';
  
              return this._getDayNamesHtml(firstDay, ++curDay, html, ++i);
          },
  
          _getCellContents: function (date, type) {
              var classes = "datepicker--cell datepicker--cell-" + type,
                  currentDate = new Date(),
                  parent = this.d,
                  minRange = dp.resetTime(parent.minRange),
                  maxRange = dp.resetTime(parent.maxRange),
                  opts = parent.opts,
                  d = dp.getParsedDate(date),
                  render = {},
                  html = d.date;
  
              switch (type) {
                  case 'day':
                      if (parent.isWeekend(d.day)) classes += " -weekend-";
                      if (d.month != this.d.parsedDate.month) {
                          classes += " -other-month-";
                          if (!opts.selectOtherMonths) {
                              classes += " -disabled-";
                          }
                          if (!opts.showOtherMonths) html = '';
                      }
                      break;
                  case 'month':
                      html = parent.loc[parent.opts.monthsField][d.month];
                      break;
                  case 'year':
                      var decade = parent.curDecade;
                      html = d.year;
                      if (d.year < decade[0] || d.year > decade[1]) {
                          classes += ' -other-decade-';
                          if (!opts.selectOtherYears) {
                              classes += " -disabled-";
                          }
                          if (!opts.showOtherYears) html = '';
                      }
                      break;
              }
  
              if (opts.onRenderCell) {
                  render = opts.onRenderCell(date, type) || {};
                  html = render.html ? render.html : html;
                  classes += render.classes ? ' ' + render.classes : '';
              }
  
              if (opts.range) {
                  if (dp.isSame(minRange, date, type)) classes += ' -range-from-';
                  if (dp.isSame(maxRange, date, type)) classes += ' -range-to-';
  
                  if (parent.selectedDates.length == 1 && parent.focused) {
                      if (
                          (dp.bigger(minRange, date) && dp.less(parent.focused, date)) ||
                          (dp.less(maxRange, date) && dp.bigger(parent.focused, date)))
                      {
                          classes += ' -in-range-'
                      }
  
                      if (dp.less(maxRange, date) && dp.isSame(parent.focused, date)) {
                          classes += ' -range-from-'
                      }
                      if (dp.bigger(minRange, date) && dp.isSame(parent.focused, date)) {
                          classes += ' -range-to-'
                      }
  
                  } else if (parent.selectedDates.length == 2) {
                      if (dp.bigger(minRange, date) && dp.less(maxRange, date)) {
                          classes += ' -in-range-'
                      }
                  }
              }
  
  
              if (dp.isSame(currentDate, date, type)) classes += ' -current-';
              if (parent.focused && dp.isSame(date, parent.focused, type)) classes += ' -focus-';
              if (parent._isSelected(date, type)) classes += ' -selected-';
              if (!parent._isInRange(date, type) || render.disabled) classes += ' -disabled-';
  
              return {
                  html: html,
                  classes: classes
              }
          },
  
          /**
           * Calculates days number to render. Generates days html and returns it.
           * @param {object} date - Date object
           * @returns {string}
           * @private
           */
          _getDaysHtml: function (date) {
              var totalMonthDays = dp.getDaysCount(date),
                  firstMonthDay = new Date(date.getFullYear(), date.getMonth(), 1).getDay(),
                  lastMonthDay = new Date(date.getFullYear(), date.getMonth(), totalMonthDays).getDay(),
                  daysFromPevMonth = firstMonthDay - this.d.loc.firstDay,
                  daysFromNextMonth = 6 - lastMonthDay + this.d.loc.firstDay;
  
              daysFromPevMonth = daysFromPevMonth < 0 ? daysFromPevMonth + 7 : daysFromPevMonth;
              daysFromNextMonth = daysFromNextMonth > 6 ? daysFromNextMonth - 7 : daysFromNextMonth;
  
              var startDayIndex = -daysFromPevMonth + 1,
                  m, y,
                  html = '';
  
              for (var i = startDayIndex, max = totalMonthDays + daysFromNextMonth; i <= max; i++) {
                  y = date.getFullYear();
                  m = date.getMonth();
  
                  html += this._getDayHtml(new Date(y, m, i))
              }
  
              return html;
          },
  
          _getDayHtml: function (date) {
             var content = this._getCellContents(date, 'day');
  
              return '<div class="' + content.classes + '" ' +
                  'data-date="' + date.getDate() + '" ' +
                  'data-month="' + date.getMonth() + '" ' +
                  'data-year="' + date.getFullYear() + '">' + content.html + '</div>';
          },
  
          /**
           * Generates months html
           * @param {object} date - date instance
           * @returns {string}
           * @private
           */
          _getMonthsHtml: function (date) {
              var html = '',
                  d = dp.getParsedDate(date),
                  i = 0;
  
              while(i < 12) {
                  html += this._getMonthHtml(new Date(d.year, i));
                  i++
              }
  
              return html;
          },
  
          _getMonthHtml: function (date) {
              var content = this._getCellContents(date, 'month');
  
              return '<div class="' + content.classes + '" data-month="' + date.getMonth() + '">' + content.html + '</div>'
          },
  
          _getYearsHtml: function (date) {
              var d = dp.getParsedDate(date),
                  decade = dp.getDecade(date),
                  firstYear = decade[0] - 1,
                  html = '',
                  i = firstYear;
  
              for (i; i <= decade[1] + 1; i++) {
                  html += this._getYearHtml(new Date(i , 0));
              }
  
              return html;
          },
  
          _getYearHtml: function (date) {
              var content = this._getCellContents(date, 'year');
  
              return '<div class="' + content.classes + '" data-year="' + date.getFullYear() + '">' + content.html + '</div>'
          },
  
          _renderTypes: {
              days: function () {
                  var dayNames = this._getDayNamesHtml(this.d.loc.firstDay),
                      days = this._getDaysHtml(this.d.currentDate);
  
                  this.$cells.html(days);
                  this.$names.html(dayNames)
              },
              months: function () {
                  var html = this._getMonthsHtml(this.d.currentDate);
  
                  this.$cells.html(html)
              },
              years: function () {
                  var html = this._getYearsHtml(this.d.currentDate);
  
                  this.$cells.html(html)
              }
          },
  
          _render: function () {
              if (this.opts.onlyTimepicker) return;
              this._renderTypes[this.type].bind(this)();
          },
  
          _update: function () {
              var $cells = $('.datepicker--cell', this.$cells),
                  _this = this,
                  classes,
                  $cell,
                  date;
              $cells.each(function (cell, i) {
                  $cell = $(this);
                  date = _this.d._getDateFromCell($(this));
                  classes = _this._getCellContents(date, _this.d.cellType);
                  $cell.attr('class',classes.classes)
              });
          },
  
          show: function () {
              if (this.opts.onlyTimepicker) return;
              this.$el.addClass('active');
              this.acitve = true;
          },
  
          hide: function () {
              this.$el.removeClass('active');
              this.active = false;
          },
  
          //  Events
          // -------------------------------------------------
  
          _handleClick: function (el) {
              var date = el.data('date') || 1,
                  month = el.data('month') || 0,
                  year = el.data('year') || this.d.parsedDate.year,
                  dp = this.d;
              // Change view if min view does not reach yet
              if (dp.view != this.opts.minView) {
                  dp.down(new Date(year, month, date));
                  return;
              }
              // Select date if min view is reached
              var selectedDate = new Date(year, month, date),
                  alreadySelected = this.d._isSelected(selectedDate, this.d.cellType);
  
              if (!alreadySelected) {
                  dp._trigger('clickCell', selectedDate);
                  return;
              }
  
              dp._handleAlreadySelectedDates.bind(dp, alreadySelected, selectedDate)();
  
          },
  
          _onClickCell: function (e) {
              var $el = $(e.target).closest('.datepicker--cell');
  
              if ($el.hasClass('-disabled-')) return;
  
              this._handleClick.bind(this)($el);
          }
      };
  })();
  
  ;(function () {
      var template = '' +
          '<div class="datepicker--nav-action" data-action="prev">#{prevHtml}</div>' +
          '<div class="datepicker--nav-title">#{title}</div>' +
          '<div class="datepicker--nav-action" data-action="next">#{nextHtml}</div>',
          buttonsContainerTemplate = '<div class="datepicker--buttons"></div>',
          button = '<span class="datepicker--button" data-action="#{action}">#{label}</span>',
          datepicker = $.fn.datepicker,
          dp = datepicker.Constructor;
  
      datepicker.Navigation = function (d, opts) {
          this.d = d;
          this.opts = opts;
  
          this.$buttonsContainer = '';
  
          this.init();
      };
  
      datepicker.Navigation.prototype = {
          init: function () {
              this._buildBaseHtml();
              this._bindEvents();
          },
  
          _bindEvents: function () {
              this.d.$nav.on('click', '.datepicker--nav-action', $.proxy(this._onClickNavButton, this));
              this.d.$nav.on('click', '.datepicker--nav-title', $.proxy(this._onClickNavTitle, this));
              this.d.$datepicker.on('click', '.datepicker--button', $.proxy(this._onClickNavButton, this));
          },
  
          _buildBaseHtml: function () {
              if (!this.opts.onlyTimepicker) {
                  this._render();
              }
              this._addButtonsIfNeed();
          },
  
          _addButtonsIfNeed: function () {
              if (this.opts.todayButton) {
                  this._addButton('today')
              }
              if (this.opts.clearButton) {
                  this._addButton('clear')
              }
          },
  
          _render: function () {
              var title = this._getTitle(this.d.currentDate),
                  html = dp.template(template, $.extend({title: title}, this.opts));
              this.d.$nav.html(html);
              if (this.d.view == 'years') {
                  $('.datepicker--nav-title', this.d.$nav).addClass('-disabled-');
              }
              this.setNavStatus();
          },
  
          _getTitle: function (date) {
              return this.d.formatDate(this.opts.navTitles[this.d.view], date)
          },
  
          _addButton: function (type) {
              if (!this.$buttonsContainer.length) {
                  this._addButtonsContainer();
              }
  
              var data = {
                      action: type,
                      label: this.d.loc[type]
                  },
                  html = dp.template(button, data);
  
              if ($('[data-action=' + type + ']', this.$buttonsContainer).length) return;
              this.$buttonsContainer.append(html);
          },
  
          _addButtonsContainer: function () {
              this.d.$datepicker.append(buttonsContainerTemplate);
              this.$buttonsContainer = $('.datepicker--buttons', this.d.$datepicker);
          },
  
          setNavStatus: function () {
              if (!(this.opts.minDate || this.opts.maxDate) || !this.opts.disableNavWhenOutOfRange) return;
  
              var date = this.d.parsedDate,
                  m = date.month,
                  y = date.year,
                  d = date.date;
  
              switch (this.d.view) {
                  case 'days':
                      if (!this.d._isInRange(new Date(y, m-1, 1), 'month')) {
                          this._disableNav('prev')
                      }
                      if (!this.d._isInRange(new Date(y, m+1, 1), 'month')) {
                          this._disableNav('next')
                      }
                      break;
                  case 'months':
                      if (!this.d._isInRange(new Date(y-1, m, d), 'year')) {
                          this._disableNav('prev')
                      }
                      if (!this.d._isInRange(new Date(y+1, m, d), 'year')) {
                          this._disableNav('next')
                      }
                      break;
                  case 'years':
                      var decade = dp.getDecade(this.d.date);
                      if (!this.d._isInRange(new Date(decade[0] - 1, 0, 1), 'year')) {
                          this._disableNav('prev')
                      }
                      if (!this.d._isInRange(new Date(decade[1] + 1, 0, 1), 'year')) {
                          this._disableNav('next')
                      }
                      break;
              }
          },
  
          _disableNav: function (nav) {
              $('[data-action="' + nav + '"]', this.d.$nav).addClass('-disabled-')
          },
  
          _activateNav: function (nav) {
              $('[data-action="' + nav + '"]', this.d.$nav).removeClass('-disabled-')
          },
  
          _onClickNavButton: function (e) {
              var $el = $(e.target).closest('[data-action]'),
                  action = $el.data('action');
  
              this.d[action]();
          },
  
          _onClickNavTitle: function (e) {
              if ($(e.target).hasClass('-disabled-')) return;
  
              if (this.d.view == 'days') {
                  return this.d.view = 'months'
              }
  
              this.d.view = 'years';
          }
      }
  
  })();
  
  ;(function () {
      var template = '<div class="datepicker--time">' +
          '<div class="datepicker--time-current">' +
          '   <span class="datepicker--time-current-hours">#{hourVisible}</span>' +
          '   <span class="datepicker--time-current-colon">:</span>' +
          '   <span class="datepicker--time-current-minutes">#{minValue}</span>' +
          '</div>' +
          '<div class="datepicker--time-sliders">' +
          '   <div class="datepicker--time-row">' +
          '      <input type="range" name="hours" value="#{hourValue}" min="#{hourMin}" max="#{hourMax}" step="#{hourStep}"/>' +
          '   </div>' +
          '   <div class="datepicker--time-row">' +
          '      <input type="range" name="minutes" value="#{minValue}" min="#{minMin}" max="#{minMax}" step="#{minStep}"/>' +
          '   </div>' +
          '</div>' +
          '</div>',
          datepicker = $.fn.datepicker,
          dp = datepicker.Constructor;
  
      datepicker.Timepicker = function (inst, opts) {
          this.d = inst;
          this.opts = opts;
  
          this.init();
      };
  
      datepicker.Timepicker.prototype = {
          init: function () {
              var input = 'input';
              this._setTime(this.d.date);
              this._buildHTML();
  
              if (navigator.userAgent.match(/trident/gi)) {
                  input = 'change';
              }
  
              this.d.$el.on('selectDate', this._onSelectDate.bind(this));
              this.$ranges.on(input, this._onChangeRange.bind(this));
              this.$ranges.on('mouseup', this._onMouseUpRange.bind(this));
              this.$ranges.on('mousemove focus ', this._onMouseEnterRange.bind(this));
              this.$ranges.on('mouseout blur', this._onMouseOutRange.bind(this));
          },
  
          _setTime: function (date) {
              var _date = dp.getParsedDate(date);
  
              this._handleDate(date);
              this.hours = _date.hours < this.minHours ? this.minHours : _date.hours;
              this.minutes = _date.minutes < this.minMinutes ? this.minMinutes : _date.minutes;
          },
  
          /**
           * Sets minHours and minMinutes from date (usually it's a minDate)
           * Also changes minMinutes if current hours are bigger then @date hours
           * @param date {Date}
           * @private
           */
          _setMinTimeFromDate: function (date) {
              this.minHours = date.getHours();
              this.minMinutes = date.getMinutes();
  
              // If, for example, min hours are 10, and current hours are 12,
              // update minMinutes to default value, to be able to choose whole range of values
              if (this.d.lastSelectedDate) {
                  if (this.d.lastSelectedDate.getHours() > date.getHours()) {
                      this.minMinutes = this.opts.minMinutes;
                  }
              }
          },
  
          _setMaxTimeFromDate: function (date) {
              this.maxHours = date.getHours();
              this.maxMinutes = date.getMinutes();
  
              if (this.d.lastSelectedDate) {
                  if (this.d.lastSelectedDate.getHours() < date.getHours()) {
                      this.maxMinutes = this.opts.maxMinutes;
                  }
              }
          },
  
          _setDefaultMinMaxTime: function () {
              var maxHours = 23,
                  maxMinutes = 59,
                  opts = this.opts;
  
              this.minHours = opts.minHours < 0 || opts.minHours > maxHours ? 0 : opts.minHours;
              this.minMinutes = opts.minMinutes < 0 || opts.minMinutes > maxMinutes ? 0 : opts.minMinutes;
              this.maxHours = opts.maxHours < 0 || opts.maxHours > maxHours ? maxHours : opts.maxHours;
              this.maxMinutes = opts.maxMinutes < 0 || opts.maxMinutes > maxMinutes ? maxMinutes : opts.maxMinutes;
          },
  
          /**
           * Looks for min/max hours/minutes and if current values
           * are out of range sets valid values.
           * @private
           */
          _validateHoursMinutes: function (date) {
              if (this.hours < this.minHours) {
                  this.hours = this.minHours;
              } else if (this.hours > this.maxHours) {
                  this.hours = this.maxHours;
              }
  
              if (this.minutes < this.minMinutes) {
                  this.minutes = this.minMinutes;
              } else if (this.minutes > this.maxMinutes) {
                  this.minutes = this.maxMinutes;
              }
          },
  
          _buildHTML: function () {
              var lz = dp.getLeadingZeroNum,
                  data = {
                      hourMin: this.minHours,
                      hourMax: lz(this.maxHours),
                      hourStep: this.opts.hoursStep,
                      hourValue: this.hours,
                      hourVisible: lz(this.displayHours),
                      minMin: this.minMinutes,
                      minMax: lz(this.maxMinutes),
                      minStep: this.opts.minutesStep,
                      minValue: lz(this.minutes)
                  },
                  _template = dp.template(template, data);
  
              this.$timepicker = $(_template).appendTo(this.d.$datepicker);
              this.$ranges = $('[type="range"]', this.$timepicker);
              this.$hours = $('[name="hours"]', this.$timepicker);
              this.$minutes = $('[name="minutes"]', this.$timepicker);
              this.$hoursText = $('.datepicker--time-current-hours', this.$timepicker);
              this.$minutesText = $('.datepicker--time-current-minutes', this.$timepicker);
  
              if (this.d.ampm) {
                  this.$ampm = $('<span class="datepicker--time-current-ampm">')
                      .appendTo($('.datepicker--time-current', this.$timepicker))
                      .html(this.dayPeriod);
  
                  this.$timepicker.addClass('-am-pm-');
              }
          },
  
          _updateCurrentTime: function () {
              var h =  dp.getLeadingZeroNum(this.displayHours),
                  m = dp.getLeadingZeroNum(this.minutes);
  
              this.$hoursText.html(h);
              this.$minutesText.html(m);
  
              if (this.d.ampm) {
                  this.$ampm.html(this.dayPeriod);
              }
          },
  
          _updateRanges: function () {
              this.$hours.attr({
                  min: this.minHours,
                  max: this.maxHours
              }).val(this.hours);
  
              this.$minutes.attr({
                  min: this.minMinutes,
                  max: this.maxMinutes
              }).val(this.minutes)
          },
  
          /**
           * Sets minHours, minMinutes etc. from date. If date is not passed, than sets
           * values from options
           * @param [date] {object} - Date object, to get values from
           * @private
           */
          _handleDate: function (date) {
              this._setDefaultMinMaxTime();
              if (date) {
                  if (dp.isSame(date, this.d.opts.minDate)) {
                      this._setMinTimeFromDate(this.d.opts.minDate);
                  } else if (dp.isSame(date, this.d.opts.maxDate)) {
                      this._setMaxTimeFromDate(this.d.opts.maxDate);
                  }
              }
  
              this._validateHoursMinutes(date);
          },
  
          update: function () {
              this._updateRanges();
              this._updateCurrentTime();
          },
  
          /**
           * Calculates valid hour value to display in text input and datepicker's body.
           * @param date {Date|Number} - date or hours
           * @param [ampm] {Boolean} - 12 hours mode
           * @returns {{hours: *, dayPeriod: string}}
           * @private
           */
          _getValidHoursFromDate: function (date, ampm) {
              var d = date,
                  hours = date;
  
              if (date instanceof Date) {
                  d = dp.getParsedDate(date);
                  hours = d.hours;
              }
  
              var _ampm = ampm || this.d.ampm,
                  dayPeriod = 'am';
  
              if (_ampm) {
                  switch(true) {
                      case hours == 0:
                          hours = 12;
                          break;
                      case hours == 12:
                          dayPeriod = 'pm';
                          break;
                      case hours > 11:
                          hours = hours - 12;
                          dayPeriod = 'pm';
                          break;
                      default:
                          break;
                  }
              }
  
              return {
                  hours: hours,
                  dayPeriod: dayPeriod
              }
          },
  
          set hours (val) {
              this._hours = val;
  
              var displayHours = this._getValidHoursFromDate(val);
  
              this.displayHours = displayHours.hours;
              this.dayPeriod = displayHours.dayPeriod;
          },
  
          get hours() {
              return this._hours;
          },
  
          //  Events
          // -------------------------------------------------
  
          _onChangeRange: function (e) {
              var $target = $(e.target),
                  name = $target.attr('name');
              
              this.d.timepickerIsActive = true;
  
              this[name] = $target.val();
              this._updateCurrentTime();
              this.d._trigger('timeChange', [this.hours, this.minutes]);
  
              this._handleDate(this.d.lastSelectedDate);
              this.update()
          },
  
          _onSelectDate: function (e, data) {
              this._handleDate(data);
              this.update();
          },
  
          _onMouseEnterRange: function (e) {
              var name = $(e.target).attr('name');
              $('.datepicker--time-current-' + name, this.$timepicker).addClass('-focus-');
          },
  
          _onMouseOutRange: function (e) {
              var name = $(e.target).attr('name');
              if (this.d.inFocus) return; // Prevent removing focus when mouse out of range slider
              $('.datepicker--time-current-' + name, this.$timepicker).removeClass('-focus-');
          },
  
          _onMouseUpRange: function (e) {
              this.d.timepickerIsActive = false;
          }
      };
  })();
   })(window, jQuery);
  