datepicker.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. var picker = {
  2. // (A) COMMON MONTH NAMES
  3. months : ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
  4. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
  5. // (B) ATTACH DATEPICKER TO TARGET
  6. // target: field to populate
  7. // container: generate datepicker in here (for inline datepicker)
  8. // startmon: start on mon? (optional, default false)
  9. // yrange: year select range (optional, default 10)
  10. // disableday: days to disable, e.g. [2,7] to disable tue and sun (optional)
  11. // onpick : function to call on select date (optional)
  12. attach : instance => {
  13. // (B1) SET DEFAULT OPTIONS
  14. instance.target.readOnly = true; // prevent onscreen keyboard
  15. instance.startmon = instance.startmon ? true : false;
  16. instance.yrange = instance.yrange ? instance.yrange : 10;
  17. // (B2) CURRENT MONTH YEAR (UTC+0)
  18. var today = new Date(),
  19. thisMonth = today.getUTCMonth(), // jan is 0
  20. thisYear = today.getUTCFullYear(); // yyyy
  21. // (B3) GENERATE HTML
  22. // (B3-1) DATEPICKER WRAPPER + BASIC STRUCTURE
  23. instance.hWrap = document.createElement("div");
  24. instance.hWrap.classList.add("picker-wrap");
  25. instance.hWrap.innerHTML =
  26. `<div class="picker">
  27. <div class="picker-p">
  28. <div class="picker-b">&lt;</div>
  29. <select class="picker-m"></select>
  30. <select class="picker-y"></select>
  31. <div class="picker-n">&gt;</div>
  32. </div>
  33. <div class="picker-d"></div>
  34. </div>`;
  35. instance.hMonth = instance.hWrap.querySelector(".picker-m");
  36. instance.hYear = instance.hWrap.querySelector(".picker-y");
  37. instance.hDays = instance.hWrap.querySelector(".picker-d");
  38. // (B3-2) SHIFT PERIOD
  39. instance.hWrap.querySelector(".picker-b").onclick = () => picker.shift(instance);
  40. instance.hWrap.querySelector(".picker-n").onclick = () => picker.shift(instance, 1);
  41. // (B3-3) MONTH SELECTOR
  42. for (let m in picker.months) {
  43. let o = document.createElement("option");
  44. o.value = +m + 1;
  45. o.text = picker.months[m];
  46. instance.hMonth.appendChild(o);
  47. }
  48. instance.hMonth.selectedIndex = thisMonth;
  49. instance.hMonth.onchange = () => picker.draw(instance);
  50. // (B3-4) YEAR SELECTOR
  51. for (let y = thisYear-instance.yrange; y < thisYear+instance.yrange; y++) {
  52. let o = document.createElement("option");
  53. o.value = y;
  54. o.text = y;
  55. instance.hYear.appendChild(o);
  56. }
  57. instance.hYear.selectedIndex = instance.yrange;
  58. instance.hYear.onchange = () => picker.draw(instance);
  59. // (B4) INLINE DATEPICKER - ATTACH INTO CONTAINER
  60. if (instance.container) { instance.container.appendChild(instance.hWrap); }
  61. // (B5) POPUP DATEPICKER - ATTACH INTO HTML BODY
  62. else {
  63. instance.hWrap.classList.add("popup");
  64. instance.target.onfocus = () => instance.hWrap.classList.add("show");
  65. instance.hWrap.onclick = e => { if (e.target == instance.hWrap) { instance.hWrap.classList.remove("show"); }};
  66. document.body.appendChild(instance.hWrap);
  67. }
  68. // (B6) INIT DRAW
  69. picker.draw(instance);
  70. },
  71. // (C) SHIFT PERIOD (BY 1 MONTH)
  72. shift : (instance, next) => {
  73. var m = +instance.hMonth.value, y = +instance.hYear.value;
  74. if (next) {
  75. m++;
  76. if (m>12) { m = 1; y++; }
  77. let max = instance.hYear.querySelector("option:last-child").value;
  78. if (y>max) { m = 12; y = max; }
  79. } else {
  80. m--;
  81. if (m<1) { m = 12; y--; }
  82. let min = instance.hYear.querySelector("option:first-child").value;
  83. if (y<min) { m = 1; y = min; }
  84. }
  85. instance.hMonth.value = m;
  86. instance.hYear.value = y;
  87. picker.draw(instance);
  88. },
  89. // (D) DRAW DAYS IN MONTH
  90. draw : instance => {
  91. // (D1) A LOT OF CALCULATIONS
  92. // (D1-1) SELECTED MONTH YEAR
  93. var month = instance.hMonth.value,
  94. year = instance.hYear.value;
  95. // (D1-2) DATE RANGE CALCULATION (UTC+0)
  96. var daysInMonth = new Date(Date.UTC(year, month, 0)).getUTCDate(),
  97. startDay = new Date(Date.UTC(year, month-1, 1)).getUTCDay(), // sun is 0
  98. endDay = new Date(Date.UTC(year, month-1, daysInMonth)).getUTCDay();
  99. startDay = startDay==0 ? 7 : startDay,
  100. endDay = endDay==0 ? 7 : endDay;
  101. // (D1-3) TODAY (FOR HIGHLIGHTING "TODAY")
  102. var today = new Date(), todayDate = null;
  103. if (today.getUTCMonth()+1 == month && today.getUTCFullYear() == year) {
  104. todayDate = today.getUTCDate();
  105. }
  106. // (D1-4) DAY NAMES
  107. var daynames = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
  108. if (instance.startmon) { daynames.push("Sun"); }
  109. else { daynames.unshift("Sun"); }
  110. // (D2) CALCULATE DATE SQUARES
  111. // (D2-1) EMPTY SQUARES BEFORE FIRST DAY OF MONTH
  112. var squares = [];
  113. if (instance.startmon && startDay!=1) {
  114. for (let i=1; i<startDay; i++) { squares.push("B"); }
  115. }
  116. if (!instance.startmon && startDay!=7) {
  117. for (let i=0; i<startDay; i++) { squares.push("B"); }
  118. }
  119. // (D2-2) DAYS OF MONTH (SOME DAYS DISABLED)
  120. if (instance.disableday) {
  121. let thisDay = startDay;
  122. for (let i=1; i<=daysInMonth; i++) {
  123. squares.push([i, instance.disableday.includes(thisDay)]);
  124. thisDay++;
  125. if (thisDay==8) { thisDay = 1; }
  126. }
  127. }
  128. // (D2-3) DAYS OF MONTH (ALL DAYS ENABLED)
  129. else {
  130. for (let i=1; i<=daysInMonth; i++) { squares.push([i, false]); }
  131. }
  132. // (D2-4) EMPTY SQUARES AFTER LAST DAY OF MONTH
  133. if (instance.startmon && endDay!=7) {
  134. for (let i=endDay; i<7; i++) { squares.push("B"); }
  135. }
  136. if (!instance.startmon && endDay!=6) {
  137. for (let i=endDay; i<(endDay==7?13:6); i++) { squares.push("B"); }
  138. }
  139. // (D3) DRAW HTML FINALLY
  140. // (D3-1) EMPTY CURRENT
  141. instance.hDays.innerHTML = "";
  142. // (D3-2) FIRST ROW - DAY NAMES HEADER
  143. var cell;
  144. for (let d of daynames) {
  145. cell = document.createElement("div");
  146. cell.innerHTML = d;
  147. cell.classList.add("picker-d-h");
  148. instance.hDays.appendChild(cell);
  149. }
  150. // (D3-3) FOLLOWING ROWS - DATE CELLS
  151. for (let i=0; i<squares.length; i++) {
  152. cell = document.createElement("div");
  153. if (squares[i] == "B") { cell.classList.add("picker-d-b"); }
  154. else {
  155. // (D3-2A) CELL DATE
  156. cell.innerHTML = squares[i][0];
  157. // (D3-2B) NOT ALLOWED TO CHOOSE THIS DAY
  158. if (squares[i][1]) { cell.classList.add("picker-d-dd"); }
  159. // (D3-2C) ALLOWED TO CHOOSE THIS DAY
  160. else {
  161. if (squares[i][0] == todayDate) { cell.classList.add("picker-d-td"); }
  162. cell.classList.add("picker-d-d");
  163. cell.onclick = () => picker.pick(instance, squares[i][0]);
  164. }
  165. }
  166. instance.hDays.appendChild(cell);
  167. }
  168. },
  169. // (E) CHOOSE A DATE
  170. pick : (instance, d) => {
  171. // (E1) GET MONTH YEAR
  172. let m = instance.hMonth.value,
  173. y = instance.hYear.value;
  174. // (E2) FORMAT & SET SELECTED DAY (YYYY-MM-DD)
  175. if (+m<10) { m = "0" + m; }
  176. if (+d<10) { d = "0" + d; }
  177. let formatted = `${y}-${m}-${d}`;
  178. instance.target.value = formatted;
  179. // (E3) POPUP ONLY - CLOSE
  180. if (instance.hWrap.classList.contains("popup")) {
  181. instance.hWrap.classList.remove("show");
  182. }
  183. // (E4) CALL ON PICK IF DEFINED
  184. if (instance.onpick) { instance.onpick(formatted); }
  185. }
  186. };