schedule.swig 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. <script{{ pjax }}>
  2. (function() {
  3. // Initialization
  4. var calendar = {
  5. orderBy : 'startTime',
  6. showLocation: false,
  7. offsetMax : 72,
  8. offsetMin : 4,
  9. showDeleted : false,
  10. singleEvents: true,
  11. maxResults : 250
  12. };
  13. // Read config form theme config file
  14. Object.assign(calendar, {{ theme.calendar | json }});
  15. var now = new Date();
  16. var timeMax = new Date();
  17. var timeMin = new Date();
  18. timeMax.setHours(now.getHours() + calendar.offsetMax);
  19. timeMin.setHours(now.getHours() - calendar.offsetMin);
  20. // Build URL
  21. const params = {
  22. key : calendar.api_key,
  23. orderBy : calendar.orderBy,
  24. timeMax : timeMax.toISOString(),
  25. timeMin : timeMin.toISOString(),
  26. showDeleted : calendar.showDeleted,
  27. singleEvents: calendar.singleEvents,
  28. maxResults : calendar.maxResults
  29. };
  30. var request_url = 'https://www.googleapis.com/calendar/v3/calendars/' + calendar.calendar_id + '/events?' + Object.entries(params).map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join('&');
  31. fetchData();
  32. var fetchDataTimer = setInterval(fetchData, 60000);
  33. window.addEventListener('pjax:send', () => {
  34. clearInterval(fetchDataTimer);
  35. });
  36. function fetchData() {
  37. var eventList = document.querySelector('.event-list');
  38. if (!eventList) return;
  39. fetch(request_url).then(response => {
  40. return response.json();
  41. }).then(data => {
  42. if (data.items.length === 0) {
  43. eventList.innerHTML = '<hr>';
  44. return;
  45. }
  46. // Clean the event list
  47. eventList.innerHTML = '';
  48. var prevEnd = 0; // used to decide where to insert an <hr>
  49. data.items.forEach(event => {
  50. // Parse data
  51. var utc = new Date().getTimezoneOffset() * 60000;
  52. var start = event.start.dateTime = new Date(event.start.dateTime || (new Date(event.start.date).getTime() + utc));
  53. var end = event.end.dateTime = new Date(event.end.dateTime || (new Date(event.end.date).getTime() + utc));
  54. tense = judgeTense(now, start, end); // 0:now 1:future -1:past
  55. if (tense === 1 && prevEnd < now) {
  56. eventList.innerHTML += '<hr>';
  57. }
  58. eventDOM = buildEventDOM(tense, event);
  59. eventList.innerHTML += eventDOM;
  60. prevEnd = end;
  61. });
  62. });
  63. }
  64. function getRelativeTime(current, previous) {
  65. var msPerMinute = 60 * 1000;
  66. var msPerHour = msPerMinute * 60;
  67. var msPerDay = msPerHour * 24;
  68. var msPerMonth = msPerDay * 30;
  69. var msPerYear = msPerDay * 365;
  70. var elapsed = current - previous;
  71. var tense = elapsed > 0 ? 'ago' : 'later';
  72. elapsed = Math.abs(elapsed);
  73. if ( elapsed < msPerHour ) {
  74. return Math.round(elapsed / msPerMinute) + ' minutes ' + tense;
  75. }
  76. else if ( elapsed < msPerDay ) {
  77. return Math.round(elapsed / msPerHour) + ' hours ' + tense;
  78. }
  79. else if ( elapsed < msPerMonth ) {
  80. return 'about ' + Math.round(elapsed / msPerDay) + ' days ' + tense;
  81. }
  82. else if ( elapsed < msPerYear ) {
  83. return 'about ' + Math.round(elapsed / msPerMonth) + ' months ' + tense;
  84. }
  85. else {
  86. return 'about' + Math.round(elapsed / msPerYear) + ' years' + tense;
  87. }
  88. }
  89. function judgeTense(now, eventStart, eventEnd) {
  90. if (eventEnd < now) { return -1; }
  91. else if (eventStart > now) { return 1; }
  92. else { return 0; }
  93. }
  94. function buildEventDOM(tense, event) {
  95. var tenseClass = '';
  96. var start = event.start.dateTime;
  97. var end = event.end.dateTime;
  98. switch(tense) {
  99. case 0 : // now
  100. tenseClass = 'event-now';
  101. break;
  102. case 1 : // future
  103. tenseClass = 'event-future';
  104. break;
  105. case -1: // past
  106. tenseClass = 'event-past';
  107. break;
  108. default:
  109. throw 'Time data error';
  110. }
  111. var durationFormat = {
  112. weekday: 'short',
  113. hour : '2-digit',
  114. minute : '2-digit'
  115. };
  116. var relativeTimeStr = (tense === 0) ? 'NOW' : getRelativeTime(now, start);
  117. var durationStr = start.toLocaleTimeString([], durationFormat) + ' - ' + end.toLocaleTimeString([], durationFormat);
  118. var locationDOM = '';
  119. if (calendar.showLocation && event.location) {
  120. locationDOM = '<span class="event-location event-details">' + event.location + '</span>';
  121. }
  122. var eventContent = `<div class="event ${tenseClass}">
  123. <h2 class="event-summary">
  124. ${event.summary}
  125. <span class="event-relative-time">${relativeTimeStr}</span>
  126. </h2>
  127. ${locationDOM}
  128. <span class="event-duration event-details">${durationStr}</span>
  129. </div>`;
  130. return eventContent;
  131. }
  132. })();
  133. </script>