metis/src/calendar.js

301 lines
7.1 KiB
JavaScript
Raw Normal View History

// https://stackoverflow.com/a/35970186
function invertColor(hex) {
if (hex.indexOf('#') === 0) {
hex = hex.slice(1)
}
// convert 3-digit hex to 6-digits.
if (hex.length === 3) {
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]
}
if (hex.length !== 6) {
throw new Error('Invalid HEX color.')
}
var r = parseInt(hex.slice(0, 2), 16),
g = parseInt(hex.slice(2, 4), 16),
b = parseInt(hex.slice(4, 6), 16)
// https://stackoverflow.com/a/3943023/112731
return r * 0.299 + g * 0.587 + b * 0.114 > 186 ? '#000000' : '#FFFFFF'
}
const clouds = {
2022-03-06 00:59:11 +01:00
KLUB_RESEAU: 'klub-reseau',
ELEVES_ENS: 'eleves-ens',
2022-07-23 00:59:27 +02:00
FRAMA_AGENDA: 'frama-agenda'
2022-03-06 00:59:11 +01:00
}
2022-02-20 22:29:15 +01:00
const calendars = {
2022-03-06 00:59:11 +01:00
'5WrcagPPARQ3BD87': {
cloud: clouds.KLUB_RESEAU,
2022-03-06 00:59:11 +01:00
name: 'Club réseau',
color: null,
default_location: "Cave d'hackENS"
2022-02-20 22:29:15 +01:00
},
2022-03-06 00:59:11 +01:00
TFEAKjAgNFQZpNjo: {
cloud: clouds.KLUB_RESEAU,
2022-03-06 00:59:11 +01:00
name: 'hackENS',
color: null,
default_location: "Cave d'hackENS"
},
2022-03-06 00:59:11 +01:00
LLWm8qK9iC5YGrrR: {
cloud: clouds.ELEVES_ENS,
2022-03-06 00:59:11 +01:00
name: 'Délégation Générale',
short_name: 'DG',
color: null
2022-03-06 15:21:46 +01:00
},
w442JdS5AaQ6czrP: {
cloud: clouds.ELEVES_ENS,
name: "Écriv'ENS",
2022-03-06 19:38:26 +01:00
color: null
2022-03-06 21:22:42 +01:00
},
fRtjDkjrZyn6fxd8: {
cloud: clouds.ELEVES_ENS,
2022-03-07 08:33:46 +01:00
name: 'K-Fêt',
2022-03-07 10:59:05 +01:00
color: '#c63b52',
default_location: 'K-Fêt'
},
gsZtZK8c9EmREofn: {
cloud: clouds.ELEVES_ENS,
2022-03-11 22:43:13 +01:00
name: 'Ernestophone',
color: null
},
2022-07-19 00:27:55 +02:00
dTHrXnYgsEoSTjWB: {
cloud: clouds.ELEVES_ENS,
2022-07-19 14:07:15 +02:00
name: 'Évènements (COF)',
short_name: 'COF',
color: null
},
bCgRFByHLiCCNc55: {
cloud: clouds.ELEVES_ENS,
2022-07-19 14:07:15 +02:00
name: 'Assemblées Générales (COF)',
short_name: 'AG COF',
color: null
},
r4yJZDHjwNtH8wkR: {
cloud: clouds.ELEVES_ENS,
name: 'BdA',
color: null
},
T5WoHbs4FT5A945Z: {
cloud: clouds.FRAMA_AGENDA,
name: 'CinéClub',
color: null
},
'6SHG6cg9d7S3qqwD': {
cloud: clouds.ELEVES_ENS,
name: 'Club Inutile ☔',
color: null,
initial: false
2022-07-23 00:58:16 +02:00
},
2022-07-25 23:35:38 +02:00
'Ekjb4kDqMMqwJXZF': {
2022-07-23 00:58:16 +02:00
cloud: clouds.ELEVES_ENS,
2022-07-25 23:35:38 +02:00
name: 'Rentrée des départements',
short_name: 'Dpt',
color: null
}
'8SKP62tQJP65K8EW': {
cloud: clouds.ELEVES_ENS,
name: 'Conférences de recherche',
short_name: 'Conf',
color: null
}
'PnRXqeq4SsSC33FM': {
cloud: clouds.ELEVES_ENS,
name: 'Visites de bibliothèques',
short_name: 'Bibli',
initial: false,
color: null
}
'NWPtiEiz62LTtjo2': {
cloud: clouds.ELEVES_ENS,
name: 'Amphis de rentrée',
short_name: 'Prés. de rentrée',
color: null
}
'JiRt58aJXay9kfyk': {
cloud: clouds.ELEVES_ENS,
name: 'Réunions de rentrée des Masters',
short_name: 'Masters',
color: null
}
'5Rb4bRjCDcsFjDdQ': {
cloud: clouds.ELEVES_ENS,
name: 'Activités pour les étudiants internationaux',
short_name: 'Internationaux',
color: null
}
'62wKfQRrLNz2WXjt': {
cloud: clouds.ELEVES_ENS,
name: 'Divers',
2022-07-23 00:58:16 +02:00
color: null
2022-03-06 00:59:11 +01:00
}
}
2022-02-20 22:29:15 +01:00
const calendarsByName = Object.fromEntries(
2022-03-06 19:38:26 +01:00
Object.entries(calendars).map(([id, { name }]) => [name, id])
)
export const initialCalendars = Array.from(
Object.entries(calendars).map(([_, { name, initial }]) => [name, initial ?? true])
)
.filter(cal => cal[1])
.map(cal => cal[0])
2022-02-20 22:29:15 +01:00
export const calendarTree = {
2022-07-19 14:07:15 +02:00
'COF': {
BdA: {},
2022-07-19 14:07:15 +02:00
'Évènements (COF)': {},
'Assemblées Générales (COF)': {}
},
2022-03-06 00:59:11 +01:00
'Clubs COF': {
'Club réseau': {},
2022-03-06 15:21:46 +01:00
hackENS: {},
"Écriv'ENS": {},
CinéClub: {},
2022-03-11 22:43:13 +01:00
Ernestophone: {},
'Club Inutile ☔': {}
},
2022-03-06 21:22:42 +01:00
'Délégation Générale': {},
'K-Fêt': {},
2022-07-25 23:35:38 +02:00
'Rentrée académique': {
'Amphis de rentrée': {},
'Rentrée des départements': {},
'Conférences de recherche': {},
'Visites de bibliothèques': {},
'Réunions de rentrée des Masters': {},
'Activités pour les étudiants internationaux': {}
},
'Divers': {}
2022-03-06 00:59:11 +01:00
}
2022-02-20 22:29:15 +01:00
export function getSubCalendars(name, tree = calendarTree) {
let ret
for (const [cal, subTree] of Object.entries(tree)) {
if (cal === name) {
ret = subTree
} else {
ret = ret || getSubCalendars(name, subTree)
}
}
return ret
}
2022-03-06 00:59:11 +01:00
const calendarIds = Object.keys(calendars)
2022-02-20 19:16:33 +01:00
2022-03-06 19:38:26 +01:00
function mkCalendarUrl(id, { cloud }, extra = {}) {
return (
`/cal/${cloud}/${id}/?` +
new URLSearchParams({
...extra,
export: true,
expand: true,
accept: 'jcal'
})
)
2022-02-20 19:16:33 +01:00
}
function mkExportUrl(id, { cloud }) {
return `/cal/${cloud}/${id}/?export`
}
2022-03-06 19:38:26 +01:00
function fetchCalendar(id, cal, extra = {}) {
return fetch(mkCalendarUrl(id, cal, extra), { credentials: 'omit' })
2022-03-06 00:59:11 +01:00
.then(resp => resp.json())
.catch(err => console.error(err))
2022-02-20 19:16:33 +01:00
}
class Calendar {
2022-03-06 00:59:11 +01:00
constructor(id, calendar) {
const metadata = calendars[id]
this.name = metadata.name
2022-03-06 13:47:05 +01:00
this.short_name = metadata.short_name
2022-03-06 00:59:11 +01:00
this.color = metadata.color || calendar[1][4][3]
2022-03-07 10:59:05 +01:00
this.default_location = metadata.default_location
2022-03-06 00:59:11 +01:00
this.events = calendar[2]
.filter(item => item[0] === 'vevent')
.map(item => this._parse_vevent(item[1]))
2022-02-20 19:16:33 +01:00
}
_parse_vevent(vevent) {
2022-03-06 00:59:11 +01:00
const event = {}
2022-02-20 19:16:33 +01:00
vevent.forEach(elt => {
2022-03-06 00:59:11 +01:00
event[elt[0]] = elt[3]
})
return event
2022-02-20 19:16:33 +01:00
}
}
2022-02-20 22:29:15 +01:00
function fcEventFromjCalEvent(cal) {
return function (evt) {
2022-03-06 00:59:11 +01:00
const start = new Date(evt.dtstart)
const end = new Date(evt.dtend)
2022-02-20 22:29:15 +01:00
const fcEvent = {
2022-03-06 13:47:05 +01:00
title: `${cal.short_name ?? cal.name} : ${evt.summary}`,
start: evt.dtstart,
end: evt.dtend,
2022-02-20 22:29:15 +01:00
color: cal.color,
textColor: invertColor(cal.color),
2022-02-20 22:29:15 +01:00
duration: end - start // in ms
2022-03-06 00:59:11 +01:00
}
2022-03-06 13:47:05 +01:00
fcEvent.calendar = cal.name
fcEvent.short_name = evt.summary
2022-03-07 10:59:05 +01:00
fcEvent.description = evt.description
fcEvent.location = evt.location || cal.default_location
2022-02-20 22:29:15 +01:00
if (evt.status) {
fcEvent.status = evt.status
fcEvent.classNames = [`st-${evt.status.toLowerCase()}`]
}
2022-02-20 22:29:15 +01:00
if (evt.rrule) {
2022-03-06 00:59:11 +01:00
const { freq, byday, interval } = evt.rrule
2022-02-20 22:29:15 +01:00
fcEvent.rrule = {
freq,
byweekday: byday,
2022-03-06 00:59:11 +01:00
dtstart: evt.dtstart
}
2022-02-21 22:13:44 +01:00
if (interval) {
2022-03-06 00:59:11 +01:00
fcEvent.rrule.interval = interval
2022-02-21 22:13:44 +01:00
}
2022-02-20 22:29:15 +01:00
}
2022-03-06 00:59:11 +01:00
return fcEvent
2022-02-20 22:29:15 +01:00
}
}
function mkEventsFromCalendar(id, cal) {
return fetchCalendar(id, cal).then(calendar => {
2022-03-06 00:59:11 +01:00
if (calendar[0] !== 'vcalendar') return
2022-02-20 22:29:15 +01:00
const cal = new Calendar(id, calendar)
return cal.events.map(fcEventFromjCalEvent(cal))
2022-03-06 00:59:11 +01:00
})
2022-02-20 19:16:33 +01:00
}
export function mkSource(name) {
2022-03-06 19:38:26 +01:00
const calendarId = calendarsByName[name]
if (!calendarId) return null
const calendar = calendars[calendarId]
2022-02-20 19:16:33 +01:00
return {
id: name,
...(calendar?.meta || {}),
success: calendarData => {
2022-03-06 19:38:26 +01:00
if (calendarData[0] !== 'vcalendar') return
const cal = new Calendar(calendarId, calendarData)
return cal.events.map(fcEventFromjCalEvent(cal))
},
failure: error => {
2022-03-06 19:38:26 +01:00
console.error(`Fatal error during event source fetching of '${name}': ${error}`)
},
events: (info, successCallback, failureCallback) => {
2022-03-06 19:38:26 +01:00
const { start, end } = info
fetchCalendar(calendarId, calendar, {
start: start.valueOf() / 1000,
end: end.valueOf() / 1000
2022-03-06 19:38:26 +01:00
}).then(successCallback, failureCallback)
},
export_url: mkExportUrl(calendarId, calendar)
2022-02-20 19:16:33 +01:00
}
}