calendar.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182
  1. /*!
  2. * Pikaday
  3. *
  4. * Copyright © 2014 David Bushell | BSD & MIT license | https://github.com/dbushell/Pikaday
  5. */
  6. var pickers = {};
  7. var clLangs = {
  8. ua: {
  9. previousMonth : 'Попередній місяць',
  10. nextMonth : 'Наступний місяць',
  11. months : ['Січень','Лютий','Березень','Квітень','Травень','Червень','Липень','Серпень','Вересень','Жовтень','Листопад','Грудень'],
  12. weekdays : ['Неділя','Понеділок','Вівторок','Середа','Четвер','П’ятниця','Субота'],
  13. weekdaysShort : ['Нд','Пн','Вв','Ср','Чт','Пт','Сб']
  14. },
  15. ru: {
  16. previousMonth : 'Предыдущий месяц',
  17. nextMonth : 'Следующий месяц',
  18. months : ['Январь','Февраль','Март','Апрель','Май','Июнь','Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'],
  19. weekdays : ['Воскресенье','Понедельник','Вторник','Среда','Четверг','Пятница','Суббота'],
  20. weekdaysShort : ['Вс','Пн','Вт','Ср','Чт','Пт','Сб']
  21. },
  22. en: {
  23. previousMonth : 'Previous Month',
  24. nextMonth : 'Next Month',
  25. months : ['January','February','March','April','May','June','July','August','September','October','November','December'],
  26. weekdays : ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
  27. weekdaysShort : ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']
  28. }
  29. };
  30. function getDateParamBySign(date,sign) {
  31. sign = sign.toUpperCase();
  32. var param;
  33. switch(sign) {
  34. case "YYYY":
  35. param = date.getFullYear().toString();break;
  36. case "YY":
  37. param = date.getFullYear().toString().substr(2,2);break;
  38. case "MM":
  39. param = (date.getMonth()+1);
  40. param = (param >= 10) ? param.toString() : ("0"+param.toString());
  41. break;
  42. case "DD":
  43. param = (date.getDate() >= 10) ? date.getDate().toString() : ("0"+date.getDate().toString());
  44. break;
  45. default:
  46. param = date.toDateString();
  47. }
  48. return param;
  49. }
  50. function formatter(date, format) {
  51. date = date || new Date();
  52. format = format || "DD.MM.YYYY";
  53. var signs = format.match(/(Y{2,4})|(M{2})|(D{2})/g);
  54. console.log(signs)
  55. var params = [];
  56. var reStr = '';
  57. for(var i=0; i<signs.length; ++i) {
  58. params.push(getDateParamBySign(date,signs[i]));
  59. reStr += ((i+1) != signs.length) ? signs[i] + "(.)" : signs[i];
  60. }
  61. var re = new RegExp(reStr,'g');
  62. var delimiters = re.exec(format);
  63. delimiters.splice(0,1);
  64. var value = "";
  65. for(i=0; i<params.length; i++) {
  66. value += ((i+1) != params.length) ? (params[i] + delimiters[i]) : params[i];
  67. }
  68. return value;
  69. }
  70. function parser(str, format) {
  71. format = format || "DD.MM.YYYY";
  72. var signs = format.match(/(Y{2,4})|(M{2})|(D{2})/g);
  73. var reStr = "(";
  74. for(var i=0; i<signs.length; ++i) {
  75. console.log(signs[i].length);
  76. console.log(".".repeat(signs[i].length));
  77. reStr += ".".repeat(signs[i].length) + (((i+1) != signs.length) ? ").(" : ")");
  78. }
  79. var re = new RegExp(reStr,'g');
  80. var values = re.exec(str);
  81. var year, month, day;
  82. if (signs.length+1 == values.length) {
  83. values = values.slice(1);
  84. for(var i=0; i<signs.length; ++i) {
  85. switch(signs[i].slice(0,1)){
  86. case "Y": year = values[i]; break;
  87. case "M": month = values[i]; break;
  88. case "D": day = values[i]; break;
  89. }
  90. }
  91. const res = new Date(year, month-1, day);
  92. console.log(res)
  93. return res;
  94. }
  95. return null;
  96. }
  97. // function parseDateFromInput(value) {
  98. // if(isNaN(Date.parse(value))) {
  99. // var res = /^(\d{1,2})\.(\d{1,2})\.(\d{4})$/.exec(value);
  100. // if(res && res.length == 4) { return new Date(res[3],(res[2]-1),res[1]); }
  101. // else { return null; }
  102. // }else{ return new Date(Date.parse(value)); }
  103. // }
  104. (function (root, factory)
  105. {
  106. 'use strict';
  107. var moment;
  108. if (typeof exports === 'object') {
  109. // CommonJS module
  110. // Load moment.js as an optional dependency
  111. try { moment = require('moment'); } catch (e) {}
  112. module.exports = factory(moment);
  113. } else if (typeof define === 'function' && define.amd) {
  114. // AMD. Register as an anonymous module.
  115. define(function (req)
  116. {
  117. // Load moment.js as an optional dependency
  118. var id = 'moment';
  119. try { moment = req(id); } catch (e) {}
  120. return factory(moment);
  121. });
  122. } else {
  123. root.Pikaday = factory(root.moment);
  124. }
  125. }(this, function (moment)
  126. {
  127. 'use strict';
  128. /**
  129. * feature detection and helper functions
  130. */
  131. var hasMoment = typeof moment === 'function',
  132. hasEventListeners = !!window.addEventListener,
  133. document = window.document,
  134. sto = window.setTimeout,
  135. addEvent = function(el, e, callback, capture)
  136. {
  137. if (hasEventListeners) {
  138. el.addEventListener(e, callback, !!capture);
  139. } else {
  140. el.attachEvent('on' + e, callback);
  141. }
  142. },
  143. removeEvent = function(el, e, callback, capture)
  144. {
  145. if (hasEventListeners) {
  146. el.removeEventListener(e, callback, !!capture);
  147. } else {
  148. el.detachEvent('on' + e, callback);
  149. }
  150. },
  151. fireEvent = function(el, eventName, data)
  152. {
  153. var ev;
  154. if (document.createEvent) {
  155. ev = document.createEvent('HTMLEvents');
  156. ev.initEvent(eventName, true, false);
  157. ev = extend(ev, data);
  158. el.dispatchEvent(ev);
  159. } else if (document.createEventObject) {
  160. ev = document.createEventObject();
  161. ev = extend(ev, data);
  162. el.fireEvent('on' + eventName, ev);
  163. }
  164. },
  165. trim = function(str)
  166. {
  167. return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g,'');
  168. },
  169. hasClass = function(el, cn)
  170. {
  171. return (' ' + el.className + ' ').indexOf(' ' + cn + ' ') !== -1;
  172. },
  173. addClass = function(el, cn)
  174. {
  175. if (!hasClass(el, cn)) {
  176. el.className = (el.className === '') ? cn : el.className + ' ' + cn;
  177. }
  178. },
  179. removeClass = function(el, cn)
  180. {
  181. el.className = trim((' ' + el.className + ' ').replace(' ' + cn + ' ', ' '));
  182. },
  183. isArray = function(obj)
  184. {
  185. return (/Array/).test(Object.prototype.toString.call(obj));
  186. },
  187. isDate = function(obj)
  188. {
  189. return (/Date/).test(Object.prototype.toString.call(obj)) && !isNaN(obj.getTime());
  190. },
  191. isWeekend = function(date)
  192. {
  193. var day = date.getDay();
  194. return day === 0 || day === 6;
  195. },
  196. isLeapYear = function(year)
  197. {
  198. // solution by Matti Virkkunen: http://stackoverflow.com/a/4881951
  199. return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
  200. },
  201. getDaysInMonth = function(year, month)
  202. {
  203. return [31, isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
  204. },
  205. setToStartOfDay = function(date)
  206. {
  207. if (isDate(date)) date.setHours(0,0,0,0);
  208. },
  209. compareDates = function(a,b)
  210. {
  211. // weak date comparison (use setToStartOfDay(date) to ensure correct result)
  212. return a.getTime() === b.getTime();
  213. },
  214. extend = function(to, from, overwrite)
  215. {
  216. var prop, hasProp;
  217. for (prop in from) {
  218. hasProp = to[prop] !== undefined;
  219. if (hasProp && typeof from[prop] === 'object' && from[prop] !== null && from[prop].nodeName === undefined) {
  220. if (isDate(from[prop])) {
  221. if (overwrite) {
  222. to[prop] = new Date(from[prop].getTime());
  223. }
  224. }
  225. else if (isArray(from[prop])) {
  226. if (overwrite) {
  227. to[prop] = from[prop].slice(0);
  228. }
  229. } else {
  230. to[prop] = extend({}, from[prop], overwrite);
  231. }
  232. } else if (overwrite || !hasProp) {
  233. to[prop] = from[prop];
  234. }
  235. }
  236. return to;
  237. },
  238. adjustCalendar = function(calendar) {
  239. if (calendar.month < 0) {
  240. calendar.year -= Math.ceil(Math.abs(calendar.month)/12);
  241. calendar.month += 12;
  242. }
  243. if (calendar.month > 11) {
  244. calendar.year += Math.floor(Math.abs(calendar.month)/12);
  245. calendar.month -= 12;
  246. }
  247. return calendar;
  248. },
  249. /**
  250. * defaults and localisation
  251. */
  252. defaults = {
  253. // bind the picker to a form field
  254. field: null,
  255. // automatically show/hide the picker on `field` focus (default `true` if `field` is set)
  256. bound: undefined,
  257. // position of the datepicker, relative to the field (default to bottom & left)
  258. // ('bottom' & 'left' keywords are not used, 'top' & 'right' are modifier on the bottom/left position)
  259. position: 'bottom left',
  260. // automatically fit in the viewport even if it means repositioning from the position option
  261. reposition: true,
  262. // the default output format for `.toString()` and `field` value
  263. format: 'DD.MM.YYYY',
  264. // the initial date to view when first opened
  265. defaultDate: null,
  266. // make the `defaultDate` the initial selected value
  267. setDefaultDate: false,
  268. // first day of week (0: Sunday, 1: Monday etc)
  269. firstDay: 0,
  270. // the minimum/earliest date that can be selected
  271. minDate: null,
  272. // the maximum/latest date that can be selected
  273. maxDate: null,
  274. // number of years either side, or array of upper/lower range
  275. yearRange: 10,
  276. // show week numbers at head of row
  277. showWeekNumber: false,
  278. // used internally (don't config outside)
  279. minYear: 0,
  280. maxYear: 9999,
  281. minMonth: undefined,
  282. maxMonth: undefined,
  283. startRange: null,
  284. endRange: null,
  285. isRTL: false,
  286. // Additional text to append to the year in the calendar title
  287. yearSuffix: '',
  288. // Render the month after year in the calendar title
  289. showMonthAfterYear: false,
  290. // how many months are visible
  291. numberOfMonths: 1,
  292. // when numberOfMonths is used, this will help you to choose where the main calendar will be (default `left`, can be set to `right`)
  293. // only used for the first display or when a selected date is not visible
  294. mainCalendar: 'left',
  295. // Specify a DOM element to render the calendar in
  296. container: undefined,
  297. // internationalization
  298. i18n: clLangs.en,
  299. // Theme Classname
  300. theme: null,
  301. // callback function
  302. onSelect: null,
  303. onOpen: null,
  304. onClose: null,
  305. onDraw: null
  306. },
  307. /**
  308. * templating functions to abstract HTML rendering
  309. */
  310. renderDayName = function(opts, day, abbr)
  311. {
  312. day += opts.firstDay;
  313. while (day >= 7) {
  314. day -= 7;
  315. }
  316. return abbr ? opts.i18n.weekdaysShort[day] : opts.i18n.weekdays[day];
  317. },
  318. renderDay = function(opts)
  319. {
  320. if (opts.isEmpty) {
  321. return '<td class="is-empty"></td>';
  322. }
  323. var arr = [];
  324. if (opts.isDisabled) {
  325. arr.push('is-disabled');
  326. }
  327. if (opts.isToday) {
  328. arr.push('is-today');
  329. }
  330. if (opts.isSelected) {
  331. arr.push('is-selected');
  332. }
  333. if (opts.isInRange) {
  334. arr.push('is-inrange');
  335. }
  336. if (opts.isStartRange) {
  337. arr.push('is-startrange');
  338. }
  339. if (opts.isEndRange) {
  340. arr.push('is-endrange');
  341. }
  342. return '<td data-day="' + opts.day + '" class="' + arr.join(' ') + '">' +
  343. '<button class="pika-button pika-day" type="button" ' +
  344. 'data-pika-year="' + opts.year + '" data-pika-month="' + opts.month + '" data-pika-day="' + opts.day + '">' +
  345. opts.day +
  346. '</button>' +
  347. '</td>';
  348. },
  349. renderWeek = function (d, m, y) {
  350. // Lifted from http://javascript.about.com/library/blweekyear.htm, lightly modified.
  351. var onejan = new Date(y, 0, 1),
  352. weekNum = Math.ceil((((new Date(y, m, d) - onejan) / 86400000) + onejan.getDay()+1)/7);
  353. return '<td class="pika-week">' + weekNum + '</td>';
  354. },
  355. renderRow = function(days, isRTL)
  356. {
  357. return '<tr>' + (isRTL ? days.reverse() : days).join('') + '</tr>';
  358. },
  359. renderBody = function(rows)
  360. {
  361. return '<tbody>' + rows.join('') + '</tbody>';
  362. },
  363. renderHead = function(opts)
  364. {
  365. var i, arr = [];
  366. if (opts.showWeekNumber) {
  367. arr.push('<th></th>');
  368. }
  369. for (i = 0; i < 7; i++) {
  370. arr.push('<th scope="col"><abbr title="' + renderDayName(opts, i) + '">' + renderDayName(opts, i, true) + '</abbr></th>');
  371. }
  372. return '<thead>' + (opts.isRTL ? arr.reverse() : arr).join('') + '</thead>';
  373. },
  374. renderTitle = function(instance, c, year, month, refYear)
  375. {
  376. var i, j, arr,
  377. opts = instance._o,
  378. isMinYear = year === opts.minYear,
  379. isMaxYear = year === opts.maxYear,
  380. html = '<div class="pika-title">',
  381. monthHtml,
  382. yearHtml,
  383. prev = true,
  384. next = true;
  385. for (arr = [], i = 0; i < 12; i++) {
  386. arr.push('<option value="' + (year === refYear ? i - c : 12 + i - c) + '"' +
  387. (i === month ? ' selected': '') +
  388. ((isMinYear && i < opts.minMonth) || (isMaxYear && i > opts.maxMonth) ? 'disabled' : '') + '>' +
  389. opts.i18n.months[i] + '</option>');
  390. }
  391. monthHtml = '<div class="pika-label">' + opts.i18n.months[month] + '<select class="pika-select pika-select-month" tabindex="-1">' + arr.join('') + '</select></div>';
  392. if (isArray(opts.yearRange)) {
  393. i = opts.yearRange[0];
  394. j = opts.yearRange[1] + 1;
  395. } else {
  396. i = year - opts.yearRange;
  397. j = 1 + year + opts.yearRange;
  398. }
  399. for (arr = []; i < j && i <= opts.maxYear; i++) {
  400. if (i >= opts.minYear) {
  401. arr.push('<option value="' + i + '"' + (i === year ? ' selected': '') + '>' + (i) + '</option>');
  402. }
  403. }
  404. yearHtml = '<div class="pika-label">' + year + opts.yearSuffix + '<select class="pika-select pika-select-year" tabindex="-1">' + arr.join('') + '</select></div>';
  405. if (opts.showMonthAfterYear) {
  406. html += yearHtml + monthHtml;
  407. } else {
  408. html += monthHtml + yearHtml;
  409. }
  410. if (isMinYear && (month === 0 || opts.minMonth >= month)) {
  411. prev = false;
  412. }
  413. if (isMaxYear && (month === 11 || opts.maxMonth <= month)) {
  414. next = false;
  415. }
  416. if (c === 0) {
  417. html += '<button class="pika-prev' + (prev ? '' : ' is-disabled') + '" type="button">' + opts.i18n.previousMonth + '</button>';
  418. }
  419. if (c === (instance._o.numberOfMonths - 1) ) {
  420. html += '<button class="pika-next' + (next ? '' : ' is-disabled') + '" type="button">' + opts.i18n.nextMonth + '</button>';
  421. }
  422. return html += '</div>';
  423. },
  424. renderTable = function(opts, data)
  425. {
  426. return '<table cellpadding="0" cellspacing="0" class="pika-table">' + renderHead(opts) + renderBody(data) + '</table>';
  427. },
  428. /**
  429. * Pikaday constructor
  430. */
  431. Pikaday = function(options)
  432. {
  433. var self = this,
  434. opts = self.config(options);
  435. self._onMouseDown = function(e)
  436. {
  437. if (!self._v) {
  438. return;
  439. }
  440. e = e || window.event;
  441. var target = e.target || e.srcElement;
  442. console.log(target);
  443. if (!target) {
  444. return;
  445. }
  446. if (!hasClass(target.parentNode, 'is-disabled')) {
  447. if (hasClass(target, 'pika-button') && !hasClass(target, 'is-empty')) {
  448. self.setDate(new Date(target.getAttribute('data-pika-year'), target.getAttribute('data-pika-month'), target.getAttribute('data-pika-day')));
  449. if (opts.bound) {
  450. sto(function() {
  451. self.hide();
  452. if (opts.field) {
  453. opts.field.blur();
  454. }
  455. }, 100);
  456. }
  457. }
  458. else if (hasClass(target, 'pika-prev')) {
  459. self.prevMonth();
  460. }
  461. else if (hasClass(target, 'pika-next')) {
  462. self.nextMonth();
  463. }
  464. }
  465. if (!hasClass(target, 'pika-select')) {
  466. // if this is touch event prevent mouse events emulation
  467. if (e.preventDefault) {
  468. e.preventDefault();
  469. } else {
  470. e.returnValue = false;
  471. return false;
  472. }
  473. } else {
  474. self._c = true;
  475. }
  476. };
  477. self._onChange = function(e)
  478. {
  479. e = e || window.event;
  480. var target = e.target || e.srcElement;
  481. if (!target) {
  482. return;
  483. }
  484. if (hasClass(target, 'pika-select-month')) {
  485. self.gotoMonth(target.value);
  486. }
  487. else if (hasClass(target, 'pika-select-year')) {
  488. self.gotoYear(target.value);
  489. }
  490. };
  491. self._onInputChange = function(e)
  492. {
  493. var date;
  494. if (e.firedBy === self) {
  495. return;
  496. }
  497. if (hasMoment) {
  498. date = moment(opts.field.value, opts.format);
  499. date = (date && date.isValid()) ? date.toDate() : null;
  500. }
  501. else {
  502. // date = parseDateFromInput(opts.field.value);
  503. date = parser(opts.field.value, opts.format);
  504. }
  505. if (isDate(date)) {
  506. self.setDate(date);
  507. }else {
  508. self.setDate(null);
  509. }
  510. if (!self._v) {
  511. self.show();
  512. }
  513. };
  514. self._onInputFocus = function()
  515. {
  516. self.show();
  517. };
  518. self._onInputClick = function()
  519. {
  520. self.show();
  521. };
  522. self._onInputBlur = function()
  523. {
  524. // IE allows pika div to gain focus; catch blur the input field
  525. var pEl = document.activeElement;
  526. do {
  527. if (hasClass(pEl, 'pika-single')) {
  528. return;
  529. }
  530. }
  531. while ((pEl = pEl.parentNode));
  532. if (!self._c) {
  533. self._b = sto(function() {
  534. self.hide();
  535. }, 50);
  536. }
  537. self._c = false;
  538. };
  539. self._onClick = function(e)
  540. {
  541. e = e || window.event;
  542. var target = e.target || e.srcElement,
  543. pEl = target;
  544. if (!target) {
  545. return;
  546. }
  547. if (!hasEventListeners && hasClass(target, 'pika-select')) {
  548. if (!target.onchange) {
  549. target.setAttribute('onchange', 'return;');
  550. addEvent(target, 'change', self._onChange);
  551. }
  552. }
  553. do {
  554. if (hasClass(pEl, 'pika-single') || pEl === opts.trigger) {
  555. return;
  556. }
  557. }
  558. while ((pEl = pEl.parentNode));
  559. if (self._v && target !== opts.trigger && pEl !== opts.trigger) {
  560. self.hide();
  561. }
  562. };
  563. self.el = document.createElement('div');
  564. self.el.className = 'pika-single' + (opts.isRTL ? ' is-rtl' : '') + (opts.theme ? ' ' + opts.theme : '');
  565. addEvent(self.el, 'mousedown', self._onMouseDown, true);
  566. addEvent(self.el, 'touchend', self._onMouseDown, true);
  567. addEvent(self.el, 'change', self._onChange);
  568. if (opts.field) {
  569. if (opts.container) {
  570. opts.container.appendChild(self.el);
  571. } else if (opts.bound) {
  572. document.body.appendChild(self.el);
  573. } else {
  574. opts.field.parentNode.insertBefore(self.el, opts.field.nextSibling);
  575. }
  576. addEvent(opts.field, 'change', self._onInputChange);
  577. if (!opts.defaultDate) {
  578. if (hasMoment && opts.field.value) {
  579. opts.defaultDate = moment(opts.field.value, opts.format).toDate();
  580. } else {
  581. opts.defaultDate = new Date(Date.parse(opts.field.value));
  582. }
  583. opts.setDefaultDate = true;
  584. }
  585. }
  586. var defDate = opts.defaultDate;
  587. if (isDate(defDate)) {
  588. if (opts.setDefaultDate) {
  589. self.setDate(defDate, true);
  590. } else {
  591. self.gotoDate(defDate);
  592. }
  593. } else {
  594. self.gotoDate(new Date());
  595. }
  596. if (opts.bound) {
  597. this.hide();
  598. self.el.className += ' is-bound';
  599. addEvent(opts.trigger, 'click', self._onInputClick);
  600. addEvent(opts.trigger, 'focus', self._onInputFocus);
  601. addEvent(opts.trigger, 'blur', self._onInputBlur);
  602. } else {
  603. this.show();
  604. }
  605. };
  606. /**
  607. * public Pikaday API
  608. */
  609. Pikaday.prototype = {
  610. /**
  611. * configure functionality
  612. */
  613. config: function(options)
  614. {
  615. if (!this._o) {
  616. this._o = extend({}, defaults, true);
  617. }
  618. var opts = extend(this._o, options, true);
  619. opts.isRTL = !!opts.isRTL;
  620. opts.field = (opts.field && opts.field.nodeName) ? opts.field : null;
  621. opts.theme = (typeof opts.theme) === 'string' && opts.theme ? opts.theme : null;
  622. opts.bound = !!(opts.bound !== undefined ? opts.field && opts.bound : opts.field);
  623. opts.trigger = (opts.trigger && opts.trigger.nodeName) ? opts.trigger : opts.field;
  624. opts.disableWeekends = !!opts.disableWeekends;
  625. opts.disableDayFn = (typeof opts.disableDayFn) === 'function' ? opts.disableDayFn : null;
  626. var nom = parseInt(opts.numberOfMonths, 10) || 1;
  627. opts.numberOfMonths = nom > 4 ? 4 : nom;
  628. if (!isDate(opts.minDate)) {
  629. opts.minDate = false;
  630. }
  631. if (!isDate(opts.maxDate)) {
  632. opts.maxDate = false;
  633. }
  634. if ((opts.minDate && opts.maxDate) && opts.maxDate < opts.minDate) {
  635. opts.maxDate = opts.minDate = false;
  636. }
  637. if (opts.minDate) {
  638. this.setMinDate(opts.minDate);
  639. }
  640. if (opts.maxDate) {
  641. setToStartOfDay(opts.maxDate);
  642. opts.maxYear = opts.maxDate.getFullYear();
  643. opts.maxMonth = opts.maxDate.getMonth();
  644. }
  645. if (isArray(opts.yearRange)) {
  646. var fallback = new Date().getFullYear() - 10;
  647. opts.yearRange[0] = parseInt(opts.yearRange[0], 10) || fallback;
  648. opts.yearRange[1] = parseInt(opts.yearRange[1], 10) || fallback;
  649. } else {
  650. opts.yearRange = Math.abs(parseInt(opts.yearRange, 10)) || defaults.yearRange;
  651. if (opts.yearRange > 100) {
  652. opts.yearRange = 100;
  653. }
  654. }
  655. return opts;
  656. },
  657. /**
  658. * return a formatted string of the current selection (using Moment.js if available or default formatter)
  659. */
  660. toString: function(format) {
  661. return !isDate(this._d) ? '' : (hasMoment) ? moment(this._d).format(format || this._o.format) : formatter(this._d,this._o.format);
  662. },
  663. /**
  664. * return a Moment.js object of the current selection (if available)
  665. */
  666. getMoment: function()
  667. {
  668. return hasMoment ? moment(this._d) : null;
  669. },
  670. /**
  671. * set the current selection from a Moment.js object (if available)
  672. */
  673. setMoment: function(date, preventOnSelect)
  674. {
  675. if (hasMoment && moment.isMoment(date)) {
  676. this.setDate(date.toDate(), preventOnSelect);
  677. }
  678. },
  679. /**
  680. * return a Date object of the current selection
  681. */
  682. getDate: function()
  683. {
  684. return isDate(this._d) ? new Date(this._d.getTime()) : null;
  685. },
  686. /**
  687. * set the current selection
  688. */
  689. setDate: function(date, preventOnSelect)
  690. {
  691. if (!date) {
  692. this._d = null;
  693. if (this._o.field) {
  694. this._o.field.value = '';
  695. fireEvent(this._o.field, 'change', { firedBy: this });
  696. }
  697. return this.draw();
  698. }
  699. if (typeof date === 'string') {
  700. date = new Date(Date.parse(date));
  701. }
  702. if (!isDate(date)) {
  703. return;
  704. }
  705. var min = this._o.minDate,
  706. max = this._o.maxDate;
  707. if (isDate(min) && date < min) {
  708. date = min;
  709. } else if (isDate(max) && date > max) {
  710. date = max;
  711. }
  712. this._d = new Date(date.getTime());
  713. setToStartOfDay(this._d);
  714. this.gotoDate(this._d);
  715. if (this._o.field) {
  716. this._o.field.value = this.toString();
  717. fireEvent(this._o.field, 'change', { firedBy: this });
  718. }
  719. if (!preventOnSelect && typeof this._o.onSelect === 'function') {
  720. this._o.onSelect.call(this, this.getDate());
  721. }
  722. },
  723. /**
  724. * change view to a specific date
  725. */
  726. gotoDate: function(date)
  727. {
  728. var newCalendar = true;
  729. if (!isDate(date)) {
  730. return;
  731. }
  732. if (this.calendars) {
  733. var firstVisibleDate = new Date(this.calendars[0].year, this.calendars[0].month, 1),
  734. lastVisibleDate = new Date(this.calendars[this.calendars.length-1].year, this.calendars[this.calendars.length-1].month, 1),
  735. visibleDate = date.getTime();
  736. // get the end of the month
  737. lastVisibleDate.setMonth(lastVisibleDate.getMonth()+1);
  738. lastVisibleDate.setDate(lastVisibleDate.getDate()-1);
  739. newCalendar = (visibleDate < firstVisibleDate.getTime() || lastVisibleDate.getTime() < visibleDate);
  740. }
  741. if (newCalendar) {
  742. this.calendars = [{
  743. month: date.getMonth(),
  744. year: date.getFullYear()
  745. }];
  746. if (this._o.mainCalendar === 'right') {
  747. this.calendars[0].month += 1 - this._o.numberOfMonths;
  748. }
  749. }
  750. this.adjustCalendars();
  751. },
  752. adjustCalendars: function() {
  753. this.calendars[0] = adjustCalendar(this.calendars[0]);
  754. for (var c = 1; c < this._o.numberOfMonths; c++) {
  755. this.calendars[c] = adjustCalendar({
  756. month: this.calendars[0].month + c,
  757. year: this.calendars[0].year
  758. });
  759. }
  760. this.draw();
  761. },
  762. gotoToday: function()
  763. {
  764. this.gotoDate(new Date());
  765. },
  766. /**
  767. * change view to a specific month (zero-index, e.g. 0: January)
  768. */
  769. gotoMonth: function(month)
  770. {
  771. if (!isNaN(month)) {
  772. this.calendars[0].month = parseInt(month, 10);
  773. this.adjustCalendars();
  774. }
  775. },
  776. nextMonth: function()
  777. {
  778. this.calendars[0].month++;
  779. this.adjustCalendars();
  780. },
  781. prevMonth: function()
  782. {
  783. this.calendars[0].month--;
  784. this.adjustCalendars();
  785. },
  786. /**
  787. * change view to a specific full year (e.g. "2012")
  788. */
  789. gotoYear: function(year)
  790. {
  791. if (!isNaN(year)) {
  792. this.calendars[0].year = parseInt(year, 10);
  793. this.adjustCalendars();
  794. }
  795. },
  796. /**
  797. * change the minDate
  798. */
  799. setMinDate: function(value)
  800. {
  801. setToStartOfDay(value);
  802. this._o.minDate = value;
  803. this._o.minYear = value.getFullYear();
  804. this._o.minMonth = value.getMonth();
  805. },
  806. /**
  807. * change the maxDate
  808. */
  809. setMaxDate: function(value)
  810. {
  811. this._o.maxDate = value;
  812. },
  813. setStartRange: function(value)
  814. {
  815. this._o.startRange = value;
  816. },
  817. setEndRange: function(value)
  818. {
  819. this._o.endRange = value;
  820. },
  821. /**
  822. * refresh the HTML
  823. */
  824. draw: function(force)
  825. {
  826. if (!this._v && !force) {
  827. return;
  828. }
  829. var opts = this._o,
  830. minYear = opts.minYear,
  831. maxYear = opts.maxYear,
  832. minMonth = opts.minMonth,
  833. maxMonth = opts.maxMonth,
  834. html = '';
  835. if (this._y <= minYear) {
  836. this._y = minYear;
  837. if (!isNaN(minMonth) && this._m < minMonth) {
  838. this._m = minMonth;
  839. }
  840. }
  841. if (this._y >= maxYear) {
  842. this._y = maxYear;
  843. if (!isNaN(maxMonth) && this._m > maxMonth) {
  844. this._m = maxMonth;
  845. }
  846. }
  847. for (var c = 0; c < opts.numberOfMonths; c++) {
  848. html += '<div class="pika-lendar">' + renderTitle(this, c, this.calendars[c].year, this.calendars[c].month, this.calendars[0].year) + this.render(this.calendars[c].year, this.calendars[c].month) + '</div>';
  849. }
  850. this.el.innerHTML = html;
  851. if (opts.bound) {
  852. if(opts.field.type !== 'hidden') {
  853. sto(function() {
  854. opts.trigger.focus();
  855. }, 1);
  856. }
  857. }
  858. if (typeof this._o.onDraw === 'function') {
  859. var self = this;
  860. sto(function() {
  861. self._o.onDraw.call(self);
  862. }, 0);
  863. }
  864. },
  865. adjustPosition: function()
  866. {
  867. var field, pEl, width, height, viewportWidth, viewportHeight, scrollTop, left, top, clientRect;
  868. if (this._o.container) return;
  869. this.el.style.position = 'absolute';
  870. field = this._o.trigger;
  871. pEl = field;
  872. width = this.el.offsetWidth;
  873. height = this.el.offsetHeight;
  874. viewportWidth = window.innerWidth || document.documentElement.clientWidth;
  875. viewportHeight = window.innerHeight || document.documentElement.clientHeight;
  876. scrollTop = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;
  877. if (typeof field.getBoundingClientRect === 'function') {
  878. clientRect = field.getBoundingClientRect();
  879. left = clientRect.left + window.pageXOffset;
  880. top = clientRect.bottom + window.pageYOffset;
  881. } else {
  882. left = pEl.offsetLeft;
  883. top = pEl.offsetTop + pEl.offsetHeight;
  884. while((pEl = pEl.offsetParent)) {
  885. left += pEl.offsetLeft;
  886. top += pEl.offsetTop;
  887. }
  888. }
  889. // default position is bottom & left
  890. if ((this._o.reposition && left + width > viewportWidth) ||
  891. (
  892. this._o.position.indexOf('right') > -1 &&
  893. left - width + field.offsetWidth > 0
  894. )
  895. ) {
  896. left = left - width + field.offsetWidth;
  897. }
  898. if ((this._o.reposition && top + height > viewportHeight + scrollTop) ||
  899. (
  900. this._o.position.indexOf('top') > -1 &&
  901. top - height - field.offsetHeight > 0
  902. )
  903. ) {
  904. top = top - height - field.offsetHeight;
  905. }
  906. this.el.style.left = left + 'px';
  907. this.el.style.top = top + 'px';
  908. },
  909. /**
  910. * render HTML for a particular month
  911. */
  912. render: function(year, month)
  913. {
  914. var opts = this._o,
  915. now = new Date(),
  916. days = getDaysInMonth(year, month),
  917. before = new Date(year, month, 1).getDay(),
  918. data = [],
  919. row = [];
  920. setToStartOfDay(now);
  921. if (opts.firstDay > 0) {
  922. before -= opts.firstDay;
  923. if (before < 0) {
  924. before += 7;
  925. }
  926. }
  927. var cells = days + before,
  928. after = cells;
  929. while(after > 7) {
  930. after -= 7;
  931. }
  932. cells += 7 - after;
  933. for (var i = 0, r = 0; i < cells; i++)
  934. {
  935. var day = new Date(year, month, 1 + (i - before)),
  936. isSelected = isDate(this._d) ? compareDates(day, this._d) : false,
  937. isToday = compareDates(day, now),
  938. isEmpty = i < before || i >= (days + before),
  939. isStartRange = opts.startRange && compareDates(opts.startRange, day),
  940. isEndRange = opts.endRange && compareDates(opts.endRange, day),
  941. isInRange = opts.startRange && opts.endRange && opts.startRange < day && day < opts.endRange,
  942. isDisabled = (opts.minDate && day < opts.minDate) ||
  943. (opts.maxDate && day > opts.maxDate) ||
  944. (opts.disableWeekends && isWeekend(day)) ||
  945. (opts.disableDayFn && opts.disableDayFn(day)),
  946. dayConfig = {
  947. day: 1 + (i - before),
  948. month: month,
  949. year: year,
  950. isSelected: isSelected,
  951. isToday: isToday,
  952. isDisabled: isDisabled,
  953. isEmpty: isEmpty,
  954. isStartRange: isStartRange,
  955. isEndRange: isEndRange,
  956. isInRange: isInRange
  957. };
  958. row.push(renderDay(dayConfig));
  959. if (++r === 7) {
  960. if (opts.showWeekNumber) {
  961. row.unshift(renderWeek(i - before, month, year));
  962. }
  963. data.push(renderRow(row, opts.isRTL));
  964. row = [];
  965. r = 0;
  966. }
  967. }
  968. return renderTable(opts, data);
  969. },
  970. isVisible: function()
  971. {
  972. return this._v;
  973. },
  974. show: function()
  975. {
  976. if (!this._v) {
  977. removeClass(this.el, 'is-hidden');
  978. this._v = true;
  979. this.draw();
  980. if (this._o.bound) {
  981. addEvent(document, 'click', this._onClick);
  982. this.adjustPosition();
  983. }
  984. if (typeof this._o.onOpen === 'function') {
  985. this._o.onOpen.call(this);
  986. }
  987. }
  988. },
  989. hide: function()
  990. {
  991. var v = this._v;
  992. if (v !== false) {
  993. if (this._o.bound) {
  994. removeEvent(document, 'click', this._onClick);
  995. }
  996. this.el.style.position = 'static'; // reset
  997. this.el.style.left = 'auto';
  998. this.el.style.top = 'auto';
  999. addClass(this.el, 'is-hidden');
  1000. this._v = false;
  1001. if (v !== undefined && typeof this._o.onClose === 'function') {
  1002. this._o.onClose.call(this);
  1003. }
  1004. }
  1005. },
  1006. /**
  1007. * GAME OVER
  1008. */
  1009. destroy: function()
  1010. {
  1011. this.hide();
  1012. removeEvent(this.el, 'mousedown', this._onMouseDown, true);
  1013. removeEvent(this.el, 'touchend', this._onMouseDown, true);
  1014. removeEvent(this.el, 'change', this._onChange);
  1015. if (this._o.field) {
  1016. removeEvent(this._o.field, 'change', this._onInputChange);
  1017. if (this._o.bound) {
  1018. removeEvent(this._o.trigger, 'click', this._onInputClick);
  1019. removeEvent(this._o.trigger, 'focus', this._onInputFocus);
  1020. removeEvent(this._o.trigger, 'blur', this._onInputBlur);
  1021. }
  1022. }
  1023. if (this.el.parentNode) {
  1024. this.el.parentNode.removeChild(this.el);
  1025. }
  1026. }
  1027. };
  1028. return Pikaday;
  1029. }));