/*!
 * TOAST UI Date Picker
 * @version 4.3.1
 * @author NHN. FE Development Lab <dl_javascript@nhn.com>
 * @license MIT
 */
(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory(require("tui-time-picker"));
	else if(typeof define === 'function' && define.amd)
		define(["tui-time-picker"], factory);
	else if(typeof exports === 'object')
		exports["DatePicker"] = factory(require("tui-time-picker"));
	else
		root["tui"] = root["tui"] || {}, root["tui"]["DatePicker"] = factory(root["tui"]["TimePicker"]);
})(window, function(__WEBPACK_EXTERNAL_MODULE__43__) {
return /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ 		}
/******/ 	};
/******/
/******/ 	// define __esModule on exports
/******/ 	__webpack_require__.r = function(exports) {
/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 		}
/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
/******/ 	};
/******/
/******/ 	// create a fake namespace object
/******/ 	// mode & 1: value is a module id, require it
/******/ 	// mode & 2: merge all properties of value into the ns
/******/ 	// mode & 4: return value when already ns object
/******/ 	// mode & 8|1: behave like require
/******/ 	__webpack_require__.t = function(value, mode) {
/******/ 		if(mode & 1) value = __webpack_require__(value);
/******/ 		if(mode & 8) return value;
/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ 		var ns = Object.create(null);
/******/ 		__webpack_require__.r(ns);
/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ 		return ns;
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "dist";
/******/
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 34);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview
 * This module provides a function to make a constructor
 * that can inherit from the other constructors like the CLASS easily.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var inherit = __webpack_require__(35);
var extend = __webpack_require__(7);

/**
 * @module defineClass
 */

/**
 * Help a constructor to be defined and to inherit from the other constructors
 * @param {*} [parent] Parent constructor
 * @param {Object} props Members of constructor
 *  @param {Function} props.init Initialization method
 *  @param {Object} [props.static] Static members of constructor
 * @returns {*} Constructor
 * @memberof module:defineClass
 * @example
 * var defineClass = require('tui-code-snippet/defineClass/defineClass'); // node, commonjs
 *
 * //-- #2. Use property --//
 * var Parent = defineClass({
 *     init: function() { // constuructor
 *         this.name = 'made by def';
 *     },
 *     method: function() {
 *         // ...
 *     },
 *     static: {
 *         staticMethod: function() {
 *              // ...
 *         }
 *     }
 * });
 *
 * var Child = defineClass(Parent, {
 *     childMethod: function() {}
 * });
 *
 * Parent.staticMethod();
 *
 * var parentInstance = new Parent();
 * console.log(parentInstance.name); //made by def
 * parentInstance.staticMethod(); // Error
 *
 * var childInstance = new Child();
 * childInstance.method();
 * childInstance.childMethod();
 */
function defineClass(parent, props) {
  var obj;

  if (!props) {
    props = parent;
    parent = null;
  }

  obj = props.init || function() {};

  if (parent) {
    inherit(obj, parent);
  }

  if (props.hasOwnProperty('static')) {
    extend(obj, props['static']);
    delete props['static'];
  }

  extend(obj.prototype, props);

  return obj;
}

module.exports = defineClass;


/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Constants of date-picker
 * @author NHN. FE Development Lab <dl_javascript@nhn.com>
 */



module.exports = {
  TYPE_DATE: 'date',
  TYPE_MONTH: 'month',
  TYPE_YEAR: 'year',
  TYPE_HOUR: 'hour',
  TYPE_MINUTE: 'minute',
  TYPE_MERIDIEM: 'meridiem',
  MIN_DATE: new Date(1900, 0, 1),
  MAX_DATE: new Date(2999, 11, 31),

  DEFAULT_LANGUAGE_TYPE: 'en',

  CLASS_NAME_SELECTED: 'tui-is-selected',

  CLASS_NAME_PREV_MONTH_BTN: 'tui-calendar-btn-prev-month',
  CLASS_NAME_PREV_YEAR_BTN: 'tui-calendar-btn-prev-year',
  CLASS_NAME_NEXT_YEAR_BTN: 'tui-calendar-btn-next-year',
  CLASS_NAME_NEXT_MONTH_BTN: 'tui-calendar-btn-next-month',
  CLASS_NAME_TITLE_TODAY: 'tui-calendar-title-today',

  DEFAULT_WEEK_START_DAY: 'Sun',
  WEEK_START_DAY_MAP: {
    sun: 0,
    mon: 1,
    tue: 2,
    wed: 3,
    thu: 4,
    fri: 5,
    sat: 6
  }
};


/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Execute the provided callback once for each element present in the array(or Array-like object) in ascending order.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



/**
 * Execute the provided callback once for each element present
 * in the array(or Array-like object) in ascending order.
 * If the callback function returns false, the loop will be stopped.
 * Callback function(iteratee) is invoked with three arguments:
 *  1) The value of the element
 *  2) The index of the element
 *  3) The array(or Array-like object) being traversed
 * @param {Array|Arguments|NodeList} arr The array(or Array-like object) that will be traversed
 * @param {function} iteratee Callback function
 * @param {Object} [context] Context(this) of callback function
 * @memberof module:collection
 * @example
 * var forEachArray = require('tui-code-snippet/collection/forEachArray'); // node, commonjs
 *
 * var sum = 0;
 *
 * forEachArray([1,2,3], function(value){
 *     sum += value;
 * });
 * alert(sum); // 6
 */
function forEachArray(arr, iteratee, context) {
  var index = 0;
  var len = arr.length;

  context = context || null;

  for (; index < len; index += 1) {
    if (iteratee.call(context, arr[index], index, arr) === false) {
      break;
    }
  }
}

module.exports = forEachArray;


/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/* eslint-disable complexity */
/**
 * @fileoverview Returns the first index at which a given element can be found in the array.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var isArray = __webpack_require__(6);

/**
 * @module array
 */

/**
 * Returns the first index at which a given element can be found in the array
 * from start index(default 0), or -1 if it is not present.
 * It compares searchElement to elements of the Array using strict equality
 * (the same method used by the ===, or triple-equals, operator).
 * @param {*} searchElement Element to locate in the array
 * @param {Array} array Array that will be traversed.
 * @param {number} startIndex Start index in array for searching (default 0)
 * @returns {number} the First index at which a given element, or -1 if it is not present
 * @memberof module:array
 * @example
 * var inArray = require('tui-code-snippet/array/inArray'); // node, commonjs
 *
 * var arr = ['one', 'two', 'three', 'four'];
 * var idx1 = inArray('one', arr, 3); // -1
 * var idx2 = inArray('one', arr); // 0
 */
function inArray(searchElement, array, startIndex) {
  var i;
  var length;
  startIndex = startIndex || 0;

  if (!isArray(array)) {
    return -1;
  }

  if (Array.prototype.indexOf) {
    return Array.prototype.indexOf.call(array, searchElement, startIndex);
  }

  length = array.length;
  for (i = startIndex; startIndex >= 0 && i < length; i += 1) {
    if (array[i] === searchElement) {
      return i;
    }
  }

  return -1;
}

module.exports = inArray;


/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Utils for Datepicker component
 * @author NHN. FE Development Lab <dl_javascript@nhn.com>
 */



var forEachArray = __webpack_require__(2);
var isHTMLNode = __webpack_require__(46);
var sendHostname = __webpack_require__(47);

var currentId = 0;

var utils = {
  /**
   * Get a target element
   * @param {Event} ev Event object
   * @returns {HTMLElement} An event target element
   */
  getTarget: function(ev) {
    return ev.target || ev.srcElement;
  },

  /**
   * Return the same element with an element or a matched element searched by a selector.
   * @param {HTMLElement|string} param HTMLElement or selector
   * @returns {HTMLElement} A matched element
   */
  getElement: function(param) {
    return isHTMLNode(param) ? param : document.querySelector(param);
  },

  /**
   * Get a selector of the element.
   * @param {HTMLElement} elem An element
   * @returns {string}
   */
  getSelector: function(elem) {
    var selector = '';
    if (elem.id) {
      selector = '#' + elem.id;
    } else if (elem.className) {
      selector = '.' + elem.className.split(' ')[0];
    }

    return selector;
  },

  /**
   * Create an unique id.
   * @returns {number}
   */
  generateId: function() {
    currentId += 1;

    return currentId;
  },

  /**
   * Create a new array with all elements that pass the test implemented by the provided function.
   * @param {Array} arr - Array that will be traversed
   * @param {function} iteratee - iteratee callback function
   * @returns {Array}
   */
  filter: function(arr, iteratee) {
    var result = [];

    forEachArray(arr, function(item) {
      if (iteratee(item)) {
        result.push(item);
      }
    });

    return result;
  },

  /**
   * Send hostname for GA
   * @ignore
   */
  sendHostName: function() {
    sendHostname('date-picker', 'UA-129987462-1');
  }
};

module.exports = utils;


/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Utils for DatePicker component
 * @author NHN. FE dev Lab. <dl_javascript@nhn.com>
 */



var isDate = __webpack_require__(28);
var isNumber = __webpack_require__(15);

var constants = __webpack_require__(1);

var TYPE_DATE = constants.TYPE_DATE;
var TYPE_MONTH = constants.TYPE_MONTH;
var TYPE_YEAR = constants.TYPE_YEAR;

/**
 * Utils of calendar
 * @namespace dateUtil
 * @ignore
 */
var utils = {
  /**
   * Get weeks count by paramenter
   * @param {number} year A year
   * @param {number} month A month
   * @returns {number} Weeks count (4~6)
   **/
  getWeeksCount: function(year, month) {
    var firstDay = utils.getFirstDay(year, month),
      lastDate = utils.getLastDayInMonth(year, month);

    return Math.ceil((firstDay + lastDate) / 7);
  },

  /**
   * @param {Date} date - Date instance
   * @returns {boolean}
   */
  isValidDate: function(date) {
    return isDate(date) && !isNaN(date.getTime());
  },

  /**
   * Get which day is first by parameters that include year and month information.
   * @param {number} year A year
   * @param {number} month A month
   * @returns {number} (0~6)
   */
  getFirstDay: function(year, month) {
    return new Date(year, month - 1, 1).getDay();
  },

  /**
   * Get timestamp of the first day.
   * @param {number} year A year
   * @param {number} month A month
   * @returns {number} timestamp
   */
  getFirstDayTimestamp: function(year, month) {
    return new Date(year, month, 1).getTime();
  },

  /**
   * Get last date by parameters that include year and month information.
   * @param {number} year A year
   * @param {number} month A month
   * @returns {number} (1~31)
   */
  getLastDayInMonth: function(year, month) {
    return new Date(year, month, 0).getDate();
  },

  /**
   * Chagne number 0~9 to '00~09'
   * @param {number} number number
   * @returns {string}
   * @example
   *  dateUtil.prependLeadingZero(0); //  '00'
   *  dateUtil.prependLeadingZero(9); //  '09'
   *  dateUtil.prependLeadingZero(12); //  '12'
   */
  prependLeadingZero: function(number) {
    var prefix = '';

    if (number < 10) {
      prefix = '0';
    }

    return prefix + number;
  },

  /**
   * Get meridiem hour
   * @param {number} hour - Original hour
   * @returns {number} Converted meridiem hour
   */
  getMeridiemHour: function(hour) {
    hour %= 12;

    if (hour === 0) {
      hour = 12;
    }

    return hour;
  },

  /**
   * Returns number or default
   * @param {*} any - Any value
   * @param {number} defaultNumber - Default number
   * @throws Will throw an error if the defaultNumber is invalid.
   * @returns {number}
   */
  getSafeNumber: function(any, defaultNumber) {
    if (isNaN(defaultNumber) || !isNumber(defaultNumber)) {
      throw Error('The defaultNumber must be a valid number.');
    }
    if (isNaN(any)) {
      return defaultNumber;
    }

    return Number(any);
  },

  /**
   * Return date of the week
   * @param {number} year - Year
   * @param {number} month - Month
   * @param {number} weekNumber - Week number (0~5)
   * @param {number} dayNumber - Day number (0: sunday, 1: monday, ....)
   * @returns {number}
   */
  getDateOfWeek: function(year, month, weekNumber, dayNumber) {
    var firstDayOfMonth = new Date(year, month - 1).getDay();
    var dateOffset = firstDayOfMonth - dayNumber - 1;

    return new Date(year, month - 1, weekNumber * 7 - dateOffset);
  },

  /**
   * Returns range arr
   * @param {number} start - Start value
   * @param {number} end - End value
   * @returns {Array}
   */
  getRangeArr: function(start, end) {
    var arr = [];
    var i;

    if (start > end) {
      for (i = end; i >= start; i -= 1) {
        arr.push(i);
      }
    } else {
      for (i = start; i <= end; i += 1) {
        arr.push(i);
      }
    }

    return arr;
  },

  /**
   * Returns cloned date with the start of a unit of time
   * @param {Date|number} date - Original date
   * @param {string} [type = TYPE_DATE] - Unit type
   * @throws {Error}
   * @returns {Date}
   */
  cloneWithStartOf: function(date, type) {
    type = type || TYPE_DATE;
    date = new Date(date);

    // Does not consider time-level yet.
    date.setHours(0, 0, 0, 0);

    switch (type) {
      case TYPE_DATE:
        break;
      case TYPE_MONTH:
        date.setDate(1);
        break;
      case TYPE_YEAR:
        date.setMonth(0, 1);
        break;
      default:
        throw Error('Unsupported type: ' + type);
    }

    return date;
  },

  /**
   * Returns cloned date with the end of a unit of time
   * @param {Date|number} date - Original date
   * @param {string} [type = TYPE_DATE] - Unit type
   * @throws {Error}
   * @returns {Date}
   */
  cloneWithEndOf: function(date, type) {
    type = type || TYPE_DATE;
    date = new Date(date);

    // Does not consider time-level yet.
    date.setHours(23, 59, 59, 999);

    switch (type) {
      case TYPE_DATE:
        break;
      case TYPE_MONTH:
        date.setMonth(date.getMonth() + 1, 0);
        break;
      case TYPE_YEAR:
        date.setMonth(11, 31);
        break;
      default:
        throw Error('Unsupported type: ' + type);
    }

    return date;
  },

  /**
   * Compare two dates
   * @param {Date|number} dateA - Date
   * @param {Date|number} dateB - Date
   * @param {string} [cmpLevel] - Comparing level
   * @returns {number}
   */
  compare: function(dateA, dateB, cmpLevel) {
    var aTimestamp, bTimestamp;

    if (!(utils.isValidDate(dateA) && utils.isValidDate(dateB))) {
      return NaN;
    }

    if (!cmpLevel) {
      aTimestamp = dateA.getTime();
      bTimestamp = dateB.getTime();
    } else {
      aTimestamp = utils.cloneWithStartOf(dateA, cmpLevel).getTime();
      bTimestamp = utils.cloneWithStartOf(dateB, cmpLevel).getTime();
    }

    if (aTimestamp > bTimestamp) {
      return 1;
    }

    return aTimestamp === bTimestamp ? 0 : -1;
  },

  /**
   * Returns whether two dates are same
   * @param {Date|number} dateA - Date
   * @param {Date|number} dateB - Date
   * @param {string} [cmpLevel] - Comparing level
   * @returns {boolean}
   */
  isSame: function(dateA, dateB, cmpLevel) {
    return utils.compare(dateA, dateB, cmpLevel) === 0;
  },

  /**
   * Returns whether the target is in range
   * @param {Date|number} start - Range start
   * @param {Date|number} end - Range end
   * @param {Date|number} target - Target
   * @param {string} [cmpLevel = TYPE_DATE] - Comparing level
   * @returns {boolean}
   */
  inRange: function(start, end, target, cmpLevel) {
    return utils.compare(start, target, cmpLevel) < 1 && utils.compare(end, target, cmpLevel) > -1;
  }
};

module.exports = utils;


/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Check whether the given variable is an instance of Array or not.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



/**
 * Check whether the given variable is an instance of Array or not.
 * If the given variable is an instance of Array, return true.
 * @param {*} obj - Target for checking
 * @returns {boolean} Is array instance?
 * @memberof module:type
 */
function isArray(obj) {
  return obj instanceof Array;
}

module.exports = isArray;


/***/ }),
/* 7 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Extend the target object from other objects.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



/**
 * @module object
 */

/**
 * Extend the target object from other objects.
 * @param {object} target - Object that will be extended
 * @param {...object} objects - Objects as sources
 * @returns {object} Extended object
 * @memberof module:object
 */
function extend(target, objects) { // eslint-disable-line no-unused-vars
  var hasOwnProp = Object.prototype.hasOwnProperty;
  var source, prop, i, len;

  for (i = 1, len = arguments.length; i < len; i += 1) {
    source = arguments[i];
    for (prop in source) {
      if (hasOwnProp.call(source, prop)) {
        target[prop] = source[prop];
      }
    }
  }

  return target;
}

module.exports = extend;


/***/ }),
/* 8 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview This module provides some functions for custom events. And it is implemented in the observer design pattern.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var extend = __webpack_require__(7);
var isExisty = __webpack_require__(37);
var isString = __webpack_require__(13);
var isObject = __webpack_require__(22);
var isArray = __webpack_require__(6);
var isFunction = __webpack_require__(39);
var forEach = __webpack_require__(9);

var R_EVENTNAME_SPLIT = /\s+/g;

/**
 * @class
 * @example
 * // node, commonjs
 * var CustomEvents = require('tui-code-snippet/customEvents/customEvents');
 */
function CustomEvents() {
  /**
     * @type {HandlerItem[]}
     */
  this.events = null;

  /**
     * only for checking specific context event was binded
     * @type {object[]}
     */
  this.contexts = null;
}

/**
 * Mixin custom events feature to specific constructor
 * @param {function} func - constructor
 * @example
 * var CustomEvents = require('tui-code-snippet/customEvents/customEvents'); // node, commonjs
 *
 * var model;
 * function Model() {
 *     this.name = '';
 * }
 * CustomEvents.mixin(Model);
 *
 * model = new Model();
 * model.on('change', function() { this.name = 'model'; }, this);
 * model.fire('change');
 * alert(model.name); // 'model';
 */
CustomEvents.mixin = function(func) {
  extend(func.prototype, CustomEvents.prototype);
};

/**
 * Get HandlerItem object
 * @param {function} handler - handler function
 * @param {object} [context] - context for handler
 * @returns {HandlerItem} HandlerItem object
 * @private
 */
CustomEvents.prototype._getHandlerItem = function(handler, context) {
  var item = {handler: handler};

  if (context) {
    item.context = context;
  }

  return item;
};

/**
 * Get event object safely
 * @param {string} [eventName] - create sub event map if not exist.
 * @returns {(object|array)} event object. if you supplied `eventName`
 *  parameter then make new array and return it
 * @private
 */
CustomEvents.prototype._safeEvent = function(eventName) {
  var events = this.events;
  var byName;

  if (!events) {
    events = this.events = {};
  }

  if (eventName) {
    byName = events[eventName];

    if (!byName) {
      byName = [];
      events[eventName] = byName;
    }

    events = byName;
  }

  return events;
};

/**
 * Get context array safely
 * @returns {array} context array
 * @private
 */
CustomEvents.prototype._safeContext = function() {
  var context = this.contexts;

  if (!context) {
    context = this.contexts = [];
  }

  return context;
};

/**
 * Get index of context
 * @param {object} ctx - context that used for bind custom event
 * @returns {number} index of context
 * @private
 */
CustomEvents.prototype._indexOfContext = function(ctx) {
  var context = this._safeContext();
  var index = 0;

  while (context[index]) {
    if (ctx === context[index][0]) {
      return index;
    }

    index += 1;
  }

  return -1;
};

/**
 * Memorize supplied context for recognize supplied object is context or
 *  name: handler pair object when off()
 * @param {object} ctx - context object to memorize
 * @private
 */
CustomEvents.prototype._memorizeContext = function(ctx) {
  var context, index;

  if (!isExisty(ctx)) {
    return;
  }

  context = this._safeContext();
  index = this._indexOfContext(ctx);

  if (index > -1) {
    context[index][1] += 1;
  } else {
    context.push([ctx, 1]);
  }
};

/**
 * Forget supplied context object
 * @param {object} ctx - context object to forget
 * @private
 */
CustomEvents.prototype._forgetContext = function(ctx) {
  var context, contextIndex;

  if (!isExisty(ctx)) {
    return;
  }

  context = this._safeContext();
  contextIndex = this._indexOfContext(ctx);

  if (contextIndex > -1) {
    context[contextIndex][1] -= 1;

    if (context[contextIndex][1] <= 0) {
      context.splice(contextIndex, 1);
    }
  }
};

/**
 * Bind event handler
 * @param {(string|{name:string, handler:function})} eventName - custom
 *  event name or an object {eventName: handler}
 * @param {(function|object)} [handler] - handler function or context
 * @param {object} [context] - context for binding
 * @private
 */
CustomEvents.prototype._bindEvent = function(eventName, handler, context) {
  var events = this._safeEvent(eventName);
  this._memorizeContext(context);
  events.push(this._getHandlerItem(handler, context));
};

/**
 * Bind event handlers
 * @param {(string|{name:string, handler:function})} eventName - custom
 *  event name or an object {eventName: handler}
 * @param {(function|object)} [handler] - handler function or context
 * @param {object} [context] - context for binding
 * //-- #1. Get Module --//
 * var CustomEvents = require('tui-code-snippet/customEvents/customEvents'); // node, commonjs
 *
 * //-- #2. Use method --//
 * // # 2.1 Basic Usage
 * CustomEvents.on('onload', handler);
 *
 * // # 2.2 With context
 * CustomEvents.on('onload', handler, myObj);
 *
 * // # 2.3 Bind by object that name, handler pairs
 * CustomEvents.on({
 *     'play': handler,
 *     'pause': handler2
 * });
 *
 * // # 2.4 Bind by object that name, handler pairs with context object
 * CustomEvents.on({
 *     'play': handler
 * }, myObj);
 */
CustomEvents.prototype.on = function(eventName, handler, context) {
  var self = this;

  if (isString(eventName)) {
    // [syntax 1, 2]
    eventName = eventName.split(R_EVENTNAME_SPLIT);
    forEach(eventName, function(name) {
      self._bindEvent(name, handler, context);
    });
  } else if (isObject(eventName)) {
    // [syntax 3, 4]
    context = handler;
    forEach(eventName, function(func, name) {
      self.on(name, func, context);
    });
  }
};

/**
 * Bind one-shot event handlers
 * @param {(string|{name:string,handler:function})} eventName - custom
 *  event name or an object {eventName: handler}
 * @param {function|object} [handler] - handler function or context
 * @param {object} [context] - context for binding
 */
CustomEvents.prototype.once = function(eventName, handler, context) {
  var self = this;

  if (isObject(eventName)) {
    context = handler;
    forEach(eventName, function(func, name) {
      self.once(name, func, context);
    });

    return;
  }

  function onceHandler() { // eslint-disable-line require-jsdoc
    handler.apply(context, arguments);
    self.off(eventName, onceHandler, context);
  }

  this.on(eventName, onceHandler, context);
};

/**
 * Splice supplied array by callback result
 * @param {array} arr - array to splice
 * @param {function} predicate - function return boolean
 * @private
 */
CustomEvents.prototype._spliceMatches = function(arr, predicate) {
  var i = 0;
  var len;

  if (!isArray(arr)) {
    return;
  }

  for (len = arr.length; i < len; i += 1) {
    if (predicate(arr[i]) === true) {
      arr.splice(i, 1);
      len -= 1;
      i -= 1;
    }
  }
};

/**
 * Get matcher for unbind specific handler events
 * @param {function} handler - handler function
 * @returns {function} handler matcher
 * @private
 */
CustomEvents.prototype._matchHandler = function(handler) {
  var self = this;

  return function(item) {
    var needRemove = handler === item.handler;

    if (needRemove) {
      self._forgetContext(item.context);
    }

    return needRemove;
  };
};

/**
 * Get matcher for unbind specific context events
 * @param {object} context - context
 * @returns {function} object matcher
 * @private
 */
CustomEvents.prototype._matchContext = function(context) {
  var self = this;

  return function(item) {
    var needRemove = context === item.context;

    if (needRemove) {
      self._forgetContext(item.context);
    }

    return needRemove;
  };
};

/**
 * Get matcher for unbind specific hander, context pair events
 * @param {function} handler - handler function
 * @param {object} context - context
 * @returns {function} handler, context matcher
 * @private
 */
CustomEvents.prototype._matchHandlerAndContext = function(handler, context) {
  var self = this;

  return function(item) {
    var matchHandler = (handler === item.handler);
    var matchContext = (context === item.context);
    var needRemove = (matchHandler && matchContext);

    if (needRemove) {
      self._forgetContext(item.context);
    }

    return needRemove;
  };
};

/**
 * Unbind event by event name
 * @param {string} eventName - custom event name to unbind
 * @param {function} [handler] - handler function
 * @private
 */
CustomEvents.prototype._offByEventName = function(eventName, handler) {
  var self = this;
  var andByHandler = isFunction(handler);
  var matchHandler = self._matchHandler(handler);

  eventName = eventName.split(R_EVENTNAME_SPLIT);

  forEach(eventName, function(name) {
    var handlerItems = self._safeEvent(name);

    if (andByHandler) {
      self._spliceMatches(handlerItems, matchHandler);
    } else {
      forEach(handlerItems, function(item) {
        self._forgetContext(item.context);
      });

      self.events[name] = [];
    }
  });
};

/**
 * Unbind event by handler function
 * @param {function} handler - handler function
 * @private
 */
CustomEvents.prototype._offByHandler = function(handler) {
  var self = this;
  var matchHandler = this._matchHandler(handler);

  forEach(this._safeEvent(), function(handlerItems) {
    self._spliceMatches(handlerItems, matchHandler);
  });
};

/**
 * Unbind event by object(name: handler pair object or context object)
 * @param {object} obj - context or {name: handler} pair object
 * @param {function} handler - handler function
 * @private
 */
CustomEvents.prototype._offByObject = function(obj, handler) {
  var self = this;
  var matchFunc;

  if (this._indexOfContext(obj) < 0) {
    forEach(obj, function(func, name) {
      self.off(name, func);
    });
  } else if (isString(handler)) {
    matchFunc = this._matchContext(obj);

    self._spliceMatches(this._safeEvent(handler), matchFunc);
  } else if (isFunction(handler)) {
    matchFunc = this._matchHandlerAndContext(handler, obj);

    forEach(this._safeEvent(), function(handlerItems) {
      self._spliceMatches(handlerItems, matchFunc);
    });
  } else {
    matchFunc = this._matchContext(obj);

    forEach(this._safeEvent(), function(handlerItems) {
      self._spliceMatches(handlerItems, matchFunc);
    });
  }
};

/**
 * Unbind custom events
 * @param {(string|object|function)} eventName - event name or context or
 *  {name: handler} pair object or handler function
 * @param {(function)} handler - handler function
 * @example
 * //-- #1. Get Module --//
 * var CustomEvents = require('tui-code-snippet/customEvents/customEvents'); // node, commonjs
 *
 * //-- #2. Use method --//
 * // # 2.1 off by event name
 * CustomEvents.off('onload');
 *
 * // # 2.2 off by event name and handler
 * CustomEvents.off('play', handler);
 *
 * // # 2.3 off by handler
 * CustomEvents.off(handler);
 *
 * // # 2.4 off by context
 * CustomEvents.off(myObj);
 *
 * // # 2.5 off by context and handler
 * CustomEvents.off(myObj, handler);
 *
 * // # 2.6 off by context and event name
 * CustomEvents.off(myObj, 'onload');
 *
 * // # 2.7 off by an Object.<string, function> that is {eventName: handler}
 * CustomEvents.off({
 *   'play': handler,
 *   'pause': handler2
 * });
 *
 * // # 2.8 off the all events
 * CustomEvents.off();
 */
CustomEvents.prototype.off = function(eventName, handler) {
  if (isString(eventName)) {
    // [syntax 1, 2]
    this._offByEventName(eventName, handler);
  } else if (!arguments.length) {
    // [syntax 8]
    this.events = {};
    this.contexts = [];
  } else if (isFunction(eventName)) {
    // [syntax 3]
    this._offByHandler(eventName);
  } else if (isObject(eventName)) {
    // [syntax 4, 5, 6]
    this._offByObject(eventName, handler);
  }
};

/**
 * Fire custom event
 * @param {string} eventName - name of custom event
 */
CustomEvents.prototype.fire = function(eventName) {  // eslint-disable-line
  this.invoke.apply(this, arguments);
};

/**
 * Fire a event and returns the result of operation 'boolean AND' with all
 *  listener's results.
 *
 * So, It is different from {@link CustomEvents#fire}.
 *
 * In service code, use this as a before event in component level usually
 *  for notifying that the event is cancelable.
 * @param {string} eventName - Custom event name
 * @param {...*} data - Data for event
 * @returns {boolean} The result of operation 'boolean AND'
 * @example
 * var map = new Map();
 * map.on({
 *     'beforeZoom': function() {
 *         // It should cancel the 'zoom' event by some conditions.
 *         if (that.disabled && this.getState()) {
 *             return false;
 *         }
 *         return true;
 *     }
 * });
 *
 * if (this.invoke('beforeZoom')) {    // check the result of 'beforeZoom'
 *     // if true,
 *     // doSomething
 * }
 */
CustomEvents.prototype.invoke = function(eventName) {
  var events, args, index, item;

  if (!this.hasListener(eventName)) {
    return true;
  }

  events = this._safeEvent(eventName);
  args = Array.prototype.slice.call(arguments, 1);
  index = 0;

  while (events[index]) {
    item = events[index];

    if (item.handler.apply(item.context, args) === false) {
      return false;
    }

    index += 1;
  }

  return true;
};

/**
 * Return whether at least one of the handlers is registered in the given
 *  event name.
 * @param {string} eventName - Custom event name
 * @returns {boolean} Is there at least one handler in event name?
 */
CustomEvents.prototype.hasListener = function(eventName) {
  return this.getListenerLength(eventName) > 0;
};

/**
 * Return a count of events registered.
 * @param {string} eventName - Custom event name
 * @returns {number} number of event
 */
CustomEvents.prototype.getListenerLength = function(eventName) {
  var events = this._safeEvent(eventName);

  return events.length;
};

module.exports = CustomEvents;


/***/ }),
/* 9 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Execute the provided callback once for each property of object(or element of array) which actually exist.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var isArray = __webpack_require__(6);
var forEachArray = __webpack_require__(2);
var forEachOwnProperties = __webpack_require__(23);

/**
 * @module collection
 */

/**
 * Execute the provided callback once for each property of object(or element of array) which actually exist.
 * If the object is Array-like object(ex-arguments object), It needs to transform to Array.(see 'ex2' of example).
 * If the callback function returns false, the loop will be stopped.
 * Callback function(iteratee) is invoked with three arguments:
 *  1) The value of the property(or The value of the element)
 *  2) The name of the property(or The index of the element)
 *  3) The object being traversed
 * @param {Object} obj The object that will be traversed
 * @param {function} iteratee Callback function
 * @param {Object} [context] Context(this) of callback function
 * @memberof module:collection
 * @example
 * var forEach = require('tui-code-snippet/collection/forEach'); // node, commonjs
 *
 * var sum = 0;
 *
 * forEach([1,2,3], function(value){
 *     sum += value;
 * });
 * alert(sum); // 6
 *
 * // In case of Array-like object
 * var array = Array.prototype.slice.call(arrayLike); // change to array
 * forEach(array, function(value){
 *     sum += value;
 * });
 */
function forEach(obj, iteratee, context) {
  if (isArray(obj)) {
    forEachArray(obj, iteratee, context);
  } else {
    forEachOwnProperties(obj, iteratee, context);
  }
}

module.exports = forEach;


/***/ }),
/* 10 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Default locale texts
 * @author NHN. FE Development Lab <dl_javascript@nhn.com>
 */



module.exports = {
  en: {
    titles: {
      DD: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
      D: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
      MMM: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
      MMMM: [
        'January',
        'February',
        'March',
        'April',
        'May',
        'June',
        'July',
        'August',
        'September',
        'October',
        'November',
        'December'
      ]
    },
    titleFormat: 'MMMM yyyy',
    todayFormat: 'To\\d\\ay: DD, MMMM d, yyyy',
    time: 'Time',
    date: 'Date'
  },
  ko: {
    titles: {
      DD: ['일요일', '월요일', '화요일', '수요일', '목요일', '금요일', '토요일'],
      D: ['일', '월', '화', '수', '목', '금', '토'],
      MMM: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
      MMMM: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월']
    },
    titleFormat: 'yyyy.MM',
    todayFormat: '오늘: yyyy.MM.dd (D)',
    date: '날짜',
    time: '시간'
  }
};


/***/ }),
/* 11 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Convert text by binding expressions with context.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var inArray = __webpack_require__(3);
var forEach = __webpack_require__(9);
var isArray = __webpack_require__(6);
var isString = __webpack_require__(13);
var extend = __webpack_require__(7);

// IE8 does not support capture groups.
var EXPRESSION_REGEXP = /{{\s?|\s?}}/g;
var BRACKET_NOTATION_REGEXP = /^[a-zA-Z0-9_@]+\[[a-zA-Z0-9_@"']+\]$/;
var BRACKET_REGEXP = /\[\s?|\s?\]/;
var DOT_NOTATION_REGEXP = /^[a-zA-Z_]+\.[a-zA-Z_]+$/;
var DOT_REGEXP = /\./;
var STRING_NOTATION_REGEXP = /^["']\w+["']$/;
var STRING_REGEXP = /"|'/g;
var NUMBER_REGEXP = /^-?\d+\.?\d*$/;

var EXPRESSION_INTERVAL = 2;

var BLOCK_HELPERS = {
  'if': handleIf,
  'each': handleEach,
  'with': handleWith
};

var isValidSplit = 'a'.split(/a/).length === 3;

/**
 * Split by RegExp. (Polyfill for IE8)
 * @param {string} text - text to be splitted\
 * @param {RegExp} regexp - regular expression
 * @returns {Array.<string>}
 */
var splitByRegExp = (function() {
  if (isValidSplit) {
    return function(text, regexp) {
      return text.split(regexp);
    };
  }

  return function(text, regexp) {
    var result = [];
    var prevIndex = 0;
    var match, index;

    if (!regexp.global) {
      regexp = new RegExp(regexp, 'g');
    }

    match = regexp.exec(text);
    while (match !== null) {
      index = match.index;
      result.push(text.slice(prevIndex, index));

      prevIndex = index + match[0].length;
      match = regexp.exec(text);
    }
    result.push(text.slice(prevIndex));

    return result;
  };
})();

/**
 * Find value in the context by an expression.
 * @param {string} exp - an expression
 * @param {object} context - context
 * @returns {*}
 * @private
 */
// eslint-disable-next-line complexity
function getValueFromContext(exp, context) {
  var splitedExps;
  var value = context[exp];

  if (exp === 'true') {
    value = true;
  } else if (exp === 'false') {
    value = false;
  } else if (STRING_NOTATION_REGEXP.test(exp)) {
    value = exp.replace(STRING_REGEXP, '');
  } else if (BRACKET_NOTATION_REGEXP.test(exp)) {
    splitedExps = exp.split(BRACKET_REGEXP);
    value = getValueFromContext(splitedExps[0], context)[getValueFromContext(splitedExps[1], context)];
  } else if (DOT_NOTATION_REGEXP.test(exp)) {
    splitedExps = exp.split(DOT_REGEXP);
    value = getValueFromContext(splitedExps[0], context)[splitedExps[1]];
  } else if (NUMBER_REGEXP.test(exp)) {
    value = parseFloat(exp);
  }

  return value;
}

/**
 * Extract elseif and else expressions.
 * @param {Array.<string>} ifExps - args of if expression
 * @param {Array.<string>} sourcesInsideBlock - sources inside if block
 * @returns {object} - exps: expressions of if, elseif, and else / sourcesInsideIf: sources inside if, elseif, and else block.
 * @private
 */
function extractElseif(ifExps, sourcesInsideBlock) {
  var exps = [ifExps];
  var sourcesInsideIf = [];
  var otherIfCount = 0;
  var start = 0;

  // eslint-disable-next-line complexity
  forEach(sourcesInsideBlock, function(source, index) {
    if (source.indexOf('if') === 0) {
      otherIfCount += 1;
    } else if (source === '/if') {
      otherIfCount -= 1;
    } else if (!otherIfCount && (source.indexOf('elseif') === 0 || source === 'else')) {
      exps.push(source === 'else' ? ['true'] : source.split(' ').slice(1));
      sourcesInsideIf.push(sourcesInsideBlock.slice(start, index));
      start = index + 1;
    }
  });

  sourcesInsideIf.push(sourcesInsideBlock.slice(start));

  return {
    exps: exps,
    sourcesInsideIf: sourcesInsideIf
  };
}

/**
 * Helper function for "if". 
 * @param {Array.<string>} exps - array of expressions split by spaces
 * @param {Array.<string>} sourcesInsideBlock - array of sources inside the if block
 * @param {object} context - context
 * @returns {string}
 * @private
 */
function handleIf(exps, sourcesInsideBlock, context) {
  var analyzed = extractElseif(exps, sourcesInsideBlock);
  var result = false;
  var compiledSource = '';

  forEach(analyzed.exps, function(exp, index) {
    result = handleExpression(exp, context);
    if (result) {
      compiledSource = compile(analyzed.sourcesInsideIf[index], context);
    }

    return !result;
  });

  return compiledSource;
}

/**
 * Helper function for "each".
 * @param {Array.<string>} exps - array of expressions split by spaces
 * @param {Array.<string>} sourcesInsideBlock - array of sources inside the each block
 * @param {object} context - context
 * @returns {string}
 * @private
 */
function handleEach(exps, sourcesInsideBlock, context) {
  var collection = handleExpression(exps, context);
  var additionalKey = isArray(collection) ? '@index' : '@key';
  var additionalContext = {};
  var result = '';

  forEach(collection, function(item, key) {
    additionalContext[additionalKey] = key;
    additionalContext['@this'] = item;
    extend(context, additionalContext);

    result += compile(sourcesInsideBlock.slice(), context);
  });

  return result;
}

/**
 * Helper function for "with ... as"
 * @param {Array.<string>} exps - array of expressions split by spaces
 * @param {Array.<string>} sourcesInsideBlock - array of sources inside the with block
 * @param {object} context - context
 * @returns {string}
 * @private
 */
function handleWith(exps, sourcesInsideBlock, context) {
  var asIndex = inArray('as', exps);
  var alias = exps[asIndex + 1];
  var result = handleExpression(exps.slice(0, asIndex), context);

  var additionalContext = {};
  additionalContext[alias] = result;

  return compile(sourcesInsideBlock, extend(context, additionalContext)) || '';
}

/**
 * Extract sources inside block in place.
 * @param {Array.<string>} sources - array of sources
 * @param {number} start - index of start block
 * @param {number} end - index of end block
 * @returns {Array.<string>}
 * @private
 */
function extractSourcesInsideBlock(sources, start, end) {
  var sourcesInsideBlock = sources.splice(start + 1, end - start);
  sourcesInsideBlock.pop();

  return sourcesInsideBlock;
}

/**
 * Handle block helper function
 * @param {string} helperKeyword - helper keyword (ex. if, each, with)
 * @param {Array.<string>} sourcesToEnd - array of sources after the starting block
 * @param {object} context - context
 * @returns {Array.<string>}
 * @private
 */
function handleBlockHelper(helperKeyword, sourcesToEnd, context) {
  var executeBlockHelper = BLOCK_HELPERS[helperKeyword];
  var helperCount = 1;
  var startBlockIndex = 0;
  var endBlockIndex;
  var index = startBlockIndex + EXPRESSION_INTERVAL;
  var expression = sourcesToEnd[index];

  while (helperCount && isString(expression)) {
    if (expression.indexOf(helperKeyword) === 0) {
      helperCount += 1;
    } else if (expression.indexOf('/' + helperKeyword) === 0) {
      helperCount -= 1;
      endBlockIndex = index;
    }

    index += EXPRESSION_INTERVAL;
    expression = sourcesToEnd[index];
  }

  if (helperCount) {
    throw Error(helperKeyword + ' needs {{/' + helperKeyword + '}} expression.');
  }

  sourcesToEnd[startBlockIndex] = executeBlockHelper(
    sourcesToEnd[startBlockIndex].split(' ').slice(1),
    extractSourcesInsideBlock(sourcesToEnd, startBlockIndex, endBlockIndex),
    context
  );

  return sourcesToEnd;
}

/**
 * Helper function for "custom helper".
 * If helper is not a function, return helper itself.
 * @param {Array.<string>} exps - array of expressions split by spaces (first element: helper)
 * @param {object} context - context
 * @returns {string}
 * @private
 */
function handleExpression(exps, context) {
  var result = getValueFromContext(exps[0], context);

  if (result instanceof Function) {
    return executeFunction(result, exps.slice(1), context);
  }

  return result;
}

/**
 * Execute a helper function.
 * @param {Function} helper - helper function
 * @param {Array.<string>} argExps - expressions of arguments
 * @param {object} context - context
 * @returns {string} - result of executing the function with arguments
 * @private
 */
function executeFunction(helper, argExps, context) {
  var args = [];
  forEach(argExps, function(exp) {
    args.push(getValueFromContext(exp, context));
  });

  return helper.apply(null, args);
}

/**
 * Get a result of compiling an expression with the context.
 * @param {Array.<string>} sources - array of sources split by regexp of expression.
 * @param {object} context - context
 * @returns {Array.<string>} - array of sources that bind with its context
 * @private
 */
function compile(sources, context) {
  var index = 1;
  var expression = sources[index];
  var exps, firstExp, result;

  while (isString(expression)) {
    exps = expression.split(' ');
    firstExp = exps[0];

    if (BLOCK_HELPERS[firstExp]) {
      result = handleBlockHelper(firstExp, sources.splice(index, sources.length - index), context);
      sources = sources.concat(result);
    } else {
      sources[index] = handleExpression(exps, context);
    }

    index += EXPRESSION_INTERVAL;
    expression = sources[index];
  }

  return sources.join('');
}

/**
 * Convert text by binding expressions with context.
 * <br>
 * If expression exists in the context, it will be replaced.
 * ex) '{{title}}' with context {title: 'Hello!'} is converted to 'Hello!'.
 * An array or object can be accessed using bracket and dot notation.
 * ex) '{{odds\[2\]}}' with context {odds: \[1, 3, 5\]} is converted to '5'.
 * ex) '{{evens\[first\]}}' with context {evens: \[2, 4\], first: 0} is converted to '2'.
 * ex) '{{project\["name"\]}}' and '{{project.name}}' with context {project: {name: 'CodeSnippet'}} is converted to 'CodeSnippet'.
 * <br>
 * If replaced expression is a function, next expressions will be arguments of the function.
 * ex) '{{add 1 2}}' with context {add: function(a, b) {return a + b;}} is converted to '3'.
 * <br>
 * It has 3 predefined block helpers '{{helper ...}} ... {{/helper}}': 'if', 'each', 'with ... as ...'.
 * 1) 'if' evaluates conditional statements. It can use with 'elseif' and 'else'.
 * 2) 'each' iterates an array or object. It provides '@index'(array), '@key'(object), and '@this'(current element).
 * 3) 'with ... as ...' provides an alias.
 * @param {string} text - text with expressions
 * @param {object} context - context
 * @returns {string} - text that bind with its context
 * @memberof module:domUtil
 * @example
 * var template = require('tui-code-snippet/domUtil/template');
 * 
 * var source = 
 *     '<h1>'
 *   +   '{{if isValidNumber title}}'
 *   +     '{{title}}th'
 *   +   '{{elseif isValidDate title}}'
 *   +     'Date: {{title}}'
 *   +   '{{/if}}'
 *   + '</h1>'
 *   + '{{each list}}'
 *   +   '{{with addOne @index as idx}}'
 *   +     '<p>{{idx}}: {{@this}}</p>'
 *   +   '{{/with}}'
 *   + '{{/each}}';
 * 
 * var context = {
 *   isValidDate: function(text) {
 *     return /^\d{4}-(0|1)\d-(0|1|2|3)\d$/.test(text);
 *   },
 *   isValidNumber: function(text) {
 *     return /^\d+$/.test(text);
 *   }
 *   title: '2019-11-25',
 *   list: ['Clean the room', 'Wash the dishes'],
 *   addOne: function(num) {
 *     return num + 1;
 *   }
 * };
 * 
 * var result = template(source, context);
 * console.log(result); // <h1>Date: 2019-11-25</h1><p>1: Clean the room</p><p>2: Wash the dishes</p>
 */
function template(text, context) {
  return compile(splitByRegExp(text, EXPRESSION_REGEXP), context);
}

module.exports = template;


/***/ }),
/* 12 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Check whether the given variable is undefined or not.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



/**
 * Check whether the given variable is undefined or not.
 * If the given variable is undefined, returns true.
 * @param {*} obj - Target for checking
 * @returns {boolean} Is undefined?
 * @memberof module:type
 */
function isUndefined(obj) {
  return obj === undefined; // eslint-disable-line no-undefined
}

module.exports = isUndefined;


/***/ }),
/* 13 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Check whether the given variable is a string or not.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



/**
 * Check whether the given variable is a string or not.
 * If the given variable is a string, return true.
 * @param {*} obj - Target for checking
 * @returns {boolean} Is string?
 * @memberof module:type
 */
function isString(obj) {
  return typeof obj === 'string' || obj instanceof String;
}

module.exports = isString;


/***/ }),
/* 14 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Remove element from parent node.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



/**
 * Remove element from parent node.
 * @param {HTMLElement} element - element to remove.
 * @memberof module:domUtil
 */
function removeElement(element) {
  if (element && element.parentNode) {
    element.parentNode.removeChild(element);
  }
}

module.exports = removeElement;


/***/ }),
/* 15 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Check whether the given variable is a number or not.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



/**
 * Check whether the given variable is a number or not.
 * If the given variable is a number, return true.
 * @param {*} obj - Target for checking
 * @returns {boolean} Is number?
 * @memberof module:type
 */
function isNumber(obj) {
  return typeof obj === 'number' || obj instanceof Number;
}

module.exports = isNumber;


/***/ }),
/* 16 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Add css class to element
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var forEach = __webpack_require__(9);
var inArray = __webpack_require__(3);
var getClass = __webpack_require__(17);
var setClassName = __webpack_require__(24);

/**
 * domUtil module
 * @module domUtil
 */

/**
 * Add css class to element
 * @param {(HTMLElement|SVGElement)} element - target element
 * @param {...string} cssClass - css classes to add
 * @memberof module:domUtil
 */
function addClass(element) {
  var cssClass = Array.prototype.slice.call(arguments, 1);
  var classList = element.classList;
  var newClass = [];
  var origin;

  if (classList) {
    forEach(cssClass, function(name) {
      element.classList.add(name);
    });

    return;
  }

  origin = getClass(element);

  if (origin) {
    cssClass = [].concat(origin.split(/\s+/), cssClass);
  }

  forEach(cssClass, function(cls) {
    if (inArray(cls, newClass) < 0) {
      newClass.push(cls);
    }
  });

  setClassName(element, newClass);
}

module.exports = addClass;


/***/ }),
/* 17 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Get HTML element's design classes.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var isUndefined = __webpack_require__(12);

/**
 * Get HTML element's design classes.
 * @param {(HTMLElement|SVGElement)} element target element
 * @returns {string} element css class name
 * @memberof module:domUtil
 */
function getClass(element) {
  if (!element || !element.className) {
    return '';
  }

  if (isUndefined(element.className.baseVal)) {
    return element.className;
  }

  return element.className.baseVal;
}

module.exports = getClass;


/***/ }),
/* 18 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Remove css class from element
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var forEachArray = __webpack_require__(2);
var inArray = __webpack_require__(3);
var getClass = __webpack_require__(17);
var setClassName = __webpack_require__(24);

/**
 * Remove css class from element
 * @param {(HTMLElement|SVGElement)} element - target element
 * @param {...string} cssClass - css classes to remove
 * @memberof module:domUtil
 */
function removeClass(element) {
  var cssClass = Array.prototype.slice.call(arguments, 1);
  var classList = element.classList;
  var origin, newClass;

  if (classList) {
    forEachArray(cssClass, function(name) {
      classList.remove(name);
    });

    return;
  }

  origin = getClass(element).split(/\s+/);
  newClass = [];
  forEachArray(origin, function(name) {
    if (inArray(name, cssClass) < 0) {
      newClass.push(name);
    }
  });

  setClassName(element, newClass);
}

module.exports = removeClass;


/***/ }),
/* 19 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Set mouse-touch event
 * @author NHN. FE Development Lab <dl_javascript@nhn.com>
 */



var on = __webpack_require__(31);
var off = __webpack_require__(33);

var mouseTouchEvent = {
  /**
   * Detect mobile browser
   * @type {boolean} Whether using Mobile browser
   * @private
   */
  _isMobile: (function() {
    return /Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile|WPDesktop/i.test(
      navigator.userAgent
    );
  })(),

  /**
   * Return a matched event type by a mouse event type
   * @param {string} type A mouse event type - mousedown, click
   * @returns {string}
   * @private
   */
  _getEventType: function(type) {
    if (this._isMobile) {
      if (type === 'mousedown') {
        type = 'touchstart';
      } else if (type === 'click') {
        type = 'touchend';
      }
    }

    return type;
  },

  /**
   * Bind touch or mouse events
   * @param {HTMLElement} element An element to bind
   * @param {string} type A mouse event type - mousedown, click
   * @param {Function} handler A handler function
   * @param {object} [context] A context for handler.
   */
  on: function(element, type, handler, context) {
    on(element, this._getEventType(type), handler, context);
  },

  /**
   * Unbind touch or mouse events
   * @param {HTMLElement} element - Target element
   * @param {string} type A mouse event type - mousedown, click
   * @param {Function} handler - Handler
   */
  off: function(element, type, handler) {
    off(element, this._getEventType(type), handler);
  }
};

module.exports = mouseTouchEvent;


/***/ }),
/* 20 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Layer base
 * @author NHN. FE Development Lab <dl_javascript@nhn.com>
 */



var defineClass = __webpack_require__(0);
var removeElement = __webpack_require__(14);

var localeText = __webpack_require__(10);
var DEFAULT_LANGUAGE_TYPE = __webpack_require__(1).DEFAULT_LANGUAGE_TYPE;

/**
 * @abstract
 * @class
 * @ignore
 * @param {string} language - Initial language
 * Layer base
 */
var LayerBase = defineClass(
  /** @lends LayerBase.prototype */ {
    init: function(language) {
      language = language || DEFAULT_LANGUAGE_TYPE;

      /**
       * Layer element
       * @type {HTMLElement}
       * @private
       */
      this._element = null;

      /**
       * Language type
       * @type {string}
       * @private
       */
      this._localeText = localeText[language];

      /**
       * Layer type
       * @type {string}
       * @private
       */
      this._type = 'base';
    },

    /**
     * Make context
     * @abstract
     * @throws {Error}
     * @returns {object}
     * @private
     */
    _makeContext: function() {
      throwOverrideError(this.getType(), '_makeContext');
    },

    /**
     * Render the layer element
     * @abstract
     * @throws {Error}
     */
    render: function() {
      throwOverrideError(this.getType(), 'render');
    },

    /**
     * Returns date elements
     * @abstract
     * @throws {Error}
     * @returns {HTMLElement[]}
     */
    getDateElements: function() {
      throwOverrideError(this.getType(), 'getDateElements');
    },

    /**
     * Returns layer type
     * @returns {string}
     */
    getType: function() {
      return this._type;
    },

    /**
     * Set language
     * @param {string} language - Language name
     */
    changeLanguage: function(language) {
      this._localeText = localeText[language];
    },

    /**
     * Remove elements
     */
    remove: function() {
      if (this._element) {
        removeElement(this._element);
      }
      this._element = null;
    }
  }
);

/**
 * Throw - method override error
 * @ignore
 * @param {string} layerType - Layer type
 * @param {string} methodName - Method name
 * @throws {Error}
 */
function throwOverrideError(layerType, methodName) {
  throw new Error(layerType + ' layer does not have the "' + methodName + '" method.');
}

module.exports = LayerBase;


/***/ }),
/* 21 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview DatePicker component
 * @author NHN. FE dev Lab <dl_javascript@nhn.com>
 */



var inArray = __webpack_require__(3);
var forEachArray = __webpack_require__(2);
var defineClass = __webpack_require__(0);
var CustomEvents = __webpack_require__(8);
var addClass = __webpack_require__(16);
var closest = __webpack_require__(25);
var getData = __webpack_require__(26);
var hasClass = __webpack_require__(27);
var removeClass = __webpack_require__(18);
var removeElement = __webpack_require__(14);
var extend = __webpack_require__(7);
var isArray = __webpack_require__(6);
var isDate = __webpack_require__(28);
var isNumber = __webpack_require__(15);
var isObject = __webpack_require__(22);

var TimePicker = __webpack_require__(43);

var Calendar = __webpack_require__(29);
var RangeModel = __webpack_require__(56);
var constants = __webpack_require__(1);
var localeTexts = __webpack_require__(10);
var dateUtil = __webpack_require__(5);
var util = __webpack_require__(4);
var mouseTouchEvent = __webpack_require__(19);
var tmpl = __webpack_require__(58);
var DatePickerInput = __webpack_require__(59);

var DEFAULT_WEEK_START_DAY = constants.DEFAULT_WEEK_START_DAY;
var DEFAULT_LANGUAGE_TYPE = constants.DEFAULT_LANGUAGE_TYPE;
var TYPE_DATE = constants.TYPE_DATE;
var TYPE_MONTH = constants.TYPE_MONTH;
var TYPE_YEAR = constants.TYPE_YEAR;
var CLASS_NAME_NEXT_YEAR_BTN = constants.CLASS_NAME_NEXT_YEAR_BTN;
var CLASS_NAME_NEXT_MONTH_BTN = constants.CLASS_NAME_NEXT_MONTH_BTN;
var CLASS_NAME_PREV_YEAR_BTN = constants.CLASS_NAME_PREV_YEAR_BTN;
var CLASS_NAME_PREV_MONTH_BTN = constants.CLASS_NAME_PREV_MONTH_BTN;
var CLASS_NAME_SELECTED = constants.CLASS_NAME_SELECTED;
var CLASS_NAME_TITLE_TODAY = constants.CLASS_NAME_TITLE_TODAY;

var CLASS_NAME_SELECTABLE = 'tui-is-selectable';
var CLASS_NAME_BLOCKED = 'tui-is-blocked';
var CLASS_NAME_CHECKED = 'tui-is-checked';
var CLASS_NAME_SELECTOR_BUTTON = 'tui-datepicker-selector-button';
var CLASS_NAME_TODAY = 'tui-calendar-today';
var CLASS_NAME_HIDDEN = 'tui-hidden';

var SELECTOR_BODY = '.tui-datepicker-body';
var SELECTOR_DATE_ICO = '.tui-ico-date';
var SELECTOR_CALENDAR_TITLE = '.tui-calendar-title';
var SELECTOR_CALENDAR_CONTAINER = '.tui-calendar-container';
var SELECTOR_TIMEPICKER_CONTAINER = '.tui-timepicker-container';

/**
 * Merge default option
 * @ignore
 * @param {object} option - DatePicker option
 * @returns {object}
 */
var mergeDefaultOption = function(option) {
  option = extend(
    {
      language: DEFAULT_LANGUAGE_TYPE,
      calendar: {},
      input: {
        element: null,
        format: null
      },
      timePicker: null,
      date: null,
      showAlways: false,
      type: TYPE_DATE,
      selectableRanges: null,
      openers: [],
      autoClose: true,
      usageStatistics: true,
      weekStartDay: DEFAULT_WEEK_START_DAY
    },
    option
  );

  option.selectableRanges = option.selectableRanges || [[constants.MIN_DATE, constants.MAX_DATE]];

  if (!isObject(option.calendar)) {
    throw new Error('Calendar option must be an object');
  }
  if (!isObject(option.input)) {
    throw new Error('Input option must be an object');
  }
  if (!isArray(option.selectableRanges)) {
    throw new Error('Selectable-ranges must be a 2d-array');
  }

  option.localeText = localeTexts[option.language];

  // override calendar option
  option.calendar.language = option.language;
  option.calendar.type = option.type;

  // @TODO: after v5.0.0, remove option.timepicker
  option.timePicker = option.timePicker || option.timepicker;

  return option;
};

/**
 * @class
 * @description
 * Create a date picker.
 * @see {@link /tutorial-example01-basic DatePicker example}
 * @param {HTMLElement|string} container - Container element or selector of DatePicker
 * @param {Object} [options] - Options
 *      @param {Date|number} [options.date = null] - Initial date. Set by a Date instance or a number(timestamp). (default: no initial date)
 *      @param {('date'|'month'|'year')} [options.type = 'date'] - DatePicker type. Determine whether to choose a date, month, or year.
 *      @param {string} [options.language='en'] - Language code. English('en') and Korean('ko') are provided as default. To set to the other languages, use {@link DatePicker#localeTexts DatePicker.localeTexts}.
 *      @param {object|boolean} [options.timePicker] - [TimePicker](https://nhn.github.io/tui.time-picker/latest) options. Refer to the [TimePicker instance's options](https://nhn.github.io/tui.time-picker/latest/TimePicker). To create the TimePicker without customization, set to true.
 *      @param {object} [options.calendar] - {@link Calendar} options. Refer to the {@link Calendar Calendar instance's options}.
 *      @param {object} [options.input] - Input option
 *      @param {HTMLElement|string} [options.input.element] - Input element or selector
 *      @param {string} [options.input.format = 'yyyy-mm-dd'] - Format of the Date string
 *      @param {Array.<Array.<Date|number>>} [options.selectableRanges = 1900/1/1 ~ 2999/12/31]
 *        - Ranges of selectable date. Set by Date instances or numbers(timestamp).
 *      @param {Array<HTMLElement|string>} [options.openers = []] - List of the openers to open the DatePicker (example - icon, button, etc.)
 *      @param {boolean} [options.showAlways = false] - Show the DatePicker always
 *      @param {boolean} [options.autoClose = true] - Close the DatePicker after clicking the date
 *      @param {boolean} [options.usageStatistics = true] - Send a hostname to Google Analytics (default: true)
 *      @param {string} [options.weekStartDay = 'Sun'] - Start of the week. 'Sun', 'Mon', ..., 'Sat'(default: 'Sun'(start on Sunday))
 * @example
 * // ES6
 * import DatePicker from 'tui-date-picker'
 *
 * // CommonJS
 * const DatePicker = require('tui-date-picker');
 *
 * // Browser
 * const DatePicker = tui.DatePicker;
 *
 * const range1 = [new Date(2015, 2, 1), new Date(2015, 3, 1)];
 * const range2 = [1465570800000, 1481266182155]; // timestamps
 *
 * const picker1 = new DatePicker('#datepicker-container1', {
 *     showAlways: true
 * });
 *
 * const picker2 = new DatePicker('#datepicker-container2', {
 *    showAlways: true,
 *    timePicker: true
 * });
 *
 * const picker3 = new DatePicker('#datepicker-container3', {
 *     language: 'ko',
 *     calendar: {
 *         showToday: true
 *     },
 *     timePicker: {
 *         showMeridiem: true,
 *         defaultHour: 13,
 *         defaultMinute: 24
 *     },
 *     input: {
 *         element: '#datepicker-input',
 *         format: 'yyyy년 MM월 dd일 hh:mm A'
 *     }
 *     type: 'date',
 *     date: new Date(2015, 0, 1)
 *     selectableRanges: [range1, range2],
 *     openers: ['#opener'],
 *     weekStartDay: 'Mon',
 * });
 */
var DatePicker = defineClass(
  /** @lends DatePicker.prototype */ {
    static: {
      /**
       * Locale text data. English('en') and Korean('ko') are provided as default.
       * @type {object}
       * @memberof DatePicker
       * @static
       * @example
       * DatePicker.localeTexts['customKey'] = {
       *     titles: {
       *         // days
       *         DD: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
       *         // daysShort
       *         D: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
       *         // months
       *         MMMM: [
       *             'January', 'February', 'March', 'April', 'May', 'June',
       *             'July', 'August', 'September', 'October', 'November', 'December'
       *         ],
       *         // monthsShort
       *         MMM: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
       *     },
       *     titleFormat: 'MMM yyyy',
       *     todayFormat: 'D, MMMM dd, yyyy',
       *     date: 'Date',
       *     time: 'Time'
       * };
       *
       * const datepicker = new DatePicker('#datepicker-container', {
       *     language: 'customKey'
       * });
       */
      localeTexts: localeTexts
    },
    init: function(container, options) {
      options = mergeDefaultOption(options);

      /**
       * Language type
       * @type {string}
       * @private
       */
      this._language = options.language;

      /**
       * DatePicker container
       * @type {HTMLElement}
       * @private
       */
      this._container = util.getElement(container);
      this._container.innerHTML = tmpl(
        extend(options, {
          isTab: options.timePicker && options.timePicker.layoutType === 'tab'
        })
      );

      /**
       * DatePicker element
       * @type {HTMLElement}
       * @private
       */
      this._element = this._container.firstChild;

      /**
       * Calendar instance
       * @type {Calendar}
       * @private
       */
      this._calendar = new Calendar(
        this._element.querySelector(SELECTOR_CALENDAR_CONTAINER),
        extend(options.calendar, {
          usageStatistics: options.usageStatistics,
          weekStartDay: options.weekStartDay
        })
      );

      /**
       * TimePicker instance
       * @type {TimePicker}
       * @private
       */
      this._timePicker = null;

      /**
       * DatePicker input
       * @type {DatePickerInput}
       * @private
       */
      this._datepickerInput = null;

      /**
       * Object having date values
       * @type {Date}
       * @private
       */
      this._date = null;

      /**
       * Selectable date-ranges model
       * @type {RangeModel}
       * @private
       */
      this._rangeModel = null;

      /**
       * openers - opener list
       * @type {Array}
       * @private
       */
      this._openers = [];

      /**
       * State of picker enable
       * @type {boolean}
       * @private
       */
      this._isEnabled = true;

      /**
       * ID of instance
       * @type {number}
       * @private
       */
      this._id = 'tui-datepicker-' + util.generateId();

      /**
       * DatePicker type
       * @type {TYPE_DATE|TYPE_MONTH|TYPE_YEAR}
       * @private
       */
      this._type = options.type;

      /**
       * Show always or not
       * @type {boolean}
       */
      this.showAlways = options.showAlways;

      /**
       * Close after select a date
       * @type {boolean}
       */
      this.autoClose = options.autoClose;

      this._initializeDatePicker(options);
    },

    /**
     * Initialize method
     * @param {Object} option - user option
     * @private
     */
    _initializeDatePicker: function(option) {
      this.setRanges(option.selectableRanges);
      this._setEvents();
      this._initTimePicker(option.timePicker, option.usageStatistics);
      this.setInput(option.input.element);
      this.setDateFormat(option.input.format);
      this.setDate(option.date);

      forEachArray(option.openers, this.addOpener, this);
      if (!this.showAlways) {
        this._hide();
      }

      if (this.getType() === TYPE_DATE) {
        addClass(this._element.querySelector(SELECTOR_BODY), 'tui-datepicker-type-date');
      }
    },

    /**
     * Set events on the date picker's element
     * @param {object} option - Constructor option
     * @private
     */
    _setEvents: function() {
      mouseTouchEvent.on(this._element, 'click', this._onClickHandler, this);
      this._calendar.on('draw', this._onDrawCalendar, this);
    },

    /**
     * Remove events on the date picker's element
     * @private
     */
    _removeEvents: function() {
      mouseTouchEvent.off(this._element, 'click', this._onClickHandler, this);
      this._calendar.off();
    },

    /**
     * Set events on the document
     * @private
     */
    _setDocumentEvents: function() {
      mouseTouchEvent.on(document, 'mousedown', this._onMousedownDocument, this);
    },

    /**
     * Remove events on the document
     * @private
     */
    _removeDocumentEvents: function() {
      mouseTouchEvent.off(document, 'mousedown', this._onMousedownDocument);
    },

    /**
     * Set events on the opener
     * @param {HTMLElement} opener An opener to bind the events
     * @private
     */
    _setOpenerEvents: function(opener) {
      mouseTouchEvent.on(opener, 'click', this.toggle, this);
    },

    /**
     * Remove events on the opener
     * @param {HTMLElement} opener An opener to unbind the events
     * @private
     */
    _removeOpenerEvents: function(opener) {
      mouseTouchEvent.off(opener, 'click', this.toggle);
    },

    /**
     * Set TimePicker instance
     * @param {object|boolean} opTimePicker - TimePicker instance options
     * @param {boolean} usageStatistics - GA tracking options
     * @private
     */
    _initTimePicker: function(opTimePicker, usageStatistics) {
      var layoutType;
      if (!opTimePicker) {
        return;
      }

      layoutType = opTimePicker.layoutType || '';

      if (isObject(opTimePicker)) {
        opTimePicker.usageStatistics = usageStatistics;
      } else {
        opTimePicker = {
          usageStatistics: usageStatistics
        };
      }

      this._timePicker = new TimePicker(
        this._element.querySelector(SELECTOR_TIMEPICKER_CONTAINER),
        opTimePicker
      );

      if (layoutType.toLowerCase() === 'tab') {
        this._timePicker.hide();
      }

      this._timePicker.on(
        'change',
        function(ev) {
          var prevDate;
          if (this._date) {
            prevDate = new Date(this._date);
            this.setDate(prevDate.setHours(ev.hour, ev.minute));
          }
        },
        this
      );
    },

    /**
     * Change picker's type by a selector button.
     * @param {HTMLElement} target A target element
     * @private
     */
    _changePicker: function(target) {
      var btnSelector = '.' + CLASS_NAME_SELECTOR_BUTTON;
      var selectedBtn = closest(target, btnSelector);
      var isDateElement = !!selectedBtn.querySelector(SELECTOR_DATE_ICO);

      if (isDateElement) {
        this._calendar.show();
        this._timePicker.hide();
      } else {
        this._calendar.hide();
        this._timePicker.show();
      }
      removeClass(this._element.querySelector('.' + CLASS_NAME_CHECKED), CLASS_NAME_CHECKED);
      addClass(selectedBtn, CLASS_NAME_CHECKED);
    },

    /**
     * Returns whether the element is opener
     * @param {string|HTMLElement} element - Element or selector
     * @returns {boolean}
     * @private
     */
    _isOpener: function(element) {
      var el = util.getElement(element);

      return inArray(el, this._openers) > -1;
    },

    /**
     * add/remove today-class-name to date element
     * @param {HTMLElement} el - date element
     * @private
     */
    _setTodayClassName: function(el) {
      var timestamp, isToday;

      if (this.getCalendarType() !== TYPE_DATE) {
        return;
      }

      timestamp = Number(getData(el, 'timestamp'));
      isToday = timestamp === new Date().setHours(0, 0, 0, 0);

      if (isToday) {
        addClass(el, CLASS_NAME_TODAY);
      } else {
        removeClass(el, CLASS_NAME_TODAY);
      }
    },

    /**
     * add/remove selectable-class-name to date element
     * @param {HTMLElement} el - date element
     * @private
     */
    _setSelectableClassName: function(el) {
      var elDate = new Date(Number(getData(el, 'timestamp')));

      if (this._isSelectableOnCalendar(elDate)) {
        addClass(el, CLASS_NAME_SELECTABLE);
        removeClass(el, CLASS_NAME_BLOCKED);
      } else {
        removeClass(el, CLASS_NAME_SELECTABLE);
        addClass(el, CLASS_NAME_BLOCKED);
      }
    },

    /**
     * add/remove selected-class-name to date element
     * @param {HTMLElement} el - date element
     * @private
     */
    _setSelectedClassName: function(el) {
      var elDate = new Date(Number(getData(el, 'timestamp')));

      if (this._isSelectedOnCalendar(elDate)) {
        addClass(el, CLASS_NAME_SELECTED);
      } else {
        removeClass(el, CLASS_NAME_SELECTED);
      }
    },

    /**
     * Returns whether the date is selectable on calendar
     * @param {Date} date - Date instance
     * @returns {boolean}
     * @private
     */
    _isSelectableOnCalendar: function(date) {
      var type = this.getCalendarType();
      var start = dateUtil.cloneWithStartOf(date, type).getTime();
      var end = dateUtil.cloneWithEndOf(date, type).getTime();

      return this._rangeModel.hasOverlap(start, end);
    },

    /**
     * Returns whether the date is selected on calendar
     * @param {Date} date - Date instance
     * @returns {boolean}
     * @private
     */
    _isSelectedOnCalendar: function(date) {
      var curDate = this.getDate();
      var calendarType = this.getCalendarType();

      return curDate && dateUtil.isSame(curDate, date, calendarType);
    },

    /**
     * Show the date picker element
     * @private
     */
    _show: function() {
      removeClass(this._element, CLASS_NAME_HIDDEN);
    },

    /**
     * Hide the date picker element
     * @private
     */
    _hide: function() {
      addClass(this._element, CLASS_NAME_HIDDEN);
    },

    /**
     * Set value a date-string of current this instance to input element
     * @private
     */
    _syncToInput: function() {
      if (!this._date) {
        return;
      }

      this._datepickerInput.setDate(this._date);
    },

    /**
     * Set date from input value
     * @param {boolean} [shouldRollback = false] - Should rollback from unselectable or error
     * @private
     */
    _syncFromInput: function(shouldRollback) {
      var isFailed = false;
      var date;

      try {
        date = this._datepickerInput.getDate();

        if (this.isSelectable(date)) {
          if (this._timePicker) {
            this._timePicker.setTime(date.getHours(), date.getMinutes());
          }
          this.setDate(date);
        } else {
          isFailed = true;
        }
      } catch (err) {
        this.fire('error', {
          type: 'ParsingError',
          message: err.message
        });
        isFailed = true;
      } finally {
        if (isFailed) {
          if (shouldRollback) {
            this._syncToInput();
          } else {
            this.setNull();
          }
        }
      }
    },

    /**
     * Event handler for mousedown of document<br>
     * - When click the out of layer, close the layer
     * @param {Event} ev - Event object
     * @private
     */
    _onMousedownDocument: function(ev) {
      var target = util.getTarget(ev);
      var selector = util.getSelector(target);
      var isContain = selector ? this._element.querySelector(selector) : false;
      var isInput = this._datepickerInput.is(target);
      var isInOpener = inArray(target, this._openers) > -1;
      var shouldClose = !(this.showAlways || isInput || isContain || isInOpener);

      if (shouldClose) {
        this.close();
      }
    },

    /**
     * Event handler for click of calendar
     * @param {Event} ev An event object
     * @private
     */
    _onClickHandler: function(ev) {
      var target = util.getTarget(ev);

      if (closest(target, '.' + CLASS_NAME_SELECTABLE)) {
        ev.preventDefault();
        this._updateDate(target);
      } else if (closest(target, '.' + CLASS_NAME_TITLE_TODAY)) {
        ev.preventDefault();
        this._updateDateToToday();
      } else if (closest(target, SELECTOR_CALENDAR_TITLE)) {
        this.drawUpperCalendar(this._date);
      } else if (closest(target, '.' + CLASS_NAME_SELECTOR_BUTTON)) {
        this._changePicker(target);
      }
    },

    /**
     * Update date to today
     * @private
     */
    _updateDateToToday: function() {
      this.setDate(Date.now());
      this.close();
    },

    /**
     * Update date from event-target
     * @param {HTMLElement} target An event target element
     * @private
     */
    _updateDate: function(target) {
      var timestamp = Number(getData(target, 'timestamp'));
      var newDate = new Date(timestamp);
      var timePicker = this._timePicker;
      var prevDate = this._date;
      var calendarType = this.getCalendarType();
      var pickerType = this.getType();

      if (calendarType !== pickerType) {
        this.drawLowerCalendar(newDate);
      } else {
        if (timePicker) {
          newDate.setHours(timePicker.getHour(), timePicker.getMinute());
        } else if (prevDate) {
          newDate.setHours(prevDate.getHours(), prevDate.getMinutes());
        }
        this.setDate(newDate);

        if (!this.showAlways && this.autoClose) {
          this.close();
        }
      }
    },

    /**
     * Event handler for 'draw'-custom event of calendar
     * @param {Object} eventData - custom event data
     * @see {@link Calendar#draw}
     * @private
     */
    _onDrawCalendar: function(eventData) {
      forEachArray(
        eventData.dateElements,
        function(el) {
          this._setTodayClassName(el);
          this._setSelectableClassName(el);
          this._setSelectedClassName(el);
        },
        this
      );
      this._setDisplayHeadButtons();

      /**
       * Occur after the calendar is drawn.
       * @event DatePicker#draw
       * @see {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents#on datepicker.on()} to bind event handlers.
       * @see {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents#off datepicker.off()} to unbind event handlers.
       * @see Refer to {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents CustomEvents from tui-code-snippet} for more methods. DatePicker mixes in the methods from CustomEvents.
       * @property {Date} date - Calendar date
       * @property {('date'|'month'|'year')} type - Calendar type
       * @property {HTMLElement[]} dateElements - elements for dates
       * @example
       * // bind the 'draw' event
       * datepicker.on('draw', (event) => {
       *     console.log(`Draw the ${event.type} calendar and its date is ${event.date}.`);
       * });
       *
       * // unbind the 'draw' event
       * datepicker.off('draw');
       */
      this.fire('draw', eventData);
    },

    /**
     * Hide useless buttons (next, next-year, prev, prev-year)
     * @see Don't save buttons reference. The buttons are rerendered every "calendar.draw".
     * @private
     */
    _setDisplayHeadButtons: function() {
      var nextYearDate = this._calendar.getNextYearDate();
      var prevYearDate = this._calendar.getPrevYearDate();
      var maxTimestamp = this._rangeModel.getMaximumValue();
      var minTimestamp = this._rangeModel.getMinimumValue();
      var nextYearBtn = this._element.querySelector('.' + CLASS_NAME_NEXT_YEAR_BTN);
      var prevYearBtn = this._element.querySelector('.' + CLASS_NAME_PREV_YEAR_BTN);
      var nextMonthDate, prevMonthDate, nextMonBtn, prevMonBtn;

      if (this.getCalendarType() === TYPE_DATE) {
        nextMonthDate = dateUtil.cloneWithStartOf(this._calendar.getNextDate(), TYPE_MONTH);
        prevMonthDate = dateUtil.cloneWithEndOf(this._calendar.getPrevDate(), TYPE_MONTH);

        nextMonBtn = this._element.querySelector('.' + CLASS_NAME_NEXT_MONTH_BTN);
        prevMonBtn = this._element.querySelector('.' + CLASS_NAME_PREV_MONTH_BTN);

        this._setDisplay(nextMonBtn, nextMonthDate.getTime() <= maxTimestamp);
        this._setDisplay(prevMonBtn, prevMonthDate.getTime() >= minTimestamp);

        prevYearDate.setDate(1);
        nextYearDate.setDate(1);
      } else {
        prevYearDate.setMonth(12, 0);
        nextYearDate.setMonth(0, 1);
      }

      this._setDisplay(nextYearBtn, nextYearDate.getTime() <= maxTimestamp);
      this._setDisplay(prevYearBtn, prevYearDate.getTime() >= minTimestamp);
    },

    /**
     * Set display show/hide by condition
     * @param {HTMLElement} el - An Element
     * @param {boolean} shouldShow - Condition
     * @private
     */
    _setDisplay: function(el, shouldShow) {
      if (el) {
        if (shouldShow) {
          removeClass(el, CLASS_NAME_HIDDEN);
        } else {
          addClass(el, CLASS_NAME_HIDDEN);
        }
      }
    },

    /**
     * Input change handler
     * @private
     * @throws {Error}
     */
    _onChangeInput: function() {
      this._syncFromInput(true);
    },

    /**
     * Returns whether the date is changed
     * @param {Date} date - Date
     * @returns {boolean}
     * @private
     */
    _isChanged: function(date) {
      var prevDate = this.getDate();

      return !prevDate || date.getTime() !== prevDate.getTime();
    },

    /**
     * Refresh datepicker
     * @private
     */
    _refreshFromRanges: function() {
      if (!this.isSelectable(this._date)) {
        this.setNull();
      } else {
        this._calendar.draw(); // view update
      }
    },

    /**
     * Return the current calendar's type.
     * @returns {('date'|'month'|'year')}
     */
    getCalendarType: function() {
      return this._calendar.getType();
    },

    /**
     * Return the date picker's type.
     * @returns {('date'|'month'|'year')}
     */
    getType: function() {
      return this._type;
    },

    /**
     * Return whether the date is selectable.
     * @param {Date} date - Date to check
     * @returns {boolean}
     */
    isSelectable: function(date) {
      var type = this.getType();
      var start, end;

      if (!dateUtil.isValidDate(date)) {
        return false;
      }
      start = dateUtil.cloneWithStartOf(date, type).getTime();
      end = dateUtil.cloneWithEndOf(date, type).getTime();

      return this._rangeModel.hasOverlap(start, end);
    },

    /**
     * Return whether the date is selected.
     * @param {Date} date - Date to check
     * @returns {boolean}
     */
    isSelected: function(date) {
      return dateUtil.isValidDate(date) && dateUtil.isSame(this._date, date, this.getType());
    },

    /**
     * Set selectable ranges. Previous ranges will be removed.
     * @param {Array.<Array<Date|number>>} ranges - Selectable ranges. Use Date instances or numbers(timestamp).
     * @example
     * datepicker.setRanges([
     *     [new Date(2017, 0, 1), new Date(2018, 0, 2)],
     *     [new Date(2015, 2, 3), new Date(2016, 4, 2)]
     * ]);
     */
    setRanges: function(ranges) {
      var result = [];
      forEachArray(ranges, function(range) {
        var start = new Date(range[0]).getTime();
        var end = new Date(range[1]).getTime();

        result.push([start, end]);
      });

      this._rangeModel = new RangeModel(result);
      this._refreshFromRanges();
    },

    /**
     * Set the calendar's type.
     * @param {('date'|'month'|'year')} type - Calendar type
     * @example
     * datepicker.setType('month');
     */
    setType: function(type) {
      this._type = type;
    },

    /**
     * Add a selectable range. Use Date instances or numbers(timestamp).
     * @param {Date|number} start - the start date
     * @param {Date|number} end - the end date
     * @example
     * const start = new Date(2015, 1, 3);
     * const end = new Date(2015, 2, 6);
     *
     * datepicker.addRange(start, end);
     */
    addRange: function(start, end) {
      start = new Date(start).getTime();
      end = new Date(end).getTime();

      this._rangeModel.add(start, end);
      this._refreshFromRanges();
    },

    /**
     * Remove a range. Use Date instances or numbers(timestamp).
     * @param {Date|number} start - the start date
     * @param {Date|number} end - the end date
     * @param {null|'date'|'month'|'year'} type - Range type. If falsy, start and end values are considered as timestamp
     * @example
     * const start = new Date(2015, 1, 3);
     * const end = new Date(2015, 2, 6);
     *
     * datepicker.removeRange(start, end);
     */
    removeRange: function(start, end, type) {
      start = new Date(start);
      end = new Date(end);

      if (type) {
        // @todo Consider time-range on timePicker
        start = dateUtil.cloneWithStartOf(start, type);
        end = dateUtil.cloneWithEndOf(end, type);
      }

      this._rangeModel.exclude(start.getTime(), end.getTime());
      this._refreshFromRanges();
    },

    /**
     * Add an opener.
     * @param {HTMLElement|string} opener - element or selector of opener
     */
    addOpener: function(opener) {
      opener = util.getElement(opener);

      if (!this._isOpener(opener)) {
        this._openers.push(opener);
        this._setOpenerEvents(opener);
      }
    },

    /**
     * Remove an opener.
     * @param {HTMLElement|string} opener - element or selector of opener
     */
    removeOpener: function(opener) {
      var index;

      opener = util.getElement(opener);
      index = inArray(opener, this._openers);

      if (index > -1) {
        this._removeOpenerEvents(opener);
        this._openers.splice(index, 1);
      }
    },

    /**
     * Remove all openers.
     */
    removeAllOpeners: function() {
      forEachArray(
        this._openers,
        function(opener) {
          this._removeOpenerEvents(opener);
        },
        this
      );
      this._openers = [];
    },

    /**
     * Open the date picker.
     * @example
     * datepicker.open();
     */
    open: function() {
      if (this.isOpened() || !this._isEnabled) {
        return;
      }

      this._calendar.draw({
        date: this._date,
        type: this._type
      });
      this._show();

      if (!this.showAlways) {
        this._setDocumentEvents();
      }

      /**
       * Occur after the date picker opens.
       * @event DatePicker#open
       * @see {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents#on datepicker.on()} to bind event handlers.
       * @see {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents#off datepicker.off()} to unbind event handlers.
       * @see Refer to {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents CustomEvents from tui-code-snippet} for more methods. DatePicker mixes in the methods from CustomEvents.
       * @example
       * // bind the 'open' event
       * datepicker.on('open', () => {
       *     alert('open');
       * });
       *
       * // unbind the 'open' event
       * datepicker.off('open');
       */
      this.fire('open');
    },

    /**
     * Raise the calendar type. (date -> month -> year)
     * @param {Date} [date] - Date to set
     */
    drawUpperCalendar: function(date) {
      var calendarType = this.getCalendarType();

      if (calendarType === TYPE_DATE) {
        this._calendar.draw({
          date: date,
          type: TYPE_MONTH
        });
      } else if (calendarType === TYPE_MONTH) {
        this._calendar.draw({
          date: date,
          type: TYPE_YEAR
        });
      }
    },

    /**
     * Lower the calendar type. (year -> month -> date)
     * @param {Date} [date] - Date to set
     */
    drawLowerCalendar: function(date) {
      var calendarType = this.getCalendarType();
      var pickerType = this.getType();
      var isLast = calendarType === pickerType;

      if (isLast) {
        return;
      }

      if (calendarType === TYPE_MONTH) {
        this._calendar.draw({
          date: date,
          type: TYPE_DATE
        });
      } else if (calendarType === TYPE_YEAR) {
        this._calendar.draw({
          date: date,
          type: TYPE_MONTH
        });
      }
    },

    /**
     * Close the date picker.
     * @exmaple
     * datepicker.close();
     */
    close: function() {
      if (!this.isOpened()) {
        return;
      }
      this._removeDocumentEvents();
      this._hide();

      /**
       * Occur after the date picker closes.
       * @event DatePicker#close
       * @see {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents#on datepicker.on()} to bind event handlers.
       * @see {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents#off datepicker.off()} to unbind event handlers.
       * @see Refer to {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents CustomEvents from tui-code-snippet} for more methods. DatePicker mixes in the methods from CustomEvents.
       * @example
       * // bind the 'close' event
       * datepicker.on('close', () => {
       *     alert('close');
       * });
       *
       * // unbind the 'close' event
       * datepicker.off('close');
       */
      this.fire('close');
    },

    /**
     * Toggle the date picker.
     * @example
     * datepicker.toggle();
     */
    toggle: function() {
      if (this.isOpened()) {
        this.close();
      } else {
        this.open();
      }
    },

    /**
     * Return the selected date.
     * @returns {?Date} - selected date
     * @example
     * // 2015-04-13
     * datepicker.getDate(); // new Date(2015, 3, 13)
     */
    getDate: function() {
      if (!this._date) {
        return null;
      }

      return new Date(this._date);
    },

    /**
     * Select the date.
     * @param {Date|number} date - Date instance or timestamp to set
     * @example
     * datepicker.setDate(new Date()); // Set today
     */
    // eslint-disable-next-line complexity
    setDate: function(date) {
      var isValidInput, newDate, shouldUpdate;

      if (date === null) {
        this.setNull();

        return;
      }

      isValidInput = isNumber(date) || isDate(date);
      newDate = new Date(date);
      shouldUpdate = isValidInput && this._isChanged(newDate) && this.isSelectable(newDate);

      if (shouldUpdate) {
        newDate = new Date(date);
        this._date = newDate;
        this._calendar.draw({ date: newDate });
        if (this._timePicker) {
          this._timePicker.setTime(newDate.getHours(), newDate.getMinutes());
        }
        this._syncToInput();

        /**
         * Occur after the selected date is changed.
         * @event DatePicker#change
         * @see {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents#on datepicker.on()} to bind event handlers.
         * @see {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents#off datepicker.off()} to unbind event handlers.
         * @see Refer to {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents CustomEvents from tui-code-snippet} for more methods. DatePicker mixes in the methods from CustomEvents.
         * @example
         * // bind the 'change' event
         * datepicker.on('change', () => {
         *     console.log(`Selected date: ${datepicker.getDate()}`);
         * });
         *
         * // unbind the 'change' event
         * datepicker.off('change');
         */
        this.fire('change');
      }
    },

    /**
     * Set no date to be selected. (Selected date: null)
     */
    setNull: function() {
      var calendarDate = this._calendar.getDate();
      var isChagned = this._date !== null;

      this._date = null;

      if (this._datepickerInput) {
        this._datepickerInput.clearText();
      }
      if (this._timePicker) {
        this._timePicker.setTime(0, 0);
      }

      // View update
      if (!this.isSelectable(calendarDate)) {
        this._calendar.draw({
          date: new Date(this._rangeModel.getMinimumValue())
        });
      } else {
        this._calendar.draw();
      }

      if (isChagned) {
        this.fire('change');
      }
    },

    /**
     * Select the date by the date string format.
     * @param {String} [format] - Date string format
     * @example
     * datepicker.setDateFormat('yyyy-MM-dd');
     * datepicker.setDateFormat('MM-dd, yyyy');
     * datepicker.setDateFormat('yy/M/d');
     * datepicker.setDateFormat('yy/MM/dd');
     */
    setDateFormat: function(format) {
      this._datepickerInput.setFormat(format);
      this._syncToInput();
    },

    /**
     * Return whether the datepicker opens or not
     * @returns {boolean}
     * @example
     * datepicker.close();
     * datepicker.isOpened(); // false
     *
     * datepicker.open();
     * datepicker.isOpened(); // true
     */
    isOpened: function() {
      return !hasClass(this._element, CLASS_NAME_HIDDEN);
    },

    /**
     * Return the time picker instance
     * @returns {?TimePicker} - TimePicker instance
     * @see {@link https://nhn.github.io/tui.time-picker/latest tui-time-picker}
     * @example
     * const timePicker = this.getTimePicker();
     */
    getTimePicker: function() {
      return this._timePicker;
    },

    /**
     * Return the calendar instance.
     * @see {@link calendar Calendar}
     * @returns {Calendar}
     */
    getCalendar: function() {
      return this._calendar;
    },

    /**
     * Return the locale text object.
     * @see {@link DatePicker#localeTexts DatePicker.localeTexts}
     * @returns {object}
     */
    getLocaleText: function() {
      return localeTexts[this._language] || localeTexts[DEFAULT_LANGUAGE_TYPE];
    },

    /**
     * Set the input element
     * @param {string|HTMLElement} element - Input element or selector
     * @param {object} [options] - Input option
     * @param {string} [options.format = prevInput.format] - Format of the Date string in the input
     * @param {boolean} [options.syncFromInput = false] - Whether set the date from the input
     */
    setInput: function(element, options) {
      var prev = this._datepickerInput;
      var localeText = this.getLocaleText();
      var prevFormat;
      options = options || {};

      if (prev) {
        prevFormat = prev.getFormat();
        prev.destroy();
      }

      this._datepickerInput = new DatePickerInput(element, {
        format: options.format || prevFormat,
        id: this._id,
        localeText: localeText
      });

      this._datepickerInput.on(
        {
          change: this._onChangeInput,
          click: this.open
        },
        this
      );

      if (options.syncFromInput) {
        this._syncFromInput();
      } else {
        this._syncToInput();
      }
    },

    /**
     * Enable the date picker.
     */
    enable: function() {
      if (this._isEnabled) {
        return;
      }
      this._isEnabled = true;
      this._datepickerInput.enable();

      forEachArray(
        this._openers,
        function(opener) {
          opener.removeAttribute('disabled');
          this._setOpenerEvents(opener);
        },
        this
      );
    },

    /**
     * Disable the date picker.
     */
    disable: function() {
      if (!this._isEnabled) {
        return;
      }

      this._isEnabled = false;
      this.close();
      this._datepickerInput.disable();

      forEachArray(
        this._openers,
        function(opener) {
          opener.setAttribute('disabled', true);
          this._removeOpenerEvents(opener);
        },
        this
      );
    },

    /**
     * Return whether the date picker is disabled
     * @returns {boolean}
     */
    isDisabled: function() {
      // @todo this._isEnabled --> this._isDisabled
      return !this._isEnabled;
    },

    /**
     * Apply a CSS class to the date picker.
     * @param {string} className - Class name
     */
    addCssClass: function(className) {
      addClass(this._element, className);
    },

    /**
     * Remove a CSS class from the date picker.
     * @param {string} className - Class name
     */
    removeCssClass: function(className) {
      removeClass(this._element, className);
    },

    /**
     * Return the date elements on the calendar.
     * @returns {HTMLElement[]}
     */
    getDateElements: function() {
      return this._calendar.getDateElements();
    },

    /**
     * Return the first overlapped range from the point or range.
     * @param {Date|number} startDate - Start date to find overlapped range
     * @param {Date|number} endDate - End date to find overlapped range
     * @returns {Array.<Date>} - \[startDate, endDate]
     */
    findOverlappedRange: function(startDate, endDate) {
      var startTimestamp = new Date(startDate).getTime();
      var endTimestamp = new Date(endDate).getTime();
      var overlappedRange = this._rangeModel.findOverlappedRange(startTimestamp, endTimestamp);

      return [new Date(overlappedRange[0]), new Date(overlappedRange[1])];
    },

    /**
     * Change language.
     * @param {string} language - Language code. English('en') and Korean('ko') are provided as default.
     * @see To set to the other languages, use {@link DatePicker#localeTexts DatePicker.localeTexts}.
     */
    changeLanguage: function(language) {
      this._language = language;
      this._calendar.changeLanguage(this._language);
      this._datepickerInput.changeLocaleTitles(this.getLocaleText().titles);
      this.setDateFormat(this._datepickerInput.getFormat());

      if (this._timePicker) {
        this._timePicker.changeLanguage(this._language);
      }
    },

    /**
     * Destroy the date picker.
     */
    destroy: function() {
      this._removeDocumentEvents();
      this._calendar.destroy();
      if (this._timePicker) {
        this._timePicker.destroy();
      }
      if (this._datepickerInput) {
        this._datepickerInput.destroy();
      }
      this._removeEvents();
      removeElement(this._element);
      this.removeAllOpeners();

      this._calendar = this._timePicker = this._datepickerInput = this._container = this._element = this._date = this._rangeModel = this._openers = this._isEnabled = this._id = null;
    }
  }
);

CustomEvents.mixin(DatePicker);
module.exports = DatePicker;


/***/ }),
/* 22 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Check whether the given variable is an object or not.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



/**
 * Check whether the given variable is an object or not.
 * If the given variable is an object, return true.
 * @param {*} obj - Target for checking
 * @returns {boolean} Is object?
 * @memberof module:type
 */
function isObject(obj) {
  return obj === Object(obj);
}

module.exports = isObject;


/***/ }),
/* 23 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Execute the provided callback once for each property of object which actually exist.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



/**
 * Execute the provided callback once for each property of object which actually exist.
 * If the callback function returns false, the loop will be stopped.
 * Callback function(iteratee) is invoked with three arguments:
 *  1) The value of the property
 *  2) The name of the property
 *  3) The object being traversed
 * @param {Object} obj The object that will be traversed
 * @param {function} iteratee  Callback function
 * @param {Object} [context] Context(this) of callback function
 * @memberof module:collection
 * @example
 * var forEachOwnProperties = require('tui-code-snippet/collection/forEachOwnProperties'); // node, commonjs
 *
 * var sum = 0;
 *
 * forEachOwnProperties({a:1,b:2,c:3}, function(value){
 *     sum += value;
 * });
 * alert(sum); // 6
 */
function forEachOwnProperties(obj, iteratee, context) {
  var key;

  context = context || null;

  for (key in obj) {
    if (obj.hasOwnProperty(key)) {
      if (iteratee.call(context, obj[key], key, obj) === false) {
        break;
      }
    }
  }
}

module.exports = forEachOwnProperties;


/***/ }),
/* 24 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Set className value
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var isArray = __webpack_require__(6);
var isUndefined = __webpack_require__(12);

/**
 * Set className value
 * @param {(HTMLElement|SVGElement)} element - target element
 * @param {(string|string[])} cssClass - class names
 * @private
 */
function setClassName(element, cssClass) {
  cssClass = isArray(cssClass) ? cssClass.join(' ') : cssClass;

  cssClass = cssClass.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');

  if (isUndefined(element.className.baseVal)) {
    element.className = cssClass;

    return;
  }

  element.className.baseVal = cssClass;
}

module.exports = setClassName;


/***/ }),
/* 25 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Find parent element recursively
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var matches = __webpack_require__(40);

/**
 * Find parent element recursively
 * @param {HTMLElement} element - base element to start find
 * @param {string} selector - selector string for find
 * @returns {HTMLElement} - element finded or null
 * @memberof module:domUtil
 */
function closest(element, selector) {
  var parent = element.parentNode;

  if (matches(element, selector)) {
    return element;
  }

  while (parent && parent !== document) {
    if (matches(parent, selector)) {
      return parent;
    }

    parent = parent.parentNode;
  }

  return null;
}

module.exports = closest;


/***/ }),
/* 26 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Get data value from data-attribute
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var convertToKebabCase = __webpack_require__(42);

/**
 * Get data value from data-attribute
 * @param {HTMLElement} element - target element
 * @param {string} key - key
 * @returns {string} value
 * @memberof module:domUtil
 */
function getData(element, key) {
  if (element.dataset) {
    return element.dataset[key];
  }

  return element.getAttribute('data-' + convertToKebabCase(key));
}

module.exports = getData;


/***/ }),
/* 27 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Check element has specific css class
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var inArray = __webpack_require__(3);
var getClass = __webpack_require__(17);

/**
 * Check element has specific css class
 * @param {(HTMLElement|SVGElement)} element - target element
 * @param {string} cssClass - css class
 * @returns {boolean}
 * @memberof module:domUtil
 */
function hasClass(element, cssClass) {
  var origin;

  if (element.classList) {
    return element.classList.contains(cssClass);
  }

  origin = getClass(element).split(/\s+/);

  return inArray(cssClass, origin) > -1;
}

module.exports = hasClass;


/***/ }),
/* 28 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Check whether the given variable is an instance of Date or not.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



/**
 * Check whether the given variable is an instance of Date or not.
 * If the given variables is an instance of Date, return true.
 * @param {*} obj - Target for checking
 * @returns {boolean} Is an instance of Date?
 * @memberof module:type
 */
function isDate(obj) {
  return obj instanceof Date;
}

module.exports = isDate;


/***/ }),
/* 29 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Calendar component
 * @author NHN. FE dev Lab <dl_javascript@nhn.com>
 */



var defineClass = __webpack_require__(0);
var CustomEvents = __webpack_require__(8);
var addClass = __webpack_require__(16);
var hasClass = __webpack_require__(27);
var removeClass = __webpack_require__(18);
var removeElement = __webpack_require__(14);
var extend = __webpack_require__(7);

var Header = __webpack_require__(44);
var Body = __webpack_require__(49);
var localeTexts = __webpack_require__(10);
var constants = __webpack_require__(1);
var dateUtil = __webpack_require__(5);
var util = __webpack_require__(4);

var DEFAULT_WEEK_START_DAY = constants.DEFAULT_WEEK_START_DAY;
var DEFAULT_LANGUAGE_TYPE = constants.DEFAULT_LANGUAGE_TYPE;

var TYPE_DATE = constants.TYPE_DATE;
var TYPE_MONTH = constants.TYPE_MONTH;
var TYPE_YEAR = constants.TYPE_YEAR;

var CLASS_NAME_PREV_MONTH_BTN = constants.CLASS_NAME_PREV_MONTH_BTN;
var CLASS_NAME_PREV_YEAR_BTN = constants.CLASS_NAME_PREV_YEAR_BTN;
var CLASS_NAME_NEXT_YEAR_BTN = constants.CLASS_NAME_NEXT_YEAR_BTN;
var CLASS_NAME_NEXT_MONTH_BTN = constants.CLASS_NAME_NEXT_MONTH_BTN;

var CLASS_NAME_CALENDAR_MONTH = 'tui-calendar-month';
var CLASS_NAME_CALENDAR_YEAR = 'tui-calendar-year';
var CLASS_NAME_HIDDEN = 'tui-hidden';

var HEADER_SELECTOR = '.tui-calendar-header';
var BODY_SELECTOR = '.tui-calendar-body';

/**
 * @class
 * @description
 * Create a calendar by {@link DatePicker#createCalendar DatePicker.createCalendar()}.
 * @see {@link /tutorial-example07-calendar Calendar example}
 * @param {HTMLElement|string} container - Container or selector of the Calendar
 * @param {Object} [options] - Calendar options
 *     @param {Date} [options.date = new Date()] - Initial date (default: today)
 *     @param {('date'|'month'|'year')} [options.type = 'date'] - Calendar type. Determine whether to show a date, month, or year.
 *     @param {string} [options.language = 'en'] - Language code. English('en') and Korean('ko') are provided as default. To use the other languages, use {@link DatePicker#localeTexts DatePicker.localeTexts}.
 *     @param {boolean} [options.showToday = true] - Show today.
 *     @param {boolean} [options.showJumpButtons = false] - Show the yearly jump buttons (move to the previous and next year in 'date' Calendar)
 *     @param {boolean} [options.usageStatistics = true] - Send a hostname to Google Analytics (default: true)
 *     @param {string} [options.weekStartDay = 'Sun'] - Start of the week. 'Sun', 'Mon', ..., 'Sat'(default: 'Sun'(start on Sunday))
 * @example
 * //ES6
 * import DatePicker from 'tui-date-picker'
 *
 * // CommonJS
 * const DatePicker = require('tui-date-picker');
 *
 * // Browser
 * const DatePicker = tui.DatePicker;
 *
 * const calendar = DatePicker.createCalendar('#calendar-wrapper', {
 *     language: 'en',
 *     showToday: true,
 *     showJumpButtons: false,
 *     date: new Date(),
 *     type: 'date',
 *     weekStartDay: 'Mon',
 * });
 *
 * calendar.on('draw', (event) => {
 *     console.log(event.date);
 *     console.log(event.type);
 *     for (let i = 0, len = event.dateElements.length; i < len; i += 1) {
 *         const el = event.dateElements[i];
 *         const date = new Date(getData(el, 'timestamp'));
 *         console.log(date);
 *     }
 * });
 */
var Calendar = defineClass(
  /** @lends Calendar.prototype */ {
    static: {
      localeTexts: localeTexts
    },
    init: function(container, options) {
      options = extend(
        {
          language: DEFAULT_LANGUAGE_TYPE,
          showToday: true,
          showJumpButtons: false,
          date: new Date(),
          type: TYPE_DATE,
          usageStatistics: true,
          weekStartDay: DEFAULT_WEEK_START_DAY
        },
        options
      );

      /**
       * Container element
       * @type {HTMLElement}
       * @private
       */
      this._container = util.getElement(container);
      this._container.innerHTML =
        '<div class="tui-calendar">' +
        '    <div class="tui-calendar-header"></div>' +
        '    <div class="tui-calendar-body"></div>' +
        '</div>';

      /**
       * Wrapper element
       * @type {HTMLElement}
       * @private
       */
      this._element = this._container.firstChild;

      /**
       * Date
       * @type {Date}
       * @private
       */
      this._date = null;

      /**
       * Layer type
       * @type {string}
       * @private
       */
      this._type = null;

      /**
       * Header box
       * @type {Header}
       * @private
       */
      this._header = null;

      /**
       * Body box
       * @type {Body}
       * @private
       */
      this._body = null;

      this._initHeader(options);
      this._initBody(options);
      this.draw({
        date: options.date,
        type: options.type
      });

      if (options.usageStatistics) {
        util.sendHostName();
      }
    },

    /**
     * Initialize header
     * @param {object} options - Header options
     * @private
     */
    _initHeader: function(options) {
      var headerContainer = this._element.querySelector(HEADER_SELECTOR);

      this._header = new Header(headerContainer, options);
      this._header.on(
        'click',
        function(ev) {
          var target = util.getTarget(ev);
          if (hasClass(target, CLASS_NAME_PREV_MONTH_BTN)) {
            this.drawPrev();
          } else if (hasClass(target, CLASS_NAME_PREV_YEAR_BTN)) {
            this._onClickPrevYear();
          } else if (hasClass(target, CLASS_NAME_NEXT_MONTH_BTN)) {
            this.drawNext();
          } else if (hasClass(target, CLASS_NAME_NEXT_YEAR_BTN)) {
            this._onClickNextYear();
          }
        },
        this
      );
    },

    /**
     * Initialize body
     * @param {object} options - Body options
     * @private
     */
    _initBody: function(options) {
      var bodyContainer = this._element.querySelector(BODY_SELECTOR);

      this._body = new Body(bodyContainer, options);
    },

    /**
     * clickHandler - prev year button
     * @private
     */
    _onClickPrevYear: function() {
      if (this.getType() === TYPE_DATE) {
        this.draw({
          date: this._getRelativeDate(-12)
        });
      } else {
        this.drawPrev();
      }
    },

    /**
     * clickHandler - next year button
     * @private
     */
    _onClickNextYear: function() {
      if (this.getType() === TYPE_DATE) {
        this.draw({
          date: this._getRelativeDate(12)
        });
      } else {
        this.drawNext();
      }
    },

    /**
     * Returns whether the layer type is valid
     * @param {string} type - Layer type to check
     * @returns {boolean}
     * @private
     */
    _isValidType: function(type) {
      return type === TYPE_DATE || type === TYPE_MONTH || type === TYPE_YEAR;
    },

    /**
     * @param {Date} date - Date to draw
     * @param {string} type - Layer type to draw
     * @returns {boolean}
     * @private
     */
    _shouldUpdate: function(date, type) {
      var prevDate = this._date;

      if (!dateUtil.isValidDate(date)) {
        throw new Error('Invalid date');
      }

      if (!this._isValidType(type)) {
        throw new Error('Invalid layer type');
      }

      return (
        !prevDate ||
        prevDate.getFullYear() !== date.getFullYear() ||
        prevDate.getMonth() !== date.getMonth() ||
        this.getType() !== type
      );
    },

    /**
     * Render header & body elements
     * @private
     */
    _render: function() {
      var date = this._date;
      var type = this.getType();

      this._header.render(date, type);
      this._body.render(date, type);
      removeClass(this._element, CLASS_NAME_CALENDAR_MONTH, CLASS_NAME_CALENDAR_YEAR);

      switch (type) {
        case TYPE_MONTH:
          addClass(this._element, CLASS_NAME_CALENDAR_MONTH);
          break;
        case TYPE_YEAR:
          addClass(this._element, CLASS_NAME_CALENDAR_YEAR);
          break;
        default:
          break;
      }
    },

    /**
     * Returns relative date
     * @param {number} step - Month step
     * @returns {Date}
     * @private
     */
    _getRelativeDate: function(step) {
      var prev = this._date;

      return new Date(prev.getFullYear(), prev.getMonth() + step);
    },

    /**
     * Draw the calendar.
     * @param {Object} [options] - Draw options
     *   @param {Date} [options.date] - Date to set
     *   @param {('date'|'month'|'year')} [options.type = 'date'] - Calendar type. Determine whether to show a date, month, or year.
     * @example
     * calendar.draw();
     * calendar.draw({
     *     date: new Date()
     * });
     * calendar.draw({
     *     type: 'month'
     * });
     * calendar.draw({
     *     type: 'month',
     *     date: new Date()
     * });
     */
    draw: function(options) {
      var date, type;

      options = options || {};
      date = options.date || this._date;
      type = (options.type || this.getType()).toLowerCase();

      if (this._shouldUpdate(date, type)) {
        this._date = date;
        this._type = type;
        this._render();
      }

      /**
       * Occur after the calendar draws.
       * @event Calendar#draw
       * @see {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents#on calendar.on()} to bind event handlers.
       * @see {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents#off calendar.off()} to unbind event handlers.
       * @see Refer to {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents CustomEvents from tui-code-snippet} for more methods. Calendar mixes in the methods from CustomEvents.
       * @property {Date} date - Calendar date
       * @property {('date'|'month'|'year')} type - Calendar type
       * @property {HTMLElement[]} dateElements - elements for dates
       * @example
       * // bind the 'draw' event
       * calendar.on('draw', ({type, date}) => {
       *     console.log(`Draw the ${type} calendar and its date is ${date}.`);
       * });
       *
       * // unbind the 'draw' event
       * calendar.off('draw');
       */
      this.fire('draw', {
        date: this._date,
        type: type,
        dateElements: this._body.getDateElements()
      });
    },

    /**
     * Show the calendar.
     */
    show: function() {
      removeClass(this._element, CLASS_NAME_HIDDEN);
    },

    /**
     * Hide the calendar.
     */
    hide: function() {
      addClass(this._element, CLASS_NAME_HIDDEN);
    },

    /**
     * Draw the next page.
     */
    drawNext: function() {
      this.draw({
        date: this.getNextDate()
      });
    },

    /**
     * Draw the previous page.
     */
    drawPrev: function() {
      this.draw({
        date: this.getPrevDate()
      });
    },

    /**
     * Return the next date.
     * @returns {Date}
     */
    getNextDate: function() {
      if (this.getType() === TYPE_DATE) {
        return this._getRelativeDate(1);
      }

      return this.getNextYearDate();
    },

    /**
     * Return the previous date.
     * @returns {Date}
     */
    getPrevDate: function() {
      if (this.getType() === TYPE_DATE) {
        return this._getRelativeDate(-1);
      }

      return this.getPrevYearDate();
    },

    /**
     * Return the date a year later.
     * @returns {Date}
     */
    getNextYearDate: function() {
      switch (this.getType()) {
        case TYPE_DATE:
        case TYPE_MONTH:
          return this._getRelativeDate(12); // 12 months = 1 year
        case TYPE_YEAR:
          return this._getRelativeDate(108); // 108 months = 9 years = 12 * 9
        default:
          throw new Error('Unknown layer type');
      }
    },

    /**
     * Return the date a year previously.
     * @returns {Date}
     */
    getPrevYearDate: function() {
      switch (this.getType()) {
        case TYPE_DATE:
        case TYPE_MONTH:
          return this._getRelativeDate(-12); // 12 months = 1 year
        case TYPE_YEAR:
          return this._getRelativeDate(-108); // 108 months = 9 years = 12 * 9
        default:
          throw new Error('Unknown layer type');
      }
    },

    /**
     * Change language.
     * @param {string} language - Language code. English('en') and Korean('ko') are provided as default.
     * @see To set to the other languages, use {@link DatePicker#localeTexts DatePicker.localeTexts}.
     */
    changeLanguage: function(language) {
      this._header.changeLanguage(language);
      this._body.changeLanguage(language);
      this._render();
    },

    /**
     * Return the rendered date.
     * @returns {Date}
     */
    getDate: function() {
      return new Date(this._date);
    },

    /**
     * Return the calendar's type.
     * @returns {('date'|'month'|'year')}
     */
    getType: function() {
      return this._type;
    },

    /**
     * Returns HTML elements for dates.
     * @returns {HTMLElement[]}
     */
    getDateElements: function() {
      return this._body.getDateElements();
    },

    /**
     * Apply a CSS class to the calendar.
     * @param {string} className - Class name
     */
    addCssClass: function(className) {
      addClass(this._element, className);
    },

    /**
     * Remove a CSS class from the calendar.
     * @param {string} className - Class name
     */
    removeCssClass: function(className) {
      removeClass(this._element, className);
    },

    /**
     * Destroy the calendar.
     */
    destroy: function() {
      this._header.destroy();
      this._body.destroy();
      removeElement(this._element);

      this._type = this._date = this._container = this._element = this._header = this._body = null;
    }
  }
);

CustomEvents.mixin(Calendar);
module.exports = Calendar;


/***/ }),
/* 30 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Date <-> Text formatting module
 * @author NHN. FE Development Lab <dl_javascript@nhn.com>
 */



var inArray = __webpack_require__(3);
var forEachArray = __webpack_require__(2);
var defineClass = __webpack_require__(0);

var util = __webpack_require__(4);
var dateUtil = __webpack_require__(5);
var constants = __webpack_require__(1);
var localeTexts = __webpack_require__(10);

var rFormableKeys = /\\?(yyyy|yy|mmmm|mmm|mm|m|dd|d|hh|h|a)/gi;
var mapForConverting = {
  yyyy: {
    expression: '(\\d{4}|\\d{2})',
    type: constants.TYPE_YEAR
  },
  yy: {
    expression: '(\\d{4}|\\d{2})',
    type: constants.TYPE_YEAR
  },
  y: {
    expression: '(\\d{4}|\\d{2})',
    type: constants.TYPE_YEAR
  },
  M: {
    expression: '(1[012]|0[1-9]|[1-9])',
    type: constants.TYPE_MONTH
  },
  MM: {
    expression: '(1[012]|0[1-9]|[1-9])',
    type: constants.TYPE_MONTH
  },
  MMM: {
    expression: '(1[012]|0[1-9]|[1-9])',
    type: constants.TYPE_MONTH
  },
  MMMM: {
    expression: '(1[012]|0[1-9]|[1-9])',
    type: constants.TYPE_MONTH
  },
  mmm: {
    expression: '(1[012]|0[1-9]|[1-9])',
    type: constants.TYPE_MONTH
  },
  mmmm: {
    expression: '(1[012]|0[1-9]|[1-9])',
    type: constants.TYPE_MONTH
  },
  dd: {
    expression: '([12]\\d{1}|3[01]|0[1-9]|[1-9])',
    type: constants.TYPE_DATE
  },
  d: {
    expression: '([12]\\d{1}|3[01]|0[1-9]|[1-9])',
    type: constants.TYPE_DATE
  },
  D: {
    expression: '([12]\\d{1}|3[01]|0[1-9]|[1-9])',
    type: constants.TYPE_DATE
  },
  DD: {
    expression: '([12]\\d{1}|3[01]|0[1-9]|[1-9])',
    type: constants.TYPE_DATE
  },
  h: {
    expression: '(d{1}|0\\d{1}|1\\d{1}|2[0123])',
    type: constants.TYPE_HOUR
  },
  hh: {
    expression: '(d{1}|[01]\\d{1}|2[0123])',
    type: constants.TYPE_HOUR
  },
  H: {
    expression: '(d{1}|0\\d{1}|1\\d{1}|2[0123])',
    type: constants.TYPE_HOUR
  },
  HH: {
    expression: '(d{1}|[01]\\d{1}|2[0123])',
    type: constants.TYPE_HOUR
  },
  m: {
    expression: '(d{1}|[012345]\\d{1})',
    type: constants.TYPE_MINUTE
  },
  mm: {
    expression: '(d{1}|[012345]\\d{1})',
    type: constants.TYPE_MINUTE
  },
  a: {
    expression: '([ap]m)',
    type: constants.TYPE_MERIDIEM
  },
  A: {
    expression: '([ap]m)',
    type: constants.TYPE_MERIDIEM
  }
};

/**
 * @class
 * @ignore
 */
var DateTimeFormatter = defineClass(
  /** @lends DateTimeFormatter.prototype */ {
    init: function(rawStr, titles) {
      /**
       * @type {string}
       * @private
       */
      this._rawStr = rawStr;

      /**
       * @type {Array}
       * @private
       * @example
       *  rawStr = "yyyy-MM-dd" --> keyOrder = ['year', 'month', 'date']
       *  rawStr = "MM/dd, yyyy" --> keyOrder = ['month', 'date', 'year']
       */
      this._keyOrder = null;

      /**
       * @type {RegExp}
       * @private
       */
      this._regExp = null;

      /**
       * Titles
       * @type {object}
       * @private
       */
      this._titles = titles || localeTexts.en.titles;

      this._parseFormat();
    },

    /**
     * Parse initial format and make the keyOrder, regExp
     * @private
     */
    _parseFormat: function() {
      var regExpStr = '^';
      var matchedKeys = this._rawStr.match(rFormableKeys);
      var keyOrder = [];

      matchedKeys = util.filter(matchedKeys, function(key) {
        return key[0] !== '\\';
      });

      forEachArray(matchedKeys, function(key, index) {
        if (!/m/i.test(key)) {
          key = key.toLowerCase();
        }

        regExpStr += mapForConverting[key].expression + '[\\D\\s]*';
        keyOrder[index] = mapForConverting[key].type;
      });

      // This formatter does not allow additional numbers at the end of string.
      regExpStr += '$';

      this._keyOrder = keyOrder;

      this._regExp = new RegExp(regExpStr, 'gi');
    },

    /**
     * Parse string to dateHash
     * @param {string} str - Date string
     * @returns {Date}
     */
    parse: function(str) {
      var dateHash = {
        year: 0,
        month: 1,
        date: 1,
        hour: 0,
        minute: 0
      };
      var hasMeridiem = false;
      var isPM = false;
      var matched;

      this._regExp.lastIndex = 0;
      matched = this._regExp.exec(str);

      if (!matched) {
        throw Error('DateTimeFormatter: Not matched - "' + str + '"');
      }

      // eslint-disable-next-line complexity
      forEachArray(this._keyOrder, function(name, index) {
        var value = matched[index + 1];

        if (name === constants.TYPE_MERIDIEM && /[ap]m/i.test(value)) {
          hasMeridiem = true;
          isPM = /pm/i.test(value);
        } else {
          value = Number(value);

          if (value !== 0 && !value) {
            throw Error('DateTimeFormatter: Unknown value - ' + matched[index + 1]);
          }

          if (name === constants.TYPE_YEAR && value < 100) {
            value += 2000;
          }

          dateHash[name] = value;
        }
      });

      if (hasMeridiem) {
        isPM = isPM || dateHash.hour > 12;
        dateHash.hour %= 12;
        if (isPM) {
          dateHash.hour += 12;
        }
      }

      return new Date(
        dateHash.year,
        dateHash.month - 1,
        dateHash.date,
        dateHash.hour,
        dateHash.minute
      );
    },

    /**
     * Returns raw string of format
     * @returns {string}
     */
    getRawString: function() {
      return this._rawStr;
    },

    /**
     * Format date to string
     * @param {Date} dateObj - Date object
     * @returns {string}
     */
    format: function(dateObj) {
      var year = dateObj.getFullYear();
      var month = dateObj.getMonth() + 1;
      var dayInMonth = dateObj.getDate();
      var day = dateObj.getDay();
      var hour = dateObj.getHours();
      var minute = dateObj.getMinutes();
      var meridiem = 'a'; // Default value for unusing meridiem format
      var replaceMap;

      if (inArray(constants.TYPE_MERIDIEM, this._keyOrder) > -1) {
        meridiem = hour >= 12 ? 'pm' : 'am';
        hour = dateUtil.getMeridiemHour(hour);
      }

      replaceMap = {
        yyyy: year,
        yy: String(year).substr(2, 2),
        M: month,
        MM: dateUtil.prependLeadingZero(month),
        MMM: this._titles.MMM[month - 1],
        MMMM: this._titles.MMMM[month - 1],
        d: dayInMonth,
        dd: dateUtil.prependLeadingZero(dayInMonth),
        D: this._titles.D[day],
        DD: this._titles.DD[day],
        hh: dateUtil.prependLeadingZero(hour),
        h: hour,
        mm: dateUtil.prependLeadingZero(minute),
        m: minute,
        A: meridiem.toUpperCase(),
        a: meridiem
      };

      return this._rawStr.replace(rFormableKeys, function(key) {
        if (key[0] === '\\') {
          return key.substr(1);
        }

        return replaceMap[key] || replaceMap[key.toLowerCase()] || '';
      });
    }
  }
);

module.exports = DateTimeFormatter;


/***/ }),
/* 31 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Bind DOM events
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var isString = __webpack_require__(13);
var forEach = __webpack_require__(9);

var safeEvent = __webpack_require__(32);

/**
 * Bind DOM events.
 * @param {HTMLElement} element - element to bind events
 * @param {(string|object)} types - Space splitted events names or eventName:handler object
 * @param {(function|object)} handler - handler function or context for handler method
 * @param {object} [context] context - context for handler method.
 * @memberof module:domEvent
 * @example
 * var div = document.querySelector('div');
 * 
 * // Bind one event to an element.
 * on(div, 'click', toggle);
 * 
 * // Bind multiple events with a same handler to multiple elements at once.
 * // Use event names splitted by a space.
 * on(div, 'mouseenter mouseleave', changeColor);
 * 
 * // Bind multiple events with different handlers to an element at once.
 * // Use an object which of key is an event name and value is a handler function.
 * on(div, {
 *   keydown: highlight,
 *   keyup: dehighlight
 * });
 * 
 * // Set a context for handler method.
 * var name = 'global';
 * var repository = {name: 'CodeSnippet'};
 * on(div, 'drag', function() {
 *  console.log(this.name);
 * }, repository);
 * // Result when you drag a div: "CodeSnippet"
 */
function on(element, types, handler, context) {
  if (isString(types)) {
    forEach(types.split(/\s+/g), function(type) {
      bindEvent(element, type, handler, context);
    });

    return;
  }

  forEach(types, function(func, type) {
    bindEvent(element, type, func, handler);
  });
}

/**
 * Bind DOM events
 * @param {HTMLElement} element - element to bind events
 * @param {string} type - events name
 * @param {function} handler - handler function or context for handler method
 * @param {object} [context] context - context for handler method.
 * @private
 */
function bindEvent(element, type, handler, context) {
  /**
     * Event handler
     * @param {Event} e - event object
     */
  function eventHandler(e) {
    handler.call(context || element, e || window.event);
  }

  if ('addEventListener' in element) {
    element.addEventListener(type, eventHandler);
  } else if ('attachEvent' in element) {
    element.attachEvent('on' + type, eventHandler);
  }
  memorizeHandler(element, type, handler, eventHandler);
}

/**
 * Memorize DOM event handler for unbinding.
 * @param {HTMLElement} element - element to bind events
 * @param {string} type - events name
 * @param {function} handler - handler function that user passed at on() use
 * @param {function} wrappedHandler - handler function that wrapped by domevent for implementing some features
 * @private
 */
function memorizeHandler(element, type, handler, wrappedHandler) {
  var events = safeEvent(element, type);
  var existInEvents = false;

  forEach(events, function(obj) {
    if (obj.handler === handler) {
      existInEvents = true;

      return false;
    }

    return true;
  });

  if (!existInEvents) {
    events.push({
      handler: handler,
      wrappedHandler: wrappedHandler
    });
  }
}

module.exports = on;


/***/ }),
/* 32 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Get event collection for specific HTML element
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var EVENT_KEY = '_feEventKey';

/**
 * Get event collection for specific HTML element
 * @param {HTMLElement} element - HTML element
 * @param {string} type - event type
 * @returns {array}
 * @private
 */
function safeEvent(element, type) {
  var events = element[EVENT_KEY];
  var handlers;

  if (!events) {
    events = element[EVENT_KEY] = {};
  }

  handlers = events[type];
  if (!handlers) {
    handlers = events[type] = [];
  }

  return handlers;
}

module.exports = safeEvent;


/***/ }),
/* 33 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Unbind DOM events
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var isString = __webpack_require__(13);
var forEach = __webpack_require__(9);

var safeEvent = __webpack_require__(32);

/**
 * Unbind DOM events
 * If a handler function is not passed, remove all events of that type.
 * @param {HTMLElement} element - element to unbind events
 * @param {(string|object)} types - Space splitted events names or eventName:handler object
 * @param {function} [handler] - handler function
 * @memberof module:domEvent
 * @example
 * // Following the example of domEvent#on
 * 
 * // Unbind one event from an element.
 * off(div, 'click', toggle);
 * 
 * // Unbind multiple events with a same handler from multiple elements at once.
 * // Use event names splitted by a space.
 * off(element, 'mouseenter mouseleave', changeColor);
 * 
 * // Unbind multiple events with different handlers from an element at once.
 * // Use an object which of key is an event name and value is a handler function.
 * off(div, {
 *   keydown: highlight,
 *   keyup: dehighlight
 * });
 * 
 * // Unbind events without handlers.
 * off(div, 'drag');
 */
function off(element, types, handler) {
  if (isString(types)) {
    forEach(types.split(/\s+/g), function(type) {
      unbindEvent(element, type, handler);
    });

    return;
  }

  forEach(types, function(func, type) {
    unbindEvent(element, type, func);
  });
}

/**
 * Unbind DOM events
 * If a handler function is not passed, remove all events of that type.
 * @param {HTMLElement} element - element to unbind events
 * @param {string} type - events name
 * @param {function} [handler] - handler function
 * @private
 */
function unbindEvent(element, type, handler) {
  var events = safeEvent(element, type);
  var index;

  if (!handler) {
    forEach(events, function(item) {
      removeHandler(element, type, item.wrappedHandler);
    });
    events.splice(0, events.length);
  } else {
    forEach(events, function(item, idx) {
      if (handler === item.handler) {
        removeHandler(element, type, item.wrappedHandler);
        index = idx;

        return false;
      }

      return true;
    });
    events.splice(index, 1);
  }
}

/**
 * Remove an event handler
 * @param {HTMLElement} element - An element to remove an event
 * @param {string} type - event type
 * @param {function} handler - event handler
 * @private
 */
function removeHandler(element, type, handler) {
  if ('removeEventListener' in element) {
    element.removeEventListener(type, handler);
  } else if ('detachEvent' in element) {
    element.detachEvent('on' + type, handler);
  }
}

module.exports = off;


/***/ }),
/* 34 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview The entry file of DatePicker components
 * @author NHN. FE Development Team
 */



var DatePicker = __webpack_require__(21);
var DateRangePicker = __webpack_require__(60);
var Calendar = __webpack_require__(29);

__webpack_require__(61);

/**
 * Create a calendar.
 * @see {@link Calendar}
 * @see {@link /tutorial-example07-calendar Calendar example}
 * @static
 * @param {HTMLElement|string} wrapperElement - Container element or selector of the Calendar
 * @param {Object} [options] - {@link Calendar} options. Refer to the {@link Calendar Calendar instance's options}.
 * @returns {Calendar}
 * @example
 * const calendar = DatePicker.createCalendar('#calendar-wrapper', {
 *    language: 'en',
 *    showToday: true,
 *    showJumpButtons: false,
 *    date: new Date(),
 *    type: 'date'
 * });
 */
DatePicker.createCalendar = function(wrapperElement, options) {
  return new Calendar(wrapperElement, options);
};

/**
 * Create a date-range picker.
 * @see {@link DateRangePicker}
 * @see {@link /tutorial-example08-daterangepicker DateRangePicker example}
 * @static
 * @param {object} options - {@link DateRangePicker} options. Refer to the {@link DateRangePicker DateRangePicker instance's options}.
 * @returns {DateRangePicker}
 * @example
 * const rangepicker = DatePicker.createRangePicker({
 *     startpicker: {
 *         input: '#start-input',
 *         container: '#start-container'
 *     },
 *     endpicker: {
 *         input: '#end-input',
 *         container: '#end-container'
 *     },
 *     type: 'date',
 *     format: 'yyyy-MM-dd'
 *     selectableRanges: [
 *         [new Date(2017, 3, 1), new Date(2017, 5, 1)],
 *         [new Date(2017, 6, 3), new Date(2017, 10, 5)]
 *     ]
 * });
 */
DatePicker.createRangePicker = function(options) {
  return new DateRangePicker(options);
};

module.exports = DatePicker;


/***/ }),
/* 35 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Provide a simple inheritance in prototype-oriented.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var createObject = __webpack_require__(36);

/**
 * Provide a simple inheritance in prototype-oriented.
 * Caution :
 *  Don't overwrite the prototype of child constructor.
 *
 * @param {function} subType Child constructor
 * @param {function} superType Parent constructor
 * @memberof module:inheritance
 * @example
 * var inherit = require('tui-code-snippet/inheritance/inherit'); // node, commonjs
 *
 * // Parent constructor
 * function Animal(leg) {
 *     this.leg = leg;
 * }
 * Animal.prototype.growl = function() {
 *     // ...
 * };
 *
 * // Child constructor
 * function Person(name) {
 *     this.name = name;
 * }
 *
 * // Inheritance
 * inherit(Person, Animal);
 *
 * // After this inheritance, please use only the extending of property.
 * // Do not overwrite prototype.
 * Person.prototype.walk = function(direction) {
 *     // ...
 * };
 */
function inherit(subType, superType) {
  var prototype = createObject(superType.prototype);
  prototype.constructor = subType;
  subType.prototype = prototype;
}

module.exports = inherit;


/***/ }),
/* 36 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Create a new object with the specified prototype object and properties.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



/**
 * @module inheritance
 */

/**
 * Create a new object with the specified prototype object and properties.
 * @param {Object} obj This object will be a prototype of the newly-created object.
 * @returns {Object}
 * @memberof module:inheritance
 */
function createObject(obj) {
  function F() {} // eslint-disable-line require-jsdoc
  F.prototype = obj;

  return new F();
}

module.exports = createObject;


/***/ }),
/* 37 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Check whether the given variable is existing or not.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var isUndefined = __webpack_require__(12);
var isNull = __webpack_require__(38);

/**
 * Check whether the given variable is existing or not.
 * If the given variable is not null and not undefined, returns true.
 * @param {*} param - Target for checking
 * @returns {boolean} Is existy?
 * @memberof module:type
 * @example
 * var isExisty = require('tui-code-snippet/type/isExisty'); // node, commonjs
 *
 * isExisty(''); //true
 * isExisty(0); //true
 * isExisty([]); //true
 * isExisty({}); //true
 * isExisty(null); //false
 * isExisty(undefined); //false
*/
function isExisty(param) {
  return !isUndefined(param) && !isNull(param);
}

module.exports = isExisty;


/***/ }),
/* 38 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Check whether the given variable is null or not.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



/**
 * Check whether the given variable is null or not.
 * If the given variable(arguments[0]) is null, returns true.
 * @param {*} obj - Target for checking
 * @returns {boolean} Is null?
 * @memberof module:type
 */
function isNull(obj) {
  return obj === null;
}

module.exports = isNull;


/***/ }),
/* 39 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Check whether the given variable is a function or not.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



/**
 * Check whether the given variable is a function or not.
 * If the given variable is a function, return true.
 * @param {*} obj - Target for checking
 * @returns {boolean} Is function?
 * @memberof module:type
 */
function isFunction(obj) {
  return obj instanceof Function;
}

module.exports = isFunction;


/***/ }),
/* 40 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Check element match selector
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var inArray = __webpack_require__(3);
var toArray = __webpack_require__(41);

var elProto = Element.prototype;
var matchSelector = elProto.matches ||
    elProto.webkitMatchesSelector ||
    elProto.mozMatchesSelector ||
    elProto.msMatchesSelector ||
    function(selector) {
      var doc = this.document || this.ownerDocument;

      return inArray(this, toArray(doc.querySelectorAll(selector))) > -1;
    };

/**
 * Check element match selector
 * @param {HTMLElement} element - element to check
 * @param {string} selector - selector to check
 * @returns {boolean} is selector matched to element?
 * @memberof module:domUtil
 */
function matches(element, selector) {
  return matchSelector.call(element, selector);
}

module.exports = matches;


/***/ }),
/* 41 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Transform the Array-like object to Array.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var forEachArray = __webpack_require__(2);

/**
 * Transform the Array-like object to Array.
 * In low IE (below 8), Array.prototype.slice.call is not perfect. So, try-catch statement is used.
 * @param {*} arrayLike Array-like object
 * @returns {Array} Array
 * @memberof module:collection
 * @example
 * var toArray = require('tui-code-snippet/collection/toArray'); // node, commonjs
 *
 * var arrayLike = {
 *     0: 'one',
 *     1: 'two',
 *     2: 'three',
 *     3: 'four',
 *     length: 4
 * };
 * var result = toArray(arrayLike);
 *
 * alert(result instanceof Array); // true
 * alert(result); // one,two,three,four
 */
function toArray(arrayLike) {
  var arr;
  try {
    arr = Array.prototype.slice.call(arrayLike);
  } catch (e) {
    arr = [];
    forEachArray(arrayLike, function(value) {
      arr.push(value);
    });
  }

  return arr;
}

module.exports = toArray;


/***/ }),
/* 42 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Convert kebab-case
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



/**
 * Convert kebab-case
 * @param {string} key - string to be converted to Kebab-case
 * @private
 */
function convertToKebabCase(key) {
  return key.replace(/([A-Z])/g, function(match) {
    return '-' + match.toLowerCase();
  });
}

module.exports = convertToKebabCase;


/***/ }),
/* 43 */
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE__43__;

/***/ }),
/* 44 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Calendar Header
 * @author NHN. FE dev Lab <dl_javascript@nhn.com>
 */



var defineClass = __webpack_require__(0);
var CustomEvents = __webpack_require__(8);
var closest = __webpack_require__(25);
var removeElement = __webpack_require__(14);

var localeTexts = __webpack_require__(10);
var headerTmpl = __webpack_require__(45);
var DateTimeFormatter = __webpack_require__(30);
var constants = __webpack_require__(1);
var util = __webpack_require__(4);
var mouseTouchEvent = __webpack_require__(19);

var TYPE_DATE = constants.TYPE_DATE;
var TYPE_MONTH = constants.TYPE_MONTH;
var TYPE_YEAR = constants.TYPE_YEAR;

var CLASS_NAME_TITLE_MONTH = 'tui-calendar-title-month';
var CLASS_NAME_TITLE_YEAR = 'tui-calendar-title-year';
var CLASS_NAME_TITLE_YEAR_TO_YEAR = 'tui-calendar-title-year-to-year';

var SELECTOR_INNER_ELEM = '.tui-calendar-header-inner';
var SELECTOR_INFO_ELEM = '.tui-calendar-header-info';
var SELECTOR_BTN = '.tui-calendar-btn';

var YEAR_TITLE_FORMAT = 'yyyy';

/**
 * @ignore
 * @class
 * @param {string|HTMLElement} container - Header container or selector
 * @param {object} option - Header option
 * @param {string} option.language - Header language
 * @param {boolean} option.showToday - Has today box or not.
 * @param {boolean} option.showJumpButtons - Has jump buttons or not.
 */
var Header = defineClass(
  /** @lends Header.prototype */ {
    init: function(container, option) {
      /**
       * Container element
       * @type {HTMLElement}
       * @private
       */
      this._container = util.getElement(container);

      /**
       * Header inner element
       * @type {HTMLElement}
       * @private
       */
      this._innerElement = null;

      /**
       * Header info element
       * @type {HTMLElement}
       * @private
       */
      this._infoElement = null;

      /**
       * Render today box or not
       * @type {boolean}
       * @private
       */
      this._showToday = option.showToday;

      /**
       * Render jump buttons or not (next,prev year on date calendar)
       * @type {boolean}
       * @private
       */
      this._showJumpButtons = option.showJumpButtons;

      /**
       * Year_Month title formatter
       * @type {DateTimeFormatter}
       * @private
       */
      this._yearMonthTitleFormatter = null;

      /**
       * Year title formatter
       * @type {DateTimeFormatter}
       * @private
       */
      this._yearTitleFormatter = null;

      /**
       * Today formatter
       * @type {DateTimeFormatter}
       * @private
       */
      this._todayFormatter = null;

      this._setFormatters(localeTexts[option.language]);
      this._setEvents(option);
    },

    /**
     * @param {object} localeText - Locale text
     * @private
     */
    _setFormatters: function(localeText) {
      this._yearMonthTitleFormatter = new DateTimeFormatter(
        localeText.titleFormat,
        localeText.titles
      );
      this._yearTitleFormatter = new DateTimeFormatter(YEAR_TITLE_FORMAT, localeText.titles);
      this._todayFormatter = new DateTimeFormatter(localeText.todayFormat, localeText.titles);
    },

    /**
     * @param {object} option - Constructor option
     * @private
     */
    _setEvents: function() {
      mouseTouchEvent.on(this._container, 'click', this._onClickHandler, this);
    },

    /**
     * @private
     */
    _removeEvents: function() {
      this.off();
      mouseTouchEvent.off(this._container, 'click', this._onClickHandler);
    },

    /**
     * Fire customEvents
     * @param {Event} ev An event object
     * @private
     */
    _onClickHandler: function(ev) {
      var target = util.getTarget(ev);

      if (closest(target, SELECTOR_BTN)) {
        this.fire('click', ev);
      }
    },

    /**
     * @param {string} type - Calendar type
     * @returns {string}
     * @private
     */
    _getTitleClass: function(type) {
      switch (type) {
        case TYPE_DATE:
          return CLASS_NAME_TITLE_MONTH;
        case TYPE_MONTH:
          return CLASS_NAME_TITLE_YEAR;
        case TYPE_YEAR:
          return CLASS_NAME_TITLE_YEAR_TO_YEAR;
        default:
          return '';
      }
    },

    /**
     * @param {Date} date - date
     * @param {string} type - Calendar type
     * @returns {string}
     * @private
     */
    _getTitleText: function(date, type) {
      var currentYear, start, end;

      switch (type) {
        case TYPE_DATE:
          return this._yearMonthTitleFormatter.format(date);
        case TYPE_MONTH:
          return this._yearTitleFormatter.format(date);
        case TYPE_YEAR:
          currentYear = date.getFullYear();
          start = new Date(currentYear - 4, 0, 1);
          end = new Date(currentYear + 4, 0, 1);

          return (
            this._yearTitleFormatter.format(start) + ' - ' + this._yearTitleFormatter.format(end)
          );
        default:
          return '';
      }
    },

    /**
     * Change langauge
     * @param {string} language - Language
     */
    changeLanguage: function(language) {
      this._setFormatters(localeTexts[language]);
    },

    /**
     * Render header
     * @param {Date} date - date
     * @param {string} type - Calendar type
     */
    render: function(date, type) {
      var context = {
        showToday: this._showToday,
        showJumpButtons: this._showJumpButtons,
        todayText: this._todayFormatter.format(new Date()),
        isDateCalendar: type === TYPE_DATE,
        titleClass: this._getTitleClass(type),
        title: this._getTitleText(date, type)
      };

      this._container.innerHTML = headerTmpl(context).replace(/^\s+|\s+$/g, '');
      this._innerElement = this._container.querySelector(SELECTOR_INNER_ELEM);
      if (context.showToday) {
        this._infoElement = this._container.querySelector(SELECTOR_INFO_ELEM);
      }
    },

    /**
     * Destroy header
     */
    destroy: function() {
      this._removeEvents();
      removeElement(this._innerElement);
      removeElement(this._infoElement);
      this._container = this._showToday = this._showJumpButtons = this._yearMonthTitleFormatter = this._yearTitleFormatter = this._todayFormatter = this._innerElement = this._infoElement = null;
    }
  }
);

CustomEvents.mixin(Header);
module.exports = Header;


/***/ }),
/* 45 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var template = __webpack_require__(11);

module.exports = function(context) {
  var source =
    '{{if isDateCalendar}}' +
    '  {{if showJumpButtons}}' +
    '    <div class="tui-calendar-header-inner tui-calendar-has-btns">' +
    '      <button class="tui-calendar-btn tui-calendar-btn-prev-year">Prev year</button>' +
    '      <button class="tui-calendar-btn tui-calendar-btn-prev-month">Prev month</button>' +
    '      <em class="tui-calendar-title {{titleClass}}">{{title}}</em>' +
    '      <button class="tui-calendar-btn tui-calendar-btn-next-month">Next month</button>' +
    '      <button class="tui-calendar-btn tui-calendar-btn-next-year">Next year</button>' +
    '    </div>' +
    '  {{else}}' +
    '    <div class="tui-calendar-header-inner">' +
    '      <button class="tui-calendar-btn tui-calendar-btn-prev-month">Prev month</button>' +
    '      <em class="tui-calendar-title {{titleClass}}">{{title}}</em>' +
    '      <button class="tui-calendar-btn tui-calendar-btn-next-month">Next month</button>' +
    '    </div>' +
    '  {{/if}}' +
    '{{else}}' +
    '  <div class="tui-calendar-header-inner">' +
    '    <button class="tui-calendar-btn tui-calendar-btn-prev-year">Prev year</button>' +
    '    <em class="tui-calendar-title {{titleClass}}">{{title}}</em>' +
    '    <button class="tui-calendar-btn tui-calendar-btn-next-year">Next year</button>' +
    '  </div>' +
    '{{/if}}' +
    '{{if showToday}}' +
    '  <div class="tui-calendar-header-info">' +
    '    <p class="tui-calendar-title-today">{{todayText}}</p>' +
    '  </div>' +
    '{{/if}}';

  return template(source, context);
};


/***/ }),
/* 46 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Check whether the given variable is a instance of HTMLNode or not.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



/**
 * Check whether the given variable is a instance of HTMLNode or not.
 * If the given variables is a instance of HTMLNode, return true.
 * @param {*} html - Target for checking
 * @returns {boolean} Is HTMLNode ?
 * @memberof module:type
 */
function isHTMLNode(html) {
  if (typeof HTMLElement === 'object') {
    return (html && (html instanceof HTMLElement || !!html.nodeType));
  }

  return !!(html && html.nodeType);
}

module.exports = isHTMLNode;


/***/ }),
/* 47 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Send hostname on DOMContentLoaded.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var isUndefined = __webpack_require__(12);
var imagePing = __webpack_require__(48);

var ms7days = 7 * 24 * 60 * 60 * 1000;

/**
 * Check if the date has passed 7 days
 * @param {number} date - milliseconds
 * @returns {boolean}
 * @private
 */
function isExpired(date) {
  var now = new Date().getTime();

  return now - date > ms7days;
}

/**
 * Send hostname on DOMContentLoaded.
 * To prevent hostname set tui.usageStatistics to false.
 * @param {string} appName - application name
 * @param {string} trackingId - GA tracking ID
 * @ignore
 */
function sendHostname(appName, trackingId) {
  var url = 'https://www.google-analytics.com/collect';
  var hostname = location.hostname;
  var hitType = 'event';
  var eventCategory = 'use';
  var applicationKeyForStorage = 'TOAST UI ' + appName + ' for ' + hostname + ': Statistics';
  var date = window.localStorage.getItem(applicationKeyForStorage);

  // skip if the flag is defined and is set to false explicitly
  if (!isUndefined(window.tui) && window.tui.usageStatistics === false) {
    return;
  }

  // skip if not pass seven days old
  if (date && !isExpired(date)) {
    return;
  }

  window.localStorage.setItem(applicationKeyForStorage, new Date().getTime());

  setTimeout(function() {
    if (document.readyState === 'interactive' || document.readyState === 'complete') {
      imagePing(url, {
        v: 1,
        t: hitType,
        tid: trackingId,
        cid: hostname,
        dp: hostname,
        dh: appName,
        el: appName,
        ec: eventCategory
      });
    }
  }, 1000);
}

module.exports = sendHostname;


/***/ }),
/* 48 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Request image ping.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */



var forEachOwnProperties = __webpack_require__(23);

/**
 * @module request
 */

/**
 * Request image ping.
 * @param {String} url url for ping request
 * @param {Object} trackingInfo infos for make query string
 * @returns {HTMLElement}
 * @memberof module:request
 * @example
 * var imagePing = require('tui-code-snippet/request/imagePing'); // node, commonjs
 *
 * imagePing('https://www.google-analytics.com/collect', {
 *     v: 1,
 *     t: 'event',
 *     tid: 'trackingid',
 *     cid: 'cid',
 *     dp: 'dp',
 *     dh: 'dh'
 * });
 */
function imagePing(url, trackingInfo) {
  var trackingElement = document.createElement('img');
  var queryString = '';
  forEachOwnProperties(trackingInfo, function(value, key) {
    queryString += '&' + key + '=' + value;
  });
  queryString = queryString.substring(1);

  trackingElement.src = url + '?' + queryString;

  trackingElement.style.display = 'none';
  document.body.appendChild(trackingElement);
  document.body.removeChild(trackingElement);

  return trackingElement;
}

module.exports = imagePing;


/***/ }),
/* 49 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Calendar body
 * @author NHN. FE Development Lab <dl_javascript@nhn.com>
 */



var forEachArray = __webpack_require__(2);
var defineClass = __webpack_require__(0);

var DateLayer = __webpack_require__(50);
var MonthLayer = __webpack_require__(52);
var YearLayer = __webpack_require__(54);
var constants = __webpack_require__(1);

var TYPE_DATE = constants.TYPE_DATE;
var TYPE_MONTH = constants.TYPE_MONTH;
var TYPE_YEAR = constants.TYPE_YEAR;

/**
 * @ignore
 * @class
 */
var Body = defineClass(
  /** @lends Body.prototype */ {
    init: function(bodyContainer, options) {
      var language = options.language;
      var weekStartDay = options.weekStartDay;

      /**
       * Body container element
       * @type {HTMLElement}
       * @private
       */
      this._container = bodyContainer;

      /**
       * DateLayer
       * @type {DateLayer}
       * @private
       */
      this._dateLayer = new DateLayer(language, weekStartDay);

      /**
       * MonthLayer
       * @type {MonthLayer}
       * @private
       */
      this._monthLayer = new MonthLayer(language);

      /**
       * YearLayer
       * @type {YearLayer}
       * @private
       */
      this._yearLayer = new YearLayer(language);

      /**
       * Current Layer
       * @type {DateLayer|MonthLayer|YearLayer}
       * @private
       */
      this._currentLayer = this._dateLayer;
    },

    /**
     * Returns matched layer
     * @param {string} type - Layer type
     * @returns {Base} - Layer
     * @private
     */
    _getLayer: function(type) {
      switch (type) {
        case TYPE_DATE:
          return this._dateLayer;
        case TYPE_MONTH:
          return this._monthLayer;
        case TYPE_YEAR:
          return this._yearLayer;
        default:
          return this._currentLayer;
      }
    },

    /**
     * Iterate each layer
     * @param {Function} fn - function
     * @private
     */
    _eachLayer: function(fn) {
      forEachArray([this._dateLayer, this._monthLayer, this._yearLayer], fn);
    },

    /**
     * Change language
     * @param {string} language - Language
     */
    changeLanguage: function(language) {
      this._eachLayer(function(layer) {
        layer.changeLanguage(language);
      });
    },

    /**
     * Render body
     * @param {Date} date - date
     * @param {string} type - Layer type
     */
    render: function(date, type) {
      var nextLayer = this._getLayer(type);
      var prevLayer = this._currentLayer;

      prevLayer.remove();
      nextLayer.render(date, this._container);

      this._currentLayer = nextLayer;
    },

    /**
     * Returns date elements
     * @returns {HTMLElement[]}
     */
    getDateElements: function() {
      return this._currentLayer.getDateElements();
    },

    /**
     * Destory
     */
    destroy: function() {
      this._eachLayer(function(layer) {
        layer.remove();
      });

      this._container = this._currentLayer = this._dateLayer = this._monthLayer = this._yearLayer = null;
    }
  }
);

module.exports = Body;


/***/ }),
/* 50 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Date layer
 * @author NHN. FE Development Lab <dl_javascript@nhn.com>
 */



var defineClass = __webpack_require__(0);

var dateUtil = __webpack_require__(5);
var bodyTmpl = __webpack_require__(51);
var LayerBase = __webpack_require__(20);
var TYPE_DATE = __webpack_require__(1).TYPE_DATE;
var WEEK_START_DAY_MAP = __webpack_require__(1).WEEK_START_DAY_MAP;

var DATE_SELECTOR = '.tui-calendar-date';
var DAYS_OF_WEEK = 7;

/**
 * @ignore
 * @class
 * @extends LayerBase
 * @param {string} language - Initial language
 */
var DateLayer = defineClass(
  LayerBase,
  /** @lends DateLayer.prototype */ {
    init: function(language, weekStartDay) {
      LayerBase.call(this, language);

      this.weekStartDay = WEEK_START_DAY_MAP[String(weekStartDay).toLowerCase()] || 0;
    },

    /**
     * Layer type
     * @type {string}
     * @private
     */
    _type: TYPE_DATE,

    /**
     * @override
     * @private
     * @returns {object} Template context
     */
    _makeContext: function(date) {
      var daysShort = this._localeText.titles.D;
      var year, month, days, i;

      date = date || new Date();
      year = date.getFullYear();
      month = date.getMonth() + 1;

      if (this.weekStartDay) {
        days = daysShort.slice();
        for (i = 0; i < this.weekStartDay; i += 1) {
          days.push(days.shift());
        }
        daysShort = days;
      }

      return {
        Sun: daysShort[0],
        Mon: daysShort[1],
        Tue: daysShort[2],
        Wed: daysShort[3],
        Thu: daysShort[4],
        Fri: daysShort[5],
        Sat: daysShort[6],
        year: year,
        month: month,
        weeks: this._getWeeks(year, month)
      };
    },

    /**
     * weeks (templating) for date-calendar
     * @param {number} year - Year
     * @param {number} month - Month
     * @returns {Array.<Array.<Date>>}
     * @private
     */
    _getWeeks: function(year, month) {
      var weekNumber = 0;
      var weeksCount = 6; // Fix for no changing height
      var weeks = [];
      var week, dates, i;

      while (weekNumber < weeksCount) {
        dates = [];

        for (i = this.weekStartDay; i < DAYS_OF_WEEK + this.weekStartDay; i += 1) {
          dates.push(dateUtil.getDateOfWeek(year, month, weekNumber, i));
        }

        week = this._getWeek(year, month, dates);

        if (this.weekStartDay && !_isFirstWeek(weekNumber, week[0].dayInMonth)) {
          weeks.push(this._getFirstWeek(year, month));
          weeksCount -= 1; // Fix for no changing height
        }

        weeks.push(week);
        weekNumber += 1;
      }

      return weeks;
    },

    /**
     * week (templating) for date-calendar
     * @param {number} currentYear
     * @param {number} currentMonth
     * @param {Array.<Date>} dates
     * @private
     */
    _getWeek: function(currentYear, currentMonth, dates) {
      var firstDateOfCurrentMonth = new Date(currentYear, currentMonth - 1, 1);
      var lastDateOfCurrentMonth = new Date(currentYear, currentMonth, 0);
      var contexts = [];
      var i = 0;
      var length = dates.length;
      var date, className;

      for (; i < length; i += 1) {
        className = 'tui-calendar-date';
        date = dates[i];

        if (date < firstDateOfCurrentMonth) {
          className += ' tui-calendar-prev-month';
        }

        if (date > lastDateOfCurrentMonth) {
          className += ' tui-calendar-next-month';
        }

        if (date.getDay() === 0) {
          className += ' tui-calendar-sun';
        } else if (date.getDay() === 6) {
          className += ' tui-calendar-sat';
        }

        contexts.push({
          dayInMonth: date.getDate(),
          className: className,
          timestamp: date.getTime()
        });
      }

      return contexts;
    },

    /**
     * Render date-layer
     * @override
     * @param {Date} date Date to render
     * @param {HTMLElement} container A container element for the rendered element
     */
    render: function(date, container) {
      var context = this._makeContext(date);

      container.innerHTML = bodyTmpl(context);
      this._element = container.firstChild;
    },

    /**
     * Return date elements
     * @override
     * @returns {HTMLElement[]}
     */
    getDateElements: function() {
      return this._element.querySelectorAll(DATE_SELECTOR);
    },

    _getFirstWeek: function(year, month) {
      var firstWeekDates = [];
      var i;

      for (i = this.weekStartDay; i < DAYS_OF_WEEK + this.weekStartDay; i += 1) {
        firstWeekDates.push(dateUtil.getDateOfWeek(year, month, -1, i));
      }

      return this._getWeek(year, month, firstWeekDates);
    }
  }
);

function _isFirstWeek(weekIndex, dayInMonth) {
  return weekIndex || dayInMonth === 1 || dayInMonth > DAYS_OF_WEEK;
}

module.exports = DateLayer;


/***/ }),
/* 51 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var template = __webpack_require__(11);

module.exports = function(context) {
  var source =
    '<table class="tui-calendar-body-inner" cellspacing="0" cellpadding="0">' +
    '  <caption><span>Dates</span></caption>' +
    '  <thead class="tui-calendar-body-header">' +
    '    <tr>' +
    '      <th class="tui-sun" scope="col">{{Sun}}</th>' +
    '      <th scope="col">{{Mon}}</th>' +
    '      <th scope="col">{{Tue}}</th>' +
    '      <th scope="col">{{Wed}}</th>' +
    '      <th scope="col">{{Thu}}</th>' +
    '      <th scope="col">{{Fri}}</th>' +
    '      <th class="tui-sat" scope="col">{{Sat}}</th>' +
    '    </tr>' +
    '  </thead>' +
    '  <tbody>' +
    '    {{each weeks}}' +
    '    <tr class="tui-calendar-week">' +
    '      {{each @this}}' +
    '      <td class="{{@this["className"]}}" data-timestamp="{{@this["timestamp"]}}">{{@this["dayInMonth"]}}</td>' +
    '      {{/each}}' +
    '    </tr>' +
    '    {{/each}}' +
    '  </tbody>' +
    '</table>';

  return template(source, context);
};


/***/ }),
/* 52 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Month layer
 * @author NHN. FE Development Lab <dl_javascript@nhn.com>
 */



var defineClass = __webpack_require__(0);

var bodyTmpl = __webpack_require__(53);
var LayerBase = __webpack_require__(20);
var TYPE_MONTH = __webpack_require__(1).TYPE_MONTH;
var dateUtil = __webpack_require__(5);

var DATE_SELECTOR = '.tui-calendar-month';

/**
 * @class
 * @extends LayerBase
 * @param {string} language - Initial language
 * @ignore
 */
var MonthLayer = defineClass(
  LayerBase,
  /** @lends MonthLayer.prototype */ {
    init: function(language) {
      LayerBase.call(this, language);
    },

    /**
     * Layer type
     * @type {string}
     * @private
     */
    _type: TYPE_MONTH,

    /**
     * @override
     * @returns {object} Template context
     * @private
     */
    _makeContext: function(date) {
      var monthsShort = this._localeText.titles.MMM;

      return {
        year: date.getFullYear(),
        Jan: monthsShort[0],
        Feb: monthsShort[1],
        Mar: monthsShort[2],
        Apr: monthsShort[3],
        May: monthsShort[4],
        Jun: monthsShort[5],
        Jul: monthsShort[6],
        Aug: monthsShort[7],
        Sep: monthsShort[8],
        Oct: monthsShort[9],
        Nov: monthsShort[10],
        Dec: monthsShort[11],
        getFirstDayTimestamp: dateUtil.getFirstDayTimestamp
      };
    },

    /**
     * Render month-layer element
     * @override
     * @param {Date} date Date to render
     * @param {HTMLElement} container A container element for the rendered element
     */
    render: function(date, container) {
      var context = this._makeContext(date);

      container.innerHTML = bodyTmpl(context);
      this._element = container.firstChild;
    },

    /**
     * Returns month elements
     * @override
     * @returns {HTMLElement[]}
     */
    getDateElements: function() {
      return this._element.querySelectorAll(DATE_SELECTOR);
    }
  }
);

module.exports = MonthLayer;


/***/ }),
/* 53 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var template = __webpack_require__(11);

module.exports = function(context) {
  var source =
    '<table class="tui-calendar-body-inner">' +
    '  <caption><span>Months</span></caption>' +
    '  <tbody>' +
    '    <tr class="tui-calendar-month-group">' +
    '      <td class="tui-calendar-month" data-timestamp={{getFirstDayTimestamp year 0}}>{{Jan}}</td>' +
    '      <td class="tui-calendar-month" data-timestamp={{getFirstDayTimestamp year 1}}>{{Feb}}</td>' +
    '      <td class="tui-calendar-month" data-timestamp={{getFirstDayTimestamp year 2}}>{{Mar}}</td>' +
    '      <td class="tui-calendar-month" data-timestamp={{getFirstDayTimestamp year 3}}>{{Apr}}</td>' +
    '    </tr>' +
    '    <tr class="tui-calendar-month-group">' +
    '      <td class="tui-calendar-month" data-timestamp={{getFirstDayTimestamp year 4}}>{{May}}</td>' +
    '      <td class="tui-calendar-month" data-timestamp={{getFirstDayTimestamp year 5}}>{{Jun}}</td>' +
    '      <td class="tui-calendar-month" data-timestamp={{getFirstDayTimestamp year 6}}>{{Jul}}</td>' +
    '      <td class="tui-calendar-month" data-timestamp={{getFirstDayTimestamp year 7}}>{{Aug}}</td>' +
    '    </tr>' +
    '    <tr class="tui-calendar-month-group">' +
    '      <td class="tui-calendar-month" data-timestamp={{getFirstDayTimestamp year 8}}>{{Sep}}</td>' +
    '      <td class="tui-calendar-month" data-timestamp={{getFirstDayTimestamp year 9}}>{{Oct}}</td>' +
    '      <td class="tui-calendar-month" data-timestamp={{getFirstDayTimestamp year 10}}>{{Nov}}</td>' +
    '      <td class="tui-calendar-month" data-timestamp={{getFirstDayTimestamp year 11}}>{{Dec}}</td>' +
    '    </tr>' +
    '  </tbody>' +
    '</table>';

  return template(source, context);
};


/***/ }),
/* 54 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Year layer
 * @author NHN. FE Development Lab <dl_javascript@nhn.com>
 */



var defineClass = __webpack_require__(0);

var bodyTmpl = __webpack_require__(55);
var LayerBase = __webpack_require__(20);
var TYPE_YEAR = __webpack_require__(1).TYPE_YEAR;
var dateUtil = __webpack_require__(5);

var DATE_SELECTOR = '.tui-calendar-year';

/**
 * @class
 * @extends LayerBase
 * @param {string} language - Initial language
 * @ignore
 */
var YearLayer = defineClass(
  LayerBase,
  /** @lends YearLayer.prototype */ {
    init: function(language) {
      LayerBase.call(this, language);
    },

    /**
     * Layer type
     * @type {string}
     * @private
     */
    _type: TYPE_YEAR,

    /**
     * @override
     * @returns {object} Template context
     * @private
     */
    _makeContext: function(date) {
      var year = date.getFullYear();

      return {
        yearGroups: [
          dateUtil.getRangeArr(year - 4, year - 2),
          dateUtil.getRangeArr(year - 1, year + 1),
          dateUtil.getRangeArr(year + 2, year + 4)
        ],
        getFirstDayTimestamp: dateUtil.getFirstDayTimestamp
      };
    },

    /**
     * Render year-layer element
     * @override
     * @param {Date} date Date to render
     * @param {HTMLElement} container A container element for the rendered element
     */
    render: function(date, container) {
      var context = this._makeContext(date);

      container.innerHTML = bodyTmpl(context);
      this._element = container.firstChild;
    },

    /**
     * Returns year elements
     * @override
     * @returns {HTMLElement[]}
     */
    getDateElements: function() {
      return this._element.querySelectorAll(DATE_SELECTOR);
    }
  }
);

module.exports = YearLayer;


/***/ }),
/* 55 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var template = __webpack_require__(11);

module.exports = function(context) {
  var source =
    '<table class="tui-calendar-body-inner">' +
    '  <caption><span>Years</span></caption>' +
    '  <tbody>' +
    '    {{each yearGroups}}' +
    '    <tr class="tui-calendar-year-group">' +
    '      {{each @this}}' +
    '      <td class="tui-calendar-year" data-timestamp={{getFirstDayTimestamp @this 0}}>' +
    '        {{@this}}' +
    '      </td>' +
    '      {{/each}}' +
    '    </tr>' +
    '    {{/each}}' +
    '  </tbody>' +
    '</table>';

  return template(source, context);
};


/***/ }),
/* 56 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview RangeModel
 * @author NHN. FE Development Lab <dl_javascript@nhn.com>
 */



var forEachArray = __webpack_require__(2);
var defineClass = __webpack_require__(0);
var isNumber = __webpack_require__(15);

var Range = __webpack_require__(57);
var util = __webpack_require__(4);

/**
 * @class
 * @ignore
 * @param {Array.<Array.<number>>} ranges - Ranges
 */
var RangeModel = defineClass(
  /** @lends RangeModel.prototype */ {
    init: function(ranges) {
      ranges = ranges || [];

      /**
       * @type {Array.<Range>}
       * @private
       */
      this._ranges = [];

      forEachArray(
        ranges,
        function(range) {
          this.add(range[0], range[1]);
        },
        this
      );
    },

    /**
     * Whether the ranges contain a time or time-range
     * @param {number} start - Start
     * @param {number} [end] - End
     * @returns {boolean}
     */
    contains: function(start, end) {
      var i = 0;
      var length = this._ranges.length;
      var range;

      for (; i < length; i += 1) {
        range = this._ranges[i];
        if (range.contains(start, end)) {
          return true;
        }
      }

      return false;
    },

    /**
     * Whether overlaps with a point or range
     * @param {number} start - Start
     * @param {number} [end] - End
     * @returns {boolean}
     */
    hasOverlap: function(start, end) {
      var i = 0;
      var length = this._ranges.length;
      var range;

      for (; i < length; i += 1) {
        range = this._ranges[i];
        if (range.isOverlapped(start, end)) {
          return true;
        }
      }

      return false;
    },

    /**
     * Add range
     * @param {number} start - Start
     * @param {number} [end] - End
     */
    add: function(start, end) {
      var overlapped = false;
      var i = 0;
      var len = this._ranges.length;
      var range;

      for (; i < len; i += 1) {
        range = this._ranges[i];
        overlapped = range.isOverlapped(start, end);

        if (overlapped) {
          range.merge(start, end);
          break;
        }

        if (start < range.start) {
          break;
        }
      }

      if (!overlapped) {
        this._ranges.splice(i, 0, new Range(start, end));
      }
    },

    /**
     * Returns minimum value in ranges
     * @returns {number}
     */
    getMinimumValue: function() {
      return this._ranges[0].start;
    },

    /**
     * Returns maximum value in ranges
     * @returns {number}
     */
    getMaximumValue: function() {
      var length = this._ranges.length;

      return this._ranges[length - 1].end;
    },

    /**
     * @param {number} start - Start
     * @param {number} [end] - End
     */
    exclude: function(start, end) {
      if (!isNumber(end)) {
        end = start;
      }

      forEachArray(
        this._ranges,
        function(range) {
          var rangeEnd;

          if (range.isOverlapped(start, end)) {
            rangeEnd = range.end; // Save before excluding
            range.exclude(start, end);

            if (end + 1 <= rangeEnd) {
              this.add(end + 1, rangeEnd); // Add split range
            }
          }
        },
        this
      );

      // Reduce empty ranges
      this._ranges = util.filter(this._ranges, function(range) {
        return !range.isEmpty();
      });
    },

    /**
     * Returns the first overlapped range from the point or range
     * @param {number} start - Start
     * @param {number} end - End
     * @returns {Array.<number>} - [start, end]
     */
    findOverlappedRange: function(start, end) {
      var i = 0;
      var len = this._ranges.length;
      var range;

      for (; i < len; i += 1) {
        range = this._ranges[i];
        if (range.isOverlapped(start, end)) {
          return [range.start, range.end];
        }
      }

      return null;
    }
  }
);

module.exports = RangeModel;


/***/ }),
/* 57 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Range (in RangeModel)
 * @author NHN. FE Development Lab <dl_javascript@nhn.com>
 */



var defineClass = __webpack_require__(0);
var isNumber = __webpack_require__(15);

/**
 * @class
 * @ignore
 * @param {number} start - Start of range
 * @param {number} [end] - End of range
 */
var Range = defineClass(
  /** @lends Range.prototype */ {
    init: function(start, end) {
      this.setRange(start, end);
    },

    /**
     * Set range
     * @param {number} start - Start number
     * @param {number} [end] - End number
     */
    setRange: function(start, end) {
      if (!isNumber(end)) {
        end = start;
      }

      this.start = Math.min(start, end);
      this.end = Math.max(start, end);
    },

    /**
     * Merge range
     * @param {number} start - Start
     * @param {number} [end] - End
     */
    merge: function(start, end) {
      if (!isNumber(start) || !isNumber(end) || !this.isOverlapped(start, end)) {
        return;
      }

      this.start = Math.min(start, this.start);
      this.end = Math.max(end, this.end);
    },

    /**
     * Whether being empty.
     * @returns {boolean}
     */
    isEmpty: function() {
      return !isNumber(this.start) || !isNumber(this.end);
    },

    /**
     * Set empty
     */
    setEmpty: function() {
      this.start = this.end = null;
    },

    /**
     * Whether containing a range.
     * @param {number} start - Start
     * @param {number} [end] - End
     * @returns {boolean}
     */
    contains: function(start, end) {
      if (!isNumber(end)) {
        end = start;
      }

      return this.start <= start && end <= this.end;
    },

    /**
     * Whether overlaps with a range
     * @param {number} start - Start
     * @param {number} [end] - End
     * @returns {boolean}
     */
    isOverlapped: function(start, end) {
      if (!isNumber(end)) {
        end = start;
      }

      return this.start <= end && this.end >= start;
    },

    /**
     * Exclude a range
     * @param {number} start - Start
     * @param {number} end - End
     */
    exclude: function(start, end) {
      if (start <= this.start && end >= this.end) {
        // Excluding range contains this
        this.setEmpty();
      } else if (this.contains(start)) {
        this.setRange(this.start, start - 1);
      } else if (this.contains(end)) {
        this.setRange(end + 1, this.end);
      }
    }
  }
);

module.exports = Range;


/***/ }),
/* 58 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var template = __webpack_require__(11);

module.exports = function(context) {
  var source =
    '<div class="tui-datepicker">' +
    '  {{if timePicker}}' +
    '    {{if isTab}}' +
    '      <div class="tui-datepicker-selector">' +
    '        <button type="button" class="tui-datepicker-selector-button tui-is-checked" aria-label="selected">' +
    '          <span class="tui-ico-date"></span>{{localeText["date"]}}' +
    '        </button>' +
    '        <button type="button" class="tui-datepicker-selector-button">' +
    '          <span class="tui-ico-time"></span>{{localeText["time"]}}' +
    '        </button>' +
    '      </div>' +
    '      <div class="tui-datepicker-body">' +
    '        <div class="tui-calendar-container"></div>' +
    '        <div class="tui-timepicker-container"></div>' +
    '      </div>' +
    '    {{else}}' +
    '      <div class="tui-datepicker-body">' +
    '        <div class="tui-calendar-container"></div>' +
    '      </div>' +
    '      <div class="tui-datepicker-footer">' +
    '        <div class="tui-timepicker-container"></div>' +
    '      </div>' +
    '    {{/if}}' +
    '  {{else}}' +
    '    <div class="tui-datepicker-body">' +
    '      <div class="tui-calendar-container"></div>' +
    '    </div>' +
    '  {{/if}}' +
    '</div>';

  return template(source, context);
};


/***/ }),
/* 59 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview DatePicker input(element) component
 * @author NHN. FE Development Lab <dl_javascript@nhn.com>
 */



var defineClass = __webpack_require__(0);
var CustomEvents = __webpack_require__(8);
var on = __webpack_require__(31);
var off = __webpack_require__(33);

var DateTimeFormatter = __webpack_require__(30);
var mouseTouchEvent = __webpack_require__(19);
var util = __webpack_require__(4);

var DEFAULT_FORMAT = 'yyyy-MM-dd';

/**
 * DatePicker Input
 * @ignore
 * @class
 * @param {string|HTMLElement} inputElement - Input element or selector
 * @param {object} option - Option
 * @param {string} option.id - Id
 * @param {string} option.format - Text format
 */
var DatePickerInput = defineClass(
  /** @lends DatePickerInput.prototype */ {
    init: function(inputElement, option) {
      option.format = option.format || DEFAULT_FORMAT;

      /**
       * Input element
       * @type {HTMLElement}
       * @private
       */
      this._input = util.getElement(inputElement);

      /**
       * Id
       * @type {string}
       * @private
       */
      this._id = option.id;

      /**
       * LocaleText titles
       * @type {Object}
       * @private
       */
      this._titles = option.localeText.titles;

      /**
       * Text<->DateTime Formatter
       * @type {DateTimeFormatter}
       * @private
       */
      this._formatter = new DateTimeFormatter(option.format, this._titles);

      this._setEvents();
    },

    /**
     * Change locale titles
     * @param {object} titles - locale text in format
     */
    changeLocaleTitles: function(titles) {
      this._titles = titles;
    },

    /**
     * Set input 'click', 'change' event
     * @private
     */
    _setEvents: function() {
      if (this._input) {
        on(this._input, 'change', this._onChangeHandler, this);
        mouseTouchEvent.on(this._input, 'click', this._onClickHandler, this);
      }
    },

    /**
     * Remove events
     * @private
     */
    _removeEvents: function() {
      this.off();

      if (this._input) {
        off(this._input, 'change', this._onChangeHandler);
        mouseTouchEvent.off(this._input, 'click', this._onClickHandler);
      }
    },

    /**
     * Onchange handler
     */
    _onChangeHandler: function() {
      this.fire('change');
    },

    /**
     * Onclick handler
     */
    _onClickHandler: function() {
      this.fire('click');
    },

    /**
     * Check element is same as the input element.
     * @param {HTMLElement} el - To check matched set of elements
     * @returns {boolean}
     */
    is: function(el) {
      return this._input === el;
    },

    /**
     * Enable input
     */
    enable: function() {
      if (this._input) {
        this._input.removeAttribute('disabled');
      }
    },

    /**
     * Disable input
     */
    disable: function() {
      if (this._input) {
        this._input.setAttribute('disabled', true);
      }
    },

    /**
     * Return format
     * @returns {string}
     */
    getFormat: function() {
      return this._formatter.getRawString();
    },

    /**
     * Set format
     * @param {string} format - Format
     */
    setFormat: function(format) {
      if (!format) {
        return;
      }

      this._formatter = new DateTimeFormatter(format, this._titles);
    },

    /**
     * Clear text
     */
    clearText: function() {
      if (this._input) {
        this._input.value = '';
      }
    },

    /**
     * Set value from date
     * @param {Date} date - Date
     */
    setDate: function(date) {
      if (this._input) {
        this._input.value = this._formatter.format(date);
      }
    },

    /**
     * Returns date from input-text
     * @returns {Date}
     * @throws {Error}
     */
    getDate: function() {
      var value = '';

      if (this._input) {
        value = this._input.value;
      }

      return this._formatter.parse(value);
    },

    /**
     * Destroy
     */
    destroy: function() {
      this._removeEvents();

      this._input = this._id = this._formatter = null;
    }
  }
);

CustomEvents.mixin(DatePickerInput);
module.exports = DatePickerInput;


/***/ }),
/* 60 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/**
 * @fileoverview Date-Range picker
 * @author NHN. FE Development Lab <dl_javascript@nhn.com>
 */



var forEachArray = __webpack_require__(2);
var defineClass = __webpack_require__(0);
var CustomEvents = __webpack_require__(8);
var addClass = __webpack_require__(16);
var getData = __webpack_require__(26);
var removeClass = __webpack_require__(18);
var extend = __webpack_require__(7);

var DatePicker = __webpack_require__(21);
var dateUtil = __webpack_require__(5);
var constants = __webpack_require__(1);
var util = __webpack_require__(4);

var CLASS_NAME_RANGE_PICKER = 'tui-rangepicker';
var CLASS_NAME_SELECTED = constants.CLASS_NAME_SELECTED;
var CLASS_NAME_SELECTED_RANGE = 'tui-is-selected-range';

/**
 * @class
 * @description
 * Create a date-range picker by {@link DatePicker#createRangePicker DatePicker.createRangePicker()}.
 * @see {@link /tutorial-example08-daterangepicker DateRangePicker example}
 * @param {object} options - DateRangePicker options
 *     @param {object} options.startpicker - Startpicker options
 *         @param {HTMLElement|string} options.startpicker.input - Startpicker input element or selector
 *         @param {HTMLElement|string} options.startpicker.container - Startpicker container element or selector
 *         @param {Date|number} [options.startpicker.date] - Initial date of the start picker. Set by a Date instance or a number(timestamp). (default: no initial date)
 *         @param {string} [options.startpicker.weekStartDay = 'Sun'] - Start of the week. 'Sun', 'Mon', ..., 'Sat'(default: 'Sun'(start on Sunday))
 *     @param {object} options.endpicker - Endpicker options
 *         @param {HTMLElement|string} options.endpicker.input - Endpicker input element or selector
 *         @param {HTMLElement|string} options.endpicker.container - Endpicker container element or selector
 *         @param {Date|number} [options.endpicker.date] - Initial date of the end picker. Set by a Date instance or a number(timestamp). (default: no initial date)
 *         @param {string} [options.endpicker.weekStartDay = 'Sun'] - Start of the week. 'Sun', 'Mon', ..., 'Sat'(default: 'Sun'(start on Sunday))
 *     @param {('date'|'month'|'year')} [options.type = 'date'] - DatePicker type. Determine whether to choose a date, month, or year.
 *     @param {string} [options.language='en'] - Language code. English('en') and Korean('ko') are provided as default. To use the other languages, use {@link DatePicker#localeTexts DatePicker.localeTexts}.
 *     @param {object|boolean} [options.timePicker] - [TimePicker](https://nhn.github.io/tui.time-picker/latest) options. Refer to the [TimePicker instance's options](https://nhn.github.io/tui.time-picker/latest/TimePicker). To create the TimePicker without customization, set to true.
 *     @param {object} [options.calendar] - {@link Calendar} options. Refer to the {@link Calendar Calendar instance's options}.
 *     @param {string} [options.format = 'yyyy-mm-dd'] - Format of the Date string
 *     @param {Array.<Array.<Date|number>>} [options.selectableRanges] - Ranges of selectable date. Set by Date instances or numbers(timestamp).
 *     @param {boolean} [options.showAlways = false] - Show the DateRangePicker always
 *     @param {boolean} [options.autoClose = true] - Close the DateRangePicker after clicking the date
 *     @param {boolean} [options.usageStatistics = true] - Send a hostname to Google Analytics (default: true)
 * @example
 * // ES6
 * import DatePicker from 'tui-date-picker'
 *
 * // CommonJS
 * const DatePicker = require('tui-date-picker');
 *
 * // Browser
 * const DatePicker = tui.DatePicker;
 *
 * const rangePicker = DatePicker.createRangePicker({
 *     startpicker: {
 *         input: '#start-input',
 *         container: '#start-container'
 *         date: new Date(2019, 3, 1),
 *         weekStartDay: 'Mon',
 *     },
 *     endpicker: {
 *         input: '#end-input',
 *         container: '#end-container',
 *         weekStartDay: 'Mon',
 *     },
 *     type: 'date',
 *     format: 'yyyy-MM-dd'
 *     selectableRanges: [
 *         [new Date(2017, 3, 1), new Date(2017, 5, 1)],
 *         [new Date(2017, 6, 3), new Date(2017, 10, 5)]
 *     ]
 * });
 */
var DateRangePicker = defineClass(
  /** @lends DateRangePicker.prototype */ {
    init: function(options) {
      var startpickerOpt, endpickerOpt;

      options = options || {};
      startpickerOpt = options.startpicker;
      endpickerOpt = options.endpicker;

      if (!startpickerOpt) {
        throw new Error('The "startpicker" option is required.');
      }
      if (!endpickerOpt) {
        throw new Error('The "endpicker" option is required.');
      }

      /**
       * Start picker
       * @type {DatePicker}
       * @private
       */
      this._startpicker = null;

      /**
       * End picker
       * @type {DatePicker}
       * @private
       */
      this._endpicker = null;

      this._isRangeSet = false;

      this._preEndPickerDate = new Date().getDate();

      this._initializePickers(options);
      this._syncRangesToEndpicker();
    },

    /**
     * Create picker
     * @param {Object} options - DatePicker options
     * @private
     */
    _initializePickers: function(options) {
      var startpickerContainer = util.getElement(options.startpicker.container);
      var endpickerContainer = util.getElement(options.endpicker.container);
      var startInput = util.getElement(options.startpicker.input);
      var endInput = util.getElement(options.endpicker.input);

      var startpickerOpt = extend({}, options, {
        input: {
          element: startInput,
          format: options.format
        },
        date: options.startpicker.date,
        weekStartDay: options.startpicker.weekStartDay
      });
      var endpickerOpt = extend({}, options, {
        input: {
          element: endInput,
          format: options.format
        },
        date: options.endpicker.date,
        weekStartDay: options.endpicker.weekStartDay
      });

      this._startpicker = new DatePicker(startpickerContainer, startpickerOpt);
      this._startpicker.addCssClass(CLASS_NAME_RANGE_PICKER);
      this._startpicker.on('change', this._onChangeStartpicker, this);
      this._startpicker.on('draw', this._onDrawPicker, this);

      this._endpicker = new DatePicker(endpickerContainer, endpickerOpt);
      this._endpicker.addCssClass(CLASS_NAME_RANGE_PICKER);
      this._endpicker.on('change', this._onChangeEndpicker, this);
      this._endpicker.on('draw', this._onDrawPicker, this);
    },

    /**
     * Set selection-class to elements after calendar drawing
     * @param {Object} eventData - Event data {@link DatePicker#event:draw}
     * @private
     */
    _onDrawPicker: function(eventData) {
      var calendarType = eventData.type;
      var startDate = this._startpicker.getDate();
      var endDate = this._endpicker.getDate();

      if (!startDate) {
        return;
      }

      if (!endDate) {
        // Convert null to invaild date.
        endDate = new Date(NaN);
      }

      forEachArray(
        eventData.dateElements,
        function(el) {
          var elDate = new Date(Number(getData(el, 'timestamp')));
          var isInRange = dateUtil.inRange(startDate, endDate, elDate, calendarType);
          var isSelected =
            dateUtil.isSame(startDate, elDate, calendarType) ||
            dateUtil.isSame(endDate, elDate, calendarType);

          this._setRangeClass(el, isInRange);
          this._setSelectedClass(el, isSelected);
        },
        this
      );
    },

    /**
     * Set range class to element
     * @param {HTMLElement} el - Element
     * @param {boolean} isInRange - In range
     * @private
     */
    _setRangeClass: function(el, isInRange) {
      if (isInRange) {
        addClass(el, CLASS_NAME_SELECTED_RANGE);
      } else {
        removeClass(el, CLASS_NAME_SELECTED_RANGE);
      }
    },

    /**
     * Set selected class to element
     * @param {HTMLElement} el - Element
     * @param {boolean} isSelected - Is selected
     * @private
     */
    _setSelectedClass: function(el, isSelected) {
      if (isSelected) {
        addClass(el, CLASS_NAME_SELECTED);
      } else {
        removeClass(el, CLASS_NAME_SELECTED);
      }
    },

    /**
     * Sync ranges to endpicker
     * @private
     */
    _syncRangesToEndpicker: function() {
      var startDate = this._startpicker.getDate();
      var overlappedRange;

      if (startDate) {
        overlappedRange = this._startpicker.findOverlappedRange(
          dateUtil.cloneWithStartOf(startDate).getTime(),
          dateUtil.cloneWithEndOf(startDate).getTime()
        );

        this._endpicker.enable();
        this._endpicker.setRanges([[startDate.getTime(), overlappedRange[1].getTime()]]);

        this._setTimeRangeOnEndPicker();
      } else {
        this._endpicker.setNull();
        this._endpicker.disable();
      }
    },

    /**
     * After change on start-picker
     * @private
     */
    _onChangeStartpicker: function() {
      this._syncRangesToEndpicker();
      /**
       * Occur after the start date is changed.
       * @event DateRangePicker#change:start
       * @see {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents#on rangePicker.on()} to bind event handlers.
       * @see {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents#off rangePicker.off()} to unbind event handlers.
       * @see Refer to {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents CustomEvents} for more methods. DateRangePicker mixes in the methods from CustomEvents.
       * @example
       * // bind the 'change:start' event
       * rangePicker.on('change:start', () => {
       *     console.log(`Start date: ${rangePicker.getStartDate()}`);
       * });
       *
       * // unbind the 'change:start' event
       * rangePicker.off('change:start');
       */
      this.fire('change:start');
    },

    /**
     * After change on end-picker
     * @private
     */
    _onChangeEndpicker: function() {
      /**
       * Occur after the end date is changed.
       * @event DateRangePicker#change:end
       * @see {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents#on rangePicker.on()} to bind event handlers.
       * @see {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents#off rangePicker.off()} to unbind event handlers.
       * @see Refer to {@link https://nhn.github.io/tui.code-snippet/latest/CustomEvents CustomEvents} for more methods. DateRangePicker mixes in the methods from CustomEvents.
       * @example
       * // bind the 'change:end' event
       * rangePicker.on('change:end', () => {
       *     console.log(`End date: ${rangePicker.getEndDate()}`);
       * });
       *
       * // unbind the 'change:end' event
       * rangePicker.off('change:end');
       */

      var date;
      var endPickerDate = this._endpicker.getDate();

      if (endPickerDate) {
        date = endPickerDate.getDate();
        if (this._preEndPickerDate !== date) {
          this._setTimeRangeOnEndPicker();
        }

        this._preEndPickerDate = date;
      } else {
        this._preEndPickerDate = null;
      }

      this.fire('change:end');
    },

    /**
     * Set time range on end picker
     * @private
     */
    _setTimeRangeOnEndPicker: function() {
      var pickerDate, timeRange;
      var endTimePicker = this._endpicker._timePicker;

      if (!endTimePicker) {
        return;
      }

      pickerDate = this._endpicker.getDate() || this._startpicker.getDate();
      timeRange = this._getTimeRangeFromStartPicker();

      if (pickerDate && timeRange[pickerDate.getDate()]) {
        endTimePicker.setRange(timeRange[pickerDate.getDate()]);
        this._isRangeSet = true;
      } else if (this._isRangeSet) {
        endTimePicker.setRange({ hour: 0, minute: 0 });
        endTimePicker.resetMinuteRange();
        this._isRangeSet = false;
      }
    },

    /**
     * Return object of time range from start picker.
     * @returns {object}
     * @private
     */
    _getTimeRangeFromStartPicker: function() {
      var startDate = this._startpicker.getDate();
      var timeRange = {};

      timeRange[startDate.getDate()] = {
        hour: startDate.getHours(),
        minute: startDate.getMinutes()
      };

      return timeRange;
    },

    /**
     * Return a start-datepicker.
     * @returns {DatePicker}
     */
    getStartpicker: function() {
      return this._startpicker;
    },

    /**
     * Return a end-datepicker.
     * @returns {DatePicker}
     */
    getEndpicker: function() {
      return this._endpicker;
    },

    /**
     * Set the start date.
     * @param {Date} date - Start date
     */
    setStartDate: function(date) {
      this._startpicker.setDate(date);
    },

    /**
     * Return the start date.
     * @returns {?Date}
     */
    getStartDate: function() {
      return this._startpicker.getDate();
    },

    /**
     * Return the end date.
     * @returns {?Date}
     */
    getEndDate: function() {
      return this._endpicker.getDate();
    },

    /**
     * Set the end date.
     * @param {Date} date - End date
     */
    setEndDate: function(date) {
      this._endpicker.setDate(date);
    },

    /**
     * Set selectable ranges.
     * @param {Array.<Array.<number|Date>>} ranges - Selectable ranges. Use Date instances or numbers(timestamp).
     */
    setRanges: function(ranges) {
      this._startpicker.setRanges(ranges);
      this._syncRangesToEndpicker();
    },

    /**
     * Add a selectable range. Use Date instances or numbers(timestamp).
     * @param {Date|number} start - the start date
     * @param {Date|number} end - the end date
     */
    addRange: function(start, end) {
      this._startpicker.addRange(start, end);
      this._syncRangesToEndpicker();
    },

    /**
     * Remove a range. Use Date instances or numbers(timestamp).
     * @param {Date|number} start - the start date
     * @param {Date|number} end - the end date
     * @param {null|'date'|'month'|'year'} type - Range type. If falsy, start and end values are considered as timestamp
     */
    removeRange: function(start, end, type) {
      this._startpicker.removeRange(start, end, type);
      this._syncRangesToEndpicker();
    },

    /**
     * Change language.
     * @param {string} language - Language code. English('en') and Korean('ko') are provided as default.
     * @see To set to the other languages, use {@link DatePicker#localeTexts DatePicker.localeTexts}.
     */
    changeLanguage: function(language) {
      this._startpicker.changeLanguage(language);
      this._endpicker.changeLanguage(language);
    },

    /**
     * Destroy the date-range picker.
     */
    destroy: function() {
      this.off();
      this._startpicker.destroy();
      this._endpicker.destroy();
      this._startpicker = this._endpicker = null;
    }
  }
);

CustomEvents.mixin(DateRangePicker);
module.exports = DateRangePicker;


/***/ }),
/* 61 */
/***/ (function(module, exports, __webpack_require__) {

// extracted by mini-css-extract-plugin

/***/ })
/******/ ]);
});