'use strict';

Object.defineProperty(exports, "__esModule", {
	value: true
});

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _classnames = require('classnames');

var _classnames2 = _interopRequireDefault(_classnames);

var _moment = require('moment');

var _moment2 = _interopRequireDefault(_moment);

var _debounce = require('lodash/debounce');

var _debounce2 = _interopRequireDefault(_debounce);

var _range = require('lodash/range');

var _range2 = _interopRequireDefault(_range);

var _utils = require('./utils');

var _locale = require('./locale');

var _locale2 = _interopRequireDefault(_locale);

var _theme = require('./theme');

var _theme2 = _interopRequireDefault(_theme);

var _Today = require('./Today');

var _Today2 = _interopRequireDefault(_Today);

var _Header = require('./Header');

var _Header2 = _interopRequireDefault(_Header);

var _List = require('./List');

var _List2 = _interopRequireDefault(_List);

var _Weekdays = require('./Weekdays');

var _Weekdays2 = _interopRequireDefault(_Weekdays);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var containerStyle = {
	'root': 'Cal__Container__root',
	'landscape': 'Cal__Container__landscape',
	'wrapper': 'Cal__Container__wrapper',
	'listWrapper': 'Cal__Container__listWrapper'
};
var dayStyle = {
	'root': 'Cal__Day__root',
	'enabled': 'Cal__Day__enabled',
	'highlighted': 'Cal__Day__highlighted',
	'today': 'Cal__Day__today',
	'disabled': 'Cal__Day__disabled',
	'selected': 'Cal__Day__selected',
	'month': 'Cal__Day__month',
	'year': 'Cal__Day__year',
	'selection': 'Cal__Day__selection',
	'day': 'Cal__Day__day'
};
var style = {
	container: containerStyle,
	day: dayStyle
};

var InfiniteCalendar = function (_Component) {
	_inherits(InfiniteCalendar, _Component);

	function InfiniteCalendar(props) {
		_classCallCheck(this, InfiniteCalendar);

		// Initialize

		var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(InfiniteCalendar).call(this));

		_initialiseProps.call(_this);

		_this.updateLocale(props.locale);
		_this.updateYears(props);
		_this.state = {
			selectedDate: _this.parseSelectedDate(props.selectedDate)
		};
		return _this;
	}

	_createClass(InfiniteCalendar, [{
		key: 'componentDidMount',
		value: function componentDidMount() {
			var _props = this.props;
			var autoFocus = _props.autoFocus;
			var keyboardSupport = _props.keyboardSupport;

			this.node = this.refs.node;
			this.list = this.refs.List;

			if (keyboardSupport && autoFocus) {
				this.node.focus();
			}
		}
	}, {
		key: 'componentWillReceiveProps',
		value: function componentWillReceiveProps(next) {
			var _props2 = this.props;
			var min = _props2.min;
			var minDate = _props2.minDate;
			var max = _props2.max;
			var maxDate = _props2.maxDate;
			var locale = _props2.locale;
			var selectedDate = _props2.selectedDate;


			if (next.locale !== locale) {
				this.updateLocale(next.locale);
			}
			if (next.min !== min || next.minDate !== minDate || next.max !== max || next.maxDate !== maxDate) {
				this.updateYears(next);
			}
			if (next.selectedDate !== selectedDate) {
				this.setState({
					selectedDate: this.parseSelectedDate(next.selectedDate)
				});
			} else if (next.minDate !== minDate || next.maxDate !== maxDate) {
				// Need to make sure the currently selected date is not before the new minDate or after maxDate
				var _selectedDate = this.parseSelectedDate(this.state.selectedDate);
				if (!_selectedDate.isSame(this.state.selectedDate, 'day')) {
					this.setState({
						selectedDate: _selectedDate
					});
				}
			}
		}
	}, {
		key: 'parseSelectedDate',
		value: function parseSelectedDate(selectedDate) {
			if (selectedDate) {
				selectedDate = (0, _moment2.default)(selectedDate);

				// Selected Date should not be before min date or after max date
				if (selectedDate.isBefore(this._minDate)) {
					return this._minDate;
				} else if (selectedDate.isAfter(this._maxDate)) {
					return this._maxDate;
				}
			}

			return selectedDate;
		}
	}, {
		key: 'updateYears',
		value: function updateYears() {
			var props = arguments.length <= 0 || arguments[0] === undefined ? this.props : arguments[0];

			var min = this._min = (0, _moment2.default)(props.min);
			var max = this._max = (0, _moment2.default)(props.max);
			this._minDate = (0, _moment2.default)(props.minDate);
			this._maxDate = (0, _moment2.default)(props.maxDate);

			this.years = (0, _range2.default)(min.year(), max.year() + 1).map(function (year) {
				return (0, _utils.getMonthsForYear)(year, min, max);
			});
			this.months = [].concat.apply([], this.years);
		}
	}, {
		key: 'updateLocale',
		value: function updateLocale(locale) {
			locale = this.getLocale(locale);
			_moment2.default.updateLocale(locale.name, locale);
			_moment2.default.locale(locale.name);
		}
	}, {
		key: 'getDisabledDates',
		value: function getDisabledDates(disabledDates) {
			return disabledDates && disabledDates.map(function (date) {
				return (0, _moment2.default)(date).format('YYYYMMDD');
			});
		}
	}, {
		key: 'getLocale',
		value: function getLocale() {
			var customLocale = arguments.length <= 0 || arguments[0] === undefined ? this.props.locale : arguments[0];

			return Object.assign({}, _locale2.default, customLocale);
		}
	}, {
		key: 'getTheme',
		value: function getTheme() {
			var customTheme = arguments.length <= 0 || arguments[0] === undefined ? this.props.theme : arguments[0];

			return Object.assign({}, _theme2.default, customTheme);
		}
	}, {
		key: 'clearHighlight',
		value: function clearHighlight() {
			if (this.highlightedEl) {
				this.highlightedEl.classList.remove(style.day.highlighted);
				this.highlightedEl = null;
			}
		}
	}, {
		key: 'render',
		value: function render() {
			var _props3 = this.props;
			var className = _props3.className;
			var disabledDays = _props3.disabledDays;
			var height = _props3.height;
			var keyboardSupport = _props3.keyboardSupport;
			var layout = _props3.layout;
			var overscanMonthCount = _props3.overscanMonthCount;
			var min = _props3.min;
			var minDate = _props3.minDate;
			var maxDate = _props3.maxDate;
			var shouldHeaderAnimate = _props3.shouldHeaderAnimate;
			var showTodayHelper = _props3.showTodayHelper;
			var showHeader = _props3.showHeader;
			var tabIndex = _props3.tabIndex;
			var width = _props3.width;

			var other = _objectWithoutProperties(_props3, ['className', 'disabledDays', 'height', 'keyboardSupport', 'layout', 'overscanMonthCount', 'min', 'minDate', 'maxDate', 'shouldHeaderAnimate', 'showTodayHelper', 'showHeader', 'tabIndex', 'width']);

			var disabledDates = this.getDisabledDates(this.props.disabledDates);
			var locale = this.getLocale();
			var theme = this.getTheme();
			var _state = this.state;
			var isScrolling = _state.isScrolling;
			var selectedDate = _state.selectedDate;
			var showToday = _state.showToday;

			var today = this.today = (0, _utils.parseDate)((0, _moment2.default)());

			// Selected date should not be disabled
			if (selectedDate && (disabledDates && disabledDates.indexOf(selectedDate.format('YYYYMMDD')) !== -1 || disabledDays && disabledDays.indexOf(selectedDate.day()) !== -1)) {
				selectedDate = null;
			}

			return _react2.default.createElement(
				'div',
				{ tabIndex: tabIndex, onKeyDown: keyboardSupport && this.handleKeyDown, className: (0, _classnames2.default)(className, style.container.root, _defineProperty({}, style.container.landscape, layout == 'landscape')), style: { color: theme.textColor.default, width: width }, 'aria-label': 'Calendar', ref: 'node' },
				showHeader && _react2.default.createElement(_Header2.default, { selectedDate: selectedDate, shouldHeaderAnimate: shouldHeaderAnimate, layout: layout, theme: theme, locale: locale, onClick: this.scrollToDate }),
				_react2.default.createElement(
					'div',
					{ className: style.container.wrapper },
					_react2.default.createElement(_Weekdays2.default, { theme: theme }),
					_react2.default.createElement(
						'div',
						{ className: style.container.listWrapper },
						showTodayHelper && _react2.default.createElement(_Today2.default, { scrollToDate: this.scrollToDate, show: showToday, today: today, theme: theme, locale: locale }),
						_react2.default.createElement(_List2.default, _extends({
							ref: 'List'
						}, other, {
							width: width,
							height: height,
							selectedDate: (0, _utils.parseDate)(selectedDate),
							disabledDates: disabledDates,
							disabledDays: disabledDays,
							months: this.months,
							onDaySelect: this.onDaySelect,
							onScroll: this.onScroll,
							isScrolling: isScrolling,
							today: today,
							min: (0, _utils.parseDate)(min),
							minDate: (0, _utils.parseDate)(minDate),
							maxDate: (0, _utils.parseDate)(maxDate),
							theme: theme,
							locale: locale,
							overscanMonthCount: overscanMonthCount
						}))
					)
				)
			);
		}
	}]);

	return InfiniteCalendar;
}(_react.Component);

InfiniteCalendar.defaultProps = {
	width: 400,
	height: 500,
	rowHeight: 56,
	overscanMonthCount: 4,
	layout: 'portrait',
	selectedDate: new Date(),
	min: { year: 1980, month: 0, day: 0 },
	minDate: { year: 1980, month: 0, day: 0 },
	max: { year: 2050, month: 11, day: 31 },
	maxDate: { year: 2050, month: 11, day: 31 },
	keyboardSupport: true,
	autoFocus: true,
	shouldHeaderAnimate: true,
	showOverlay: true,
	showTodayHelper: true,
	showHeader: true,
	tabIndex: 1,
	locale: {},
	theme: {}
};
InfiniteCalendar.propTypes = {
	selectedDate: _utils.validDate,
	min: _utils.validDate,
	max: _utils.validDate,
	minDate: _utils.validDate,
	maxDate: _utils.validDate,
	locale: _react.PropTypes.object,
	theme: _react.PropTypes.object,
	width: _react.PropTypes.oneOfType([_react.PropTypes.number, _react.PropTypes.string]),
	height: _react.PropTypes.number,
	rowHeight: _react.PropTypes.number,
	className: _react.PropTypes.string,
	overscanMonthCount: _react.PropTypes.number,
	disabledDays: _react.PropTypes.arrayOf(_react.PropTypes.number),
	disabledDates: _react.PropTypes.arrayOf(_utils.validDate),
	beforeSelect: _react.PropTypes.func,
	onSelect: _react.PropTypes.func,
	afterSelect: _react.PropTypes.func,
	onScroll: _react.PropTypes.func,
	onScrollEnd: _react.PropTypes.func,
	keyboardSupport: _react.PropTypes.bool,
	autoFocus: _react.PropTypes.bool,
	onKeyDown: _react.PropTypes.func,
	tabIndex: _react.PropTypes.number,
	layout: function layout(props, propName, componentName) {
		if (['portrait', 'landscape'].indexOf(props[propName]) == -1) {
			return new Error('Invalid prop `' + propName + '` supplied to ' + componentName + '. Should be one of `landscape` or `portrait`.');
		}
	},
	shouldHeaderAnimate: _react.PropTypes.bool,
	showOverlay: _react.PropTypes.bool,
	showTodayHelper: _react.PropTypes.bool,
	showHeader: _react.PropTypes.bool
};

var _initialiseProps = function _initialiseProps() {
	var _this2 = this;

	this.onDaySelect = function (selectedDate, e) {
		var _props4 = _this2.props;
		var afterSelect = _props4.afterSelect;
		var beforeSelect = _props4.beforeSelect;
		var onSelect = _props4.onSelect;


		if (!beforeSelect || typeof beforeSelect == 'function' && beforeSelect(selectedDate)) {
			if (typeof onSelect == 'function') {
				onSelect(selectedDate, e);
			}

			_this2.setState({
				selectedDate: selectedDate,
				highlightedDate: selectedDate.clone()
			}, function () {
				_this2.clearHighlight();
				if (typeof afterSelect == 'function') {
					afterSelect(selectedDate);
				}
			});
		}
	};

	this.getCurrentOffset = function () {
		return _this2.scrollTop;
	};

	this.getDateOffset = function (date) {
		return _this2.list && _this2.list.getDateOffset(date);
	};

	this.scrollTo = function (offset) {
		return _this2.list && _this2.list.scrollTo(offset);
	};

	this.scrollToDate = function () {
		var date = arguments.length <= 0 || arguments[0] === undefined ? (0, _moment2.default)() : arguments[0];
		var offset = arguments[1];

		return _this2.list && _this2.list.scrollToDate(date, offset);
	};

	this.getScrollSpeed = (0, _utils.getScrollSpeed)();

	this.onScroll = function (_ref) {
		var scrollTop = _ref.scrollTop;
		var _props5 = _this2.props;
		var onScroll = _props5.onScroll;
		var showOverlay = _props5.showOverlay;
		var showTodayHelper = _props5.showTodayHelper;
		var isScrolling = _this2.state.isScrolling;

		var scrollSpeed = _this2.scrollSpeed = Math.abs(_this2.getScrollSpeed(scrollTop));
		_this2.scrollTop = scrollTop;

		// We only want to display the months overlay if the user is rapidly scrolling
		if (showOverlay && scrollSpeed >= 50 && !isScrolling) {
			_this2.setState({
				isScrolling: true
			});
		}

		if (showTodayHelper) {
			_this2.updateTodayHelperPosition(scrollSpeed);
		}
		if (typeof onScroll == 'function') {
			onScroll(scrollTop);
		}
		_this2.onScrollEnd();
	};

	this.onScrollEnd = (0, _debounce2.default)(function () {
		var _props6 = _this2.props;
		var onScrollEnd = _props6.onScrollEnd;
		var showTodayHelper = _props6.showTodayHelper;
		var isScrolling = _this2.state.isScrolling;


		if (isScrolling) _this2.setState({ isScrolling: false });
		if (showTodayHelper) _this2.updateTodayHelperPosition(0);
		if (typeof onScrollEnd == 'function') onScrollEnd(_this2.scrollTop);
	}, 150);

	this.updateTodayHelperPosition = function (scrollSpeed) {
		if (!_this2.todayOffset) _this2.todayOffset = _this2.getDateOffset(_this2.today.date);
		var scrollTop = _this2.scrollTop;
		var showToday = _this2.state.showToday;

		var newState = void 0;

		if (scrollTop >= _this2.todayOffset + 480) {
			if (showToday !== 1) newState = 1;
		} else if (scrollTop <= _this2.todayOffset - 500) {
			if (showToday !== -1) newState = -1;
		} else if (showToday && scrollSpeed <= 1) {
			newState = false;
		}

		if (scrollTop == 0) {
			newState = false;
		}

		if (newState != null) {
			_this2.setState({ showToday: newState });
		}
	};

	this.handleKeyDown = function (e) {
		var onKeyDown = _this2.props.onKeyDown;
		var _state2 = _this2.state;
		var highlightedDate = _state2.highlightedDate;
		var showToday = _state2.showToday;

		var delta = 0;

		if (typeof onKeyDown == 'function') {
			onKeyDown(e);
		}
		if (!highlightedDate) {
			highlightedDate = _this2.state.selectedDate.clone();
			_this2.setState({ highlightedDate: highlightedDate });
		}

		if ([_utils.keyCodes.left, _utils.keyCodes.up, _utils.keyCodes.right, _utils.keyCodes.down].indexOf(e.keyCode) > -1 && typeof e.preventDefault == 'function') {
			e.preventDefault();
		}
		switch (e.keyCode) {
			case _utils.keyCodes.enter:
				_this2.setState({
					selectedDate: (0, _moment2.default)(highlightedDate)
				});
				return;
			case _utils.keyCodes.left:
				delta = -1;
				break;
			case _utils.keyCodes.right:
				delta = +1;
				break;
			case _utils.keyCodes.down:
				delta = +7;
				break;
			case _utils.keyCodes.up:
				delta = -7;
				break;
		}

		if (delta) {
			var _props7 = _this2.props;
			var maxDate = _props7.maxDate;
			var minDate = _props7.minDate;
			var rowHeight = _props7.rowHeight;

			var newHighlightedDate = (0, _moment2.default)(highlightedDate).add(delta, 'days');

			// Make sure the new highlighted date isn't before min / max
			if (newHighlightedDate.isBefore(minDate)) {
				newHighlightedDate = (0, _moment2.default)(minDate);
			} else if (newHighlightedDate.isAfter(maxDate)) {
				newHighlightedDate = (0, _moment2.default)(maxDate);
			}

			// Update the highlight indicator
			_this2.clearHighlight();
			var highlightedEl = _this2.highlightedEl = _this2.node.querySelector('[data-date=\'' + newHighlightedDate.format('YYYYMMDD') + '\']');
			if (highlightedEl) {
				highlightedEl.classList.add(style.day.highlighted);
			}

			// Scroll the view
			if (!_this2.currentOffset) _this2.currentOffset = _this2.getCurrentOffset();
			var currentOffset = _this2.currentOffset;
			var monthOffset = _this2.getDateOffset(newHighlightedDate);
			var dateOffset = highlightedEl.offsetTop - rowHeight;
			var newOffset = monthOffset + dateOffset;
			var navOffset = showToday ? 36 : 0;

			if (currentOffset !== newOffset) {
				_this2.currentOffset = newOffset;
				_this2.scrollTo(newOffset - navOffset);
			}

			// Update the reference to the currently highlighted date
			_this2.setState({
				highlightedDate: newHighlightedDate
			});
		}
	};
};

exports.default = InfiniteCalendar;