flatpickr 4.6.10.0 → 4.6.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 70d6de393aaa727a6cbaa2a81a1176079bda538c0dfb1305032cb917293cf995
4
- data.tar.gz: 2795da991bbf422ebf201e31b70efae19c264679d77cce4f816a8ba63f0535e7
3
+ metadata.gz: e0b24ac1f05dc35f22a635d58fd5f865671d1e87f11a8a9acf05c2465d00c1d0
4
+ data.tar.gz: 07dccae99de9f9629397fe3b16cc0ca5d176514b584b7bd7c28f4a8fb3ed694f
5
5
  SHA512:
6
- metadata.gz: 1157b71c92e9b8a897c432d3df5e77ab72d4c98e95c45dd053e0af2dc0bd72b2b0abb7337df7298cbc0054db3fcd38f20e1f513bb0513d3bfd75a305a841cf94
7
- data.tar.gz: 9bbe7dd7aeb201f4fcbcbc171e0ed2d1b5e61381008b0149ea8aa31b0c1e4fa9c8178fa4df8e7fd070c98707fcdc68334738cf42d930c6c5298d12733514ba61
6
+ metadata.gz: 2cce33a0713111ed603143cd3c453aeee843e1f88ee98e3a6c6eabfd520499d61148abfebbb9d6fb185628cebb3effa43bb1075a2ca9443f6fcba43b59324407
7
+ data.tar.gz: c067dc48482dc6f635fd8c5d900ee17d5f9d9466f60d1d2eb0b44eb3c2150018a7945d2073b1f8e9705b189ec73cac8a0748099a570c6cfde47b75a7424e6f31
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Flatpickr
4
- VERSION = '4.6.10.0'
4
+ VERSION = '4.6.13.0'
5
5
  end
data/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "dependencies": {
3
- "flatpickr": "^4.6.10"
3
+ "flatpickr": "^4.6.13"
4
4
  }
5
5
  }
@@ -11,7 +11,15 @@
11
11
  };
12
12
  var Kurdish = {
13
13
  weekdays: {
14
- shorthand: ["یەکشەممە", "دووشەممە", "سێشەممە", "چوارشەممە", "پێنجشەممە", "هەینی", "شەممە"],
14
+ shorthand: [
15
+ "یەکشەممە",
16
+ "دووشەممە",
17
+ "سێشەممە",
18
+ "چوارشەممە",
19
+ "پێنجشەممە",
20
+ "هەینی",
21
+ "شەممە",
22
+ ],
15
23
  longhand: [
16
24
  "یەکشەممە",
17
25
  "دووشەممە",
@@ -11,15 +11,7 @@
11
11
  };
12
12
  var Armenian = {
13
13
  weekdays: {
14
- shorthand: [
15
- "Կիր",
16
- "Երկ",
17
- "Երք",
18
- "Չրք",
19
- "Հնգ",
20
- "Ուրբ",
21
- "Շբթ",
22
- ],
14
+ shorthand: ["Կիր", "Երկ", "Երք", "Չրք", "Հնգ", "Ուրբ", "Շբթ"],
23
15
  longhand: [
24
16
  "Կիրակի",
25
17
  "Եկուշաբթի",
@@ -502,7 +502,15 @@
502
502
  };
503
503
  var Kurdish = {
504
504
  weekdays: {
505
- shorthand: ["یەکشەممە", "دووشەممە", "سێشەممە", "چوارشەممە", "پێنجشەممە", "هەینی", "شەممە"],
505
+ shorthand: [
506
+ "یەکشەممە",
507
+ "دووشەممە",
508
+ "سێشەممە",
509
+ "چوارشەممە",
510
+ "پێنجشەممە",
511
+ "هەینی",
512
+ "شەممە",
513
+ ],
506
514
  longhand: [
507
515
  "یەکشەممە",
508
516
  "دووشەممە",
@@ -1587,15 +1595,7 @@
1587
1595
  };
1588
1596
  var Armenian = {
1589
1597
  weekdays: {
1590
- shorthand: [
1591
- "Կիր",
1592
- "Երկ",
1593
- "Երք",
1594
- "Չրք",
1595
- "Հնգ",
1596
- "Ուրբ",
1597
- "Շբթ",
1598
- ],
1598
+ shorthand: ["Կիր", "Երկ", "Երք", "Չրք", "Հնգ", "Ուրբ", "Շբթ"],
1599
1599
  longhand: [
1600
1600
  "Կիրակի",
1601
1601
  "Եկուշաբթի",
@@ -2353,15 +2353,7 @@
2353
2353
  var Malaysian = {
2354
2354
  weekdays: {
2355
2355
  shorthand: ["Aha", "Isn", "Sel", "Rab", "Kha", "Jum", "Sab"],
2356
- longhand: [
2357
- "Ahad",
2358
- "Isnin",
2359
- "Selasa",
2360
- "Rabu",
2361
- "Khamis",
2362
- "Jumaat",
2363
- "Sabtu",
2364
- ],
2356
+ longhand: ["Ahad", "Isnin", "Selasa", "Rabu", "Khamis", "Jumaat", "Sabtu"],
2365
2357
  },
2366
2358
  months: {
2367
2359
  shorthand: [
@@ -2520,62 +2512,62 @@
2520
2512
  fp$E.l10ns.nl = Dutch;
2521
2513
  fp$E.l10ns;
2522
2514
 
2523
- var fp$F = typeof window !== 'undefined' && window.flatpickr !== undefined
2515
+ var fp$F = typeof window !== "undefined" && window.flatpickr !== undefined
2524
2516
  ? window.flatpickr
2525
2517
  : {
2526
2518
  l10ns: {},
2527
2519
  };
2528
2520
  var NorwegianNynorsk = {
2529
2521
  weekdays: {
2530
- shorthand: ['Sø.', 'Må.', 'Ty.', 'On.', 'To.', 'Fr.', 'La.'],
2522
+ shorthand: ["Sø.", "Må.", "Ty.", "On.", "To.", "Fr.", "La."],
2531
2523
  longhand: [
2532
- 'Søndag',
2533
- 'Måndag',
2534
- 'Tysdag',
2535
- 'Onsdag',
2536
- 'Torsdag',
2537
- 'Fredag',
2538
- 'Laurdag',
2524
+ "Søndag",
2525
+ "Måndag",
2526
+ "Tysdag",
2527
+ "Onsdag",
2528
+ "Torsdag",
2529
+ "Fredag",
2530
+ "Laurdag",
2539
2531
  ],
2540
2532
  },
2541
2533
  months: {
2542
2534
  shorthand: [
2543
- 'Jan',
2544
- 'Feb',
2545
- 'Mars',
2546
- 'Apr',
2547
- 'Mai',
2548
- 'Juni',
2549
- 'Juli',
2550
- 'Aug',
2551
- 'Sep',
2552
- 'Okt',
2553
- 'Nov',
2554
- 'Des',
2555
- ],
2556
- longhand: [
2557
- 'Januar',
2558
- 'Februar',
2559
- 'Mars',
2560
- 'April',
2561
- 'Mai',
2562
- 'Juni',
2563
- 'Juli',
2564
- 'August',
2565
- 'September',
2566
- 'Oktober',
2567
- 'November',
2568
- 'Desember',
2535
+ "Jan",
2536
+ "Feb",
2537
+ "Mars",
2538
+ "Apr",
2539
+ "Mai",
2540
+ "Juni",
2541
+ "Juli",
2542
+ "Aug",
2543
+ "Sep",
2544
+ "Okt",
2545
+ "Nov",
2546
+ "Des",
2547
+ ],
2548
+ longhand: [
2549
+ "Januar",
2550
+ "Februar",
2551
+ "Mars",
2552
+ "April",
2553
+ "Mai",
2554
+ "Juni",
2555
+ "Juli",
2556
+ "August",
2557
+ "September",
2558
+ "Oktober",
2559
+ "November",
2560
+ "Desember",
2569
2561
  ],
2570
2562
  },
2571
2563
  firstDayOfWeek: 1,
2572
- rangeSeparator: ' til ',
2573
- weekAbbreviation: 'Veke',
2574
- scrollTitle: 'Scroll for å endre',
2575
- toggleTitle: 'Klikk for å veksle',
2564
+ rangeSeparator: " til ",
2565
+ weekAbbreviation: "Veke",
2566
+ scrollTitle: "Scroll for å endre",
2567
+ toggleTitle: "Klikk for å veksle",
2576
2568
  time_24hr: true,
2577
2569
  ordinal: function () {
2578
- return '.';
2570
+ return ".";
2579
2571
  },
2580
2572
  };
2581
2573
  fp$F.l10ns.nn = NorwegianNynorsk;
@@ -3147,6 +3139,13 @@
3147
3139
  "Dhjetor",
3148
3140
  ],
3149
3141
  },
3142
+ firstDayOfWeek: 1,
3143
+ rangeSeparator: " deri ",
3144
+ weekAbbreviation: "Java",
3145
+ yearAriaLabel: "Viti",
3146
+ monthAriaLabel: "Muaji",
3147
+ hourAriaLabel: "Ora",
3148
+ minuteAriaLabel: "Minuta",
3150
3149
  time_24hr: true,
3151
3150
  };
3152
3151
  fp$P.l10ns.sq = Albanian;
@@ -3258,7 +3257,7 @@
3258
3257
  "december",
3259
3258
  ],
3260
3259
  },
3261
- rangeSeparator: ' till ',
3260
+ rangeSeparator: " till ",
3262
3261
  time_24hr: true,
3263
3262
  ordinal: function () {
3264
3263
  return ".";
@@ -12,15 +12,7 @@
12
12
  var Malaysian = {
13
13
  weekdays: {
14
14
  shorthand: ["Aha", "Isn", "Sel", "Rab", "Kha", "Jum", "Sab"],
15
- longhand: [
16
- "Ahad",
17
- "Isnin",
18
- "Selasa",
19
- "Rabu",
20
- "Khamis",
21
- "Jumaat",
22
- "Sabtu",
23
- ],
15
+ longhand: ["Ahad", "Isnin", "Selasa", "Rabu", "Khamis", "Jumaat", "Sabtu"],
24
16
  },
25
17
  months: {
26
18
  shorthand: [
@@ -4,62 +4,62 @@
4
4
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.nn = {}));
5
5
  }(this, (function (exports) { 'use strict';
6
6
 
7
- var fp = typeof window !== 'undefined' && window.flatpickr !== undefined
7
+ var fp = typeof window !== "undefined" && window.flatpickr !== undefined
8
8
  ? window.flatpickr
9
9
  : {
10
10
  l10ns: {},
11
11
  };
12
12
  var NorwegianNynorsk = {
13
13
  weekdays: {
14
- shorthand: ['Sø.', 'Må.', 'Ty.', 'On.', 'To.', 'Fr.', 'La.'],
14
+ shorthand: ["Sø.", "Må.", "Ty.", "On.", "To.", "Fr.", "La."],
15
15
  longhand: [
16
- 'Søndag',
17
- 'Måndag',
18
- 'Tysdag',
19
- 'Onsdag',
20
- 'Torsdag',
21
- 'Fredag',
22
- 'Laurdag',
16
+ "Søndag",
17
+ "Måndag",
18
+ "Tysdag",
19
+ "Onsdag",
20
+ "Torsdag",
21
+ "Fredag",
22
+ "Laurdag",
23
23
  ],
24
24
  },
25
25
  months: {
26
26
  shorthand: [
27
- 'Jan',
28
- 'Feb',
29
- 'Mars',
30
- 'Apr',
31
- 'Mai',
32
- 'Juni',
33
- 'Juli',
34
- 'Aug',
35
- 'Sep',
36
- 'Okt',
37
- 'Nov',
38
- 'Des',
27
+ "Jan",
28
+ "Feb",
29
+ "Mars",
30
+ "Apr",
31
+ "Mai",
32
+ "Juni",
33
+ "Juli",
34
+ "Aug",
35
+ "Sep",
36
+ "Okt",
37
+ "Nov",
38
+ "Des",
39
39
  ],
40
40
  longhand: [
41
- 'Januar',
42
- 'Februar',
43
- 'Mars',
44
- 'April',
45
- 'Mai',
46
- 'Juni',
47
- 'Juli',
48
- 'August',
49
- 'September',
50
- 'Oktober',
51
- 'November',
52
- 'Desember',
41
+ "Januar",
42
+ "Februar",
43
+ "Mars",
44
+ "April",
45
+ "Mai",
46
+ "Juni",
47
+ "Juli",
48
+ "August",
49
+ "September",
50
+ "Oktober",
51
+ "November",
52
+ "Desember",
53
53
  ],
54
54
  },
55
55
  firstDayOfWeek: 1,
56
- rangeSeparator: ' til ',
57
- weekAbbreviation: 'Veke',
58
- scrollTitle: 'Scroll for å endre',
59
- toggleTitle: 'Klikk for å veksle',
56
+ rangeSeparator: " til ",
57
+ weekAbbreviation: "Veke",
58
+ scrollTitle: "Scroll for å endre",
59
+ toggleTitle: "Klikk for å veksle",
60
60
  time_24hr: true,
61
61
  ordinal: function () {
62
- return '.';
62
+ return ".";
63
63
  },
64
64
  };
65
65
  fp.l10ns.nn = NorwegianNynorsk;
@@ -52,6 +52,13 @@
52
52
  "Dhjetor",
53
53
  ],
54
54
  },
55
+ firstDayOfWeek: 1,
56
+ rangeSeparator: " deri ",
57
+ weekAbbreviation: "Java",
58
+ yearAriaLabel: "Viti",
59
+ monthAriaLabel: "Muaji",
60
+ hourAriaLabel: "Ora",
61
+ minuteAriaLabel: "Minuta",
55
62
  time_24hr: true,
56
63
  };
57
64
  fp.l10ns.sq = Albanian;
@@ -54,7 +54,7 @@
54
54
  "december",
55
55
  ],
56
56
  },
57
- rangeSeparator: ' till ',
57
+ rangeSeparator: " till ",
58
58
  time_24hr: true,
59
59
  ordinal: function () {
60
60
  return ".";
@@ -96,6 +96,16 @@
96
96
  frag.appendChild(month);
97
97
  }
98
98
  self.monthsContainer.appendChild(frag);
99
+ if (fp.config.minDate &&
100
+ fp.currentYear === fp.config.minDate.getFullYear())
101
+ fp.prevMonthNav.classList.add("flatpickr-disabled");
102
+ else
103
+ fp.prevMonthNav.classList.remove("flatpickr-disabled");
104
+ if (fp.config.maxDate &&
105
+ fp.currentYear === fp.config.maxDate.getFullYear())
106
+ fp.nextMonthNav.classList.add("flatpickr-disabled");
107
+ else
108
+ fp.nextMonthNav.classList.remove("flatpickr-disabled");
99
109
  }
100
110
  function bindEvents() {
101
111
  fp._bind(fp.prevMonthNav, "click", function (e) {
@@ -181,21 +191,25 @@
181
191
  }
182
192
  function setMonth(date) {
183
193
  var selectedDate = new Date(fp.currentYear, date.getMonth(), date.getDate());
194
+ var selectedDates = [];
184
195
  switch (fp.config.mode) {
185
196
  case "single":
186
- fp.selectedDates = [selectedDate];
197
+ selectedDates = [selectedDate];
198
+ break;
199
+ case "multiple":
200
+ selectedDates.push(selectedDate);
187
201
  break;
188
202
  case "range":
189
203
  if (fp.selectedDates.length === 2) {
190
- fp.clear(false, false);
191
- buildMonths();
204
+ selectedDates = [selectedDate];
205
+ }
206
+ else {
207
+ selectedDates = fp.selectedDates.concat([selectedDate]);
208
+ selectedDates.sort(function (a, b) { return a.getTime() - b.getTime(); });
192
209
  }
193
- fp.selectedDates.push(selectedDate);
194
- fp.selectedDates.sort(function (a, b) { return a.getTime() - b.getTime(); });
195
210
  break;
196
211
  }
197
- fp.latestSelectedDateObj = selectedDate;
198
- fp.updateValue();
212
+ fp.setDate(selectedDates, true);
199
213
  setCurrentlySelected();
200
214
  }
201
215
  var shifts = {
@@ -228,8 +242,7 @@
228
242
  }
229
243
  function closeHook() {
230
244
  var _a;
231
- if (((_a = fp.config) === null || _a === void 0 ? void 0 : _a.mode) === "range" &&
232
- fp.selectedDates.length === 1)
245
+ if (((_a = fp.config) === null || _a === void 0 ? void 0 : _a.mode) === "range" && fp.selectedDates.length === 1)
233
246
  fp.clear(false);
234
247
  if (!fp.selectedDates.length)
235
248
  buildMonths();
@@ -237,8 +250,8 @@
237
250
  // Help the prev/next year nav honor config.minDate (see 3fa5a69)
238
251
  function stubCurrentMonth() {
239
252
  config._stubbedCurrentMonth = fp._initialDate.getMonth();
240
- fp._initialDate.setMonth(0);
241
- fp.currentMonth = 0;
253
+ fp._initialDate.setMonth(config._stubbedCurrentMonth);
254
+ fp.currentMonth = config._stubbedCurrentMonth;
242
255
  }
243
256
  function unstubCurrentMonth() {
244
257
  if (!config._stubbedCurrentMonth)
@@ -1,4 +1,4 @@
1
- /* flatpickr v4.6.10, @license MIT */
1
+ /* flatpickr v4.6.13, @license MIT */
2
2
  (function (global, factory) {
3
3
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
4
4
  typeof define === 'function' && define.amd ? define(factory) :
@@ -667,7 +667,8 @@
667
667
  }
668
668
  function getClosestActiveElement() {
669
669
  var _a;
670
- return ((_a = self.calendarContainer) === null || _a === void 0 ? void 0 : _a.getRootNode()).activeElement || document.activeElement;
670
+ return (((_a = self.calendarContainer) === null || _a === void 0 ? void 0 : _a.getRootNode())
671
+ .activeElement || document.activeElement);
671
672
  }
672
673
  function bindToInstance(fn) {
673
674
  return fn.bind(self);
@@ -715,17 +716,10 @@
715
716
  if (e !== undefined && e.type !== "blur") {
716
717
  timeWrapper(e);
717
718
  }
718
- var valueFromInput = self._input.value;
719
- var dateFromInput = self.parseDate(valueFromInput);
720
- var latestDate = self.latestSelectedDateObj;
721
- if (valueFromInput && latestDate && (dateFromInput === null || dateFromInput === void 0 ? void 0 : dateFromInput.getTime()) !== (latestDate === null || latestDate === void 0 ? void 0 : latestDate.getTime())) {
722
- setDate(dateFromInput);
723
- }
724
- else {
725
- setHoursFromInputs();
726
- }
719
+ var prevValue = self._input.value;
720
+ setHoursFromInputs();
727
721
  updateValue();
728
- if (self._input.value !== valueFromInput) {
722
+ if (self._input.value !== prevValue) {
729
723
  self._debouncedChange();
730
724
  }
731
725
  }
@@ -807,7 +801,7 @@
807
801
  */
808
802
  function setHoursFromDate(dateObj) {
809
803
  var date = dateObj || self.latestSelectedDateObj;
810
- if (date) {
804
+ if (date && date instanceof Date) {
811
805
  setHours(date.getHours(), date.getMinutes(), date.getSeconds());
812
806
  }
813
807
  }
@@ -1049,7 +1043,7 @@
1049
1043
  ? self.config.appendTo
1050
1044
  : window.document.body).appendChild(self.calendarContainer);
1051
1045
  }
1052
- function createDay(className, date, dayNumber, i) {
1046
+ function createDay(className, date, _dayNumber, i) {
1053
1047
  var dateIsEnabled = isEnabled(date, true), dayElement = createElement("span", className, date.getDate().toString());
1054
1048
  dayElement.dateObj = date;
1055
1049
  dayElement.$i = i;
@@ -1085,7 +1079,7 @@
1085
1079
  if (self.weekNumbers &&
1086
1080
  self.config.showMonths === 1 &&
1087
1081
  className !== "prevMonthDay" &&
1088
- dayNumber % 7 === 1) {
1082
+ i % 7 === 6) {
1089
1083
  self.weekNumbers.insertAdjacentHTML("beforeend", "<span class='flatpickr-day'>" + self.config.getWeek(date) + "</span>");
1090
1084
  }
1091
1085
  triggerEvent("onDayCreate", dayElement);
@@ -1561,19 +1555,15 @@
1561
1555
  e.path.indexOf &&
1562
1556
  (~e.path.indexOf(self.input) ||
1563
1557
  ~e.path.indexOf(self.altInput)));
1564
- var lostFocus = e.type === "blur"
1565
- ? isInput &&
1566
- e.relatedTarget &&
1567
- !isCalendarElem(e.relatedTarget)
1568
- : !isInput &&
1569
- !isCalendarElement &&
1570
- !isCalendarElem(e.relatedTarget);
1558
+ var lostFocus = !isInput &&
1559
+ !isCalendarElement &&
1560
+ !isCalendarElem(e.relatedTarget);
1571
1561
  var isIgnored = !self.config.ignoredFocusElements.some(function (elem) {
1572
1562
  return elem.contains(eventTarget_1);
1573
1563
  });
1574
1564
  if (lostFocus && isIgnored) {
1575
1565
  if (self.config.allowInput) {
1576
- self.setDate(self._input.value, true, self.config.altInput
1566
+ self.setDate(self._input.value, false, self.config.altInput
1577
1567
  ? self.config.altFormat
1578
1568
  : self.config.dateFormat);
1579
1569
  }
@@ -1668,8 +1658,9 @@
1668
1658
  }
1669
1659
  function onBlur(e) {
1670
1660
  var isInput = e.target === self._input;
1661
+ var valueChanged = self._input.value.trimEnd() !== getDateStr();
1671
1662
  if (isInput &&
1672
- (self.selectedDates.length > 0 || self._input.value.length > 0) &&
1663
+ valueChanged &&
1673
1664
  !(e.relatedTarget && isCalendarElem(e.relatedTarget))) {
1674
1665
  self.setDate(self._input.value, true, e.target === self.altInput
1675
1666
  ? self.config.altFormat
@@ -2516,7 +2507,9 @@
2516
2507
  }
2517
2508
  function isDateSelected(date) {
2518
2509
  for (var i = 0; i < self.selectedDates.length; i++) {
2519
- if (compareDates(self.selectedDates[i], date) === 0)
2510
+ var selectedDate = self.selectedDates[i];
2511
+ if (selectedDate instanceof Date &&
2512
+ compareDates(selectedDate, date) === 0)
2520
2513
  return "" + i;
2521
2514
  }
2522
2515
  return false;
@@ -2554,7 +2547,9 @@
2554
2547
  ? self.currentMonth + 1 > self.config.maxDate.getMonth()
2555
2548
  : self.currentYear > self.config.maxDate.getFullYear());
2556
2549
  }
2557
- function getDateStr(format) {
2550
+ function getDateStr(specificFormat) {
2551
+ var format = specificFormat ||
2552
+ (self.config.altInput ? self.config.altFormat : self.config.dateFormat);
2558
2553
  return self.selectedDates
2559
2554
  .map(function (dObj) { return self.formatDate(dObj, format); })
2560
2555
  .filter(function (d, i, arr) {
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flatpickr
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.6.10.0
4
+ version: 4.6.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zoran
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-04-01 00:00:00.000000000 Z
11
+ date: 2023-07-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -180,7 +180,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
180
180
  - !ruby/object:Gem::Version
181
181
  version: '0'
182
182
  requirements: []
183
- rubygems_version: 3.2.32
183
+ rubygems_version: 3.4.3
184
184
  signing_key:
185
185
  specification_version: 4
186
186
  summary: Flatpickr packaged for use in Rails projects.