calendarhelper.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. 'use strict';
  2. /**
  3. * @ngdoc service
  4. * @name angularBootstrapCalendarApp.calendarHelper
  5. * @description
  6. * # calendarHelper
  7. * Service in the angularBootstrapCalendarApp.
  8. */
  9. angular.module('mwl.calendar')
  10. .service('calendarHelper', function calendarHelper($filter, moment) {
  11. var self = this;
  12. function isISOWeekBasedOnLocale() {
  13. return moment().startOf('week').day() === 1;
  14. }
  15. function isISOWeek(userValue) {
  16. //If a manual override has been set in the directive, use that
  17. if (angular.isDefined(userValue)) return userValue;
  18. //Otherwise fallback to the locale
  19. return isISOWeekBasedOnLocale();
  20. }
  21. this.getMonthNames = function(short) {
  22. var format = short ? 'MMM' : 'MMMM';
  23. var months = [];
  24. for (var i = 0; i <= 11; i++) {
  25. months.push($filter('date')(new Date(2014, i), format));
  26. }
  27. return months;
  28. };
  29. this.getWeekDayNames = function(short, useISOWeek) {
  30. var format = short ? 'EEE' : 'EEEE';
  31. var weekdays = [];
  32. var startDay = isISOWeek(useISOWeek) ? 22 : 21;
  33. for (var i = 0; i <= 6; i++) {
  34. weekdays.push($filter('date')(new Date(2014, 8, startDay + i), format));
  35. }
  36. return weekdays;
  37. };
  38. this.eventIsInPeriod = function(eventStart, eventEnd, periodStart, periodEnd) {
  39. return (
  40. moment(eventStart).isAfter(moment(periodStart)) &&
  41. moment(eventStart).isBefore(moment(periodEnd))
  42. ) || (
  43. moment(eventEnd).isAfter(moment(periodStart)) &&
  44. moment(eventEnd).isBefore(moment(periodEnd))
  45. ) || (
  46. moment(eventStart).isBefore(moment(periodStart)) &&
  47. moment(eventEnd).isAfter(moment(periodEnd))
  48. ) || (
  49. moment(eventStart).isSame(moment(periodStart))
  50. ) || (
  51. moment(eventEnd).isSame(moment(periodEnd))
  52. );
  53. };
  54. this.getYearView = function(events, currentDay) {
  55. var grid = [];
  56. var months = self.getMonthNames();
  57. for (var i = 0; i < 3; i++) {
  58. var row = [];
  59. for (var j = 0; j < 4; j++) {
  60. var monthIndex = 12 - months.length;
  61. var startPeriod = new Date(moment(currentDay).format('YYYY'), monthIndex, 1);
  62. var endPeriod = moment(startPeriod).add(1, 'month').subtract(1, 'second').toDate();
  63. row.push({
  64. label: months.shift(),
  65. monthIndex: monthIndex,
  66. isToday: moment(startPeriod).startOf('month').isSame(moment().startOf('month')),
  67. events: events.filter(function(event) {
  68. return self.eventIsInPeriod(event.starts_at, event.ends_at, startPeriod, endPeriod);
  69. }),
  70. date: moment(startPeriod).startOf('month')
  71. });
  72. }
  73. grid.push(row);
  74. }
  75. return grid;
  76. };
  77. this.getMonthView = function(events, currentDay, useISOWeek) {
  78. var dateOffset = isISOWeek(useISOWeek) ? 1 : 0;
  79. function getWeekDayIndex() {
  80. var day = startOfMonth.day() - dateOffset;
  81. if (day < 0) day = 6;
  82. return day;
  83. }
  84. var startOfMonth = moment(currentDay).startOf('month');
  85. var numberOfDaysInMonth = moment(currentDay).endOf('month').date();
  86. var grid = [];
  87. var buildRow = new Array(7);
  88. var eventsWithIds = events.map(function(event, index) {
  89. event.$id = index;
  90. return event;
  91. });
  92. for (var i = 1; i <= numberOfDaysInMonth; i++) {
  93. if (i == 1) {
  94. var weekdayIndex = getWeekDayIndex(startOfMonth);
  95. var prefillMonth = startOfMonth.clone();
  96. while (weekdayIndex > 0) {
  97. weekdayIndex--;
  98. prefillMonth = prefillMonth.subtract(1, 'day');
  99. buildRow[weekdayIndex] = {
  100. label: prefillMonth.date(),
  101. date: prefillMonth.clone(),
  102. inMonth: false,
  103. events: []
  104. };
  105. }
  106. }
  107. buildRow[getWeekDayIndex(startOfMonth)] = {
  108. label: startOfMonth.date(),
  109. inMonth: true,
  110. isToday: moment().startOf('day').isSame(startOfMonth),
  111. date: startOfMonth.clone(),
  112. events: eventsWithIds.filter(function(event) {
  113. return self.eventIsInPeriod(event.starts_at, event.ends_at, startOfMonth.clone().startOf('day'), startOfMonth.clone().endOf('day'));
  114. })
  115. };
  116. if (i == numberOfDaysInMonth) {
  117. var weekdayIndex = getWeekDayIndex(startOfMonth);
  118. var postfillMonth = startOfMonth.clone();
  119. while (weekdayIndex < 6) {
  120. weekdayIndex++;
  121. postfillMonth = postfillMonth.add(1, 'day');
  122. buildRow[weekdayIndex] = {
  123. label: postfillMonth.date(),
  124. date: postfillMonth.clone(),
  125. inMonth: false,
  126. events: []
  127. };
  128. }
  129. }
  130. if (getWeekDayIndex(startOfMonth) === 6 || i == numberOfDaysInMonth) {
  131. grid.push(buildRow);
  132. buildRow = new Array(7);
  133. }
  134. startOfMonth = startOfMonth.add(1, 'day');
  135. }
  136. return grid;
  137. };
  138. this.getWeekView = function(events, currentDay, useISOWeek) {
  139. var dateOffset = isISOWeek(useISOWeek) ? 1 : 0;
  140. var columns = new Array(7);
  141. var weekDays = self.getWeekDayNames(false, useISOWeek);
  142. var currentWeekDayIndex = currentDay.getDay();
  143. var beginningOfWeek, endOfWeek;
  144. for (var i = currentWeekDayIndex; i >= 0; i--) {
  145. var date = moment(currentDay).subtract(currentWeekDayIndex - i, 'days').add(dateOffset, 'day').toDate();
  146. columns[i] = {
  147. weekDay: weekDays[i],
  148. day: $filter('date')(date, 'd'),
  149. date: $filter('date')(date, 'd MMM'),
  150. isToday: moment(date).startOf('day').isSame(moment().startOf('day'))
  151. };
  152. if (i == 0) {
  153. beginningOfWeek = date;
  154. } else if (i == 6) {
  155. endOfWeek = date;
  156. }
  157. }
  158. for (var i = currentWeekDayIndex + 1; i < 7; i++) {
  159. var date = moment(currentDay).add(i - currentWeekDayIndex, 'days').add(dateOffset, 'day').toDate();
  160. columns[i] = {
  161. weekDay: weekDays[i],
  162. day: $filter('date')(date, 'd'),
  163. date: $filter('date')(date, 'd MMM'),
  164. isToday: moment(date).startOf('day').isSame(moment().startOf('day'))
  165. };
  166. if (i == 0) {
  167. beginningOfWeek = date;
  168. } else if (i == 6) {
  169. endOfWeek = date;
  170. }
  171. }
  172. endOfWeek = moment(endOfWeek).endOf('day').toDate();
  173. beginningOfWeek = moment(beginningOfWeek).startOf('day').toDate();
  174. var eventsSorted = events.filter(function(event) {
  175. return self.eventIsInPeriod(event.starts_at, event.ends_at, beginningOfWeek, endOfWeek);
  176. }).map(function(event) {
  177. var eventStart = moment(event.starts_at).startOf('day');
  178. var eventEnd = moment(event.ends_at).startOf('day');
  179. var weekViewStart = moment(beginningOfWeek).startOf('day');
  180. var weekViewEnd = moment(endOfWeek).startOf('day');
  181. var offset, span;
  182. if (eventStart.isBefore(weekViewStart) || eventStart.isSame(weekViewStart)) {
  183. offset = 0;
  184. } else {
  185. offset = eventStart.diff(weekViewStart, 'days');
  186. }
  187. if (eventEnd.isAfter(weekViewEnd)) {
  188. eventEnd = weekViewEnd;
  189. }
  190. if (eventStart.isBefore(weekViewStart)) {
  191. eventStart = weekViewStart;
  192. }
  193. span = moment(eventEnd).diff(eventStart, 'days') + 1;
  194. event.daySpan = span;
  195. event.dayOffset = offset;
  196. return event;
  197. });
  198. return {columns: columns, events: eventsSorted};
  199. };
  200. this.getDayView = function(events, currentDay, dayStartHour, dayEndHour) {
  201. var calendarStart = moment(currentDay).startOf('day').add(dayStartHour, 'hours');
  202. var calendarEnd = moment(currentDay).startOf('day').add(dayEndHour, 'hours');
  203. var calendarHeight = (dayEndHour - dayStartHour + 1) * 60;
  204. var buckets = [];
  205. return events.filter(function(event) {
  206. return self.eventIsInPeriod(event.starts_at, event.ends_at, moment(currentDay).startOf('day').toDate(), moment(currentDay).endOf('day').toDate());
  207. }).map(function(event) {
  208. if (moment(event.starts_at).isBefore(calendarStart)) {
  209. event.top = 0;
  210. } else {
  211. event.top = moment(event.starts_at).startOf('minute').diff(calendarStart.startOf('minute'), 'minutes') - 2;
  212. }
  213. if (moment(event.ends_at).isAfter(calendarEnd)) {
  214. event.height = calendarHeight - event.top;
  215. } else {
  216. var diffStart = event.starts_at;
  217. if (moment(event.starts_at).isBefore(calendarStart)) {
  218. diffStart = calendarStart.toDate();
  219. }
  220. event.height = moment(event.ends_at).diff(diffStart, 'minutes');
  221. }
  222. if (event.top - event.height > calendarHeight) {
  223. event.height = 0;
  224. }
  225. event.left = 0;
  226. return event;
  227. }).filter(function(event) {
  228. return event.height > 0;
  229. }).map(function(event) {
  230. var cannotFitInABucket = true;
  231. buckets.forEach(function(bucket, bucketIndex) {
  232. var canFitInThisBucket = true;
  233. bucket.forEach(function(bucketItem) {
  234. if (self.eventIsInPeriod(event.starts_at, event.ends_at, bucketItem.starts_at, bucketItem.ends_at) || self.eventIsInPeriod(bucketItem.starts_at, bucketItem.ends_at, event.starts_at, event.ends_at)) {
  235. canFitInThisBucket = false;
  236. }
  237. });
  238. if (canFitInThisBucket && cannotFitInABucket) {
  239. cannotFitInABucket = false;
  240. event.left = bucketIndex * 150;
  241. buckets[bucketIndex].push(event);
  242. }
  243. });
  244. if (cannotFitInABucket) {
  245. event.left = buckets.length * 150;
  246. buckets.push([event]);
  247. }
  248. return event;
  249. });
  250. };
  251. this.toggleEventBreakdown = function(view, rowIndex, cellIndex) {
  252. var openEvents = [];
  253. function closeAllOpenItems() {
  254. view = view.map(function(row) {
  255. row.isOpened = false;
  256. return row.map(function(cell) {
  257. cell.isOpened = false;
  258. return cell;
  259. });
  260. });
  261. }
  262. if (view[rowIndex][cellIndex].events.length > 0) {
  263. var isCellOpened = view[rowIndex][cellIndex].isOpened;
  264. closeAllOpenItems();
  265. view[rowIndex][cellIndex].isOpened = !isCellOpened;
  266. view[rowIndex].isOpened = !isCellOpened;
  267. openEvents = view[rowIndex][cellIndex].events;
  268. } else {
  269. closeAllOpenItems();
  270. }
  271. return {view: view, openEvents: openEvents};
  272. };
  273. });