Dashboard sipadu mbip
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

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