PageSourceSearch

https://addisonadvantage.org/revize/plugins/revize_calendar/index.v2.js

js addisonadvantage.org collected 2026-09-24 10:08:59 UTC 51,873 bytes, 1,582 lines download raw bytes

1var VIEWS = {
2    'month': 'dayGridMonth',
3    'week': 'timeGridWeek',
4    'day': 'timeGridDay',
5    'list-month': 'listMonth',
6    'list-week': 'listWeek',
7    'list-day': 'listDay',
8    'list': 'listMonth'
9}
10
11var SHORT_MONTHS =  [
12    'JAN',
13    'FEB',
14    'MAR',
15    'APR',
16    'MAY',
17    'JUN',
18    'JUL',
19    'AUG',
20    'SEP',
21    'OCT',
22    'NOV',
23    'DEC'
24];
25
26var RZ_ISO_FORMAT = 'YYYY-MM-DDTHH:mm:ss';
27
28function buildRZCalendarProps() {
29	var baseCalendar = {
30		calendars: [],
31		calendarProps: (typeof calendarProps === 'object') ? calendarProps : {},
32		defaultTextColor: function(el) { return window.revizeCalendar.fns.getDefaultTextColor(el); },
33		eventDisplay: 'auto',
34		eventListLookBack: function(moment) { return moment.subtract(1, 'hour'); },
35		eventListLookAhead: function(moment) { return moment.add(60, 'days'); },
36		showLoading: (typeof calShowLoading === 'boolean') ? calShowLoading : true,
37		importData: [],
38		get pluginEntryPoint() { return window.revizeCalendar.fns.fullCalendarEntryPoint },
39		fns: {
40			full: {},
41			mini: {},
42			list: {},
43		},
44		hasDynamicSources: false,
45		editformPrefix: '\/revize\/plugins\/revize_calendar\/editforms',
46	}
47	if (typeof RZ === 'object') {
48		baseCalendar.importData = [{ url: false, webspace: RZ.webspace }];
49	}
50	if (typeof window.revizeCalendar === 'object') {
51		baseCalendar = $.extend(true, baseCalendar, window.revizeCalendar);
52	}
53	window.revizeCalendar = baseCalendar;
54}
55buildRZCalendarProps();
56
57window.revizeCalendar_proto = { fns: { full: {}, mini: {}, list: {} } };
58window.revizeCalendar_proto.fns.preInit = function() {
59	if (window.revizeCalendar.showLoading) {
60		$('[id^=mini-events][data-template-file], #calendar').each(function(i, el) {
61			$(this).append('<div class="progress-spinner-container" style="text-align:center;padding:15px;"><span class="progress-spinner"></span></div>');
62		});
63	}
64}
65
66// --------------------------------------- Data Retrieval Section --------------------------------------- //
67
68window.revizeCalendar_proto.fns.prepareImportData = async function() {
69	var importData = window.revizeCalendar.importData;
70	for (var i = 0; i < importData.length; i++) {
71		try {
72			importData[i].index = i;
73			if (importData[i].url === false) {
74				if (importData[i].hasOwnProperty('webspace')) {
75					importData[i].url = "./_assets_/plugins/revizeCalendar/calendar_data_handler.php?webspace=" + importData[i].webspace + "&relative_revize_url=" + RZ.protocolRelativeRevizeBaseUrl + "&protocol=" + window.location.protocol;
76
77					if (!importData[i].hasOwnProperty('parser')) { importData[i].parser = window.revizeCalendar.fns.defaultRevizeParser; }
78
79				} else if (importData[i].hasOwnProperty('gFeed')) {
80					if (!importData[i].hasOwnProperty('gKey')) { console.warn("[revizeCalendar.fns.prepareImportData] Google Feed import attempted without a key.", importData[i]); continue; }
81
82					var now = moment();
83
84					if (!importData[i].hasOwnProperty('spread')) { importData[i].spread = { months: 3 }; }
85					if (!importData[i].hasOwnProperty('parser')) { importData[i].parser = window.revizeCalendar.fns.defaultGoogleParser; }
86					if (!importData[i].hasOwnProperty('updater')) { importData[i].updater = window.revizeCalendar.fns.defaultGoogleUpdater; }
87
88					window.revizeCalendar.fns.defaultGoogleQueryBuilder(importData[i], now);
89					window.revizeCalendar.hasDynamicSources = true;
90				}
91			}
92			await window.revizeCalendar.fns.getImportData(importData[i]);
93		} catch (e) {
94			console.warn("[revizeCalendar.fns.prepareImportData] Failed to parse import props.", importData[i], e);
95		}
96	}
97}
98
99window.revizeCalendar_proto.fns.getImportData = async function(source, dynamic = false) {
100	if (source.hasOwnProperty("url")) {
101		$.ajax(
102			source.url,
103			{ importParams: source })
104			.fail(function(res) {
105				console.warn("[revizeCalendar.fns.getImportData] Error in ajax response.", this.importParams, res);
106				window.revizeCalendar.fns.updateImportData(this.importParams, res, res.status, dynamic);
107			})
108			.done(function(res, status, xhr) {
109				window.revizeCalendar.fns.updateImportData(this.importParams, res, xhr.status, dynamic);
110			});
111	} else {
112		window.revizeCalendar.fns.updateImportData(source, null, 200, dynamic);
113	}
114}
115
116window.revizeCalendar_proto.fns.updateImportData = function(importParams, res, status, dynamic = false) {
117	for (var i = 0; i < window.revizeCalendar.importData.length; i++) {
118		if (importParams.index == window.revizeCalendar.importData[i].index) {
119			window.revizeCalendar.importData[i].status = status;
120			if (status < 400) {
121				if (typeof window.revizeCalendar.importData[i].response === 'undefined') { window.revizeCalendar.importData[i].response = []; }
122				window.revizeCalendar.importData[i].response = window.revizeCalendar.importData[i].response.concat(window.revizeCalendar.importData[i].parser(res));
123			}
124			break;
125		}
126	}
127	if (!dynamic) { window.revizeCalendar.fns.calendarReadyCheck(); }
128}
129
130window.revizeCalendar_proto.fns.getImportedSources = function() {
131	var sources = [];
132	window.revizeCalendar.importData.forEach(function(source, i) {
133		if (source.status < 400) {
134			if (!Array.isArray(window.revizeCalendar.importData[i].response)) {
135				console.warn("[revizeCalendar.fns.getImportedEvents] Import data response is not well formatted.", window.revizeCalendar.importData[i]);
136				return;
137			}
138			sources.push({ index: i });
139		}
140	});
141	return sources;
142}
143
144window.revizeCalendar_proto.fns.getImportedEvents = function(flatten = false) {
145	var eventsArray = [];
146	window.revizeCalendar.importData.forEach(function(source, i) {
147		if (source.status < 400) {
148			if (!Array.isArray(source.response)) {
149				console.warn("[revizeCalendar.fns.getImportedEvents] Import data response is not well formatted.", source);
150				return;
151			}
152			if (flatten) {
153				eventsArray = eventsArray.concat(source.response);
154			} else {
155				eventsArray.push(source.response);
156			}
157		}
158	});
159	return $.extend(true, [], eventsArray);
160}
161
162window.revizeCalendar_proto.fns.calendarReadyCheck = function() {
163	for (var i = 0; i < window.revizeCalendar.importData.length; i++) {
164		if (!window.revizeCalendar.importData[i].hasOwnProperty('status')) { return; }
165	}
166	window.revizeCalendar.pluginEntryPoint();
167}
168
169window.revizeCalendar_proto.fns.defaultRevizeParser = function(data) {
170	data.forEach(function(ev, i) {
171		data[i].color = window.revizeCalendar.fns.getEventColor(ev);
172	});
173
174	return data;
175}
176
177window.revizeCalendar_proto.fns.defaultGoogleParser = function(data, calendarFilter) {
178	if (!data.hasOwnProperty('items')) { return; }
179	var events = [];
180	data.items.forEach(function(ev, i) {
181		var event = {
182			allDay: false,
183			calendar_displays: ['-1'],
184			title: "",
185			desc: "",
186			location: "",
187			image: "",
188			start: moment(),
189			gid: "",
190		};
191		if (ev.hasOwnProperty('start')) {
192			if (ev.start.hasOwnProperty('dateTime')) {
193				var t = moment(ev.start.dateTime);
194				event.start = t.format(RZ_ISO_FORMAT);
195			} else {
196				event.allDay = true;
197				var t = moment(ev.start.date);
198				event.start = t.format(RZ_ISO_FORMAT);
199			}
200		}
201		if (ev.hasOwnProperty('end')) {
202			if (ev.end.hasOwnProperty('dateTime')) {
203				var t = moment(ev.end.dateTime);
204				event.end = t.format(RZ_ISO_FORMAT);
205			} else {
206				var t = moment(ev.end.date);
207				event.end = t.format(RZ_ISO_FORMAT);
208			}
209		}
210		if (ev.hasOwnProperty('summary')) {
211			event.title = ev.summary;
212		}
213		if (ev.hasOwnProperty('description')) {
214			event.desc = ev.description;
215		}
216		if (ev.hasOwnProperty('htmlLink')) {
217			event.url = ev.htmlLink;
218		}
219		if (ev.hasOwnProperty('location')) {
220			event.location = ev.location;
221		}
222		if (ev.hasOwnProperty('id')) {
223			event.gid = ev.id;
224		}
225		if (typeof calendarFilter !== 'undefined') {
226			if (typeof calendarFilter === 'string' || typeof calendarFilter === 'number') {
227				event.calendar_displays = [calendarFilter.toString()];
228			} else if (typeof calendarFilter === 'function') {
229				event.calendar_displays = [calendarFilter(ev)];
230			}
231		}
232		events.push(event);
233
234	});
235	return events;
236}
237
238window.revizeCalendar_proto.fns.defaultGoogleQueryBuilder = function(source, date) {
239
240	var pastDate;
241	var futureDate;
242
243	if (moment.isMoment(date)) {
244		pastDate = date.clone().subtract(source.spread);
245		futureDate = date.clone().add(source.spread);
246	} else {
247		pastDate = date.start;
248		futureDate = date.end;
249	}
250
251	source.url = "https://www.googleapis.com/calendar/v3/calendars/"
252	+ source.gFeed
253	+ "/events?key="
254	+ source.gKey
255	+ "&timeMin=" + pastDate.format('YYYY-MM-DD') + "T00%3A00%3A00-00%3A00"
256	+ "&timeMax=" + futureDate.format('YYYY-MM-DD') + "T00%3A00%3A00-04%3A00&singleEvents=true&maxResults=2500";
257
258	if (typeof source.dynamicSets === 'undefined') { source.dynamicSets = []; }
259	source.dynamicSets.push(
260		{
261			start: pastDate,
262			end: futureDate,
263		}
264	);
265}
266
267window.revizeCalendar_proto.fns.defaultGoogleUpdater = async function(source, date) {
268	window.revizeCalendar.fns.defaultGoogleQueryBuilder(source, date);
269	await window.revizeCalendar.fns.getImportData(source, true);
270	var UIDs = [];
271	source.response = source.response.filter(function(ev) {
272		if (UIDs.indexOf(ev.gid) == -1) {
273			UIDs.push(ev.gid);
274			return true;
275		} else {
276			return false;
277		}
278	});
279}
280
281// --------------------------------------- Calendar Plugin Section --------------------------------------- //
282
283window.revizeCalendar_proto.fns.full.clickHandler = function(data) {
284	var eInfo = {
285		title: data.event.title,
286		desc: data.event.extendedProps.desc,
287		calendar_displays: data.event.extendedProps.calendar_displays,
288
289		start: moment(data.event.start),
290		end: moment(0),
291		allDay: data.event.allDay,
292		rrule: ((typeof data.event._def.recurringDef === 'object' && data.event._def.recurringDef !== null) ? data.event._def.recurringDef.typeData.rruleSet.toString() : false),
293
294		url: data.event.url,
295		location: data.event.extendedProps.location,
296		image: data.event.extendedProps.image,
297
298		options: data.event.extendedProps.options,
299
300		rid: data.event.extendedProps.rid ?? false,
301		id: data.event.id ?? false,
302	};
303
304	if (eInfo.allDay !== true) {
305		eInfo.end = moment(data.event.end);
306	}
307
308	window.revizeCalendar.fns.showModal(eInfo);
309}
310
311window.revizeCalendar_proto.fns.full.eventMountHandler = function(info) {
312	info.el.removeAttribute('href');
313	info.el.style.borderColor = window.revizeCalendar.fns.getEventC
313olor(info.event); // Event definition color can't be trusted
314	if (info.el.classList.contains("fc-daygrid-block-event")) {
315		info.el.style.backgroundColor = window.revizeCalendar.fns.getEventColor(info.event);
316		info.el.classList.add(window.revizeCalendar.fns.getContrastYIQ(window.revizeCalendar.fns.getEventColor(info.event)));
317	} else {
318		info.el.classList.add(window.revizeCalendar.defaultTextColor(info.el));
319	}
320	window.revizeCalendar.fns.tagCalendarDay(info.el);
321}
322
323window.revizeCalendar_proto.fns.full.datesSetHandler = function(plugin, info) {
324	window.revizeCalendar.fns.mini.datesSetHandler(plugin, info);
325
326	var start = info.view.currentStart;
327	var day = start.getDate().toString();
328	var month = (start.getMonth() + 1 ).toString();
329	var year = start.getFullYear();
330
331	day = day.length < 2 ? '0' + day : day;
332	month = month.length < 2 ? '0' + month : month;
333
334	var viewName = info.view.type;
335	var viewSets = (typeof VIEWS == "object" ? Object.entries(VIEWS) : []);
336	for (var i = 0; i < viewSets.length; i++) {
337		if (viewSets[i].indexOf(info.view.type) > -1) {
338			viewName = viewSets[i][0];
339		}
340	}
341
342	window.revizeCalendar.fns.changeUrlQuery([{
343		prop: 'view',
344		val: viewName
345	}, {
346		prop: 'month',
347		val: month
348	}, {
349		prop: 'day',
350		val: day
351	}, {
352		prop: 'year',
353		val: year
354	}]);
355}
356
357window.revizeCalendar_proto.fns.full.calendarProps = function() {
358
359	var oldQuery = $.query.parseNew(location.search, location.hash.split("?").length > 1 ? location.hash.split("?")[1] : "");
360    if(oldQuery.get('id')){
361        window.revizeCalendar.fns.changeUrlQuery('calendar');
362	}
363    var calendarParam = oldQuery.get('calendar').toString();
364    var searchParam = oldQuery.get('query');
365    var activeSearch = typeof searchParam === 'string' && searchParam.length ? searchParam : "";
366    var yearParam = oldQuery.get('year').toString();
367    var monthParam = oldQuery.get('month').toString();
368    var dayParam = oldQuery.get('day').toString();
369    var viewParam = oldQuery.get('view').toString().toLowerCase();
370    var idParam = oldQuery.get('id');
371    idParam = typeof idParam === 'string' && idParam.length || typeof idParam === 'number' ? idParam.toString() : '';
372    var dt = new  Date();
373    var filterNum = 0;
374
375
376    var year = ""
377    var month = "";
378    var day = "";
379    var defaultView = undefined;
380
381    if(typeof viewParam === 'string' && viewParam.length){
382        if(viewParam === 'day'){
383            defaultView = 'timeGridDay';
384        } else if(viewParam === 'week'){
385            defaultView = 'timeGridWeek';
386        } else if(viewParam === 'list'){
387            defaultView = 'listMonth';
388        } else {
389            defaultView = 'dayGridMonth';
390        }
391    }
392
393
394    // Get Year
395    year = typeof yearParam === 'string' && yearParam.length === 4 ? yearParam : dt.getFullYear();
396
397    // Get Month
398    month = typeof month === 'string' && monthParam.length && month.length < 3 ? monthParam : (dt.getMonth() + 1).toString();
399    month = month.length < 2 ? '0' + month : month;
400
401    // Get Day
402    day = typeof dayParam === 'string' && dayParam.length && dayParam.length < 3 ? dayParam : (dt.getDate()).toString();
403    day = day.length < 2 ? '0' + day : day;
404
405    var defaultDate = year + '-' + month + '-' + day;
406
407    if(activeSearch !== ""){
408        $('#calendar-search').val(activeSearch);
409    }
410
411    if(calendarParam.length && $('[data-calendar-id='+calendarParam+']').length){
412        activeCalendar = calendarParam;
413        $('[data-calendar-id='+calendarParam+']').parent().addClass('calendar-active');
414    }
415
416	return {
417		eventStartEditable: false,
418		plugins: FullCalendar.globalPlugins,
419		initialDate: defaultDate,
420		initialView: defaultView,
421		lazyFetching: true,
422		headerToolbar: {
423			left: 'prev,next,today dayGridMonth,timeGridWeek,timeGridDay,listMonth',
424			center: '',
425			right: 'title'
426		},
427		navLinks: true, // can click day/week names to navigate views
428		editable: true,
429		dayMaxEventRows: true,
430		eventSources: [],
431		eventDisplay: window.revizeCalendar.eventDisplay,
432		eventDidMount: function(info) {
433			window.revizeCalendar.fns.full.eventMountHandler(info);
434		},
435		eventClick: function(e){
436			e.jsEvent.preventDefault();
437			window.revizeCalendar.fns.full.clickHandler(e);
438			return;
439		},
440		datesSet: function(info){
441			window.revizeCalendar.fns.full.datesSetHandler(this, info);
442		}
443	};
444}
445
446window.revizeCalendar_proto.fns.mini.clickHandler = function(data) {
447	window.revizeCalendar.fns.full.clickHandler(data);
448}
449
450window.revizeCalendar_proto.fns.mini.eventMountHandler = function(info) {
451	window.revizeCalendar.fns.full.eventMountHandler(info);
452}
453
454window.revizeCalendar_proto.fns.mini.datesSetHandler = function(plugin, info) {
455	if (window.revizeCalendar.hasDynamicSources) {
456		var start = moment(info.view.currentStart);
457		var end = moment(info.view.currentEnd);
458
459		var fullCal = plugin;
460
461		window.revizeCalendar.importData.forEach(async function(source, i) {
462			if (source.hasOwnProperty('dynamicSets')) {
463				var update = true;
464				var closestStart = start.clone().subtract(source.spread);
465				var closestEnd = start.clone().add(source.spread);
466
467				source.dynamicSets.forEach(function(set) {
468					if (start.isAfter(set.start) && end.isBefore(set.end)) {
469						update = false;
470						return;
471					} else {
472						if (closestStart.isBetween(set.start, set.end) && closestEnd.isAfter(set.end)) {
473							closestStart = moment.min(closestStart, set.end);
474						}
475						if (closestStart.isBefore(set.start) && closestEnd.isBetween(set.start, set.end)) {
476							closestEnd = moment.max(closestEnd, set.start);
477						}
478					}
479				});
480				if (update && closestEnd.isAfter(closestStart)) {
481					await source.updater(source, { start: closestStart, end: closestEnd });
482					fullCal.refetchEvents();
483				}
484			}
485		});
486	}
487}
488
489window.revizeCalendar_proto.fns.mini.calendarProps = function() {
490	return {
491		eventStartEditable: false,
492		height: 'auto',
493		plugins: FullCalendar.globalPlugins,
494		headerToolbar: {
495			left: 'prev,next today',
496			center: '',
497			right: 'title'
498		},
499		navLinks: true, // can click day/week names to navigate views
500		editable: false,
501		initialView: 'dayGridMonth',
502		eventSources: [],
503		eventDisplay: window.revizeCalendar.eventDisplay,
504		eventDidMount: function(info) {
505			window.revizeCalendar.fns.mini.eventMountHandler(info);
506		},
507		eventClick: function(e){
508			e.jsEvent.preventDefault();
509			window.revizeCalendar.fns.mini.clickHandler(e);
510			return;
511		},
512		datesSet: function(info){
513			window.revizeCalendar.fns.mini.datesSetHandler(this, info);
514		}
515	};
516}
517
518window.revizeCalendar_proto.fns.full.eventsFilter = function(eventSource) {
519
520	var activeCalendar = $.query.get('calendar').toString();
521	var activeSearch = $.query.get('query').toString().toLowerCase();
522
523	function filterSource(source) {
524		return source.filter(function(ev) {
525			if (activeCalendar !== "" && ev.calendar_displays.indexOf(activeCalendar) === -1) {
526				return false;
527			}
528			if (activeSearch !== "" && ev.title.toLowerCase().indexOf(activeSearch) === -1) {
529				return false;
530			}
531			if (!ev.hasOwnProperty('calendar_displays') || ev.calendar_displays.length === 0) {
532				console.warn("[revizeCalendar.fns.full.eventsFilter] Event does not have an assigned calendar.", ev);
533				return true;
534			}
535			for (var i = 0; i < ev.calendar_displays.length; i++) {
536				if (ACTIVE_CALENDAR_IDS.indexOf('|'+ev.calendar_displays[i]+'|') >= 0) {
537					return true;
538				}
539			}
540
541			return false;
542		});
543	}
544
545	if (Array.isArray(eventSource[0])) {
546		eventSource.forEach(function(es, i) {
547			eventSource[i] = filterSource(es);
548		});
549	} else {
550		eventSource = filterSource(eventSource);
551	}
552	return eventSource;
553}
554
555window.revizeCalendar_proto.fns.mini.eventsFilter = function(eventSource, el) {
556	if (!eventSource.length) { return eventSource; }
557	var activeMiniCalendars = el.dataset.hasOwnProperty('calendar') ? el.dataset['calendar'].split(",") : [];
558
559	var includeAll = true;
560	var includeList = [];
561	var ignoreList = []; // Exclude event if is primary calendar
562	var excludeList = []; // Exclude event if on ANY of these calendars
563	for (var c = 0; c < activeMiniCalendars.length; c++) {
564		var cID = parseInt(activeMiniCalendars[c]);
565		if (isNaN(cID)) {
566			excludeList.push(parseInt(activeMiniCalendars[c].replace(/[^0-9]/g, "")));
567		} else if (cID < 0) {
568			ignoreList.push(cID * -1);
569		} else {
570			includeList.push(cID);
571			includeAll = false;
572		}
573	}
574
575	function filterSource(source) {
576		return source.filter(function(ev) {
577			if (typeof ev !== 'object' || !ev.hasOwnProperty('calendar_displays') || ev.calendar_displays.length === 0) {
578				console.warn("[revizeCalendar.fns.mini.eventsFilter] Event does not have an assigned calendar.", ev, source);
579				return false;
580			}
581			var includeFlag = includeAll;
582			for (var i = 0; i < ev.calendar_displays.length; i++) {
583				var cID = parseInt(ev.calendar_displays[i]);
584				if (i == 0) {
585					if (ignoreList.indexOf(cID) > -1) {
586						return false;
587					}
588				}
589
590				if (includeList.indexOf(cID) > -1) {
591					includeFlag = true;
592				}
593
594				if (excludeList.indexOf(cID) > -1) {
595					return false;
596				}
597			}
598			return includeFlag;
599		});
600	}
601
602	if (Array.isArray(eventSource[0])) {
603		eventSource.forEach(function(es) {
604			es = filterSource(es);
605		});
606	} else {
607		eventSource = filterSource(eventSource);
608	}
609
610	return eventSource;
611}
612
613window.revizeCalendar_proto.fns.list.eventsFilter = function(events, el) {
614	events = window.revizeCalendar.fns.mini.eventsFilter(events, el);
615
616	var rruledEvents = events.filter(function(ev) {
617		if (ev.hasOwnProperty('rrule')) {
618			return true;
619		}
620		return false;
621	});
622
623	var now = moment().toDate();
624	var lookAhead = window.revizeCalendar.eventListLookAhead(moment()).toDate();
625
626	try {
627		rruledEvents.forEach(function(ev, ei) {
628			var rule = new rrule.rrulestr(ev.rrule, { forceset: true });
629
630			var dstart = moment(ev.start);
631			var dend = (ev.hasOwnProperty('end') ? moment(ev.end) : undefined);
632
633			var dur = undefined;
634			if (dend) {
635				dur = moment.duration(dend.diff(dstart));
636			}
637
638			var UTCfiedDate = dstart.toDate();
639			UTCfiedDate.setMinutes(UTCfiedDate.getMinutes() - UTCfiedDate.getTimezoneOffset()); // RRule library recognizes that browser Date sucks and tries to double offset inputted dates.
640
641			var occurences = rule.between(UTCfiedDate, lookAhead, false);
642			occurences.forEach(function(oc, oi) {
643				var recurredEvent = $.extend({}, ev);
644				oc = oc.setMinutes(oc.getMinutes() + oc.getTimezoneOffset()); // RRule library recognizes that browser Date sucks and suggests doing this.
645				var rstart = moment(oc);
646				//rstart.set({ hours: dstart.hours(), minutes: dstart.minutes(), seconds: dstart.seconds});
647				var rend = dend;
648				if (rend) {
649					rend = moment(rstart);
650					rend.add(dur);
651				}
652
653				recurredEvent.start = rstart;
654				recurredEvent.end = rend;
655
656				events.push(recurredEvent);
657			});
658		});
659	} catch (e) {
660		console.warn("[revizeCalendar.fns.mini.eventsFilter] RRule expansion has encountered an error", e);
661	}
662
663	var thisMorning = moment().startOf('day');
664	var todayLookBack = moment();
665	if (window.revizeCalendar.eventListLookBack == 'morning') {
666		todayLookBack = todayLookBack.startOf('day');
667	} else if (typeof window.revizeCalendar.eventListLookBack == 'function') {
668		try {
669			todayLookBack = window.revizeCalendar.eventListLookBack(todayLookBack);
670		} catch (e) {
671			console.warn("[revizeCalendar.fns.mini.eventsFilter] Supplied Look Back function encountered an error", e);
672		}
673	}
674	events = events.filter(function(ev) {
675		if (typeof ev.start === 'string' && ev.start.length > 0) { ev.start = moment(ev.start); }
676		if (typeof ev.end === 'string' && ev.end.length > 0) { ev.end = moment(ev.end); }
677		if (ev.allDay) {
678			if (ev.end !== undefined) {
679				if (moment(ev.end).isSameOrAfter(thisMorning)) { return true; }
680			} else {
681				if (moment(ev.start).isSameOrAfter(thisMorning)) { return true; }
682			}
683		} else {
684			if (moment(ev.end).isSameOrAfter(todayLookBack)) { return true; }
685		}
686		return false
687	})
688	events = events.sort(function(a, b) { return a.start.valueOf() - b.start.valueOf(); });
689
690	return events;
691}
692
693window.revizeCalendar_proto.fns.full.build = function() {
694	$("#calendar").each(function(i, el) {
695		var fCalendarProps = window.revizeCalendar.fns.full.calendarProps();
696
697		var eventSources = window.revizeCalendar.fns.getImportedSources();
698		eventSources.forEach(function(source) {
699			source.events = function(info, succ, fail, a) {
700				var evs = window.revizeCalendar.fns.full.eventsFilter(
701					window.revizeCalendar.importData[source.index].response
702				);
703				succ(evs);
704			}
705		});
706
707		fCalendarProps.eventSources = eventSources;
708
709		var calendar = new FullCalendar.Calendar(el, fCalendarProps);
710		calendar.render();
711
712		window.revizeCalendar.calendars.push(calendar);
713		el.classList.add("fc-" + FullCalendar.version.replaceAll(".", "-"));
714		window.revizeCalendar.fns.full.postRender(el, calendar);
715	});
716}
717
718window.revizeCalendar_proto.fns.mini.build = function() {
719	var mCalendarProps = window.revizeCalendar.fns.mini.calendarProps();
720	$("[id^=mini-events][data-template-file]").each(function(i, el) {
721
722		if (el.dataset.hasOwnProperty('templateFile')) {
723			$.ajax('./_assets_/plugins/revizeCalendar/templates/'+el.dataset['templateFile'])
724				.fail(function() {
725					console.warn("[revizeCalendar.fns.mini.calendarProps] Failed to get template for mini calendar.", el);
726				}).done(function(data) {
727					$(el).html(data);
728
729					var templateOptions = null;
730					if (el.dataset['templateOptions']) {
731						try {
732							templateOptions = JSON.parse(el.dataset['templateOptions']);
733							if (templateOptions.hasOwnProperty("calendar")) {
734								$(el).find("[data-calendar]").attr("data-calendar", templateOptions.calendar);
735							}
736
737							if (templateOptions.hasOwnProperty("list-length")) {
738								$(el).find("[data-list-length]").attr("data-list-length", templateOptions["list-length"]);
739							}
740						} catch (e) {
741							console.warn(e);
742						}
743					}
744
745					$(el).find(".mini-calendar").each(function(ci, cal) {
746						var eventSources = window.revizeCalendar.fns.getImportedSources();
747						eventSources.forEach(function(source) {
748							source.events = function(info, succ, fail, a) {
749								var evs = window.revizeCalendar.fns.mini.eventsFilter(
750									window.revizeCalendar.importData[source.index].response,
751									cal
752								);
753								succ(evs);
754							}
755						});
756
757						var calendarView = (cal.dataset.hasOwnProperty('calendarView') ? cal.dataset['calendarView'] : 'month');
758						if(VIEWS.hasOwnProperty(calendarView)){
759							calendarView = VIEWS[calendarView];
760						} else {
761							calendarView = VIEWS['month'];
762						}
763
764						mCalendarProps.initialView = calendarView;
765
766						var foundViewButton = false;
767						for (key in mCalendarProps.headerToolbar) {
768							if (mCalendarProps.headerToolbar[key].indexOf(calendarView) >= 0) {
769								foundViewButton = true;
770							}
771						}
772						if (!foundViewButton) {
773							mCalendarProps.headerToolbar.left = mCalendarProps.headerToolbar.left + " " + calendarView;
774						}
775
776						mCalendarProps.eventSources = eventSources;
777
778						var calendar = new FullCalendar.Calendar(cal, mCalendarProps);
779						calendar.render();
780
781						window.revizeCalendar.calendars.push(calendar);
782						cal.classList.add("fc-" + FullCalendar.version.replaceAll(".", "-"));
783						window.revizeCalendar.fns.mini.postRender(cal, calendar);
784
785					});
786					$(el).find(".mini-events-list").each(function(ei, list) {
787						var jsonEvents = window.revizeCalendar.fns.getImportedEvents(true);
788
789						var listLen = list.dataset.hasOwnProperty('listLength') ? parseInt(list.dataset['listLength']) : 3;
790						var descLimit = list.dataset.hasOwnProperty('characterLimit') ? parseInt(list.dataset['characterLimit']) : 120;
791						var eventSources = window.revizeCalendar.fns.list.eventsFilter(jsonEvents, list)
792							.slice(0, listLen);
793
794						$(list).html(window.revizeCalendar.fns.list.dottedTemplate(list, eventSources, { descLimit: descLimit }, templateOptions ));
795					});
796				});
797		}
798	});
799}
800
801window.revizeCalendar_proto.fns.refreshCalendars = function() {
802	window.revizeCalendar.calendars.forEach(function(cal) {
803		cal.getEventSources().forEach(function(source) {
804			source.refetch();
805		});
806	});
807}
808
809window.revizeCalendar_proto.fns.full.postRender = function(el, calendar) {
810	var exportButton = document.createElement("button");
811	exportButton.id = "ics-view-export";
812	exportButton.className = "btn btn-primary";
813	exportButton.innerText = "Export Calendar";
814	exportButton.addEventListener("click", function() {
815		window.revizeCalendar.fns.exportCalendar(calendar);
816	});
817
818	el.append(exportButton);
819
820}
821window.revizeCalendar_proto.fns.mini.postRender = function(el, calendar) {}
822window.revizeCalendar_proto.fns.list.postRender = function(el) {}
823
824window.revizeCalendar_proto.fns.list.dottedTemplate = function(el, eventSources, options = {}, templateOptions = null) {
825	options = $.extend({ descLimit: 120 }, options);
826	var template = doT.template(el.innerHTML);
827
828
829	var eventData = eventSources.map(function(ev, ei) {
830		var evStart = ev.start;
831		var evEnd = ev.end;
832		var desc = '';
833		var calendarName = ev.primary_calendar_name;
834		calendarName = calendarName == 'undefined' || calendarName === '' || calendarName === ' ' ? false : calendarName;
835		var location = ev.location;
836
837		// Parse Description
838		desc = window.revizeCalendar.fns.decodeEventDescription(ev.desc, ev);
839
840		var strippedDesc = desc.replace(/<\/?br ?\/?>/gi,"&lt;br&gt;").replace(/(<([^>]+)>)/gi," ").replace(/\s\s/gi," ").replace(/&lt;br&gt;/gi,"<br>").replace(/href=\"\.\.\//g,"href=\"");
841		if (strippedDesc.length > options.descLimit) {
842			var nextBreak = strippedDesc.indexOf(" ", options.descLimit);
843			if (nextBreak > -1) {
844				strippedDesc = strippedDesc.substr(0, nextBreak) + "...";
845			}
846		}
847
848		var evImage = "";
849		if (ev.hasOwnProperty("image") && ev.image.indexOf('noimage.gif') < 0) {
850			evImage = decodeURIComponent(ev.image);
851		}
852
853		var templateEvent = {
854			extended: ev,
855			title: ev.title,
856			desc: strippedDesc,
857			image: evImage,
858			color: window.revizeCalendar.fns.getEventColor(ev),
859			loaded: 'mini-list-loaded',
860			i: ei,
861			calendar_name: calendarName,
862			location: location,
863			start: {
864				date: evStart,
865				month:{
866					num: evStart.format('M'),
867					numPadded: evStart.format('MM'),
868					ord: evStart.format('Mo'),
869					short: evStart.format('MMM'),
870					long: evStart.format('MMMM')
871				},
872				day: {
873					num: evStart.format('D'),
874					ord: evStart.format('Do'),
875					numPadded: evStart.format('DD')
876				},
877				year: {
878					short: evStart.format('YY'),
879					long: evStart.format('YYYY')
880				}
881			},
882			end: {
883				date: undefined,
884				hidden: ev.hide_end_date === 'true' || (typeof ev.options === "string" && ev.options.indexOf("\"hide_end_date\":true") >= 0),
885			}
886		};
887
888		if (ev.allDay !== true) {
889			templateEvent.end = {
890				date: evEnd,
891				hidden: ev.hide_end_date === 'true' || (typeof ev.options === "string" && ev.options.indexOf("\"hide_end_date\":true") >= 0),
892				month:{
893					num: evEnd.format('M'),
894					numPadded: evEnd.format('MM'),
895					ord: evEnd.format('Mo'),
896					short: evEnd.format('MMM'),
897					long: evEnd.format('MMMM')
898				},
899				day: {
900					num: evEnd.format('D'),
901					ord: evEnd.format('Do'),
902					numPadded: evEnd.format('DD')
903				},
904				year: {
905					short: evEnd.format('YY'),
906					long: evEnd.format('YYYY')
907				}
908			};
909		}
910
911		return templateEvent;
912	});
913
914	var calendarData = eventData;
915	if (templateOptions != null) {
916		calendarData = {
917			events: calendarData,
918			options: templateOptions,
919		};
920	}
921
922	$(el).html(template(calendarData));
923
924	window.revizeCalendar.fns.list.postRender(el);
925	$(el).find('[data-click-event][data-index]').each(function(i, eventElem) {
926		var eventIndex = parseInt(eventElem.dataset['index']);
927		if (isNaN(eventIndex)) {
928			console.warn("[revizeCalendar.fns.list.dottedTemplate] Event did not have a proper index attribute", eventElem);
929			return;
930		}
931		$(eventElem).on('click', function(e) { e.preventDefault(); (function(eventData) { window.revizeCalendar.fns.showModal(eventData); }).bind(null, eventSources[eventIndex])(); return false; });
932	});
933
934	$(el).find(".progress-spinner-container").remove();
935	$(el).addClass('mini-list-loaded');
936	if( typeof RZRenderMini !== 'undefined' ){
937		RZRenderMini(el);
938	}
939
940}
941
942window.revizeCalendar_proto.fns.fullCalendarEntryPoint = function() {
943	window.revizeCalendar_proto.fns.full.build();
944	window.revizeCalendar_proto.fns.mini.build();
945	window.revizeCalendar_proto.fns.buildModal();
946	window.revizeCalendar_proto.fns.urlQueryModal();
947}
948
949// --------------------------------------- Modal Section --------------------------------------- //
950
951window.revizeCalendar_proto.fns.resetModal = function(eInfo) {
952	$('#event-modal #ics-export').remove();
953	$('#event-modal #modal-event-duration').remove();
954	$('#event-modal .modal-footer').prepend('<button onclick="javascript:icalExport.download(\''+eInfo.title+'\');return false;" id="ics-export" class="btn btn-primary">ICS</button>');
955	$('#event-modal .event-modal-header').css('background','none');
956	$('#event-modal .modal-event-date-wrap').css('background-color', window.revizeCalendar.fns.getEventC
956olor(eInfo));
957	$('#event-edit, #event-delete').remove();
958	$('#event-modal .modal-footer .btn').css('background-color', window.revizeCalendar.fns.getEventColor(eInfo));
959	$('#modal-event-location').html('');
960}
961
962window.revizeCalendar_proto.fns.buildModal = function() {
963	if (document.querySelectorAll("#event-modal").length < 1) {
964		var modal = document.createElement("div");
965		modal.className = "modal fade";
966		modal.id = "event-modal";
967		modal.tabIndex = "-1";
968		modal.setAttribute("role", "dialog");
969		modal.setAttribute("aria-labelledby", "modal-event-title");
970		modal.setAttribute("aria-hidden", "true");
971
972		modal.innerHTML = '<div id="event-modal-dialog" class="modal-dialog modal-dialog-centered" role="document"> <div class="modal-content"> <div class="event-modal-header"> <div class="modal-event-date-wrap"> <span class="modal-event-date-number"></span> <span class="modal-event-date-month"></span> </div> </div> <div class="modal-body" id="modal-event-body"> <h2 class="modal-title" id="modal-event-title"></h2> <h3 class="modal-subtitle" id="modal-event-subtitle"></h3> <div class="modal-description clearfix" id="modal-event-description"></div> <div class="modal-location" id="modal-event-location"></div> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> <a href="#" id="modal-event-readmore" class="btn btn-primary">Read More</a> </div> </div> </div>';
973
974		document.body.appendChild(modal);
975	}
976}
977
978window.revizeCalendar_proto.fns.createModalHeader = function(eInfo) {
979	if (eInfo.image !== '') {
980		var $img = $(decodeURIComponent(eInfo.image));
981		var src = $img.attr('src');
982		if (!src || src.indexOf("noimage.gif") >= 0 || src.indexOf("/revize/plugins/") >= 0) {
983			eInfo.image = "./_assets_/plugins/revizeCalendar/images/default-event.jpg";
984		} else {
985			eInfo.image = src;
986		}
987	}
988	if (eInfo.image === '') {
989		eInfo.image = "./_assets_/plugins/revizeCalendar/images/default-event.jpg";
990	}
991
992	if(eInfo.location !== '' && eInfo.location !== 'none'){
993		$('#modal-event-location').html('<iframe class="rz-business-inner-map" height="175" frameborder="0" style="width:100%;margin:10px 0;
993border:1px solid '+window.revizeCalendar.fns.getEventColor(eInfo)+'" src=https://www.google.com/maps/embed/v1/place?key=AIzaSyC5cMe92Yw3JgHrkvgIyHgdS2lTsH4C95k&amp;q="'+encodeURIComponent(eInfo.location)+'" allowfullscreen="true"></iframe>')
994	}
995
996	var eventTimes = "";
997	if (eInfo.allDay) {
998		if (eInfo.end.isAfter(eInfo.start, 'day')) {
999			eventTimes = '<span class="dur-start">' + eInfo.start.format('MMM Do ') + '</span> '
1000			eventTimes += '- <span class="dur-end">' + eInfo.end.format('MMM Do ') + '</span>';
1001		} else {
1002			eventTimes = "All Day";
1003		}
1004	} else {
1005		eventTimes = '<span class="dur-start">' + eInfo.start.format('hh:mm A') + '</span> ';
1006		if (typeof eInfo.options === "string" && eInfo.options.indexOf("\"hide_end_date\":true") === -1) {
1007			var endString = eInfo.end.format('hh:mm A');
1008			if (eInfo.end.isAfter(eInfo.start, 'year')) {
1009				endString = eInfo.end.format(', YYYY ') + endString;
1010			}
1011			if (eInfo.end.isAfter(eInfo.start, 'day')) {
1012				endString = eInfo.end.format('MMM Do ') + endString;
1013			}
1014			eventTimes += '- <span class="dur-end">' + endString + '</span>';
1015		}
1016	}
1017
1018	$('#event-modal .event-modal-header').css('background', "url('" + eInfo.image + "') center center / cover no-repeat");
1019	$('#event-modal .event-modal-header').append('<div style="background-color:'+ window.revizeCalendar.fns.getEventC
1019olor(eInfo)+'" id="modal-event-duration">'
1020		+ '<h3>'+eventTimes+'</h3>'
1021		+ '</div>');
1022
1023	var monthDate = eInfo.start.date();
1024	if (monthDate.toString().length < 2) { monthDate = "0" + monthDate; }
1025
1026	$('#event-modal .modal-event-date-number').text(monthDate);
1027	$('#event-modal .modal-event-date-month').text(SHORT_MONTHS[eInfo.start.month()]);
1028}
1029
1030window.revizeCalendar_proto.fns.createModalBody = function(eInfo) {
1031	$('#modal-event-title').text(eInfo.title);
1032	$('#modal-event-description').html(eInfo.desc.replace(/href=\"\.\.\//g,"href=\""));
1033}
1034
1035window.revizeCalendar_proto.fns.editPermitted = function(eInfo) {
1036
1037	if (typeof RZpermits !== "function") {
1038		return false;
1039	} else if (RZpermits("rzevents", "u") === false) {
1040		return false;
1041	}
1042
1043	if (/\bsuperuser\b/.test(RZ.roles) || /\badministrator\b/.test(RZ.roles)) {
1044		return true;
1045	}
1046
1047	var hasCalendarPerms = false;
1048
1049	if (calendarProps.hasOwnProperty(eInfo.calendar_displays[0])) {
1050		var prop = calendarProps[eInfo.calendar_displays[0]];
1051		if (prop.hasOwnProperty("permissions")) {
1052			hasCalendarPerms = true;
1053			if (prop['permissions'].indexOf(RZ.hashed_username) >= 0) {
1054				return true;
1055			} else {
1056				var roles = RZ.roles.split("|");
1057				for (var i = 0; i < roles.length; i++) {
1058					var roleh = roles[i].split("").reduce(function(a, b) { a = (a << 5) - a + b.charCodeAt(0); return a & a }, 0);
1059					if (prop['permissions'].indexOf(roleh) >= 0) {
1060						return true;
1061					}
1062				}
1063			}
1064		}
1065	}
1066
1067	return !hasCalendarPerms; // If calendar HAS individual permissions set, we should've returned a value already.
1068}
1069
1070window.revizeCalendar_proto.fns.createModalFooter = function(eInfo) {
1071	if(eInfo.url.length){
1072		$('#modal-event-readmore').attr('href', eInfo.url).show();
1073	} else {
1074		$('#modal-event-readmore').hide();
1075	}
1076
1077	if(typeof RZ !== 'undefined' && RZ.login && eInfo.edit && typeof eInfo.rid === "string" && eInfo.rid.indexOf("RevizeCalendar") < 0) {
1078
1079		if (window.revizeCalendar.fns.editPermitted(eInfo)) {
1080			$('#event-modal .modal-footer').prepend('<a id="event-edit" href="'+eInfo.edit+'" class="btn btn-secondary">Edit Event</a>');
1081			$('#event-modal .modal-footer').prepend('<a id="event-delete" class="btn btn-danger" href="javascript:RZdelete( \''+eInfo.rid+'\', \'rzevents\' );">Delete</a>');
1082		}
1083	}
1084}
1085
1086window.revizeCalendar_proto.fns.createModalExport = function(eInfo) {
1087	var icsStart = eInfo.start;
1088	var icsEnd = moment(icsStart).add(1, 'day');
1089	if (!eInfo.allDay) {
1090		icsEnd = eInfo.end;
1091	}
1092
1093	if (eInfo.allDay) {
1094		icsStart = icsStart.format('YYYY-MM-DD') + 'T00:00:00'
1095		icsEnd = icsEnd.format('YYYY-MM-DD') + 'T00:00:00'
1096	} else {
1097		icsStart = icsStart.toISOString();
1098		icsEnd = icsEnd.toISOString();
1099	}
1100
1101	var sanitizeDesc = eInfo.desc.replace(/(<([^>]+)>)/ig,"").replace("\n","");
1102
1103	icalExport = ics();
1104	/*if(eInfo.rrule !== false){
1105		var tempRule = {};
1106		tempRule.rrule = eInfo.rrule;
1107		tempRule.rrule = tempRule.rrule.split('\n').filter(function(rule){
1108			return rule.indexOf('RRULE:') >= 0;
1109		}).join('\n');
1110
1111		//var UTCShift = new Date(icsStart);
1112		//UTCShift.setMinutes( UTCShift.getMinutes() + UTCShift.getTimezoneOffset() );
1113
1114		icalExport.addEvent(eInfo.title, sanitizeDesc, eInfo.location, icsStart, icsEnd, tempRule);
1115	} else {*/
1116		icalExport.addEvent(eInfo.title, sanitizeDesc, eInfo.location, icsStart, icsEnd);
1117	//}
1118}
1119
1120window.revizeCalendar_proto.fns.showModal = function(event){
1121	var eInfo = {
1122		title: '',
1123		desc: '',
1124		calendar_displays: [0],
1125
1126		start: moment(0),
1127		end: moment(0),
1128		rrule: false,
1129
1130		url: '',
1131		location: '',
1132		image: '',
1133
1134		options: '',
1135
1136		edit: '',
1137		rid: false,
1138		id: false,
1139	};
1140
1141	eInfo = $.extend(eInfo, event);
1142	// Parse Description
1143	eInfo.desc = window.revizeCalendar.fns.decodeEventDescription(eInfo.desc, eInfo);
1144
1145	if (eInfo.rid) {
1146		var selectedDate = eInfo.start;
1147		var dateStr = eInfo.start.format().slice(0,-1).replace(/[-:\.]/g,'').slice(0,-3); //new Date(selectedDate.getTime() - (selectedDate.getTimezoneOffset() * 60000)).toISOString().slice(0,-1).replace(/[-:\.]/g,'').slice(0,-3);
1148		eInfo.edit = 'javascript:RZedit(\''+eInfo.rid+'\',\''+window.revizeCalendar.editformPrefix+'\/calendar_events-editform.jsp?selected_date='+dateStr+'\',\'0\',\'0\',\'\',\'\',\'rzevents\',\'\',\'\',\'editform\',\'*none*\',\'\',\'\')';
1149	}
1150
1151	window.revizeCalendar.fns.resetModal(eInfo);
1152
1153	window.revizeCalendar.fns.createModalHeader(eInfo);
1154	window.revizeCalendar.fns.createModalBody(eInfo);
1155	window.revizeCalendar.fns.createModalFooter(eInfo);
1156
1157	window.revizeCalendar.fns.createModalExport(eInfo);
1158
1159	if (eInfo.id !== false) {
1160		var queryParam = eInfo.id;
1161		if (eInfo.rrule !== false) {
1162			queryParam += "+" + eInfo.start.format(RZ_ISO_FORMAT);
1163		}
1164		window.revizeCalendar.fns.changeUrlQuery([{ prop: 'event', val: queryParam }]);
1165	}
1166
1167	$('#event-modal').modal('show');
1168}
1169
1170window.revizeCalendar_proto.fns.urlQueryModal = function() {
1171	var q = $.query.parseNew(location.search, location.hash.split("?").length > 1 ? location.hash.split("?")[1] : "");
1172	var evId = q.get('event').toString();
1173	if (evId == "") { return; }
1174
1175	var evParams = evId.split("+");
1176	var event = null;
1177
1178	for (var i = 0; i < window.revizeCalendar.importData.length; i++) {
1179		if (event !== null) { break; }
1180		if (!window.revizeCalendar.importData[i].hasOwnProperty("response")) { continue; }
1181
1182		for (var e = 0; e < window.revizeCalendar.importData[i].response.length; e++) {
1183			if (window.revizeCalendar.importData[i].response[e].id == evParams[0]) { event = window.revizeCalendar.importData[i].response[e]; break; }
1184		}
1185	}
1186
1187	if (event === null) { return; }
1188
1189	var eInfo = {
1190		title: event.title,
1191		desc: event.desc,
1192		calendar_displays: event.calendar_displays,
1193
1194		start: moment(event.start),
1195		end: moment(0),
1196		allDay: event.allDay,
1197		rrule: event.rrule ?? false,
1198
1199		url: event.url,
1200		location: event.location,
1201		image: event.image,
1202
1203		options: event.options,
1204
1205		rid: event.rid,
1206		id: event.id,
1207	};
1208
1209	if (eInfo.allDay !== true) {
1210		eInfo.end = moment(event.end);
1211	}
1212
1213	if (evParams.length == 2) {
1214		// Recurring Event
1215		if (!event.hasOwnProperty("rrule") || event.rrule == "") { return; }
1216
1217		var foundOccurence = false;
1218		var specificDate = moment(decodeURIComponent(evParams[1]));
1219		if (!specificDate.isValid()) { return; }
1220
1221		var rule = new rrule.rrulestr(event.rrule, { forceset: true });
1222
1223		var dstart = moment(event.start);
1224		var dend = (event.hasOwnProperty('end') ? moment(event.end) : undefined);
1225
1226		var dur = undefined;
1227		if (dend) {
1228			dur = moment.duration(dend.diff(dstart));
1229		}
1230
1231		var lookAhead = moment(specificDate).add('1 day').toDate();
1232
1233		var UTCfiedDate = dstart.toDate();
1234		UTCfiedDate.setMinutes(UTCfiedDate.getMinutes() - UTCfiedDate.getTimezoneOffset()); // RRule library recognizes that browser Date sucks and tries to double offset inputted dates.
1235
1236		var occurences = rule.between(UTCfiedDate, lookAhead, true);
1237		for (var i = 0; i < occurences.length; i++) {
1238			occurences[i] = occurences[i].setMinutes(occurences[i].getMinutes() + occurences[i].getTimezoneOffset());
1239			var rstart = moment(occurences[i]);
1240
1241			if (!specificDate.isSame(rstart)) { continue; }
1242
1243			var rend = dend;
1244			if (rend) {
1245				rend = moment(rstart);
1246				rend.add(dur);
1247			}
1248
1249			eInfo.start = rstart;
1250			eInfo.end = rend;
1251
1252			foundOccurence = true;
1253		}
1254
1255		if (!foundOccurence) { return; }
1256	}
1257	window.revizeCalendar.fns.showModal(eInfo);
1258}
1259
1260// --------------------------------------- Helpers Section --------------------------------------- //
1261
1262window.revizeCalendar_proto.fns.exportCalendar = function(cal) {
1263	if (!cal) { return; }
1264
1265	var fullEvents = cal.getEvents();
1266	icalExport = ics();
1267	window.revizeCalendar.fns.addEventsToExport(fullEvents, icalExport);
1268
1269	var exportName = "Full Calendar";
1270
1271	var activeCalendar = $.query.get('calendar').toString();
1272	if (activeCalendar !== "") {
1273		exportName = $(".calendar-name-filter[data-calendar-id="+activeCalendar+"]").text();
1274	}
1275
1276	icalExport.download(exportName);
1277}
1278
1279window.revizeCalendar_proto.fns.addEventsToExport = function(events, ical) {
1280	$.each(events, function(i, ev){
1281
1282		try {
1283			decodeURIComponent(ev.desc);
1284		} catch (e) {
1285			console.warn("[addEventsToExport] Could not add event " + ev.id + " to the export as the description is malformed.");
1286			return;
1287		}
1288
1289		var startStr = "";
1290		var endStr = "";
1291		var locationStr = "";
1292		var descStr = "";
1293
1294		if (ev._def.extendedProps.hasOwnProperty("desc")) {
1295			try {
1296				descStr = decodeURIComponent(ev._def.extendedProps.desc);
1297			} catch (e) {}
1298		}
1299
1300		if (ev._def.extendedProps.hasOwnProperty("location")) {
1301			locationStr = ev._def.extendedProps.location;
1302		}
1303
1304		if (ev.allDay) {
1305			startStr = moment(ev.start).startOf('day').toISOString();
1306			endStr = startStr;
1307		} else {
1308			var startM = moment(ev.start);
1309			var endM = moment(ev.end);
1310
1311			if (startM.isSame(endM)) {
1312				startStr = moment(ev.start).toISOString();
1313				endStr = moment(ev.end).add(1, 'second').toISOString();
1314			} else {
1315				startStr = moment(ev.start).toISOString();
1316				endStr = moment(ev.end).toISOString();
1317			}
1318		}
1319
1320		/*if (ev._def.extendedProps.hasOwnProperty("rrule")) {
1321			var tempRule = {};
1322			tempRule.rrule = ev.rrule;
1323
1324			if(tempRule.rrule.indexOf('DTSTART') >= 0 ){
1325				tempRule.rrule = tempRule.rrule.split('\n').filter(function(dt){
1326					return dt.indexOf('DTSTART') < 0;
1327				}).join('\n');
1328			}
1329
1330			if(tempRule.rrule.indexOf('RDATE') >= 0 ){
1331				tempRule.rrule = tempRule.rrule.split('\n').filter(function(dt){
1332					return dt.indexOf('RDATE') < 0;
1333				}).join('\n');
1334			}
1335
1336			ical.addEvent(ev.title, descStr, locationStr, startStr, endStr, tempRule);
1337		} else {*/
1338			ical.addEvent(ev.title, descStr, locationStr, startStr, endStr, null);
1339		//}
1340	});
1341}
1342
1343window.revizeCalendar_proto.fns.getEventColor = function(event) {
1344	var color = defaultCalendarColor; // var color = event.backgroundColor;
1345	var ind;
1346
1347	if (event.extendedProps) {
1348		ind = event.extendedProps.calendar_displays[0];
1349	} else {
1350		ind = event.calendar_displays[0];
1351	}
1352	if (window.revizeCalendar.calendarProps[ind]) {
1353		color = window.revizeCalendar.calendarProps[ind].color;
1354	}
1355
1356	return color;
1357}
1358
1359window.revizeCalendar_proto.fns.getContrastYIQ = function(color){
1360
1361	var r, g, b;
1362	var a = 1;
1363
1364	if (color.indexOf("#") !== -1 && color.length === 7) {
1365		color = color.replace("#", "");
1366		r = parseInt(color.substr(0,2),16);
1367		g = parseInt(color.substr(2,2),16);
1368		b = parseInt(color.substr(4,2),16);
1369	} else if (color.indexOf("rgb") !== -1) {
1370		color = color.split(/[()]/);
1371		if (color.length == 3) {
1372			color = color[1].split(",");
1373			r = parseInt(color[0].trim());
1374			g = parseInt(color[1].trim());
1375			b = parseInt(color[2].trim());
1376			if (color.length > 3) {
1377				a = parseFloat(color[3].trim());
1378			}
1379		}
1380	}
1381
1382	if (r !== undefined && g !== undefined && b !== undefined) {
1383		var yiq = (((r*299)+(g*587)+(b*114))/a)/1000;
1384		return (yiq >= 150) ? 'dark-text' : 'light-text';
1385	}
1386
1387	return 'dark-text';
1388}
1389
1390window.revizeCalendar_proto.fns.tagCalendarDay = function(el) {
1391	var par = el.parentElement;
1392	var colSpan = 0;
1393
1394	if (el.fcSeg) {
1395		colSpan = el.fcSeg.lastCol - el.fcSeg.firstCol;
1396	}
1397
1398	while (par !== null && par.tagName !== 'TABLE') {
1399		par = par.parentElement;
1400		if (par === null || par.tagName === 'TABLE') { break; }
1401		if (par.classList.contains("fc-daygrid-day")) {
1402			par.classList.add("day-has-event");
1403			if (colSpan > 0) {
1404				var nextCell = par;
1405				for (var i = 0; i < colSpan; i++) {
1406					nextCell = nextCell.nextElementSibling;
1407					if (!nextCell || !nextCell.classList.contains("fc-daygrid-day")) { break; }
1408					nextCell.classList.add("day-has-event");
1409				}
1410			}
1411			return;
1412		}
1413	}
1414}
1415
1416window.revizeCalendar_proto.fns.tagCalendarDays = function(cal) {
1417	cal.querySelectorAll(".fc-daygrid-event").forEach(function(el, i) {
1418		el.classList.remove("day-has-event");
1419	});
1420	cal.querySelectorAll(".fc-daygrid-event").forEach(function(el, i) {
1421		window.revizeCalendar.fns.tagCalendarDay(el);
1422	});
1423}
1424
1425window.revizeCalendar_proto.fns.changeUrlQuery = function(args) {
1426	var q = $.query.parseNew(location.search, location.hash.split("?").length > 1 ? location.hash.split("?")[1] : "");
1427
1428	var url = window.location.href.split('?').shift();
1429	var s;
1430	if (arguments.length){
1431		if (Array.isArray(args)){
1432			for (var i = 0; i < args.length; i++){
1433				if (args[i].prop)
1434				q = q.SET(args[i].prop, args[i].val);
1435			}
1436		} else if(typeof args === 'string'){
1437			q = q.REMOVE(args);
1438		}
1439	}
1440	s = q.toString();
1441
1442	window.history.replaceState(q, 'Calendar', url + s);
1443}
1444
1445window.revizeCalendar_proto.fns.getDefaultTextColor = function(cal) {
1446	var el = cal;
1447	while (el !== null) {
1448		if (window.getComputedStyle(el).backgroundColor !== 'rgba(0, 0, 0, 0)') {
1449			return window.revizeCalendar.fns.getContrastYIQ(window.getComputedStyle(el).backgroundColor);
1450		}
1451		el = el.parentElement;
1452	}
1453	return window.revizeCalendar.fns.getContrastYIQ("#FFFFFF");
1454}
1455
1456window.revizeCalendar_proto.fns.decodeEventDescription = function(string, ev) {
1457	try {
1458		var base64 = false;
1459		if (/^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$/.test(string)) {
1460			var rzEncodedPrefix = "[RZ:base64]";
1461			var decoded = atob(string);
1462			if (decoded.indexOf(rzEncodedPrefix) === 0) {
1463				string = decoded.substr(rzEncodedPrefix.length);
1464				base64 = true;
1465			}
1466		}
1467		if (!base64) {
1468			string = decodeURIComponent(string);
1469		}
1470	} catch (e){
1471		string = unescape(string);
1472		console.warn("[revizeCalendar.fns.list.dottedTemplate] Garbage Characters detected in event", ev);
1473	}
1474	return string;
1475}
1476
1477window.revizeCalendar.fns.__proto__ = window.revizeCalendar_proto.fns;
1478window.revizeCalendar.fns.full.__proto__ = window.revizeCalendar_proto.fns.full;
1479window.revizeCalendar.fns.mini.__proto__ = window.revizeCalendar_proto.fns.mini;
1480window.revizeCalendar.fns.list.__proto__ = window.revizeCalendar_proto.fns.list;
1481
1482// --------------------------------------- Main Calendar Filter Section --------------------------------------- //
1483
1484setTimeout(function() {
1485
1486	$('.calendar-name-filter, .calendar-tab-filter').off('click');
1487	$('.calendar-name-filter, .calendar-tab-filter').on('click', function(e){
1488		e.preventDefault();
1489		if ($(this).data('calendar-id').toString() === "clear") {
1490			$('.calendar-active').removeClass('calendar-active');
1491
1492			window.revizeCalendar.fns.changeUrlQuery("calendar");
1493		} else {
1494			if(!$(this).parent().hasClass('calendar-active')){
1495				$('.calendar-active').removeClass('calendar-active');
1496				$(this).parent().addClass('calendar-active');
1497
1498				window.revizeCalendar.fns.changeUrlQuery([{
1499					prop: 'calendar',
1500					val: $(this).data('calendar-id').toString()
1501				}]);
1502			} else {
1503				$('.calendar-active').removeClass('calendar-active');
1504
1505				window.revizeCalendar.fns.changeUrlQuery('calendar');
1506			}
1507		}
1508		window.revizeCalendar.fns.refreshCalendars();
1509	});
1510
1511	$('#calendar-search').off('keyup');
1512	$('#calendar-search').on('keyup', function(e) {
1513		if (e.which === 13) {
1514			window.revizeCalendar.fns.submitSearch($(this).val());
1515		}
1516	});
1517
1518	$('#calendar-submit').off('click');
1519	$('#calendar-submit').on('click',function(e) {
1520		e.preventDefault();
1521		window.revizeCalendar.fns.submitSearch($('#calendar-search').val());
1522	});
1523
1524	$('#event-modal').off('hidden.bs.modal');
1525	$('#event-modal').on('hidden.bs.modal', function() {
1526		window.revizeCalendar.fns.changeUrlQuery('event','');
1527	});
1528
1529}, 1000);
1530
1531window.revizeCalendar.fns.submitSearch = function(searchStr) {
1532	if (searchStr.trim().length > 0) {
1533		window.revizeCalendar.fns.changeUrlQuery([{
1534			prop: 'query',
1535			val: searchStr.toLowerCase()
1536		}]);
1537	} else {
1538		window.revizeCalendar.fns.changeUrlQuery('query');
1539	}
1540	window.revizeCalendar.fns.refreshCalendars();
1541}
1542
1543// --------------------------------------- Init --------------------------------------- //
1544
1545async function scriptToPromise(url, base = "") {
1546	return new Promise(function(resolve, reject) {
1547		var scriptTag = document.createElement("script");
1548		scriptTag.async = false;
1549		scriptTag.src = base + url;
1550		scriptTag.onload = function() { resolve(); };
1551		scriptTag.onerror = function() { reject(); };
1552		document.body.appendChild(scriptTag);
1553	});
1554}
1555
1556FullCalendar = {};
1557
1558window.addEventListener('load', async function() {
1559	if (typeof FullCalendar === 'undefined' || !FullCalendar.hasOwnProperty("globalPlugins")) {
1560		await $.getScript("//cdn.jsdelivr.net/npm/[email protected]/index.global.min.js");
1561		await $.getScript("//cdn.jsdelivr.net/npm/[email protected]/dist/es5/rrule.min.js");
1562		await $.getScript("//cdn.jsdelivr.net/npm/@fullcalendar/[email protected]/index.global.min.js");
1563	}
1564	if (typeof $.query === 'undefined') {
1565		await scriptToPromise("/revize/plugins/revize_calendar/jquery.query-object.js");
1566	}
1567	if (typeof doT === 'undefined') {
1568		await scriptToPromise("/revize/plugins/revize_calendar/doT.min.js");
1569	}
1570	if (typeof ics === 'undefined') {
1571		await scriptToPromise("/revize/plugins/revize_calendar/ics/ics.min.js");
1572	}
1573	if (typeof moment === 'undefined') {
1574		await scriptToPromise("/revize/plugins/revize_calendar/moment.min.js");
1575	}
1576	window.dispatchEvent(new Event('RZCalendar.AssetsLoaded'));
1577});
1578
1579window.addEventListener('RZCalendar.AssetsLoaded', function() {
1580	window.revizeCalendar.fns.preInit();
1581	window.revizeCalendar.fns.prepareImportData();
1582});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.