flatpickr.js 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185
  1. "use strict";
  2. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
  3. /*! flatpickr v2.0, @license MIT */
  4. function Flatpickr(element, config) {
  5. var self = this;
  6. function init() {
  7. self.element = element;
  8. self.instanceConfig = config || {};
  9. self.isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
  10. setupFormats();
  11. parseConfig();
  12. setupInputs();
  13. setupDates();
  14. setupHelperFunctions();
  15. self.changeMonth = changeMonth;
  16. self.clear = clear;
  17. self.close = close;
  18. self.destroy = destroy;
  19. self.formatDate = formatDate;
  20. self.jumpToDate = jumpToDate;
  21. self.open = open;
  22. self.parseDate = parseDate;
  23. self.redraw = redraw;
  24. self.set = set;
  25. self.setDate = setDate;
  26. self.toggle = toggle;
  27. if (self.isMobile && !self.config.disableMobile) {
  28. bind();
  29. setupMobile();
  30. } else {
  31. build();
  32. bind();
  33. }
  34. if (self.selectedDateObj) updateValue();
  35. triggerEvent("Ready");
  36. }
  37. function updateTime(e) {
  38. timeWrapper(e);
  39. updateValue(e);
  40. }
  41. function bind() {
  42. if (self.config.wrap) {
  43. ["open", "close", "toggle", "clear"].forEach(function (el) {
  44. try {
  45. self.element.querySelector("[data-" + el + "]").addEventListener("click", self[el]);
  46. } catch (e) {
  47. //
  48. }
  49. });
  50. }
  51. if (self.isMobile) return;
  52. document.addEventListener("keydown", onKeyDown);
  53. window.addEventListener("resize", debounce(onResize, 300));
  54. document.addEventListener("click", documentClick);
  55. document.addEventListener("blur", documentClick);
  56. if (self.config.clickOpens) (self.altInput || self.input).addEventListener("focus", open);
  57. if (!self.config.noCalendar) {
  58. self.prevMonthNav.addEventListener("click", function () {
  59. return changeMonth(-1);
  60. });
  61. self.nextMonthNav.addEventListener("click", function () {
  62. return changeMonth(1);
  63. });
  64. self.currentYearElement.addEventListener("wheel", yearScroll);
  65. self.currentYearElement.addEventListener("focus", function () {
  66. self.currentYearElement.select();
  67. });
  68. self.currentYearElement.addEventListener("input", function (event) {
  69. if (event.target.value.length === 4) self.currentYearElement.blur();
  70. self.currentYear = parseInt(event.target.value, 10) || self.currentYear;
  71. self.redraw();
  72. });
  73. self.days.addEventListener("click", selectDate);
  74. }
  75. if (self.config.enableTime) {
  76. self.timeContainer.addEventListener("wheel", updateTime);
  77. self.timeContainer.addEventListener("wheel", debounce(function () {
  78. return triggerEvent("Change");
  79. }, 1000));
  80. self.timeContainer.addEventListener("input", updateTime);
  81. self.hourElement.addEventListener("focus", function () {
  82. return self.hourElement.select();
  83. });
  84. self.minuteElement.addEventListener("focus", function () {
  85. return self.minuteElement.select();
  86. });
  87. if (self.secondElement) self.secondElement.addEventListener("focus", function () {
  88. return self.secondElement.select();
  89. });
  90. if (self.amPM) self.amPM.addEventListener("click", updateTime);
  91. }
  92. }
  93. function jumpToDate(jumpDate) {
  94. jumpDate = jumpDate ? parseDate(jumpDate) : self.selectedDateObj || self.config.defaultDate || self.config.minDate || self.now;
  95. self.currentYear = jumpDate.getFullYear();
  96. self.currentMonth = jumpDate.getMonth();
  97. self.redraw();
  98. }
  99. function build() {
  100. var fragment = document.createDocumentFragment();
  101. self.calendarContainer = createElement("div", "flatpickr-calendar");
  102. if (!self.config.noCalendar) {
  103. fragment.appendChild(buildMonthNav());
  104. if (self.config.weekNumbers) fragment.appendChild(buildWeeks());
  105. self.rContainer = createElement("div", "flatpickr-rContainer");
  106. self.rContainer.appendChild(buildWeekdays());
  107. self.rContainer.appendChild(buildDays());
  108. fragment.appendChild(self.rContainer);
  109. }
  110. if (self.config.enableTime) fragment.appendChild(buildTime());
  111. self.calendarContainer.appendChild(fragment);
  112. if (self.config.inline || self.config.static) {
  113. self.calendarContainer.classList.add(self.config.inline ? "inline" : "static");
  114. positionCalendar();
  115. self.element.parentNode.appendChild(self.calendarContainer);
  116. } else document.body.appendChild(self.calendarContainer);
  117. }
  118. function buildDays() {
  119. if (!self.days) {
  120. self.days = createElement("div", "flatpickr-days");
  121. self.days.tabIndex = -1;
  122. }
  123. var firstOfMonth = (new Date(self.currentYear, self.currentMonth, 1).getDay() - Flatpickr.l10n.firstDayOfWeek + 7) % 7,
  124. daysInMonth = self.utils.getDaysinMonth(),
  125. prevMonthDays = self.utils.getDaysinMonth((self.currentMonth - 1 + 12) % 12),
  126. days = document.createDocumentFragment();
  127. var dayNumber = prevMonthDays + 1 - firstOfMonth,
  128. currentDate = void 0,
  129. dateIsDisabled = void 0;
  130. if (self.config.weekNumbers) self.weekNumbers.innerHTML = "";
  131. self.days.innerHTML = "";
  132. // prepend days from the ending of previous month
  133. for (; dayNumber <= prevMonthDays; dayNumber++) {
  134. var curDate = new Date(self.currentYear, self.currentMonth - 1, dayNumber, 0, 0, 0, 0, 0),
  135. dateIsEnabled = isEnabled(curDate),
  136. dayElem = createElement("span", "flatpickr-day prevMonthDay" + (dateIsEnabled ? "" : " disabled"), dayNumber);
  137. if (dateIsEnabled) dayElem.tabIndex = 0;
  138. days.appendChild(dayElem);
  139. }
  140. // Start at 1 since there is no 0th day
  141. for (dayNumber = 1; dayNumber <= daysInMonth; dayNumber++) {
  142. currentDate = new Date(self.currentYear, self.currentMonth, dayNumber, 0, 0, 0, 0, 0);
  143. if (self.config.weekNumbers && dayNumber % 7 === 1) {
  144. self.weekNumbers.insertAdjacentHTML("beforeend", "<span class='disabled flatpickr-day'>" + self.getWeek(currentDate) + "</span>");
  145. }
  146. dateIsDisabled = !isEnabled(currentDate);
  147. var dayElement = createElement("span", dateIsDisabled ? "flatpickr-day disabled" : "flatpickr-day", dayNumber);
  148. if (!dateIsDisabled) {
  149. dayElement.tabIndex = 0;
  150. if (equalDates(currentDate, new Date())) dayElement.classList.add("today");
  151. if (self.selectedDateObj && equalDates(currentDate, self.selectedDateObj)) {
  152. dayElement.classList.add("selected");
  153. self.selectedDateElem = dayElement;
  154. }
  155. }
  156. days.appendChild(dayElement);
  157. }
  158. // append days from the next month
  159. for (var dayNum = daysInMonth + 1; dayNum <= 42 - firstOfMonth; dayNum++) {
  160. var _curDate = new Date(self.currentYear, self.currentMonth + 1, dayNum % daysInMonth, 0, 0, 0, 0, 0),
  161. _dateIsEnabled = isEnabled(_curDate),
  162. _dayElement = createElement("span", "flatpickr-day nextMonthDay" + (_dateIsEnabled ? "" : " disabled"), dayNum % daysInMonth);
  163. if (self.config.weekNumbers && dayNum % 7 === 1) {
  164. self.weekNumbers.insertAdjacentHTML("beforeend", "<span class='disabled flatpickr-day'>" + self.getWeek(_curDate) + "</span>");
  165. }
  166. if (_dateIsEnabled) _dayElement.tabIndex = 0;
  167. days.appendChild(_dayElement);
  168. }
  169. self.days.appendChild(days);
  170. return self.days;
  171. }
  172. function buildMonthNav() {
  173. var monthNavFragment = document.createDocumentFragment();
  174. self.monthNav = createElement("div", "flatpickr-month");
  175. self.prevMonthNav = createElement("span", "flatpickr-prev-month");
  176. self.prevMonthNav.innerHTML = self.config.prevArrow;
  177. self.currentMonthElement = createElement("span", "cur_month");
  178. self.currentYearElement = createElement("input", "cur_year");
  179. self.currentYearElement.type = "number";
  180. self.currentYearElement.title = Flatpickr.l10n.scrollTitle;
  181. self.nextMonthNav = createElement("span", "flatpickr-next-month");
  182. self.nextMonthNav.innerHTML = self.config.nextArrow;
  183. self.navigationCurrentMonth = createElement("span", "flatpickr-current-month");
  184. self.navigationCurrentMonth.appendChild(self.currentMonthElement);
  185. self.navigationCurrentMonth.appendChild(self.currentYearElement);
  186. monthNavFragment.appendChild(self.prevMonthNav);
  187. monthNavFragment.appendChild(self.navigationCurrentMonth);
  188. monthNavFragment.appendChild(self.nextMonthNav);
  189. self.monthNav.appendChild(monthNavFragment);
  190. updateNavigationCurrentMonth();
  191. return self.monthNav;
  192. }
  193. function buildTime() {
  194. self.calendarContainer.classList.add("hasTime");
  195. self.timeContainer = createElement("div", "flatpickr-time");
  196. self.timeContainer.tabIndex = -1;
  197. var separator = createElement("span", "flatpickr-time-separator", ":");
  198. self.hourElement = createElement("input", "flatpickr-hour");
  199. self.minuteElement = createElement("input", "flatpickr-minute");
  200. self.hourElement.tabIndex = self.minuteElement.tabIndex = 0;
  201. self.hourElement.type = self.minuteElement.type = "number";
  202. self.hourElement.value = self.selectedDateObj ? pad(self.selectedDateObj.getHours()) : 12;
  203. self.minuteElement.value = self.selectedDateObj ? pad(self.selectedDateObj.getMinutes()) : "00";
  204. self.hourElement.step = self.config.hourIncrement;
  205. self.minuteElement.step = self.config.minuteIncrement;
  206. self.hourElement.min = -(self.config.time_24hr ? 1 : 0);
  207. self.hourElement.max = self.config.time_24hr ? 24 : 13;
  208. self.minuteElement.min = -self.minuteElement.step;
  209. self.minuteElement.max = 60;
  210. self.hourElement.title = self.minuteElement.title = Flatpickr.l10n.scrollTitle;
  211. self.timeContainer.appendChild(self.hourElement);
  212. self.timeContainer.appendChild(separator);
  213. self.timeContainer.appendChild(self.minuteElement);
  214. if (self.config.enableSeconds) {
  215. self.timeContainer.classList.add("has-seconds");
  216. self.secondElement = createElement("input", "flatpickr-second");
  217. self.secondElement.type = "number";
  218. self.secondElement.value = self.selectedDateObj ? pad(self.selectedDateObj.getSeconds()) : "00";
  219. self.secondElement.step = self.minuteElement.step;
  220. self.secondElement.min = self.minuteElement.min;
  221. self.secondElement.max = self.minuteElement.max;
  222. self.timeContainer.appendChild(createElement("span", "flatpickr-time-separator", ":"));
  223. self.timeContainer.appendChild(self.secondElement);
  224. }
  225. if (!self.config.time_24hr) {
  226. // add self.amPM if appropriate
  227. self.amPM = createElement("span", "flatpickr-am-pm", ["AM", "PM"][self.hourElement.value > 11 | 0]);
  228. self.amPM.title = Flatpickr.l10n.toggleTitle;
  229. self.amPM.tabIndex = 0;
  230. self.timeContainer.appendChild(self.amPM);
  231. }
  232. return self.timeContainer;
  233. }
  234. function buildWeekdays() {
  235. if (!self.weekdayContainer) self.weekdayContainer = createElement("div", "flatpickr-weekdays");
  236. var firstDayOfWeek = Flatpickr.l10n.firstDayOfWeek;
  237. var weekdays = Flatpickr.l10n.weekdays.shorthand.slice();
  238. if (firstDayOfWeek > 0 && firstDayOfWeek < weekdays.length) {
  239. weekdays = [].concat(weekdays.splice(firstDayOfWeek, weekdays.length), weekdays.splice(0, firstDayOfWeek));
  240. }
  241. self.weekdayContainer.innerHTML = "<span class=flatpickr-weekday>" + weekdays.join("</span><span class=flatpickr-weekday>") + "</span>";
  242. return self.weekdayContainer;
  243. }
  244. function buildWeeks() {
  245. self.calendarContainer.classList.add("hasWeeks");
  246. self.weekWrapper = createElement("div", "flatpickr-weekwrapper");
  247. self.weekWrapper.appendChild(createElement("span", "flatpickr-weekday", Flatpickr.l10n.weekAbbreviation));
  248. self.weekNumbers = createElement("div", "flatpickr-weeks");
  249. self.weekWrapper.appendChild(self.weekNumbers);
  250. return self.weekWrapper;
  251. }
  252. function changeMonth(offset) {
  253. self.currentMonth += offset;
  254. handleYearChange();
  255. updateNavigationCurrentMonth();
  256. buildDays();
  257. (self.config.noCalendar ? self.timeContainer : self.days).focus();
  258. }
  259. function clear() {
  260. self.input.value = "";
  261. if (self.altInput) self.altInput.value = "";
  262. self.selectedDateObj = null;
  263. triggerEvent("Change");
  264. jumpToDate(self.now);
  265. }
  266. function close() {
  267. self.isOpen = false;
  268. self.calendarContainer.classList.remove("open");
  269. (self.altInput || self.input).classList.remove("active");
  270. triggerEvent("Close");
  271. }
  272. function destroy() {
  273. self.calendarContainer.parentNode.removeChild(self.calendarContainer);
  274. self.input.value = "";
  275. if (self.altInput) {
  276. self.input.type = "text";
  277. self.altInput.parentNode.removeChild(self.altInput);
  278. }
  279. document.removeEventListener("keydown", onKeyDown);
  280. window.removeEventListener("resize", onResize);
  281. document.removeEventListener("click", documentClick);
  282. document.removeEventListener("blur", documentClick);
  283. delete self.input._flatpickr;
  284. }
  285. function documentClick(e) {
  286. var isCalendarElement = self.calendarContainer.contains(e.target),
  287. isInput = self.element.contains(e.target) || e.target === self.altInput;
  288. if (self.isOpen && !isCalendarElement && !isInput) self.close();
  289. }
  290. function formatDate(frmt, dateObj) {
  291. var chars = frmt.split("");
  292. return chars.map(function (c, i) {
  293. return self.formats[c] && chars[i - 1] !== "\\" ? self.formats[c](dateObj) : c !== "\\" ? c : "";
  294. }).join("");
  295. }
  296. function handleYearChange() {
  297. if (self.currentMonth < 0 || self.currentMonth > 11) {
  298. self.currentYear += self.currentMonth % 11;
  299. self.currentMonth = (self.currentMonth + 12) % 12;
  300. }
  301. }
  302. function isEnabled(dateToCheck) {
  303. if (self.config.minDate && dateToCheck < self.config.minDate || self.config.maxDate && dateToCheck > self.config.maxDate) return false;
  304. if (!self.config.enable.length && !self.config.disable.length) return true;
  305. dateToCheck = parseDate(dateToCheck, true); // timeless
  306. var bool = self.config.enable.length > 0,
  307. array = bool ? self.config.enable : self.config.disable;
  308. var d = void 0;
  309. for (var i = 0; i < array.length; i++) {
  310. d = array[i];
  311. if (d instanceof Function && d(dateToCheck)) // disabled by function
  312. return bool;else if ((d instanceof Date || typeof d === "string") && parseDate(d, true).getTime() === dateToCheck.getTime())
  313. // disabled by date string
  314. return bool;else if ( // disabled by range
  315. (typeof d === "undefined" ? "undefined" : _typeof(d)) === "object" && d.hasOwnProperty("from") && dateToCheck >= parseDate(d.from) && dateToCheck <= parseDate(d.to)) return bool;
  316. }
  317. return !bool;
  318. }
  319. function onKeyDown(e) {
  320. if (!self.isOpen) return;
  321. switch (e.which) {
  322. case 13:
  323. if (self.timeContainer && self.timeContainer.contains(e.target)) updateValue(e);else selectDate(e);
  324. break;
  325. case 27:
  326. self.close();
  327. break;
  328. case 37:
  329. if (e.target !== self.input & e.target !== self.altInput) changeMonth(-1);
  330. break;
  331. case 38:
  332. e.preventDefault();
  333. if (self.timeContainer.contains(e.target)) updateTime(e);else {
  334. self.currentYear++;
  335. self.redraw();
  336. }
  337. break;
  338. case 39:
  339. if (e.target !== self.input & e.target !== self.altInput) changeMonth(1);
  340. break;
  341. case 40:
  342. e.preventDefault();
  343. if (self.timeContainer.contains(e.target)) updateTime(e);else {
  344. self.currentYear--;
  345. self.redraw();
  346. }
  347. break;
  348. default:
  349. break;
  350. }
  351. }
  352. function onResize() {
  353. if (self.isOpen && !self.config.inline && !self.config.static) positionCalendar();
  354. }
  355. function open(e) {
  356. if (self.isMobile) {
  357. e.preventDefault();
  358. e.target.blur();
  359. setTimeout(function () {
  360. self.mobileInput.click();
  361. }, 0);
  362. triggerEvent("Open");
  363. return;
  364. } else if (self.isOpen || (self.altInput || self.input).disabled || self.config.inline) return;
  365. self.calendarContainer.classList.add("open");
  366. if (!self.config.static) positionCalendar();
  367. self.isOpen = true;
  368. if (!self.config.allowInput) {
  369. (self.altInput || self.input).blur();
  370. (self.config.noCalendar ? self.timeContainer : self.selectedDateObj ? self.selectedDateElem : self.days).focus();
  371. }
  372. (self.altInput || self.input).classList.add("active");
  373. triggerEvent("Open");
  374. }
  375. function pad(number) {
  376. return ("0" + number).slice(-2);
  377. }
  378. function parseConfig() {
  379. self.config = self.instanceConfig;
  380. Object.keys(self.element.dataset).forEach(function (k) {
  381. return self.config[k] = typeof Flatpickr.defaultConfig[k] === "boolean" ? self.element.dataset[k] !== "false" : self.element.dataset[k];
  382. });
  383. if (!self.config.dateFormat && self.config.enableTime) {
  384. self.config.dateFormat = Flatpickr.defaultConfig.dateFormat;
  385. if (self.config.noCalendar) {
  386. // time picker
  387. self.config.dateFormat = "H:i" + (self.config.enableSeconds ? ":S" : "");
  388. self.config.altFormat = "h:i" + (self.config.enableSeconds ? ":S K" : " K");
  389. } else {
  390. self.config.dateFormat += " H:i" + (self.config.enableSeconds ? ":S" : "");
  391. self.config.altFormat = "h:i" + (self.config.enableSeconds ? ":S" : "") + " K";
  392. }
  393. }
  394. Object.keys(Flatpickr.defaultConfig).forEach(function (k) {
  395. return self.config[k] = typeof self.config[k] !== "undefined" ? self.config[k] : Flatpickr.defaultConfig[k];
  396. });
  397. }
  398. function parseDate(date) {
  399. var timeless = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
  400. if (typeof date === "string") {
  401. date = date.trim();
  402. if (date === "today") {
  403. date = new Date();
  404. timeless = true;
  405. } else if (self.config.parseDate) date = self.config.parseDate(date);else if (/^\d\d:\d\d/.test(date)) {
  406. // time picker
  407. var m = date.match(/^(\d{1,2})[:\s]?(\d\d)?[:\s]?(\d\d)?/);
  408. date = new Date();
  409. date.setHours(m[1], m[2] || 0, m[2] || 0);
  410. } else if (/Z$/.test(date) || /GMT$/.test(date)) // datestrings w/ timezone
  411. date = new Date(date);else if (/(\d+)/g.test(date)) {
  412. var d = date.match(/(\d+)/g);
  413. date = new Date(d[0] + "/" + (d[1] || 1) + "/" + (d[2] || 1) + " " + (d[3] || 0) + ":" + (d[4] || 0) + ":" + (d[5] || 0));
  414. }
  415. }
  416. if (!(date instanceof Date) || !date.getTime()) {
  417. console.warn("flatpickr: invalid date " + date);
  418. console.info(self.element);
  419. return null;
  420. }
  421. if (self.config.utc && !date.fp_isUTC) date = date.fp_toUTC();
  422. if (timeless) date.setHours(0, 0, 0, 0);
  423. return date;
  424. }
  425. function positionCalendar() {
  426. var calendarHeight = self.calendarContainer.offsetHeight,
  427. input = self.altInput || self.input,
  428. inputBounds = input.getBoundingClientRect(),
  429. distanceFromBottom = window.innerHeight - inputBounds.bottom + input.offsetHeight;
  430. var top = void 0,
  431. left = window.pageXOffset + inputBounds.left;
  432. if (distanceFromBottom < calendarHeight) {
  433. top = window.pageYOffset - calendarHeight + inputBounds.top - 2;
  434. self.calendarContainer.classList.remove("arrowTop");
  435. self.calendarContainer.classList.add("arrowBottom");
  436. } else {
  437. top = window.pageYOffset + input.offsetHeight + inputBounds.top + 2;
  438. self.calendarContainer.classList.remove("arrowBottom");
  439. self.calendarContainer.classList.add("arrowTop");
  440. }
  441. if (!self.config.inline) {
  442. self.calendarContainer.style.top = top + "px";
  443. self.calendarContainer.style.left = left + "px";
  444. }
  445. }
  446. function redraw() {
  447. if (self.config.noCalendar || self.isMobile) return;
  448. buildWeekdays();
  449. updateNavigationCurrentMonth();
  450. buildDays();
  451. }
  452. function selectDate(e) {
  453. e.preventDefault();
  454. e.stopPropagation();
  455. if (self.config.allowInput && (e.target === self.altInput || e.target === self.input) && e.which === 13) self.setDate((self.altInput || self.input).value);else if (e.target.classList.contains("flatpickr-day") && !e.target.classList.contains("disabled")) {
  456. var isPrevMonthDay = e.target.classList.contains("prevMonthDay"),
  457. isNextMonthDay = e.target.classList.contains("nextMonthDay");
  458. if (isPrevMonthDay || isNextMonthDay) changeMonth(+isNextMonthDay - isPrevMonthDay);
  459. self.selectedDateObj = parseDate(new Date(self.currentYear, self.currentMonth, e.target.innerHTML));
  460. updateValue(e);
  461. buildDays();
  462. triggerEvent("Change");
  463. if (!self.config.enableTime) self.close();
  464. }
  465. }
  466. function set(option, value) {
  467. self.config[option] = value;
  468. jumpToDate();
  469. }
  470. function setDate(date, triggerChange) {
  471. date = parseDate(date);
  472. if (date instanceof Date && date.getTime()) {
  473. self.selectedDateObj = date;
  474. jumpToDate(self.selectedDateObj);
  475. updateValue(false);
  476. if (triggerChange) triggerEvent("Change");
  477. } else (self.altInput || self.input).value = "";
  478. }
  479. function setupDates() {
  480. self.now = new Date();
  481. if (self.config.defaultDate || self.input.value) self.selectedDateObj = parseDate(self.config.defaultDate || self.input.value);
  482. if (self.config.minDate) self.config.minDate = parseDate(self.config.minDate, true);
  483. if (self.config.maxDate) self.config.maxDate = parseDate(self.config.maxDate, true);
  484. var initialDate = self.selectedDateObj || self.config.defaultDate || self.config.minDate || new Date();
  485. self.currentYear = initialDate.getFullYear();
  486. self.currentMonth = initialDate.getMonth();
  487. }
  488. function setupFormats() {
  489. self.formats = {
  490. // weekday name, short, e.g. Thu
  491. D: function D(date) {
  492. return Flatpickr.l10n.weekdays.shorthand[self.formats.w(date)];
  493. },
  494. // full month name e.g. January
  495. F: function F(date) {
  496. return self.utils.monthToStr(self.formats.n(date) - 1, false);
  497. },
  498. // hours with leading zero e.g. 03
  499. H: function H(date) {
  500. return pad(date.getHours());
  501. },
  502. // day (1-30) with ordinal suffix e.g. 1st, 2nd
  503. J: function J(date) {
  504. return date.getDate() + Flatpickr.l10n.ordinal(date.getDate());
  505. },
  506. // AM/PM
  507. K: function K(date) {
  508. return date.getHours() > 11 ? "PM" : "AM";
  509. },
  510. // shorthand month e.g. Jan, Sep, Oct, etc
  511. M: function M(date) {
  512. return self.utils.monthToStr(date.getMonth(), true);
  513. },
  514. // seconds 00-59
  515. S: function S(date) {
  516. return pad(date.getSeconds());
  517. },
  518. // unix timestamp
  519. U: function U(date) {
  520. return date.getTime() / 1000;
  521. },
  522. // full year e.g. 2016
  523. Y: function Y(date) {
  524. return date.getFullYear();
  525. },
  526. // day in month, padded (01-30)
  527. d: function d(date) {
  528. return pad(self.formats.j(date));
  529. },
  530. // hour from 1-12 (am/pm)
  531. h: function h(date) {
  532. return date.getHours() % 12 ? date.getHours() % 12 : 12;
  533. },
  534. // minutes, padded with leading zero e.g. 09
  535. i: function i(date) {
  536. return pad(date.getMinutes());
  537. },
  538. // day in month (1-30)
  539. j: function j(date) {
  540. return date.getDate();
  541. },
  542. // weekday name, full, e.g. Thursday
  543. l: function l(date) {
  544. return Flatpickr.l10n.weekdays.longhand[self.formats.w(date)];
  545. },
  546. // padded month number (01-12)
  547. m: function m(date) {
  548. return pad(self.formats.n(date));
  549. },
  550. // the month number (1-12)
  551. n: function n(date) {
  552. return date.getMonth() + 1;
  553. },
  554. // seconds 0-59
  555. s: function s(date) {
  556. return date.getSeconds();
  557. },
  558. // number of the day of the week
  559. w: function w(date) {
  560. return date.getDay();
  561. },
  562. // last two digits of year e.g. 16 for 2016
  563. y: function y(date) {
  564. return String(self.formats.Y(date)).substring(2);
  565. }
  566. };
  567. }
  568. function setupHelperFunctions() {
  569. self.utils = {
  570. getDaysinMonth: function getDaysinMonth() {
  571. var month = arguments.length <= 0 || arguments[0] === undefined ? self.currentMonth : arguments[0];
  572. var yr = arguments.length <= 1 || arguments[1] === undefined ? self.currentYear : arguments[1];
  573. if (month === 1 && yr % 4 === 0 && yr % 100 !== 0 || yr % 400 === 0) return 29;
  574. return Flatpickr.l10n.daysInMonth[month];
  575. },
  576. monthToStr: function monthToStr(monthNumber) {
  577. var short = arguments.length <= 1 || arguments[1] === undefined ? self.config.shorthandCurrentMonth : arguments[1];
  578. return Flatpickr.l10n.months[(short ? "short" : "long") + "hand"][monthNumber];
  579. }
  580. };
  581. }
  582. function setupInputs() {
  583. self.input = self.config.wrap ? self.element.querySelector("[data-input]") : self.element;
  584. self.input.classList.add("flatpickr-input");
  585. if (self.config.altInput) {
  586. // replicate self.element
  587. self.altInput = createElement(self.input.nodeName, "flatpickr-input " + self.config.altInputClass);
  588. self.altInput.placeholder = self.input.placeholder;
  589. self.altInput.type = "text";
  590. self.input.type = "hidden";
  591. self.input.parentNode.insertBefore(self.altInput, self.input.nextSibling);
  592. }
  593. if (!self.config.allowInput) (self.altInput || self.input).setAttribute("readonly", "readonly");
  594. }
  595. function setupMobile() {
  596. var inputType = self.config.enableTime ? self.config.noCalendar ? "time" : "datetime-local" : "date";
  597. self.mobileInput = createElement("input", "flatpickr-input");
  598. self.mobileInput.step = "any";
  599. self.mobileInput.tabIndex = -1;
  600. self.mobileInput.type = inputType;
  601. if (self.selectedDateObj) {
  602. var formatStr = inputType === "datetime-local" ? "Y-m-d\\TH:i:S" : inputType === "date" ? "Y-m-d" : "H:i:S";
  603. var mobileFormattedDate = formatDate(formatStr, self.selectedDateObj);
  604. self.mobileInput.defaultValue = self.mobileInput.value = mobileFormattedDate;
  605. }
  606. if (self.config.minDate) self.mobileInput.min = formatDate("Y-m-d", self.config.minDate);
  607. if (self.config.maxDate) self.mobileInput.max = formatDate("Y-m-d", self.config.maxDate);
  608. self.input.type = "hidden";
  609. if (self.config.altInput) self.altInput.type = "hidden";
  610. try {
  611. self.input.parentNode.insertBefore(self.mobileInput, self.input.nextSibling);
  612. } catch (e) {
  613. //
  614. }
  615. self.mobileInput.addEventListener("change", function (e) {
  616. self.setDate(e.target.value);
  617. triggerEvent("Change");
  618. triggerEvent("Close");
  619. });
  620. }
  621. function toggle() {
  622. if (self.isOpen) self.close();else self.open();
  623. }
  624. function triggerEvent(event) {
  625. if (self.config["on" + event]) self.config["on" + event](self.selectedDateObj, self.input.value, self);
  626. }
  627. function updateNavigationCurrentMonth() {
  628. self.currentMonthElement.textContent = self.utils.monthToStr(self.currentMonth) + " ";
  629. self.currentYearElement.value = self.currentYear;
  630. }
  631. function updateValue() {
  632. var readTimeInput = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0];
  633. if (self.config.noCalendar && !self.selectedDateObj)
  634. // picking time only and method triggered from picker
  635. self.selectedDateObj = new Date();else if (!self.selectedDateObj) return;
  636. if (self.config.enableTime && !self.isMobile) {
  637. var hours = void 0,
  638. minutes = void 0,
  639. seconds = void 0;
  640. if (readTimeInput) {
  641. // update time
  642. hours = parseInt(self.hourElement.value, 10) || 0;
  643. minutes = (60 + (parseInt(self.minuteElement.value, 10) || 0)) % 60;
  644. if (self.config.enableSeconds) seconds = (60 + parseInt(self.secondElement.value, 10) || 0) % 60;
  645. if (!self.config.time_24hr)
  646. // the real number of hours for the date object
  647. hours = hours % 12 + 12 * (self.amPM.innerHTML === "PM");
  648. self.selectedDateObj.setHours(hours, minutes, seconds || 0, 0);
  649. } else {
  650. hours = self.selectedDateObj.getHours();
  651. minutes = self.selectedDateObj.getMinutes();
  652. seconds = self.selectedDateObj.getSeconds();
  653. }
  654. self.hourElement.value = pad(!self.config.time_24hr ? (12 + hours) % 12 + 12 * (hours % 12 === 0) : hours);
  655. self.minuteElement.value = pad(minutes);
  656. if (self.secondElement !== undefined) self.secondElement.value = pad(seconds);
  657. }
  658. self.input.value = formatDate(self.config.dateFormat, self.selectedDateObj);
  659. if (self.altInput) self.altInput.value = formatDate(self.config.altFormat, self.selectedDateObj);
  660. triggerEvent("ValueUpdate");
  661. }
  662. function yearScroll(e) {
  663. e.preventDefault();
  664. var delta = Math.max(-1, Math.min(1, e.wheelDelta || -e.deltaY));
  665. self.currentYear = e.target.value = parseInt(e.target.value, 10) + delta;
  666. self.redraw();
  667. }
  668. function createElement(tag) {
  669. var className = arguments.length <= 1 || arguments[1] === undefined ? "" : arguments[1];
  670. var content = arguments.length <= 2 || arguments[2] === undefined ? "" : arguments[2];
  671. var e = document.createElement(tag);
  672. e.className = className;
  673. if (content) e.textContent = content;
  674. return e;
  675. }
  676. function debounce(func, wait, immediate) {
  677. var timeout = void 0;
  678. return function () {
  679. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  680. args[_key] = arguments[_key];
  681. }
  682. var context = this;
  683. var later = function later() {
  684. timeout = null;
  685. if (!immediate) func.apply(context, args);
  686. };
  687. clearTimeout(timeout);
  688. timeout = setTimeout(later, wait);
  689. if (immediate && !timeout) func.apply(context, args);
  690. };
  691. }
  692. function equalDates(date1, date2) {
  693. return date1.getDate() === date2.getDate() && date1.getMonth() === date2.getMonth() && date1.getFullYear() === date2.getFullYear();
  694. }
  695. function timeWrapper(e) {
  696. e.preventDefault();
  697. if (e && e.type !== "keydown") e.target.blur();
  698. if (e.target.className === "flatpickr-am-pm") {
  699. e.target.textContent = ["AM", "PM"][e.target.textContent === "AM" | 0];
  700. e.stopPropagation();
  701. return;
  702. }
  703. var min = parseInt(e.target.min, 10),
  704. max = parseInt(e.target.max, 10),
  705. step = parseInt(e.target.step, 10),
  706. value = parseInt(e.target.value, 10);
  707. var newValue = value;
  708. if (e.type === "wheel") newValue = value + step * Math.max(-1, Math.min(1, e.wheelDelta || -e.deltaY));else if (e.type === "keydown") newValue = value + step * (e.which === 38 ? 1 : -1);
  709. if (newValue <= min) newValue = max - step;else if (newValue >= max) newValue = min + step;
  710. e.target.value = pad(newValue);
  711. }
  712. init();
  713. return self;
  714. }
  715. Flatpickr.defaultConfig = {
  716. /* if true, dates will be parsed, formatted, and displayed in UTC.
  717. preloading date strings w/ timezones is recommended but not necessary */
  718. utc: false,
  719. // wrap: see https://chmln.github.io/flatpickr/#strap
  720. wrap: false,
  721. // enables week numbers
  722. weekNumbers: false,
  723. allowInput: false,
  724. /*
  725. clicking on input opens the date(time)picker.
  726. disable if you wish to open the calendar manually with .open()
  727. */
  728. clickOpens: true,
  729. // display time picker in 24 hour mode
  730. time_24hr: false,
  731. // enables the time picker functionality
  732. enableTime: false,
  733. // noCalendar: true will hide the calendar. use for a time picker along w/ enableTime
  734. noCalendar: false,
  735. // more date format chars at https://chmln.github.io/flatpickr/#dateformat
  736. dateFormat: "Y-m-d",
  737. // altInput - see https://chmln.github.io/flatpickr/#altinput
  738. altInput: null,
  739. // the created altInput element will have this class.
  740. altInputClass: "",
  741. // same as dateFormat, but for altInput
  742. altFormat: "F j, Y", // defaults to e.g. June 10, 2016
  743. // defaultDate - either a datestring or a date object. used for datetimepicker"s initial value
  744. defaultDate: null,
  745. // the minimum date that user can pick (inclusive)
  746. minDate: null,
  747. // the maximum date that user can pick (inclusive)
  748. maxDate: null,
  749. // dateparser that transforms a given string to a date object
  750. parseDate: null,
  751. // see https://chmln.github.io/flatpickr/#disable
  752. enable: [],
  753. // see https://chmln.github.io/flatpickr/#disable
  754. disable: [],
  755. // display the short version of month names - e.g. Sep instead of September
  756. shorthandCurrentMonth: false,
  757. // displays calendar inline. see https://chmln.github.io/flatpickr/#inline-calendar
  758. inline: false,
  759. // position calendar inside wrapper and next to the input element
  760. // leave at false unless you know what you"re doing
  761. static: false,
  762. // code for previous/next icons. this is where you put your custom icon code e.g. fontawesome
  763. prevArrow: "<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 17 17'><g></g><path d='M5.207 8.471l7.146 7.147-0.707 0.707-7.853-7.854 7.854-7.853 0.707 0.707-7.147 7.146z' /></svg>",
  764. nextArrow: "<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 17 17'><g></g><path d='M13.207 8.472l-7.854 7.854-0.707-0.707 7.146-7.146-7.146-7.148 0.707-0.707 7.854 7.854z' /></svg>",
  765. // enables seconds in the time picker
  766. enableSeconds: false,
  767. // step size used when scrolling/incrementing the hour element
  768. hourIncrement: 1,
  769. // step size used when scrolling/incrementing the minute element
  770. minuteIncrement: 5,
  771. // disable native mobile datetime input support
  772. disableMobile: false,
  773. // onChange callback when user selects a date or time
  774. onChange: null, // function (dateObj, dateStr) {}
  775. // called every time calendar is opened
  776. onOpen: null, // function (dateObj, dateStr) {}
  777. // called every time calendar is closed
  778. onClose: null, // function (dateObj, dateStr) {}
  779. // called after calendar is ready
  780. onReady: null, // function (dateObj, dateStr) {}
  781. onValueUpdate: null
  782. };
  783. Flatpickr.l10n = {
  784. weekdays: {
  785. shorthand: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
  786. longhand: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
  787. },
  788. months: {
  789. shorthand: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
  790. longhand: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
  791. },
  792. daysInMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
  793. firstDayOfWeek: 0,
  794. ordinal: function ordinal(nth) {
  795. var s = nth % 100;
  796. if (s > 3 && s < 21) return "th";
  797. switch (s % 10) {
  798. case 1:
  799. return "st";
  800. case 2:
  801. return "nd";
  802. case 3:
  803. return "rd";
  804. default:
  805. return "th";
  806. }
  807. },
  808. weekAbbreviation: "Wk",
  809. scrollTitle: "Scroll to increment",
  810. toggleTitle: "Click to toggle"
  811. };
  812. Flatpickr.localize = function (l10n) {
  813. Object.keys(l10n).forEach(function (k) {
  814. return Flatpickr.l10n[k] = l10n[k];
  815. });
  816. };
  817. function _flatpickr(nodeList, config) {
  818. var instances = [];
  819. for (var i = 0; i < nodeList.length; i++) {
  820. if (nodeList[i]._flatpickr) nodeList[i]._flatpickr.destroy();
  821. try {
  822. nodeList[i]._flatpickr = new Flatpickr(nodeList[i], config || {});
  823. instances.push(nodeList[i]._flatpickr);
  824. } catch (e) {
  825. console.warn(e, e.stack);
  826. }
  827. }
  828. return instances.length === 1 ? instances[0] : instances;
  829. }
  830. HTMLCollection.prototype.flatpickr = NodeList.prototype.flatpickr = function (config) {
  831. return _flatpickr(this, config);
  832. };
  833. HTMLElement.prototype.flatpickr = function (config) {
  834. return _flatpickr([this], config);
  835. };
  836. if (typeof jQuery !== "undefined") {
  837. jQuery.fn.flatpickr = function (config) {
  838. return _flatpickr(this, config);
  839. };
  840. }
  841. Date.prototype.fp_incr = function (days) {
  842. return new Date(this.getFullYear(), this.getMonth(), this.getDate() + parseInt(days, 10));
  843. };
  844. Date.prototype.fp_isUTC = false;
  845. Date.prototype.fp_toUTC = function () {
  846. var newDate = new Date(this.getTime() + this.getTimezoneOffset() * 60000);
  847. newDate.fp_isUTC = true;
  848. return newDate;
  849. };
  850. Flatpickr.prototype.getWeek = function (givenDate) {
  851. var date = new Date(givenDate.getTime());
  852. date.setHours(0, 0, 0, 0);
  853. // Thursday in current week decides the year.
  854. date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
  855. // January 4 is always in week 1.
  856. var week1 = new Date(date.getFullYear(), 0, 4);
  857. // Adjust to Thursday in week 1 and count number of weeks from date to week1.
  858. return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7);
  859. };
  860. // IE9 classList polyfill
  861. if (!("classList" in document.documentElement) && Object.defineProperty && typeof HTMLElement !== "undefined") {
  862. Object.defineProperty(HTMLElement.prototype, "classList", {
  863. get: function get() {
  864. var self = this;
  865. function update(fn) {
  866. return function (value) {
  867. var classes = self.className.split(/\s+/),
  868. index = classes.indexOf(value);
  869. fn(classes, index, value);
  870. self.className = classes.join(" ");
  871. };
  872. }
  873. var ret = {
  874. add: update(function (classes, index, value) {
  875. ~index || classes.push(value);
  876. }),
  877. remove: update(function (classes, index) {
  878. ~index && classes.splice(index, 1);
  879. }),
  880. toggle: update(function (classes, index, value) {
  881. ~index ? classes.splice(index, 1) : classes.push(value);
  882. }),
  883. contains: function contains(value) {
  884. return !!~self.className.split(/\s+/).indexOf(value);
  885. },
  886. item: function item(i) {
  887. return self.className.split(/\s+/)[i] || null;
  888. }
  889. };
  890. Object.defineProperty(ret, "length", {
  891. get: function get() {
  892. return self.className.split(/\s+/).length;
  893. }
  894. });
  895. return ret;
  896. }
  897. });
  898. }
  899. if (typeof module !== "undefined") module.exports = Flatpickr;