diff --git a/CHANGES.md b/CHANGES.md index c7b347c2b..2ea559542 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -25,7 +25,13 @@ ### Features - Implenent lazy loading for external libraries via dynamic imports. Leads to significantly reduced bundle sizes. -- Upgrade pat-calendar to use lates fullcalendar version (5.3.0). +- Upgrade pat-calendar to use latest fullcalendar version (5.3.0). +- pat calendar: Add fullcalendar list views. +- pat calendar: Store the current date and view in query parameters. +- pat calendar: Fetch events from the backend. +- pat calendar: Allow filtering/hiding events based in comparing the checkbox id with the classes of the displayed events. +- pat calendar: Support injection of events when clicking on and event rather than redirecting to them. +- pat calendar: Store view, date and active categories per URL, allowing to individually customize the calendar per page. - pat tooltip: Use tippy v6 based implementation. - Allow overriding the public path from outside via the definition of a ``window.__patternslib_public_path__`` global variable. @@ -54,6 +60,7 @@ - pat calendar: Fix language loading error "Error: Cannot find module './en.js'" - pat depends, pat auto suggest: Fix a problem with initialization of ``pat-auto-suggest`` which occurred after the lazy loading changes. +- pat checklist: Also dispatch standard ``change`` event when de/selecting all items. ## 3.0.0-dev - unreleased diff --git a/Makefile b/Makefile index d9e761e41..5d45fb61b 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,6 @@ all-css:: css @$(SASS) -I . -I _sass src/pat/auto-suggest/_auto-suggest.scss src/pat/auto-suggest/auto-suggest.css @$(SASS) -I . -I _sass src/pat/autofocus/_autofocus.scss src/pat/autofocus/autofocus.css @$(SASS) -I . -I _sass src/pat/bumper/_bumper.scss src/pat/bumper/bumper.css - @$(SASS) -I . -I _sass src/pat/calendar/_calendar.scss src/pat/calendar/calendar.css @$(SASS) -I . -I _sass src/pat/carousel/_carousel.scss src/pat/carousel/carousel.css @$(SASS) -I . -I _sass src/pat/checklist/_checklist.scss src/pat/checklist/checklist.css @$(SASS) -I . -I _sass src/pat/clone/_clone.scss src/pat/clone/clone.css @@ -69,7 +68,6 @@ all-css:: css @$(SASS) -I . -I _sass src/pat/focus/_focus.scss src/pat/focus/focus.css @echo "Almost there, don't give up!" @$(SASS) -I . -I _sass src/pat/forward/_forward.scss src/pat/forward/forward.css - @$(SASS) -I . -I _sass src/pat/fullscreen/_fullscreen.scss src/pat/fullscreen/fullscreen.css @$(SASS) -I . -I _sass src/pat/gallery/_gallery.scss src/pat/gallery/gallery.css @$(SASS) -I . -I _sass src/pat/grid/_grid.scss src/pat/grid/grid.css @$(SASS) -I . -I _sass src/pat/image-crop/_image-crop.scss src/pat/image-crop/image-crop.css diff --git a/_sass/_patterns.scss b/_sass/_patterns.scss index bfb30208a..ef0707d3e 100644 --- a/_sass/_patterns.scss +++ b/_sass/_patterns.scss @@ -28,7 +28,6 @@ @import "src/pat/equaliser/equaliser"; @import "src/pat/expandable-tree/expandable-tree"; @import "src/pat/focus/focus"; -@import "src/pat/fullscreen/fullscreen"; @import "src/pat/gallery/gallery"; @import "src/pat/grid/grid"; @import "src/pat/syntax-highlight/syntax-highlight"; diff --git a/_sass/components/_fullcalendar.scss b/_sass/components/_fullcalendar.scss deleted file mode 100644 index a389261df..000000000 --- a/_sass/components/_fullcalendar.scss +++ /dev/null @@ -1,599 +0,0 @@ -/* - * FullCalendar v1.5.4 Stylesheet - * - * Copyright (c) 2011 Adam Shaw - * Dual licensed under the MIT and GPL licenses, located in - * MIT-LICENSE.txt and GPL-LICENSE.txt respectively. - * - * Date: Sun Jan 27 00:57:41 2013 +0100 - * - */ - -.fc { - direction: ltr; - text-align: left; -} - -.fc table { - border-collapse: collapse; - border-spacing: 0; -} - -html .fc, -.fc table { - font-size: 1em; -} - -.fc td, -.fc th { - padding: 0; - vertical-align: top; -} - -/* Header -------------------------------------------------------------------------*/ - -.fc-header td { - white-space: nowrap; -} - -.fc-header-left { - width: 25%; - text-align: left; -} - -.fc-header-center { - text-align: center; -} - -.fc-header-right { - width: 25%; - text-align: right; -} - -.fc-header-title { - display: inline-block; - vertical-align: top; -} - -.fc-header-title h2 { - margin-top: 0; - white-space: nowrap; -} - -.fc .fc-header-space { - padding-left: 10px; -} - -.fc-header .fc-button { - margin-bottom: 1em; - vertical-align: top; -} - -/* buttons edges butting together */ - -.fc-header .fc-button { - margin-right: -1px; -} - -.fc-header .fc-corner-right { - margin-right: 1px; /* back to normal */ -} - -.fc-header .ui-corner-right { - margin-right: 0; /* back to normal */ -} - -/* button layering (for border precedence) */ - -.fc-header .fc-state-hover, -.fc-header .ui-state-hover { - z-index: 2; -} - -.fc-header .fc-state-down { - z-index: 3; -} - -.fc-header .fc-state-active, -.fc-header .ui-state-active { - z-index: 4; -} - -/* Content -------------------------------------------------------------------------*/ - -.fc-content { - clear: both; -} - -.fc-view { - width: 100%; /* needed for view switching (when view is absolute) */ - overflow: hidden; -} - -/* Cell Styles -------------------------------------------------------------------------*/ - -.fc-widget-header, /* , usually */ -.fc-widget-content { - /* , usually */ - border: 1px solid #ccc; -} - -.fc-state-highlight { - /* today cell */ /* TODO: add .fc-today to */ - background: #ffc; -} - -.fc-cell-overlay { - /* semi-transparent rectangle while dragging */ - background: #9cf; - opacity: 0.2; - filter: alpha(opacity=20); /* for IE */ -} - -/* Buttons -------------------------------------------------------------------------*/ - -.fc-button { - position: relative; - display: inline-block; - cursor: pointer; -} - -.fc-state-default { - /* non-theme */ - border-style: solid; - border-width: 1px 0; -} - -.fc-button-inner { - position: relative; - float: left; - overflow: hidden; -} - -.fc-state-default .fc-button-inner { - /* non-theme */ - border-style: solid; - border-width: 0 1px; -} - -.fc-button-content { - position: relative; - float: left; - height: 1.9em; - line-height: 1.9em; - padding: 0 0.6em; - white-space: nowrap; -} - -/* icon (for jquery ui) */ - -.fc-button-content .fc-icon-wrap { - position: relative; - float: left; - top: 50%; -} - -.fc-button-content .ui-icon { - position: relative; - float: left; - margin-top: -50%; - *margin-top: 0; - *top: -50%; -} - -/* gloss effect */ - -.fc-state-default .fc-button-effect { - position: absolute; - top: 50%; - left: 0; -} - -.fc-state-default .fc-button-effect span { - position: absolute; - top: -100px; - left: 0; - width: 500px; - height: 100px; - border-width: 100px 0 0 1px; - border-style: solid; - border-color: #fff; - background: #444; - opacity: 0.09; - filter: alpha(opacity=9); -} - -/* button states (determines colors) */ - -.fc-state-default, -.fc-state-default .fc-button-inner { - border-style: solid; - border-color: #ccc #bbb #aaa; - background: #f3f3f3; - color: #000; -} - -.fc-state-hover, -.fc-state-hover .fc-button-inner { - border-color: #999; -} - -.fc-state-down, -.fc-state-down .fc-button-inner { - border-color: #555; - background: #777; -} - -.fc-state-active, -.fc-state-active .fc-button-inner { - border-color: #555; - background: #777; - color: #fff; -} - -.fc-state-disabled, -.fc-state-disabled .fc-button-inner { - color: #999; - border-color: #ddd; -} - -.fc-state-disabled { - cursor: default; -} - -.fc-state-disabled .fc-button-effect { - display: none; -} - -/* Global Event Styles -------------------------------------------------------------------------*/ - -.fc-event { - border-style: solid; - border-width: 0; - font-size: 0.85em; - cursor: default; -} - -a.fc-event, -.fc-event-draggable { - cursor: pointer; -} - -a.fc-event { - text-decoration: none; -} - -.fc-rtl .fc-event { - text-align: right; -} - -.fc-event-skin { - border-color: #36c; /* default BORDER color */ - background-color: #36c; /* default BACKGROUND color */ - color: #fff; /* default TEXT color */ -} - -.fc-event-inner { - position: relative; - width: 100%; - height: 100%; - border-style: solid; - border-width: 0; - overflow: hidden; -} - -.fc-event-time, -.fc-event-title { - padding: 0 1px; -} - -.fc .ui-resizable-handle { - /*** TODO: don't use ui-resizable anymore, change class ***/ - display: block; - position: absolute; - z-index: 99999; - overflow: hidden; /* hacky spaces (IE6/7) */ - font-size: 300%; /* */ - line-height: 50%; /* */ -} - -/* Horizontal Events -------------------------------------------------------------------------*/ - -.fc-event-hori { - border-width: 1px 0; - margin-bottom: 1px; -} - -/* resizable */ - -.fc-event-hori .ui-resizable-e { - top: 0 !important; /* importants override pre jquery ui 1.7 styles */ - right: -3px !important; - width: 7px !important; - height: 100% !important; - cursor: e-resize; -} - -.fc-event-hori .ui-resizable-w { - top: 0 !important; - left: -3px !important; - width: 7px !important; - height: 100% !important; - cursor: w-resize; -} - -.fc-event-hori .ui-resizable-handle { - _padding-bottom: 14px; /* IE6 had 0 height */ -} - -/* Fake Rounded Corners (for buttons and events) -------------------------------------------------------------*/ - -.fc-corner-left { - margin-left: 1px; -} - -.fc-corner-left .fc-button-inner, -.fc-corner-left .fc-event-inner { - margin-left: -1px; -} - -.fc-corner-right { - margin-right: 1px; -} - -.fc-corner-right .fc-button-inner, -.fc-corner-right .fc-event-inner { - margin-right: -1px; -} - -.fc-corner-top { - margin-top: 1px; -} - -.fc-corner-top .fc-event-inner { - margin-top: -1px; -} - -.fc-corner-bottom { - margin-bottom: 1px; -} - -.fc-corner-bottom .fc-event-inner { - margin-bottom: -1px; -} - -/* Fake Rounded Corners SPECIFICALLY FOR EVENTS ------------------------------------------------------------------*/ - -.fc-corner-left .fc-event-inner { - border-left-width: 1px; -} - -.fc-corner-right .fc-event-inner { - border-right-width: 1px; -} - -.fc-corner-top .fc-event-inner { - border-top-width: 1px; -} - -.fc-corner-bottom .fc-event-inner { - border-bottom-width: 1px; -} - -/* Reusable Separate-border Table -------------------------------------------------------------*/ - -table.fc-border-separate { - border-collapse: separate; -} - -.fc-border-separate th, -.fc-border-separate td { - border-width: 1px 0 0 1px; -} - -.fc-border-separate th.fc-last, -.fc-border-separate td.fc-last { - border-right-width: 1px; -} - -.fc-border-separate tr.fc-last th, -.fc-border-separate tr.fc-last td { - border-bottom-width: 1px; -} - -.fc-border-separate tbody tr.fc-first td, -.fc-border-separate tbody tr.fc-first th { - border-top-width: 0; -} - -/* Month View, Basic Week View, Basic Day View -------------------------------------------------------------------------*/ - -.fc-grid th { - text-align: center; -} - -.fc-grid .fc-day-number { - float: right; - padding: 0 2px; -} - -.fc-grid .fc-other-month .fc-day-number { - opacity: 0.3; - filter: alpha(opacity=30); /* for IE */ - /* opacity with small font can sometimes look too faded - might want to set the 'color' property instead - making day-numbers bold also fixes the problem */ -} - -.fc-grid .fc-day-content { - clear: both; - padding: 2px 2px 1px; /* distance between events and day edges */ -} - -/* event styles */ - -.fc-grid .fc-event-time { - font-weight: bold; -} - -/* right-to-left */ - -.fc-rtl .fc-grid .fc-day-number { - float: left; -} - -.fc-rtl .fc-grid .fc-event-time { - float: right; -} - -/* Agenda Week View, Agenda Day View -------------------------------------------------------------------------*/ - -.fc-agenda table { - border-collapse: separate; -} - -.fc-agenda-days th { - text-align: center; -} - -.fc-agenda .fc-agenda-axis { - width: 50px; - padding: 0 4px; - vertical-align: middle; - text-align: right; - white-space: nowrap; - font-weight: normal; -} - -.fc-agenda .fc-day-content { - padding: 2px 2px 1px; -} - -/* make axis border take precedence */ - -.fc-agenda-days .fc-agenda-axis { - border-right-width: 1px; -} - -.fc-agenda-days .fc-col0 { - border-left-width: 0; -} - -/* all-day area */ - -.fc-agenda-allday th { - border-width: 0 1px; -} - -.fc-agenda-allday .fc-day-content { - min-height: 34px; /* TODO: doesnt work well in quirksmode */ - _height: 34px; -} - -/* divider (between all-day and slots) */ - -.fc-agenda-divider-inner { - height: 2px; - overflow: hidden; -} - -.fc-widget-header .fc-agenda-divider-inner { - background: #eee; -} - -/* slot rows */ - -.fc-agenda-slots th { - border-width: 1px 1px 0; -} - -.fc-agenda-slots td { - border-width: 1px 0 0; - background: none; -} - -.fc-agenda-slots td div { - height: 20px; -} - -.fc-agenda-slots tr.fc-slot0 th, -.fc-agenda-slots tr.fc-slot0 td { - border-top-width: 0; -} - -.fc-agenda-slots tr.fc-minor th, -.fc-agenda-slots tr.fc-minor td { - border-top-style: dotted; -} - -.fc-agenda-slots tr.fc-minor th.ui-widget-header { - *border-top-style: solid; /* doesn't work with background in IE6/7 */ -} - -/* Vertical Events -------------------------------------------------------------------------*/ - -.fc-event-vert { - border-width: 0 1px; -} - -.fc-event-vert .fc-event-head, -.fc-event-vert .fc-event-content { - position: relative; - z-index: 2; - width: 100%; - overflow: hidden; -} - -.fc-event-vert .fc-event-time { - white-space: nowrap; - font-size: 10px; -} - -.fc-event-vert .fc-event-bg { - /* makes the event lighter w/ a semi-transparent overlay */ - position: absolute; - z-index: 1; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: #fff; - opacity: 0.3; - filter: alpha(opacity=30); -} - -.fc .ui-draggable-dragging .fc-event-bg, /* TODO: something nicer like .fc-opacity */ -.fc-select-helper .fc-event-bg { - display: none\9; /* for IE6/7/8. nested opacity filters while dragging don't work */ -} - -/* resizable */ - -.fc-event-vert .ui-resizable-s { - bottom: 0 !important; /* importants override pre jquery ui 1.7 styles */ - width: 100% !important; - height: 8px !important; - overflow: hidden !important; - line-height: 8px !important; - font-size: 11px !important; - font-family: monospace; - text-align: center; - cursor: s-resize; -} - -.fc-agenda .ui-resizable-resizing { - /* TODO: better selector */ - _overflow: hidden; -} diff --git a/package.json b/package.json index 041f48ad2..5a127a05d 100644 --- a/package.json +++ b/package.json @@ -15,13 +15,20 @@ "url": "https://github.com/Patternslib/patterns.git" }, "dependencies": { + "@fullcalendar/adaptive": "^5.3.1", + "@fullcalendar/core": "^5.3.1", + "@fullcalendar/daygrid": "^5.3.2", + "@fullcalendar/interaction": "^5.3.1", + "@fullcalendar/list": "^5.3.1", + "@fullcalendar/luxon": "^5.3.1", + "@fullcalendar/timegrid": "^5.3.1", "@stomp/stompjs": "^5.4.4", - "fullcalendar": "^2.9.1", "google-code-prettify": "^1.0.5", "imagesloaded": "^4.1.4", "intersection-observer": "^0.7.0", "jquery": "^3.5.1", "jquery-jcrop": "^0.9.13", + "luxon": "^1.24.1", "masonry-layout": "^4.2.2", "moment": "^2.29.0", "moment-timezone": "^0.5.31", diff --git a/src/pat/calendar/_calendar.scss b/src/pat/calendar/_calendar.scss deleted file mode 100644 index b630c4b48..000000000 --- a/src/pat/calendar/_calendar.scss +++ /dev/null @@ -1,9 +0,0 @@ -@import "./_fullcalendar.scss"; - -.calendar-container { - overflow-y: scroll; - height: 500px; -} -#calendar > .fc-content { - display: none; -} diff --git a/src/pat/calendar/_fullcalendar.scss b/src/pat/calendar/_fullcalendar.scss deleted file mode 100644 index b3ae73e07..000000000 --- a/src/pat/calendar/_fullcalendar.scss +++ /dev/null @@ -1,825 +0,0 @@ -/*! - * FullCalendar v2.9.1 Stylesheet - * Docs & License: http://fullcalendar.io/ - * (c) 2016 Adam Shaw - */ -.fc-bgevent, -.fc-highlight { - opacity: 0.3; - filter: alpha(opacity=30); -} -.fc-icon, -body .fc { - font-size: 1em; -} -.fc-button-group, -.fc-icon { - display: inline-block; -} -.fc-bg, -.fc-row .fc-bgevent-skeleton, -.fc-row .fc-highlight-skeleton { - bottom: 0; -} -.fc-icon, -.fc-unselectable { - -khtml-user-select: none; - -webkit-touch-callout: none; -} -.fc .fc-axis, -.fc button, -.fc-time-grid-event .fc-time, -.fc-time-grid-event.fc-short .fc-content { - white-space: nowrap; -} -.fc { - direction: ltr; - text-align: left; -} -.fc-rtl { - text-align: right; -} -.fc th, -.fc-basic-view .fc-week-number, -.fc-icon, -.fc-toolbar { - text-align: center; -} -.fc-unthemed .fc-content, -.fc-unthemed .fc-divider, -.fc-unthemed .fc-popover, -.fc-unthemed .fc-row, -.fc-unthemed tbody, -.fc-unthemed td, -.fc-unthemed th, -.fc-unthemed thead { - border-color: #ddd; -} -.fc-unthemed .fc-popover { - background-color: #fff; -} -.fc-unthemed .fc-divider, -.fc-unthemed .fc-popover .fc-header { - background: #eee; -} -.fc-unthemed .fc-popover .fc-header .fc-close { - color: #666; -} -.fc-unthemed .fc-today { - background: #fcf8e3; -} -.fc-highlight { - background: #bce8f1; -} -.fc-bgevent { - background: #8fdf82; -} -.fc-nonbusiness { - background: #d7d7d7; -} -.fc-icon { - height: 1em; - line-height: 1em; - overflow: hidden; - font-family: "Courier New", Courier, monospace; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -.fc-icon:after { - position: relative; -} -.fc-icon-left-single-arrow:after { - content: "\02039"; - font-weight: 700; - font-size: 200%; - top: -7%; -} -.fc-icon-right-single-arrow:after { - content: "\0203A"; - font-weight: 700; - font-size: 200%; - top: -7%; -} -.fc-icon-left-double-arrow:after { - content: "\000AB"; - font-size: 160%; - top: -7%; -} -.fc-icon-right-double-arrow:after { - content: "\000BB"; - font-size: 160%; - top: -7%; -} -.fc-icon-left-triangle:after { - content: "\25C4"; - font-size: 125%; - top: 3%; -} -.fc-icon-right-triangle:after { - content: "\25BA"; - font-size: 125%; - top: 3%; -} -.fc-icon-down-triangle:after { - content: "\25BC"; - font-size: 125%; - top: 2%; -} -.fc-icon-x:after { - content: "\000D7"; - font-size: 200%; - top: 6%; -} -.fc button { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - margin: 0; - height: 2.1em; - padding: 0 0.6em; - font-size: 1em; - cursor: pointer; -} -.fc button::-moz-focus-inner { - margin: 0; - padding: 0; -} -.fc-state-default { - border: 1px solid; - background-color: #f5f5f5; - background-image: -moz-linear-gradient(top, #fff, #e6e6e6); - background-image: -webkit-gradient( - linear, - 0 0, - 0 100%, - from(#fff), - to(#e6e6e6) - ); - background-image: -webkit-linear-gradient(top, #fff, #e6e6e6); - background-image: -o-linear-gradient(top, #fff, #e6e6e6); - background-image: linear-gradient(to bottom, #fff, #e6e6e6); - background-repeat: repeat-x; - border-color: #e6e6e6 #e6e6e6 #bfbfbf; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - color: #333; - text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), - 0 1px 2px rgba(0, 0, 0, 0.05); -} -.fc-state-default.fc-corner-left { - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; -} -.fc-state-default.fc-corner-right { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} -.fc button .fc-icon { - position: relative; - top: -0.05em; - margin: 0 0.2em; - vertical-align: middle; -} -.fc-state-active, -.fc-state-disabled, -.fc-state-down, -.fc-state-hover { - color: #333; - background-color: #e6e6e6; -} -.fc-state-hover { - color: #333; - text-decoration: none; - background-position: 0 -15px; - -webkit-transition: background-position 0.1s linear; - -moz-transition: background-position 0.1s linear; - -o-transition: background-position 0.1s linear; - transition: background-position 0.1s linear; -} -.fc-state-active, -.fc-state-down { - background-color: #ccc; - background-image: none; - box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), - 0 1px 2px rgba(0, 0, 0, 0.05); -} -.fc-state-disabled { - cursor: default; - background-image: none; - opacity: 0.65; - filter: alpha(opacity=65); - box-shadow: none; -} -.fc-event.fc-draggable, -.fc-event[href], -.fc-popover .fc-header .fc-close { - cursor: pointer; -} -.fc .fc-button-group > * { - float: left; - margin: 0 0 0 -1px; -} -.fc .fc-button-group > :first-child { - margin-left: 0; -} -.fc-popover { - position: absolute; - box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); -} -.fc-popover .fc-header { - padding: 2px 4px; -} -.fc-popover .fc-header .fc-title { - margin: 0 2px; -} -.fc-ltr .fc-popover .fc-header .fc-title, -.fc-rtl .fc-popover .fc-header .fc-close { - float: left; -} -.fc-ltr .fc-popover .fc-header .fc-close, -.fc-rtl .fc-popover .fc-header .fc-title { - float: right; -} -.fc-unthemed .fc-popover { - border-width: 1px; - border-style: solid; -} -.fc-unthemed .fc-popover .fc-header .fc-close { - font-size: 0.9em; - margin-top: 2px; -} -.fc-popover > .ui-widget-header + .ui-widget-content { - border-top: 0; -} -.fc-divider { - border-style: solid; - border-width: 1px; -} -hr.fc-divider { - height: 0; - margin: 0; - padding: 0 0 2px; - border-width: 1px 0; -} -.fc-bg table, -.fc-row .fc-bgevent-skeleton table, -.fc-row .fc-highlight-skeleton table { - height: 100%; -} -.fc-clear { - clear: both; -} -.fc-bg, -.fc-bgevent-skeleton, -.fc-helper-skeleton, -.fc-highlight-skeleton { - position: absolute; - top: 0; - left: 0; - right: 0; -} -.fc table { - width: 100%; - box-sizing: border-box; - table-layout: fixed; - border-collapse: collapse; - border-spacing: 0; - font-size: 1em; -} -.fc td, -.fc th { - border-style: solid; - border-width: 1px; - padding: 0; - vertical-align: top; -} -.fc td.fc-today { - border-style: double; -} -.fc .fc-row { - border-style: solid; - border-width: 0; -} -.fc-row table { - border-left: 0 hidden transparent; - border-right: 0 hidden transparent; - border-bottom: 0 hidden transparent; -} -.fc-row:first-child table { - border-top: 0 hidden transparent; -} -.fc-row { - position: relative; -} -.fc-row .fc-bg { - z-index: 1; -} -.fc-row .fc-bgevent-skeleton td, -.fc-row .fc-highlight-skeleton td { - border-color: transparent; -} -.fc-row .fc-bgevent-skeleton { - z-index: 2; -} -.fc-row .fc-highlight-skeleton { - z-index: 3; -} -.fc-row .fc-content-skeleton { - position: relative; - z-index: 4; - padding-bottom: 2px; -} -.fc-row .fc-helper-skeleton { - z-index: 5; -} -.fc-row .fc-content-skeleton td, -.fc-row .fc-helper-skeleton td { - background: 0 0; - border-color: transparent; - border-bottom: 0; -} -.fc-row .fc-content-skeleton tbody td, -.fc-row .fc-helper-skeleton tbody td { - border-top: 0; -} -.fc-scroller { - -webkit-overflow-scrolling: touch; -} -.fc-row.fc-rigid, -.fc-time-grid-event { - overflow: hidden; -} -.fc-scroller > .fc-day-grid, -.fc-scroller > .fc-time-grid { - position: relative; - width: 100%; -} -.fc-event { - position: relative; - display: block; - font-size: 0.85em; - line-height: 1.3; - border-radius: 3px; - border: 1px solid #3a87ad; - background-color: #3a87ad; - font-weight: 400; -} -.fc-event, -.fc-event:hover, -.ui-widget .fc-event { - color: #fff; - text-decoration: none; -} -.fc-not-allowed, -.fc-not-allowed .fc-event { - cursor: not-allowed; -} -.fc-event .fc-bg { - z-index: 1; - background: #fff; - opacity: 0.25; - filter: alpha(opacity=25); -} -.fc-event .fc-content { - position: relative; - z-index: 2; -} -.fc-event .fc-resizer { - position: absolute; - z-index: 4; - display: none; -} -.fc-event.fc-allow-mouse-resize .fc-resizer, -.fc-event.fc-selected .fc-resizer { - display: block; -} -.fc-event.fc-selected .fc-resizer:before { - content: ""; - position: absolute; - z-index: 9999; - top: 50%; - left: 50%; - width: 40px; - height: 40px; - margin-left: -20px; - margin-top: -20px; -} -.fc-event.fc-selected { - z-index: 9999 !important; - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); -} -.fc-event.fc-selected.fc-dragging { - box-shadow: 0 2px 7px rgba(0, 0, 0, 0.3); -} -.fc-h-event.fc-selected:before { - content: ""; - position: absolute; - z-index: 3; - top: -10px; - bottom: -10px; - left: 0; - right: 0; -} -.fc-ltr .fc-h-event.fc-not-start, -.fc-rtl .fc-h-event.fc-not-end { - margin-left: 0; - border-left-width: 0; - padding-left: 1px; - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.fc-ltr .fc-h-event.fc-not-end, -.fc-rtl .fc-h-event.fc-not-start { - margin-right: 0; - border-right-width: 0; - padding-right: 1px; - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.fc-ltr .fc-h-event .fc-start-resizer, -.fc-rtl .fc-h-event .fc-end-resizer { - cursor: w-resize; - left: -1px; -} -.fc-ltr .fc-h-event .fc-end-resizer, -.fc-rtl .fc-h-event .fc-start-resizer { - cursor: e-resize; - right: -1px; -} -.fc-h-event.fc-allow-mouse-resize .fc-resizer { - width: 7px; - top: -1px; - bottom: -1px; -} -.fc-h-event.fc-selected .fc-resizer { - border-radius: 4px; - border-width: 1px; - width: 6px; - height: 6px; - border-style: solid; - border-color: inherit; - background: #fff; - top: 50%; - margin-top: -4px; -} -.fc-ltr .fc-h-event.fc-selected .fc-start-resizer, -.fc-rtl .fc-h-event.fc-selected .fc-end-resizer { - margin-left: -4px; -} -.fc-ltr .fc-h-event.fc-selected .fc-end-resizer, -.fc-rtl .fc-h-event.fc-selected .fc-start-resizer { - margin-right: -4px; -} -.fc-day-grid-event { - margin: 1px 2px 0; - padding: 0 1px; -} -.fc-day-grid-event.fc-selected:after { - content: ""; - position: absolute; - z-index: 1; - top: -1px; - right: -1px; - bottom: -1px; - left: -1px; - background: #000; - opacity: 0.25; - filter: alpha(opacity=25); -} -.fc-day-grid-event .fc-content { - white-space: nowrap; - overflow: hidden; -} -.fc-day-grid-event .fc-time { - font-weight: 700; -} -.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer, -.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer { - margin-left: -2px; -} -.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer, -.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer { - margin-right: -2px; -} -a.fc-more { - margin: 1px 3px; - font-size: 0.85em; - cursor: pointer; - text-decoration: none; -} -a.fc-more:hover { - text-decoration: underline; -} -.fc-limited { - display: none; -} -.fc-day-grid .fc-row { - z-index: 1; -} -.fc-more-popover { - z-index: 2; - width: 220px; -} -.fc-more-popover .fc-event-container { - padding: 10px; -} -.fc-now-indicator { - position: absolute; - border: 0 solid red; -} -.fc-unselectable { - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - -webkit-tap-highlight-color: transparent; -} -.fc-toolbar { - margin-bottom: 1em; -} -.fc-toolbar .fc-left { - float: left; -} -.fc-toolbar .fc-right { - float: right; -} -.fc-toolbar .fc-center { - display: inline-block; -} -.fc .fc-toolbar > * > * { - float: left; - margin-left: 0.75em; -} -.fc .fc-toolbar > * > :first-child { - margin-left: 0; -} -.fc-toolbar h2 { - margin: 0; -} -.fc-toolbar button { - position: relative; -} -.fc-toolbar .fc-state-hover, -.fc-toolbar .ui-state-hover { - z-index: 2; -} -.fc-toolbar .fc-state-down { - z-index: 3; -} -.fc-toolbar .fc-state-active, -.fc-toolbar .ui-state-active { - z-index: 4; -} -.fc-toolbar button:focus { - z-index: 5; -} -.fc-view-container *, -.fc-view-container :after, -.fc-view-container :before { - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; -} -.fc-view, -.fc-view > table { - position: relative; - z-index: 1; -} -.fc-basicDay-view .fc-content-skeleton, -.fc-basicWeek-view .fc-content-skeleton { - padding-top: 1px; - padding-bottom: 1em; -} -.fc-basic-view .fc-body .fc-row { - min-height: 4em; -} -.fc-row.fc-rigid .fc-content-skeleton { - position: absolute; - top: 0; - left: 0; - right: 0; -} -.fc-basic-view .fc-day-number, -.fc-basic-view .fc-week-number { - padding: 0 2px; -} -.fc-basic-view td.fc-day-number, -.fc-basic-view td.fc-week-number span { - padding-top: 2px; - padding-bottom: 2px; -} -.fc-basic-view .fc-week-number span { - display: inline-block; - min-width: 1.25em; -} -.fc-ltr .fc-basic-view .fc-day-number { - text-align: right; -} -.fc-rtl .fc-basic-view .fc-day-number { - text-align: left; -} -.fc-day-number.fc-other-month { - opacity: 0.3; - filter: alpha(opacity=30); -} -.fc-agenda-view .fc-day-grid { - position: relative; - z-index: 2; -} -.fc-agenda-view .fc-day-grid .fc-row { - min-height: 3em; -} -.fc-agenda-view .fc-day-grid .fc-row .fc-content-skeleton { - padding-top: 1px; - padding-bottom: 1em; -} -.fc .fc-axis { - vertical-align: middle; - padding: 0 4px; -} -.fc-ltr .fc-axis { - text-align: right; -} -.fc-rtl .fc-axis { - text-align: left; -} -.ui-widget td.fc-axis { - font-weight: 400; -} -.fc-time-grid, -.fc-time-grid-container { - position: relative; - z-index: 1; -} -.fc-time-grid { - min-height: 100%; -} -.fc-time-grid table { - border: 0 hidden transparent; -} -.fc-time-grid > .fc-bg { - z-index: 1; -} -.fc-time-grid .fc-slats, -.fc-time-grid > hr { - position: relative; - z-index: 2; -} -.fc-time-grid .fc-content-col { - position: relative; -} -.fc-time-grid .fc-content-skeleton { - position: absolute; - z-index: 3; - top: 0; - left: 0; - right: 0; -} -.fc-time-grid .fc-business-container { - position: relative; - z-index: 1; -} -.fc-time-grid .fc-bgevent-container { - position: relative; - z-index: 2; -} -.fc-time-grid .fc-highlight-container { - z-index: 3; - position: relative; -} -.fc-time-grid .fc-event-container { - position: relative; - z-index: 4; -} -.fc-time-grid .fc-now-indicator-line { - z-index: 5; -} -.fc-time-grid .fc-helper-container { - position: relative; - z-index: 6; -} -.fc-time-grid .fc-slats td { - height: 1.5em; - border-bottom: 0; -} -.fc-time-grid .fc-slats .fc-minor td { - border-top-style: dotted; -} -.fc-time-grid .fc-slats .ui-widget-content { - background: 0 0; -} -.fc-time-grid .fc-highlight { - position: absolute; - left: 0; - right: 0; -} -.fc-ltr .fc-time-grid .fc-event-container { - margin: 0 2.5% 0 2px; -} -.fc-rtl .fc-time-grid .fc-event-container { - margin: 0 2px 0 2.5%; -} -.fc-time-grid .fc-bgevent, -.fc-time-grid .fc-event { - position: absolute; - z-index: 1; -} -.fc-time-grid .fc-bgevent { - left: 0; - right: 0; -} -.fc-v-event.fc-not-start { - border-top-width: 0; - padding-top: 1px; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.fc-v-event.fc-not-end { - border-bottom-width: 0; - padding-bottom: 1px; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; -} -.fc-time-grid-event.fc-selected { - overflow: visible; -} -.fc-time-grid-event.fc-selected .fc-bg { - display: none; -} -.fc-time-grid-event .fc-content { - overflow: hidden; -} -.fc-time-grid-event .fc-time, -.fc-time-grid-event .fc-title { - padding: 0 1px; -} -.fc-time-grid-event .fc-time { - font-size: 0.85em; -} -.fc-time-grid-event.fc-short .fc-time, -.fc-time-grid-event.fc-short .fc-title { - display: inline-block; - vertical-align: top; -} -.fc-time-grid-event.fc-short .fc-time span { - display: none; -} -.fc-time-grid-event.fc-short .fc-time:before { - content: attr(data-start); -} -.fc-time-grid-event.fc-short .fc-time:after { - content: "\000A0-\000A0"; -} -.fc-time-grid-event.fc-short .fc-title { - font-size: 0.85em; - padding: 0; -} -.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer { - left: 0; - right: 0; - bottom: 0; - height: 8px; - overflow: hidden; - line-height: 8px; - font-size: 11px; - font-family: monospace; - text-align: center; - cursor: s-resize; -} -.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer:after { - content: "="; -} -.fc-time-grid-event.fc-selected .fc-resizer { - border-radius: 5px; - border-width: 1px; - width: 8px; - height: 8px; - border-style: solid; - border-color: inherit; - background: #fff; - left: 50%; - margin-left: -5px; - bottom: -5px; -} -.fc-time-grid .fc-now-indicator-line { - border-top-width: 1px; - left: 0; - right: 0; -} -.fc-time-grid .fc-now-indicator-arrow { - margin-top: -5px; -} -.fc-ltr .fc-time-grid .fc-now-indicator-arrow { - left: 0; - border-width: 5px 0 5px 6px; - border-top-color: transparent; - border-bottom-color: transparent; -} -.fc-rtl .fc-time-grid .fc-now-indicator-arrow { - right: 0; - border-width: 5px 6px 5px 0; - border-top-color: transparent; - border-bottom-color: transparent; -} diff --git a/src/pat/calendar/calendar-sources.html b/src/pat/calendar/calendar-sources.html deleted file mode 100644 index f0b73af4e..000000000 --- a/src/pat/calendar/calendar-sources.html +++ /dev/null @@ -1,2117 +0,0 @@ - - - - - Demo page - - - - - - -
-
-

- -

-

- Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month - Next month -

- -
-
-

- -

-

- Previous month -

- -
-
- - diff --git a/src/pat/calendar/calendar.js b/src/pat/calendar/calendar.js index 99e109e7d..c62c12e42 100644 --- a/src/pat/calendar/calendar.js +++ b/src/pat/calendar/calendar.js @@ -1,32 +1,38 @@ -/** - * Patterns calendar - Calendar with different views for patterns. - * - * Copyright 2013-2014 Marko Durkovic - * Copyright 2014 Florian Friesdorf - * Copyright 2014 Syslab.com GmbH - */ import "regenerator-runtime/runtime"; // needed for ``await`` support -import $ from "jquery"; +import Base from "../../core/base"; import logging from "../../core/logging"; +import Modal from "../modal/modal"; import Parser from "../../core/parser"; -import store from "../../core/store"; -import utils from "../../core/utils"; import registry from "../../core/registry"; -import _ from "underscore"; - -// Lazy loading modules. -let MomentTZData; +import store from "../../core/store"; -var log = logging.getLogger("calendar"), - parser = new Parser("calendar"); +const log = logging.getLogger("calendar"); +const parser = new Parser("calendar"); parser.addArgument("calendar-controls", ""); // Calendar controls must have "id" attr set parser.addArgument("category-controls", ""); parser.addArgument("column-day", "dddd M/d"); parser.addArgument("column-month", "ddd"); parser.addArgument("column-week", "ddd M/d"); -parser.addArgument("default-view", "month", [ +parser.addArgument("initial-date", null); +parser.addArgument("initial-view", "month", [ + // Simple names "month", + "week", + "day", + "list", + // FC 5 names + "dayGridMonth", + "dayGridWeek", + "dayGridDay", + "timeGridWeek", + "timeGridDay", + "listDay", + "listWeek", + "listMonth", + "listYear", + "listWeek", + // FC 3 names "basicWeek", "basicDay", "agendaWeek", @@ -35,19 +41,32 @@ parser.addArgument("default-view", "month", [ parser.addArgument("drag-and-drop", true, [true, false]); parser.addArgument("drop-external-events", true, [true, false]); parser.addArgument("external-event-selector", ""); -parser.addArgument("first-day", "0"); +parser.addArgument("first-day", null); parser.addArgument("first-hour", "6"); parser.addArgument("height", "auto"); parser.addArgument("ignore-url", false); parser.addArgument("lang", "en"); -parser.addArgument("start-date"); parser.addArgument("store", "none", ["none", "session", "local"]); parser.addArgument("time-format", "h(:mm)t"); +parser.addArgument("timezone", null); parser.addArgument("title-day", "dddd, MMM d, YYYY"); parser.addArgument("title-month", "MMMM YYYY"); parser.addArgument("title-week", "MMM D YYYY"); +parser.addArgument("event-color", "blue"); + +parser.addArgument("url", null); +parser.addArgument("event-sources", [], undefined, true); +parser.addArgument("event-sources-classes", [], undefined, true); +parser.addArgument("event-sources-active", [], undefined, true); +//parser.addArgument("add-url", null); -var calendar = { +parser.addArgument("pat-inject-source", null); +parser.addArgument("pat-inject-target", null); + +parser.addAlias("default-date", "initial-date"); +parser.addAlias("default-view", "initial-view"); + +export default Base.extend({ name: "calendar", trigger: ".pat-calendar", classMap: { @@ -55,623 +74,527 @@ var calendar = { agendaWeek: ".view-week", agendaDay: ".view-day", }, - dayNames: ["su", "mo", "tu", "we", "th", "fr", "sa"], - - _parseSearchString: function () { - var context = {}; - window.location.search - .substr(1) - .split("&") - .forEach(function (str) { - if (str) { - var keyValue = str.split("="), - key = keyValue[0], - value = decodeURIComponent(keyValue[1]); - if ( - value && - (value.match(/^\[.*\]$/) || value.match(/^\{.*\}$/)) - ) { - context[key] = JSON.parse(value); - } else { - context[key] = value; - } - } - }); - return context; + viewMap: { + month: "dayGridMonth", + week: "timeGridWeek", + day: "timeGridDay", + list: "listMonth", + basicWeek: "dayGridWeek", + basicDay: "dayGridDay", + agendaWeek: "timeGridWeek", + agendaDay: "timeGridDay", }, - - async init($elem, opts) { - MomentTZData = await import("./moment-timezone-with-data-2010-2020"); - await import("fullcalendar"); - - var lang = document - .getElementsByTagName("html")[0] - .getAttribute("lang"); - if (lang) { - // we don't support any country-specific language variants, always use first 2 letters - lang = lang.substr(0, 2).toLowerCase(); - if (lang !== "en") { - try { - await import(`fullcalendar/dist/lang/${lang}.js`); - console.log("loaded cal locale for " + lang); - } catch { - // ignore. default is english. - } + dayNames: ["su", "mo", "tu", "we", "th", "fr", "sa"], + eventSources: [], + active_categories: null, + + async init(el, opts) { + let Calendar = await import("@fullcalendar/core"); + Calendar = Calendar.Calendar; + let fcDayGrid = await import("@fullcalendar/daygrid"); + let fcInteraction = await import("@fullcalendar/interaction"); + let fcList = await import("@fullcalendar/list"); + let fcLuxon = await import("@fullcalendar/luxon"); + let fcTimeGrid = await import("@fullcalendar/timegrid"); + fcDayGrid = fcDayGrid.default; + fcInteraction = fcInteraction.default; + fcList = fcList.default; + fcLuxon = fcLuxon.default; + fcTimeGrid = fcTimeGrid.default; + + if (el.jquery) { + el = el[0]; + } + this.el = el; + + // Save some UI elements for reuse. + this.el_jump_next = el.querySelector(".jump-next"); + this.el_jump_prev = el.querySelector(".jump-prev"); + this.el_jump_today = el.querySelector(".jump-today"); + this.el_view_month = el.querySelector(".view-month"); + this.el_view_week = el.querySelector(".view-week"); + this.el_view_day = el.querySelector(".view-day"); + this.el_view_list_year = el.querySelector(".view-listYear"); + this.el_view_list_month = el.querySelector(".view-listMonth"); + this.el_view_list_week = el.querySelector(".view-listWeek"); + this.el_view_list_day = el.querySelector(".view-listDay"); + this.el_timezone = el.querySelector("select[name='timezone']"); + this.el_title = el.querySelector(".cal-title"); + + const config = {}; + opts = this.options = store.updateOptions(el, parser.parse(el, opts)); + + const storage_prefix = `${this.name}-${window.location.pathname}`; + const storage = (this.storage = + opts.store === "none" ? null : store[opts.store](storage_prefix)); + + const query_string = new URLSearchParams(window.location.search); + + config.headerToolbar = false; + config.initialDate = + query_string.get("date") || + (storage && storage.get("date")) || + opts.initial.date; + config.initialView = + query_string.get("view") || + (storage && storage.get("view")) || + opts.initial.view; + config.initialView = + this.viewMap[config.initialView] || config.initialView; + config.editable = opts.editable || false; + config.plugins = [ + fcDayGrid, + fcInteraction, + fcList, + fcLuxon, + fcTimeGrid, + ]; + config.eventColor = opts.eventColor; + + config.dateClick = this.addNewEvent.bind(this); + + let lang = + opts.lang || + document.querySelector("html").getAttribute("lang") || + "en"; + // we don't support any country-specific language variants, always use first 2 letters + lang = lang.substr(0, 2).toLowerCase(); + if (lang !== "en") { + const locale = await import(`@fullcalendar/core/locales/${lang}`); + config.locale = locale.default; + console.log("loaded cal locale for " + lang); + } + if (opts.first.day !== null) { + config.firstDay = opts.first.day; + if (this.dayNames.indexOf(opts.first.day) >= 0) { + // Set firstDay as string + config.firstDay = this.dayNames.indexOf(opts.first.day); } } - opts = opts || {}; - var $el = $elem, - cfg = store.updateOptions($el[0], parser.parse($el)), - storage = - cfg.store === "none" - ? null - : store[cfg.store](this.name + $el[0].id); - this.$el = $el; - this.cfg = cfg; - this.storage = storage; - cfg.defaultDate = (storage && storage.get("date")) || cfg.defaultDate; - cfg.defaultView = (storage && storage.get("view")) || cfg.defaultView; - cfg.tooltipConfig = $el.data("patCalendarTooltip"); - cfg.modalConfig = $el.data("patCalendarModal"); - if (cfg.tooltipConfig) { - var match = cfg.tooltipConfig.match(/url:[ ](.*?)(;|$)/); - cfg.tooltipConfig = cfg.tooltipConfig.replace(match[0], ""); - cfg.newEventURL = match[1]; + let timezone = this.el_timezone?.value || opts.timezone || null; + if (timezone) { + config.timeZone = timezone; } - if (!opts.ignoreUrl) { - var search = calendar._parseSearchString(); - if (search["default-date"]) { - cfg.defaultDate = search["default-date"]; - } - if (search["default-view"]) { - cfg.defaultView = search["default-view"]; + const sources = opts.event.sources || []; + if (opts.url && !sources.includes(opts.url)) { + // add, but do not re-add same source twice. + sources.push(opts.url); + } + config.eventSources = []; + for (const [idx, url] of sources.entries()) { + const src = this.create_event_source(idx, url); + this.eventSources.push(src); // we need to keep all srcs untouched to add/remove from fc eventSources. + if ( + opts.event["sources-active"].length === 0 || + opts.event["sources-active"][idx] === "on" + ) { + config.eventSources.push(src); } } - var calOpts = { - lang: lang, - axisFormat: cfg.timeFormat, - columnFormat: cfg.column, - defaultDate: cfg.defaultDate, - defaultView: cfg.defaultView, - droppable: cfg.dropExternalEvents, // Enable dropping of external elements (i.e. not events) - editable: cfg.dragAndDrop, // Enable drag&drop and drag2resize of events - dropAccept: cfg.externalEventSelector, - firstDay: - this.dayNames.indexOf(cfg.first.day) >= 0 - ? this.dayNames.indexOf(cfg.first.day) - : 0, - firstHour: cfg.first.hour, - header: false, - height: cfg.height !== "auto" ? cfg.height : undefined, - timeFormat: cfg.timeFormat, - titleFormat: cfg.title, - viewRender: calendar.highlightButtons, - - // Callback functions - // ------------------ - drop: this._externalEventDropped, - eventDrop: this._changeEventDates, - eventResize: this._changeEventDates, - events: function (start, end, timezone, callback) { - var events = calendar.parseEvents($el, timezone); - callback(events); - }.bind(this), - eventAfterRender: function (ev, $event) { - var url = ""; - if (ev.id === "pat-calendar-new-event") { - url = utils.addURLQueryParameter( - cfg.newEventURL, - "date", - ev.start.format() - ); - registry.scan( - $event - .addClass("pat-tooltip") - .attr({ "data-pat-tooltip": cfg.tooltipConfig }) - .attr({ href: url }) - ); - $event.trigger("click.tooltip"); - $event.on("pat-update", function (event, data) { - if ( - data.pattern === "tooltip" && - data.hidden === true - ) { - event.stopPropagation(); - if ($(this).is(":visible")) { - $el.fullCalendar("removeEvents", ev.id); - } - } - }); - } else { - url = ev.url; - registry.scan( - $event - .addClass("pat-tooltip") - .attr({ "data-pat-tooltip": cfg.tooltipConfig }) - .attr({ href: url }) - ); - $event.on("pat-update", function (event, data) { - if ( - data.pattern === "tooltip" && - data.hidden === true - ) { - event.stopPropagation(); - } - }); - } - }, - dayClick: function (moment, ev, view) { - /* If "data-pat-calendar-tooltip" was configured, we open - * a tooltip when the user clicks on an day in the - * calendar. - */ - if (!(cfg.tooltipConfig && cfg.newEventURL)) { - return; - } - var end; - if (view.name !== "month") { - end = moment.clone().add(30, "minutes"); - } else { - end = undefined; - } - var id = "pat-calendar-new-event"; - $el.fullCalendar("removeEvents", id); - $el.fullCalendar("renderEvent", { - title: "New Event", - start: moment, - end: end, - id: id, - }); - }, + // Restore category controls from local storage before showing events. + this._restoreCategoryControls(); + this._registerCategoryControls(); + this.reset_active_categories(); + config.eventDidMount = (args) => this.init_event(args); + + // Need to create a sub-element of ``pat-calendar`` to allow custom + // controls within pat-calendar to not be overwritten. + const cal_el = document.createElement("div"); + cal_el.setAttribute("class", "pat-calendar__fc"); + el.appendChild(cal_el); + + // Create a element for modals/injections + this.mod_el = document.createElement("section"); + this.mod_el.setAttribute("class", "pat-calendar__modal"); + el.appendChild(this.mod_el); + + let calendar = (this.calendar = new Calendar(cal_el, config)); + calendar.render(); + + calendar.on("datesSet", this._viewChanged.bind(this)); + calendar.on("dateClick", this._viewChanged.bind(this)); + + if (this.el_title) { + this.el_title.innerHTML = calendar.view.title; + } + + this._registerCalendarControls(); + this.setActiveClasses(); + }, + + create_event_source(idx, url) { + return { + id: `event-source--${idx + 1}`, + events: (info, success, failure) => + this._fetch_events(url, info, success, failure), + className: + this.options.event["sources-classes"][idx] || + `event-source--${idx + 1}`, }; + }, - $el.categories = $( - _.uniq( - $el.find(".cal-events .cal-event").map(function () { - return this.className.split(" ").filter(function (cls) { - return /^cal-cat/.test(cls); - }); - }) - ) - ); - this._registerEventRefetchers($el); - this._registerCategoryControls($el); - var $controlRoot = cfg.calendarControls ? $(cfg.calendarControls) : $el; - $el.$controlRoot = $controlRoot; - cfg.timezone = calOpts.timezone = $controlRoot - .find("select.timezone") - .val(); - $el.fullCalendar(calOpts); - $el.find(".fc-content").appendTo($el); // move to end of $el - this._registerRedrawHandlers(); - $el.find(".cal-title").text($el.fullCalendar("getView").title); - $el.$controlRoot - .find(this.classMap[calOpts.defaultView]) - .addClass("active"); - calendar._registerCalendarControls($el); - $el.find(".cal-events").css("display", "none"); - this._restoreCalendarControls(); - setTimeout(function () { - $el.fullCalendar( - "option", - "height", - $el.find(".fc-view-container").height() - ); - $el.fullCalendar("refetchEvents"); - }, 900); - // } ) + event_mapper(event) { + // Maps backend results to the fullcalendar event schema. + // Default implementation confirms to plone.restapi conventions. + const ret = { + id: event.UID, + title: event.title, + start: new Date(event.start), + end: new Date(event.end), + allDay: event.whole_day, + url: event["@id"], + + backgroundColor: event.color, + borderColor: event.color, + classNames: event.class ? event.class.split(" ") : [], + + // non fullcalendar standard fields + description: event.description, + text: event.text, + location: event.location, + open_end: event.open_end, + recurrence: event.recurrence, + attendees: event.attendees, + contact_name: event.contact_name, + contact_phone: event.contact_phone, + contact_email: event.contact_email, + event_url: event.event_url, + }; + return ret; }, - _addNewEvent: function ($el, $event, data) { - /* Add a new event to the list of events parsed by fullcalendar. - * Used when dropping a foreign element. - */ - // FIXME: this code is makes too much assumptions of the structure - // of the dropped element. Needs to be made more generic, together - // with parseEvents. - var $events = $el.find(".cal-events"); - var $details = $event.find("ul.details"); - $details.append( - $("
  • ").append( - $("
  • ").append( - $("