summaryrefslogtreecommitdiff
path: root/includes/composer/vendor
diff options
context:
space:
mode:
authorRaindropsSys <contact@minteck.org>2023-03-21 16:21:21 +0100
committerRaindropsSys <contact@minteck.org>2023-03-21 16:21:21 +0100
commit475c5731bf3362b6ac8d2dc5d5b43e4b4a6117bd (patch)
tree2cff46debf9c1e13892e7babff9deb6874ecb4b2 /includes/composer/vendor
parent7ccc2de87f9e25c715dc09b9aba4eb5c66f80424 (diff)
downloadpluralconnect-475c5731bf3362b6ac8d2dc5d5b43e4b4a6117bd.tar.gz
pluralconnect-475c5731bf3362b6ac8d2dc5d5b43e4b4a6117bd.tar.bz2
pluralconnect-475c5731bf3362b6ac8d2dc5d5b43e4b4a6117bd.zip
Updated 26 files and added 1074 files (automated)
Diffstat (limited to 'includes/composer/vendor')
-rw-r--r--includes/composer/vendor/composer/autoload_classmap.php4
-rw-r--r--includes/composer/vendor/composer/autoload_static.php4
-rw-r--r--includes/composer/vendor/composer/installed.json54
-rw-r--r--includes/composer/vendor/composer/installed.php13
-rw-r--r--includes/composer/vendor/composer/platform_check.php4
-rw-r--r--includes/composer/vendor/om/icalparser/.github/workflows/php.yml40
-rw-r--r--includes/composer/vendor/om/icalparser/.github/workflows/typo.yml14
-rw-r--r--includes/composer/vendor/om/icalparser/.gitignore4
-rw-r--r--includes/composer/vendor/om/icalparser/LICENSE24
-rw-r--r--includes/composer/vendor/om/icalparser/bin/timezones.php20
-rw-r--r--includes/composer/vendor/om/icalparser/composer.json33
-rw-r--r--includes/composer/vendor/om/icalparser/example/index.php33
-rw-r--r--includes/composer/vendor/om/icalparser/readme.md79
-rw-r--r--includes/composer/vendor/om/icalparser/src/EventsList.php56
-rw-r--r--includes/composer/vendor/om/icalparser/src/Freq.php634
-rw-r--r--includes/composer/vendor/om/icalparser/src/IcalParser.php502
-rw-r--r--includes/composer/vendor/om/icalparser/src/Recurrence.php234
-rw-r--r--includes/composer/vendor/om/icalparser/src/WindowsTimezones.php214
-rw-r--r--includes/composer/vendor/om/icalparser/tests/bootstrap.php21
-rw-r--r--includes/composer/vendor/om/icalparser/tests/cal/38_weekly_recurring_event_missing_day.ics59
-rw-r--r--includes/composer/vendor/om/icalparser/tests/cal/FrenchHolidays.ics382
-rw-r--r--includes/composer/vendor/om/icalparser/tests/cal/basic.ics616
-rwxr-xr-xincludes/composer/vendor/om/icalparser/tests/cal/blank_description.ics51
-rwxr-xr-xincludes/composer/vendor/om/icalparser/tests/cal/blank_line_end.ics4
-rwxr-xr-xincludes/composer/vendor/om/icalparser/tests/cal/blank_line_mid.ics4
-rwxr-xr-xincludes/composer/vendor/om/icalparser/tests/cal/daily_recur.ics52
-rwxr-xr-xincludes/composer/vendor/om/icalparser/tests/cal/daily_recur2.ics41
-rwxr-xr-xincludes/composer/vendor/om/icalparser/tests/cal/day_long_recur_yearly.ics52
-rwxr-xr-xincludes/composer/vendor/om/icalparser/tests/cal/forced_types.ics50
-rwxr-xr-xincludes/composer/vendor/om/icalparser/tests/cal/google_birthday.ics90
-rwxr-xr-xincludes/composer/vendor/om/icalparser/tests/cal/minimal.ics39
-rw-r--r--includes/composer/vendor/om/icalparser/tests/cal/missing-timezone.ics47
-rw-r--r--includes/composer/vendor/om/icalparser/tests/cal/missing_RRULE_notice.ics39
-rw-r--r--includes/composer/vendor/om/icalparser/tests/cal/multiline_description.ics52
-rwxr-xr-xincludes/composer/vendor/om/icalparser/tests/cal/multiple_attachments.ics45
-rwxr-xr-xincludes/composer/vendor/om/icalparser/tests/cal/multiple_categories.ics67
-rwxr-xr-xincludes/composer/vendor/om/icalparser/tests/cal/multiple_rrules.ics45
-rwxr-xr-xincludes/composer/vendor/om/icalparser/tests/cal/parserv2.ics45
-rw-r--r--includes/composer/vendor/om/icalparser/tests/cal/readme.md3
-rwxr-xr-xincludes/composer/vendor/om/icalparser/tests/cal/recur_instances.ics92
-rwxr-xr-xincludes/composer/vendor/om/icalparser/tests/cal/recur_instances_finite.ics50
-rw-r--r--includes/composer/vendor/om/icalparser/tests/cal/recur_instances_with_modifications.ics63
-rw-r--r--includes/composer/vendor/om/icalparser/tests/cal/recur_instances_with_modifications_and_interval.ics578
-rwxr-xr-xincludes/composer/vendor/om/icalparser/tests/cal/recur_instances_with_modifications_to_first_day.ics57
-rw-r--r--includes/composer/vendor/om/icalparser/tests/cal/rrule_interval.ics34
-rw-r--r--includes/composer/vendor/om/icalparser/tests/cal/url.ics32
-rwxr-xr-xincludes/composer/vendor/om/icalparser/tests/cal/utc_negative_zero.ics27
-rw-r--r--includes/composer/vendor/om/icalparser/tests/cal/weird_windows_timezones.ics237
-rw-r--r--includes/composer/vendor/om/icalparser/tests/cal/wrong_dates.ics37
-rw-r--r--includes/composer/vendor/om/icalparser/tests/event.attachements.phpt26
-rw-r--r--includes/composer/vendor/om/icalparser/tests/event.categories.phpt25
-rw-r--r--includes/composer/vendor/om/icalparser/tests/event.dates.phpt25
-rw-r--r--includes/composer/vendor/om/icalparser/tests/event.description.phpt34
-rw-r--r--includes/composer/vendor/om/icalparser/tests/event.timezones.phpt39
-rw-r--r--includes/composer/vendor/om/icalparser/tests/event.url.phpt22
-rw-r--r--includes/composer/vendor/om/icalparser/tests/events.recurring.phpt260
-rw-r--r--includes/composer/vendor/om/icalparser/tests/events.sorting.phpt36
-rw-r--r--includes/composer/vendor/om/icalparser/tests/missing.timezone.phpt24
58 files changed, 5464 insertions, 4 deletions
diff --git a/includes/composer/vendor/composer/autoload_classmap.php b/includes/composer/vendor/composer/autoload_classmap.php
index 0fb0a2c..6d7608b 100644
--- a/includes/composer/vendor/composer/autoload_classmap.php
+++ b/includes/composer/vendor/composer/autoload_classmap.php
@@ -7,4 +7,8 @@ $baseDir = dirname($vendorDir);
return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
+ 'om\\EventsList' => $vendorDir . '/om/icalparser/src/EventsList.php',
+ 'om\\Freq' => $vendorDir . '/om/icalparser/src/Freq.php',
+ 'om\\IcalParser' => $vendorDir . '/om/icalparser/src/IcalParser.php',
+ 'om\\Recurrence' => $vendorDir . '/om/icalparser/src/Recurrence.php',
);
diff --git a/includes/composer/vendor/composer/autoload_static.php b/includes/composer/vendor/composer/autoload_static.php
index a31b292..25a84e5 100644
--- a/includes/composer/vendor/composer/autoload_static.php
+++ b/includes/composer/vendor/composer/autoload_static.php
@@ -22,6 +22,10 @@ class ComposerStaticInit7bef9e3ab3ffae606875b2457b7bf1cb
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
+ 'om\\EventsList' => __DIR__ . '/..' . '/om/icalparser/src/EventsList.php',
+ 'om\\Freq' => __DIR__ . '/..' . '/om/icalparser/src/Freq.php',
+ 'om\\IcalParser' => __DIR__ . '/..' . '/om/icalparser/src/IcalParser.php',
+ 'om\\Recurrence' => __DIR__ . '/..' . '/om/icalparser/src/Recurrence.php',
);
public static function getInitializer(ClassLoader $loader)
diff --git a/includes/composer/vendor/composer/installed.json b/includes/composer/vendor/composer/installed.json
index e3ad241..51c1004 100644
--- a/includes/composer/vendor/composer/installed.json
+++ b/includes/composer/vendor/composer/installed.json
@@ -62,6 +62,60 @@
"source": "https://github.com/ksubileau/color-thief-php/tree/v2.0.1"
},
"install-path": "../ksubileau/color-thief-php"
+ },
+ {
+ "name": "om/icalparser",
+ "version": "v3.0.6",
+ "version_normalized": "3.0.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/OzzyCzech/icalparser.git",
+ "reference": "1481bc442a48cd1678291958b33efa22a628fb0b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/OzzyCzech/icalparser/zipball/1481bc442a48cd1678291958b33efa22a628fb0b",
+ "reference": "1481bc442a48cd1678291958b33efa22a628fb0b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "require-dev": {
+ "nette/tester": "^2.4"
+ },
+ "suggest": {
+ "ext-dom": "for timezone tool"
+ },
+ "time": "2023-02-09T07:45:07+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Roman Ožana",
+ "email": "roman@ozana.cz"
+ }
+ ],
+ "description": "Simple iCal parser",
+ "keywords": [
+ "calendar",
+ "ical",
+ "parser"
+ ],
+ "support": {
+ "issues": "https://github.com/OzzyCzech/icalparser/issues",
+ "source": "https://github.com/OzzyCzech/icalparser/tree/v3.0.6"
+ },
+ "install-path": "../om/icalparser"
}
],
"dev": true,
diff --git a/includes/composer/vendor/composer/installed.php b/includes/composer/vendor/composer/installed.php
index ade214e..a9241b9 100644
--- a/includes/composer/vendor/composer/installed.php
+++ b/includes/composer/vendor/composer/installed.php
@@ -3,7 +3,7 @@
'name' => '__root__',
'pretty_version' => 'dev-mane',
'version' => 'dev-mane',
- 'reference' => '8cc1f13c17fa2fb5a4410542d39e650e02945634',
+ 'reference' => '7ccc2de87f9e25c715dc09b9aba4eb5c66f80424',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -13,7 +13,7 @@
'__root__' => array(
'pretty_version' => 'dev-mane',
'version' => 'dev-mane',
- 'reference' => '8cc1f13c17fa2fb5a4410542d39e650e02945634',
+ 'reference' => '7ccc2de87f9e25c715dc09b9aba4eb5c66f80424',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -28,5 +28,14 @@
'aliases' => array(),
'dev_requirement' => false,
),
+ 'om/icalparser' => array(
+ 'pretty_version' => 'v3.0.6',
+ 'version' => '3.0.6.0',
+ 'reference' => '1481bc442a48cd1678291958b33efa22a628fb0b',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../om/icalparser',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
),
);
diff --git a/includes/composer/vendor/composer/platform_check.php b/includes/composer/vendor/composer/platform_check.php
index 589e9e7..adfb472 100644
--- a/includes/composer/vendor/composer/platform_check.php
+++ b/includes/composer/vendor/composer/platform_check.php
@@ -4,8 +4,8 @@
$issues = array();
-if (!(PHP_VERSION_ID >= 70200)) {
- $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0". You are running ' . PHP_VERSION . '.';
+if (!(PHP_VERSION_ID >= 80000)) {
+ $issues[] = 'Your Composer dependencies require a PHP version ">= 8.0.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
diff --git a/includes/composer/vendor/om/icalparser/.github/workflows/php.yml b/includes/composer/vendor/om/icalparser/.github/workflows/php.yml
new file mode 100644
index 0000000..e502b81
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/.github/workflows/php.yml
@@ -0,0 +1,40 @@
+name: PHP Tests
+
+on: [ push, pull_request ]
+
+jobs:
+
+ build:
+ strategy:
+ matrix:
+ operating-system: [ ubuntu-latest ]
+ php-versions: [ '8.0', '8.1', '8.2' ]
+ experimental: [ false ]
+ include:
+ - php-versions: '8.3'
+ operating-system: ubuntu-latest
+ experimental: true
+ fail-fast: false
+ runs-on: ${{ matrix.operating-system }}
+ continue-on-error: ${{ matrix.experimental }}
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php-versions }}
+
+ - name: Validate composer.json and composer.lock
+ run: composer validate
+
+ - name: Install dependencies
+ if: "${{ matrix.experimental == false }}"
+ run: composer install --prefer-dist --no-progress --no-suggest
+
+ - name: Install dependencies, ignore php requirements
+ if: "${{ matrix.experimental == true }}"
+ run: composer install --prefer-dist --no-progress --no-suggest --ignore-platform-reqs
+
+ - name: Run test suite
+ run: composer run-script test
diff --git a/includes/composer/vendor/om/icalparser/.github/workflows/typo.yml b/includes/composer/vendor/om/icalparser/.github/workflows/typo.yml
new file mode 100644
index 0000000..97d55a2
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/.github/workflows/typo.yml
@@ -0,0 +1,14 @@
+name: Typo Tests
+on: [ pull_request ]
+
+jobs:
+ misspell:
+ name: Check for typos / misspells
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Run misspell with reviewdog
+ uses: reviewdog/action-misspell@v1
+ with:
+ github_token: ${{ secrets.github_token }}
diff --git a/includes/composer/vendor/om/icalparser/.gitignore b/includes/composer/vendor/om/icalparser/.gitignore
new file mode 100644
index 0000000..4a59cd1
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/.gitignore
@@ -0,0 +1,4 @@
+.idea
+composer.lock
+/vendor/
+tests/output/ \ No newline at end of file
diff --git a/includes/composer/vendor/om/icalparser/LICENSE b/includes/composer/vendor/om/icalparser/LICENSE
new file mode 100644
index 0000000..ce7d41d
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2014-2022, Roman Ožana
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the <organization> nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/includes/composer/vendor/om/icalparser/bin/timezones.php b/includes/composer/vendor/om/icalparser/bin/timezones.php
new file mode 100644
index 0000000..3bf3708
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/bin/timezones.php
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * This file generates a map from windows timezones to tz database timezones
+ *
+ * @author Clement Wong <cw@clement.hk>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+$windows_timezones = [];
+$windowstimezonexml = new DOMDocument();
+$windowstimezonexml->load('https://raw.githubusercontent.com/unicode-org/cldr/master/common/supplemental/windowsZones.xml');
+$zones = $windowstimezonexml->getElementsByTagName('mapZone');
+foreach ($zones as $zone) {
+ if ($zone->getAttribute('territory') === '001') {
+ $windows_timezones[$zone->getAttribute('other')] = $zone->getAttribute('type');
+ }
+}
+
+file_put_contents(__DIR__ . '/../src/WindowsTimezones.php', "<?php\n\$windows_timezones = " . var_export($windows_timezones, true) . ';');
+
diff --git a/includes/composer/vendor/om/icalparser/composer.json b/includes/composer/vendor/om/icalparser/composer.json
new file mode 100644
index 0000000..736fc7a
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/composer.json
@@ -0,0 +1,33 @@
+{
+ "name": "om/icalparser",
+ "license": "BSD-3-Clause",
+ "description": "Simple iCal parser",
+ "keywords": [
+ "iCal",
+ "calendar",
+ "parser"
+ ],
+ "authors": [
+ {
+ "name": "Roman Ožana",
+ "email": "roman@ozana.cz"
+ }
+ ],
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "suggest": {
+ "ext-dom": "for timezone tool"
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "scripts": {
+ "test": "tester tests -s -p php"
+ },
+ "require-dev": {
+ "nette/tester": "^2.4"
+ }
+}
diff --git a/includes/composer/vendor/om/icalparser/example/index.php b/includes/composer/vendor/om/icalparser/example/index.php
new file mode 100644
index 0000000..70c5598
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/example/index.php
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="cs-CZ">
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Ical Parser example</title>
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap/dist/css/bootstrap.min.css">
+</head>
+<body>
+<div class="container">
+ <h1>Czech holidays</h1>
+ <table class="table table-bordered">
+ <thead>
+ <tr>
+ <th class="text-end">Date</th>
+ <th>Summary</th>
+ </tr>
+ </thead>
+ <tbody>
+ <?php
+ require_once __DIR__ . '/../vendor/autoload.php';
+ $cal = new \om\IcalParser();
+ $results = $cal->parseFile('https://www.google.com/calendar/ical/cs.czech%23holiday%40group.v.calendar.google.com/public/basic.ics');
+ foreach ($cal->getEvents()->sorted() as $event) {
+ printf('<tr><th class="text-end">%s</th><td>%s</td></tr>', $event['DTSTART']->format('j.n.Y'), $event['SUMMARY']);
+ }
+ ?>
+ </tbody>
+ </table>
+</div>
+</body>
+</html>
diff --git a/includes/composer/vendor/om/icalparser/readme.md b/includes/composer/vendor/om/icalparser/readme.md
new file mode 100644
index 0000000..6666c10
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/readme.md
@@ -0,0 +1,79 @@
+# PHP iCal Parser
+
+[![PHP Tests](https://github.com/OzzyCzech/icalparser/actions/workflows/php.yml/badge.svg)](https://github.com/OzzyCzech/icalparser/actions/workflows/php.yml)
+[![Latest Stable Version](https://poser.pugx.org/om/icalparser/v/stable)](https://packagist.org/packages/om/icalparser)
+[![Total Downloads](https://poser.pugx.org/om/icalparser/downloads)](https://packagist.org/packages/om/icalparser)
+[![License](https://poser.pugx.org/om/icalparser/license)](https://packagist.org/packages/om/icalparser)
+[![PHP Version Require](http://poser.pugx.org/om/icalparser/require/php)](https://packagist.org/packages/om/icalparser)
+
+Internet Calendaring Parser [rfc2445](https://www.ietf.org/rfc/rfc2445.txt) or iCal parser is simple PHP class for parsing format into array.
+
+[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/ozzyczech)
+
+## How to install
+
+The recommended way to is via Composer:
+
+```shell script
+composer require om/icalparser
+```
+
+## Usage and example
+
+```php
+<?php
+use om\IcalParser;
+require_once '../vendor/autoload.php';
+
+$cal = new IcalParser();
+$results = $cal->parseFile(
+ 'https://www.google.com/calendar/ical/cs.czech%23holiday%40group.v.calendar.google.com/public/basic.ics'
+);
+
+foreach ($cal->getEvents()->sorted() as $event) {
+ printf('%s - %s' . PHP_EOL, $event['DTSTART']->format('j.n.Y'), $event['SUMMARY']);
+
+}
+```
+
+Each property of each event is available using the property name (in capital letters) as a key.
+There are some special cases:
+
+- multiple attendees with individual parameters: use `ATTENDEES` as key to get all attendees in the following scheme:
+```php
+[
+ [
+ 'ROLE' => 'REQ-PARTICIPANT',
+ 'PARTSTAT' => 'NEEDS-ACTION',
+ 'CN' => 'John Doe',
+ 'VALUE' => 'mailto:john.doe@example.org'
+ ],
+ [
+ 'ROLE' => 'REQ-PARTICIPANT',
+ 'PARTSTAT' => 'NEEDS-ACTION',
+ 'CN' => 'Test Example',
+ 'VALUE' => 'mailto:test@example.org'
+ ]
+]
+```
+- organizer's name: the *CN* parameter of the organizer property can be retrieved using the key `ORGANIZER-CN`
+
+You can run example with [PHP Built-in web server](https://www.php.net/manual/en/features.commandline.webserver.php) as follow:
+
+```shell
+php -S localhost:8000 -t example
+```
+
+## Requirements
+
+- PHP 8.0+
+
+## Run tests
+
+iCal parser using [Nette Tester](https://github.com/nette/tester). The tests can be invoked via [composer](https://getcomposer.org/).
+
+```shell script
+composer update
+composer test
+```
+
diff --git a/includes/composer/vendor/om/icalparser/src/EventsList.php b/includes/composer/vendor/om/icalparser/src/EventsList.php
new file mode 100644
index 0000000..241eb8c
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/src/EventsList.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace om;
+
+use ArrayObject;
+
+/**
+ * Copyright (c) 2004-2022 Roman Ožana (https://ozana.cz)
+ *
+ * @license BSD-3-Clause
+ * @author Roman Ožana <roman@ozana.cz>
+ */
+class EventsList extends ArrayObject {
+
+ /**
+ * Return array of Events
+ *
+ * @return array
+ */
+ public function getArrayCopy(): array {
+ return array_values(parent::getArrayCopy());
+ }
+
+ /**
+ * Return sorted EventList (the newest dates are first)
+ *
+ * @return $this
+ */
+ public function sorted(): EventsList {
+ $this->uasort(static function ($a, $b): int {
+ if ($a['DTSTART'] === $b['DTSTART']) {
+ return 0;
+ }
+ return ($a['DTSTART'] < $b['DTSTART']) ? -1 : 1;
+ });
+
+ return $this;
+ }
+
+ /**
+ * Return reversed sorted EventList (the oldest dates are first)
+ *
+ * @return $this
+ */
+ public function reversed(): EventsList {
+ $this->uasort(static function ($a, $b): int {
+ if ($a['DTSTART'] === $b['DTSTART']) {
+ return 0;
+ }
+ return ($a['DTSTART'] > $b['DTSTART']) ? -1 : 1;
+ });
+
+ return $this;
+ }
+
+}
diff --git a/includes/composer/vendor/om/icalparser/src/Freq.php b/includes/composer/vendor/om/icalparser/src/Freq.php
new file mode 100644
index 0000000..15cf626
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/src/Freq.php
@@ -0,0 +1,634 @@
+<?php
+
+namespace om;
+
+use DateTime;
+use DateTimeZone;
+use Exception;
+
+/**
+ * Class taken from https://github.com/coopTilleuls/intouch-iCalendar.git (Freq.php)
+ *
+ * @author PC Drew <pc@schoolblocks.com>
+ */
+
+/**
+ * A class to store Frequency-rules in. Will allow a easy way to find the
+ * last and next occurrence of the rule.
+ *
+ * No - this is so not pretty. But.. ehh.. You do it better, and I will
+ * gladly accept patches.
+ *
+ * Created by trail-and-error on the examples given in the RFC.
+ *
+ * TODO: Update to a better way of doing calculating the different options.
+ * Instead of only keeping track of the best of the current dates found
+ * it should instead keep a array of all the calculated dates within the
+ * period.
+ * This should fix the issues with multi-rule + multi-rule interference,
+ * and make it possible to implement the SETPOS rule.
+ * By pushing the next period onto the stack as the last option will
+ * (hopefully) remove the need for the awful simpleMode
+ *
+ * @author Morten Fangel (C) 2008
+ * @author Michael Kahn (C) 2013
+ * @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
+ */
+class Freq {
+
+ /** @var bool */
+ static bool $debug = false;
+
+ protected array $weekdays = [
+ 'MO' => 'monday',
+ 'TU' => 'tuesday',
+ 'WE' => 'wednesday',
+ 'TH' => 'thursday',
+ 'FR' => 'friday',
+ 'SA' => 'saturday',
+ 'SU' => 'sunday',
+ ];
+ protected array $knownRules = [
+ 'month',
+ 'weekno',
+ 'day',
+ 'monthday',
+ 'yearday',
+ 'hour',
+ 'minute',
+ ]; //others : 'setpos', 'second'
+
+ protected array $ruleModifiers = ['wkst'];
+ protected bool $simpleMode = true;
+
+ protected array $rules = ['freq' => 'yearly', 'interval' => 1];
+ protected int $start = 0;
+ protected string $freq = '';
+
+ protected array $excluded; //EXDATE
+ protected array $added; //RDATE
+
+ protected array $cache; // getAllOccurrences()
+
+ /**
+ * Constructs a new Frequency-rule
+ *
+ * @param array|string $rule
+ * @param int $start Unix-timestamp (important : Need to be the start of Event)
+ * @param array $excluded of int (timestamps), see EXDATE documentation
+ * @param array $added of int (timestamps), see RDATE documentation
+ * @throws Exception
+ */
+ public function __construct(array|string $rule, int $start, array $excluded = [], array $added = []) {
+ $this->start = $start;
+ $this->excluded = [];
+
+ $rules = [];
+ foreach ($rule as $k => $v) {
+ $this->rules[strtolower($k)] = $v;
+ }
+
+ if (isset($this->rules['until']) && is_string($this->rules['until'])) {
+ $this->rules['until'] = strtotime($this->rules['until']);
+ } elseif ($this->rules['until'] instanceof DateTime) {
+ $this->rules['until'] = $this->rules['until']->getTimestamp();
+ }
+ $this->freq = strtolower($this->rules['freq']);
+
+ foreach ($this->knownRules as $rule) {
+ if (isset($this->rules['by' . $rule])) {
+ if ($this->isPrerule($rule, $this->freq)) {
+ $this->simpleMode = false;
+ }
+ }
+ }
+
+ if (!$this->simpleMode) {
+ if (!(isset($this->rules['byday']) || isset($this->rules['bymonthday']) || isset($this->rules['byyearday']))) {
+ $this->rules['bymonthday'] = date('d', $this->start);
+ }
+ }
+
+ //set until, and cache
+ if (isset($this->rules['count'])) {
+
+ $cache[$ts] = $ts = $this->start;
+ for ($n = 1; $n < $this->rules['count']; $n++) {
+ $ts = $this->findNext($ts);
+ $cache[$ts] = $ts;
+ }
+ $this->rules['until'] = $ts;
+
+ //EXDATE
+ if (!empty($excluded)) {
+ foreach ($excluded as $ts) {
+ unset($cache[$ts]);
+ }
+ }
+ //RDATE
+ if (!empty($added)) {
+ $cache = array_unique(array_merge(array_values($cache), $added));
+ asort($cache);
+ }
+
+ $this->cache = array_values($cache);
+ }
+
+ $this->excluded = $excluded;
+ $this->added = $added;
+ }
+
+ private function isPrerule(string $rule, string $freq): bool {
+ if ($rule === 'year') {
+ return false;
+ }
+ if ($rule === 'month' && $freq === 'yearly') {
+ return true;
+ }
+ if ($rule === 'monthday' && in_array($freq, ['yearly', 'monthly']) && !isset($this->rules['byday'])) {
+ return true;
+ }
+ // TODO: is it faster to do monthday first, and ignore day if monthday exists? - prolly by a factor of 4..
+ if ($rule === 'yearday' && $freq === 'yearly') {
+ return true;
+ }
+ if ($rule === 'weekno' && $freq === 'yearly') {
+ return true;
+ }
+ if ($rule === 'day' && in_array($freq, ['yearly', 'monthly', 'weekly'])) {
+ return true;
+ }
+ if ($rule === 'hour' && in_array($freq, ['yearly', 'monthly', 'weekly', 'daily'])) {
+ return true;
+ }
+ if ($rule === 'minute') {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Calculates the next time after the given offset that the rule
+ * will apply.
+ *
+ * The approach to finding the next is as follows:
+ * First we establish a timeframe to find timestamps in. This is
+ * between $offset and the end of the period that $offset is in.
+ *
+ * We then loop though all the rules (that is a Prerule in the
+ * current freq.), and finds the smallest timestamp inside the
+ * timeframe.
+ *
+ * If we find something, we check if the date is a valid recurrence
+ * (with validDate). If it is, we return it. Otherwise we try to
+ * find a new date inside the same timeframe (but using the new-
+ * found date as offset)
+ *
+ * If no new timestamps were found in the period, we try in the
+ * next period
+ *
+ * @param int $offset
+ * @return int|bool
+ * @throws Exception
+ */
+ public function findNext(int $offset): bool|int {
+ if (!empty($this->cache)) {
+ foreach ($this->cache as $ts) {
+ if ($ts > $offset) {
+ return $ts;
+ }
+ }
+ }
+
+ //make sure the offset is valid
+ if ($offset === false || (isset($this->rules['until']) && $offset > $this->rules['until'])) {
+ if (static::$debug) printf("STOP: %s\n", date('r', $offset));
+ return false;
+ }
+
+ $found = true;
+
+ //set the timestamp of the offset (ignoring hours and minutes unless we want them to be
+ //part of the calculations.
+ if (static::$debug) printf("O: %s\n", date('r', $offset));
+ $hour = (in_array($this->freq, ['hourly', 'minutely']) && $offset > $this->start) ? date('H', $offset) : date(
+ 'H', $this->start
+ );
+ $minute = (($this->freq === 'minutely' || isset($this->rules['byminute'])) && $offset > $this->start) ? date(
+ 'i', $offset
+ ) : date('i', $this->start);
+ $t = mktime($hour, $minute, date('s', $this->start), date('m', $offset), date('d', $offset), date('Y', $offset));
+ if (static::$debug) printf("START: %s\n", date('r', $t));
+
+ if ($this->simpleMode) {
+ if ($offset < $t) {
+ $ts = $t;
+ if ($ts && in_array($ts, $this->excluded, true)) {
+ $ts = $this->findNext($ts);
+ }
+ } else {
+ $ts = $this->findStartingPoint($t, $this->rules['interval'], false);
+ if (!$this->validDate($ts)) {
+ $ts = $this->findNext($ts);
+ }
+ }
+
+ return $ts;
+ }
+
+ //EOP needs to have the same TIME as START ($t)
+ $tO = new DateTime('@' . $t, new DateTimeZone('UTC'));
+
+ $eop = $this->findEndOfPeriod($offset);
+ $eopO = new DateTime('@' . $eop, new DateTimeZone('UTC'));
+ $eopO->setTime($tO->format('H'), $tO->format('i'), $tO->format('s'));
+ $eop = $eopO->getTimestamp();
+ unset($eopO, $tO);
+
+ if (static::$debug) {
+ echo 'EOP: ' . date('r', $eop) . "\n";
+ }
+ foreach ($this->knownRules as $rule) {
+ if ($found && isset($this->rules['by' . $rule])) {
+ if ($this->isPrerule($rule, $this->freq)) {
+ $subRules = explode(',', $this->rules['by' . $rule]);
+ $_t = null;
+ foreach ($subRules as $subRule) {
+ $imm = call_user_func_array([$this, "ruleBy$rule"], [$subRule, $t]);
+ if ($imm === false) {
+ break;
+ }
+ if (static::$debug) {
+ printf("%s: %s A: %d\n", strtoupper($rule), date('r', $imm), intval($imm > $offset && $imm < $eop));
+ }
+ if ($imm > $offset && $imm <= $eop && ($_t == null || $imm < $_t)) {
+ $_t = $imm;
+ }
+ }
+ if ($_t !== null) {
+ $t = $_t;
+ } else {
+ $found = $this->validDate($t);
+ }
+ }
+ }
+ }
+
+ if ($offset < $this->start && $this->start < $t) {
+ $ts = $this->start;
+ } elseif ($found && ($t != $offset)) {
+ if ($this->validDate($t)) {
+ if (static::$debug) echo 'OK' . "\n";
+ $ts = $t;
+ } else {
+ if (static::$debug) echo 'Invalid' . "\n";
+ $ts = $this->findNext($t);
+ }
+ } else {
+ if (static::$debug) echo 'Not found' . "\n";
+ $ts = $this->findNext($this->findStartingPoint($offset, $this->rules['interval']));
+ }
+ if ($ts && in_array($ts, $this->excluded, true)) {
+ return $this->findNext($ts);
+ }
+
+ return $ts;
+ }
+
+ /**
+ * Finds the starting point for the next rule. It goes $interval
+ * 'freq' forward in time since the given offset
+ *
+ * @param int $offset
+ * @param int $interval
+ * @param boolean $truncate
+ * @return int
+ */
+ private function findStartingPoint(int $offset, int $interval, bool $truncate = true): int {
+ $_freq = ($this->freq === 'daily') ? 'day__' : $this->freq;
+ $t = '+' . $interval . ' ' . substr($_freq, 0, -2) . 's';
+ if ($_freq === 'monthly' && $truncate) {
+ if ($interval > 1) {
+ $offset = strtotime('+' . ($interval - 1) . ' months ', $offset); // FIXME return type int|false
+ }
+ $t = '+' . (date('t', $offset) - date('d', $offset) + 1) . ' days';
+ }
+
+ $sp = strtotime($t, $offset);
+
+ if ($truncate) {
+ $sp = $this->truncateToPeriod($sp, $this->freq);
+ }
+
+ return $sp;
+ }
+
+ /**
+ * Resets the timestamp to the beginning of the
+ * period specified by freq
+ *
+ * Yes - the fall-through is on purpose!
+ *
+ * @param int $time
+ * @param string $freq
+ * @return int
+ */
+ private function truncateToPeriod(int $time, string $freq): int {
+ $date = getdate($time);
+ switch ($freq) {
+ case 'yearly':
+ $date['mon'] = 1;
+ case 'monthly':
+ $date['mday'] = 1;
+ case 'daily':
+ $date['hours'] = 0;
+ case 'hourly':
+ $date['minutes'] = 0;
+ case 'minutely':
+ $date['seconds'] = 0;
+ break;
+ case 'weekly':
+ if (date('N', $time) == 1) { // FIXME wrong compare, date return string|false
+ $date['hours'] = 0;
+ $date['minutes'] = 0;
+ $date['seconds'] = 0;
+ } else {
+ $date = getdate(strtotime('last monday 0:00', $time));
+ }
+ break;
+ }
+ return mktime($date['hours'], $date['minutes'], $date['seconds'], $date['mon'], $date['mday'], $date['year']);
+ }
+
+ private function validDate($t): bool {
+ if (isset($this->rules['until']) && $t > $this->rules['until']) {
+ return false;
+ }
+
+ if (in_array($t, $this->excluded, true)) {
+ return false;
+ }
+
+ if (isset($this->rules['bymonth'])) {
+ $months = explode(',', $this->rules['bymonth']);
+ if (!in_array(date('m', $t), $months, true)) {
+ return false;
+ }
+ }
+ if (isset($this->rules['byday'])) {
+ $days = explode(',', $this->rules['byday']);
+ foreach ($days as $i => $k) {
+ $days[$i] = $this->weekdays[preg_replace('/[^A-Z]/', '', $k)];
+ }
+ if (!in_array(strtolower(date('l', $t)), $days, true)) {
+ return false;
+ }
+ }
+ if (isset($this->rules['byweekno'])) {
+ $weeks = explode(',', $this->rules['byweekno']);
+ if (!in_array(date('W', $t), $weeks, true)) {
+ return false;
+ }
+ }
+ if (isset($this->rules['bymonthday'])) {
+ $weekdays = explode(',', $this->rules['bymonthday']);
+ foreach ($weekdays as $i => $k) {
+ if ($k < 0) {
+ $weekdays[$i] = date('t', $t) + $k + 1;
+ }
+ }
+ if (!in_array(date('d', $t), $weekdays, true)) {
+ return false;
+ }
+ }
+ if (isset($this->rules['byhour'])) {
+ $hours = explode(',', $this->rules['byhour']);
+ if (!in_array(date('H', $t), $hours, true)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Finds the earliest timestamp possible outside this period.
+ *
+ * @param int $offset
+ * @return int
+ */
+ public function findEndOfPeriod(int $offset = 0): int {
+ return $this->findStartingPoint($offset, 1, false);
+ }
+
+ /**
+ * Returns the previous (most recent) occurrence of the rule from the
+ * given offset
+ *
+ * @param int $offset
+ * @return int
+ * @throws Exception
+ */
+ public function previousOccurrence(int $offset): bool|int {
+ if (!empty($this->cache)) {
+ $t2 = $this->start;
+ foreach ($this->cache as $ts) {
+ if ($ts >= $offset) {
+ return $t2;
+ }
+ $t2 = $ts;
+ }
+ } else {
+ $ts = $this->start;
+ while (($t2 = $this->findNext($ts)) < $offset) {
+ if (!$t2) {
+ break;
+ }
+ $ts = $t2;
+ }
+ }
+
+ return $ts;
+ }
+
+ /**
+ * Returns the next occurrence of this rule after the given offset
+ *
+ * @param int $offset
+ * @return int
+ * @throws Exception
+ */
+ public function nextOccurrence(int $offset): bool|int {
+ if ($offset < $this->start) {
+ return $this->firstOccurrence();
+ }
+ return $this->findNext($offset);
+ }
+
+ /**
+ * Finds the first occurrence of the rule.
+ *
+ * @return bool|int timestamp
+ * @throws \Exception
+ */
+ public function firstOccurrence(): bool|int {
+ $t = $this->start;
+ if (in_array($t, $this->excluded)) {
+ $t = $this->findNext($t);
+ }
+
+ return $t;
+ }
+
+ /**
+ * Finds the absolute last occurrence of the rule from the given offset.
+ * Builds also the cache, if not set before...
+ *
+ * @return int timestamp
+ * @throws Exception
+ */
+ public function lastOccurrence(): int {
+ //build cache if not done
+ $this->getAllOccurrences();
+ //return last timestamp in cache
+ return end($this->cache);
+ }
+
+ /**
+ * Returns all timestamps array(), build the cache if not made before
+ *
+ * @return array
+ * @throws Exception
+ */
+ public function getAllOccurrences(): array {
+ if (empty($this->cache)) {
+ $cache = [];
+
+ //build cache
+ $next = $this->firstOccurrence();
+ while ($next) {
+ $cache[] = $next;
+ $next = $this->findNext($next);
+ }
+ if (!empty($this->added)) {
+ $cache = array_unique(array_merge($cache, $this->added));
+ asort($cache);
+ }
+ $this->cache = $cache;
+ }
+
+ return $this->cache;
+ }
+
+ /**
+ * Applies the BYDAY rule to the given timestamp
+ *
+ * @param string $rule
+ * @param int $t
+ * @return int
+ */
+ private function ruleByDay(string $rule, int $t): int {
+ $dir = ($rule[0] === '-') ? -1 : 1;
+ $dir_t = ($dir === 1) ? 'next' : 'last';
+
+ $d = $this->weekdays[substr($rule, -2)];
+ $s = $dir_t . ' ' . $d . ' ' . date('H:i:s', $t);
+
+ if ($rule == substr($rule, -2)) {
+ if (date('l', $t) == ucfirst($d)) {
+ $s = 'today ' . date('H:i:s', $t);
+ }
+
+ $_t = strtotime($s, $t);
+
+ if ($_t == $t && in_array($this->freq, ['weekly', 'monthly', 'yearly'])) {
+ // Yes. This is not a great idea.. but hey, it works.. for now
+ $s = 'next ' . $d . ' ' . date('H:i:s', $t);
+ $_t = strtotime($s, $_t);
+ }
+
+ return $_t;
+ } else {
+ $_f = $this->freq;
+ if (isset($this->rules['bymonth']) && $this->freq === 'yearly') {
+ $this->freq = 'monthly';
+ }
+ if ($dir === -1) {
+ $_t = $this->findEndOfPeriod($t);
+ } else {
+ $_t = $this->truncateToPeriod($t, $this->freq);
+ }
+ $this->freq = $_f;
+
+ $c = preg_replace('/[^0-9]/', '', $rule);
+ $c = ($c == '') ? 1 : $c;
+
+ $n = $_t;
+ while ($c > 0) {
+ if ($dir === 1 && $c == 1 && date('l', $t) == ucfirst($d)) {
+ $s = 'today ' . date('H:i:s', $t);
+ }
+ $n = strtotime($s, $n);
+ $c--;
+ }
+
+ return $n;
+ }
+ }
+
+ private function ruleByMonth($rule, int $t): bool|int {
+ $_t = mktime(date('H', $t), date('i', $t), date('s', $t), $rule, date('d', $t), date('Y', $t));
+ if ($t == $_t && isset($this->rules['byday'])) {
+ // TODO: this should check if one of the by*day's exists, and have a multi-day value
+ return false;
+ } else {
+ return $_t;
+ }
+ }
+
+ private function ruleByMonthday($rule, int $t): bool|int {
+ if ($rule < 0) {
+ $rule = date('t', $t) + $rule + 1;
+ }
+
+ return mktime(date('H', $t), date('i', $t), date('s', $t), date('m', $t), $rule, date('Y', $t));
+ }
+
+ private function ruleByYearday($rule, int $t): bool|int {
+ if ($rule < 0) {
+ $_t = $this->findEndOfPeriod();
+ $d = '-';
+ } else {
+ $_t = $this->truncateToPeriod($t, $this->freq);
+ $d = '+';
+ }
+ $s = $d . abs($rule - 1) . ' days ' . date('H:i:s', $t);
+
+ return strtotime($s, $_t);
+ }
+
+ private function ruleByWeekno($rule, int $t): bool|int {
+ if ($rule < 0) {
+ $_t = $this->findEndOfPeriod();
+ $d = '-';
+ } else {
+ $_t = $this->truncateToPeriod($t, $this->freq);
+ $d = '+';
+ }
+
+ $sub = (date('W', $_t) == 1) ? 2 : 1;
+ $s = $d . abs($rule - $sub) . ' weeks ' . date('H:i:s', $t);
+ $_t = strtotime($s, $_t);
+
+ return $_t;
+ }
+
+ private function ruleByHour($rule, int $t): bool|int {
+ return mktime($rule, date('i', $t), date('s', $t), date('m', $t), date('d', $t), date('Y', $t));
+ }
+
+ private function ruleByMinute($rule, int $t): bool|int {
+ return mktime(date('h', $t), $rule, date('s', $t), date('m', $t), date('d', $t), date('Y', $t));
+ }
+}
diff --git a/includes/composer/vendor/om/icalparser/src/IcalParser.php b/includes/composer/vendor/om/icalparser/src/IcalParser.php
new file mode 100644
index 0000000..282deef
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/src/IcalParser.php
@@ -0,0 +1,502 @@
+<?php
+
+namespace om;
+
+use ArrayObject;
+use DateInterval;
+use DateTime;
+use DateTimeZone;
+use Exception;
+use InvalidArgumentException;
+use RuntimeException;
+
+/**
+ * Copyright (c) 2004-2022 Roman Ožana (https://ozana.cz)
+ *
+ * @license BSD-3-Clause
+ * @author Roman Ožana <roman@ozana.cz>
+ */
+class IcalParser {
+
+ /** @var ?DateTimeZone */
+ public ?DateTimeZone $timezone = null;
+
+ /** @var array|null */
+ public ?array $data = null;
+
+ /** @var array */
+ protected array $counters = [];
+
+ /** @var array */
+ private array $windowsTimezones;
+
+ public function __construct() {
+ $this->windowsTimezones = require __DIR__ . '/WindowsTimezones.php'; // load Windows timezones from separate file
+ }
+
+ /**
+ * @param string $file
+ * @param callable|null $callback
+ * @return array|null
+ * @throws Exception
+ */
+ public function parseFile(string $file, callable $callback = null): ?array {
+ if (!$handle = fopen($file, 'rb')) {
+ throw new RuntimeException('Can\'t open file' . $file . ' for reading.');
+ }
+ fclose($handle);
+
+ return $this->parseString(file_get_contents($file), $callback);
+ }
+
+ /**
+ * @param string $string
+ * @param callable|null $callback
+ * @param boolean $add if true the parsed string is added to existing data
+ * @return array|null
+ * @throws Exception
+ */
+ public function parseString(string $string, callable $callback = null, bool $add = false): ?array {
+ if ($add === false) {
+ // delete old data
+ $this->data = [];
+ $this->counters = [];
+ }
+
+ if (!str_contains($string, 'BEGIN:VCALENDAR')) {
+ throw new InvalidArgumentException('Invalid ICAL data format');
+ }
+
+ $section = 'VCALENDAR';
+
+ // Replace \r\n with \n
+ $string = str_replace("\r\n", "\n", $string);
+
+ // Unfold multi-line strings
+ $string = str_replace("\n ", '', $string);
+
+ foreach (explode("\n", $string) as $row) {
+
+ switch ($row) {
+ case 'BEGIN:DAYLIGHT':
+ case 'BEGIN:VALARM':
+ case 'BEGIN:VTIMEZONE':
+ case 'BEGIN:VFREEBUSY':
+ case 'BEGIN:VJOURNAL':
+ case 'BEGIN:STANDARD':
+ case 'BEGIN:VTODO':
+ case 'BEGIN:VEVENT':
+ $section = substr($row, 6);
+ $this->counters[$section] = isset($this->counters[$section]) ? $this->counters[$section] + 1 : 0;
+ continue 2; // while
+ case 'END:VEVENT':
+ $section = substr($row, 4);
+ $currCounter = $this->counters[$section];
+ $event = $this->data[$section][$currCounter];
+ if (!empty($event['RECURRENCE-ID'])) {
+ $this->data['_RECURRENCE_IDS'][$event['RECURRENCE-ID']] = $event;
+ }
+
+ continue 2; // while
+ case 'END:DAYLIGHT':
+ case 'END:VALARM':
+ case 'END:VTIMEZONE':
+ case 'END:VFREEBUSY':
+ case 'END:VJOURNAL':
+ case 'END:STANDARD':
+ case 'END:VTODO':
+ continue 2; // while
+
+ case 'END:VCALENDAR':
+ $veventSection = 'VEVENT';
+ if (!empty($this->data[$veventSection])) {
+ foreach ($this->data[$veventSection] as $currCounter => $event) {
+ if (!empty($event['RRULE']) || !empty($event['RDATE'])) {
+ $recurrences = $this->parseRecurrences($event);
+ if (!empty($recurrences)) {
+ $this->data[$veventSection][$currCounter]['RECURRENCES'] = $recurrences;
+ }
+
+ if (!empty($event['UID'])) {
+ $this->data["_RECURRENCE_COUNTERS_BY_UID"][$event['UID']] = $currCounter;
+ }
+ }
+ }
+ }
+ continue 2; // while
+ }
+
+ [$key, $middle, $value] = $this->parseRow($row);
+
+ if ($callback) {
+ // call user function for processing line
+ call_user_func($callback, $row, $key, $middle, $value, $section, $this->counters[$section]);
+ } else {
+ if ($section === 'VCALENDAR') {
+ $this->data[$key] = $value;
+ } else {
+
+ // use an array since there can be multiple entries for this key. This does not
+ // break the current implementation--it leaves the original key alone and adds
+ // a new one specifically for the array of values.
+
+ if ($newKey = $this->isMultipleKey($key)) {
+ $this->data[$section][$this->counters[$section]][$newKey][] = $value;
+ }
+
+ // CATEGORIES can be multiple also but there is special case that there are comma separated categories
+
+ if ($this->isMultipleKeyWithCommaSeparation($key)) {
+
+ if (str_contains($value, ',')) {
+ $values = array_map('trim', preg_split('/(?<![^\\\\]\\\\),/', $value));
+ } else {
+ $values = [$value];
+ }
+
+ foreach ($values as $value) {
+ $this->data[$section][$this->counters[$section]][$key][] = $value;
+ }
+
+ } else {
+ if (in_array($key, ['ORGANIZER'])) {
+ foreach ($middle as $midKey => $midVal) {
+ $this->data[$section][$this->counters[$section]][$key . '-' . $midKey] = $midVal;
+ }
+ }
+ if (in_array($key, ['ATTENDEE', 'ORGANIZER'])) {
+ $value = $value['VALUE']; // backwards compatibility (leaves ATTENDEE entry as it was)
+ }
+ $this->data[$section][$this->counters[$section]][$key] = $value;
+ }
+
+ }
+
+ }
+ }
+
+ return ($callback) ? null : $this->data;
+ }
+
+ /**
+ * @param $event
+ * @return array
+ * @throws Exception
+ */
+ public function parseRecurrences($event): array {
+ $recurring = new Recurrence($event['RRULE']);
+ $exclusions = [];
+ $additions = [];
+
+ if (!empty($event['EXDATES'])) {
+ foreach ($event['EXDATES'] as $exDate) {
+ if (is_array($exDate)) {
+ foreach ($exDate as $singleExDate) {
+ $exclusions[] = $singleExDate->getTimestamp();
+ }
+ } else {
+ $exclusions[] = $exDate->getTimestamp();
+ }
+ }
+ }
+
+ if (!empty($event['RDATES'])) {
+ foreach ($event['RDATES'] as $rDate) {
+ if (is_array($rDate)) {
+ foreach ($rDate as $singleRDate) {
+ $additions[] = $singleRDate->getTimestamp();
+ }
+ } else {
+ $additions[] = $rDate->getTimestamp();
+ }
+ }
+ }
+
+ $until = $recurring->getUntil();
+ if ($until === false) {
+ //forever... limit to 3 years from now
+ $end = new DateTime('now');
+ $end->add(new DateInterval('P3Y')); // + 3 years
+ $recurring->setUntil($end);
+ $until = $recurring->getUntil();
+ }
+
+ date_default_timezone_set($event['DTSTART']->getTimezone()->getName());
+ $frequency = new Freq($recurring->rrule, $event['DTSTART']->getTimestamp(), $exclusions, $additions);
+ $recurrenceTimestamps = $frequency->getAllOccurrences();
+
+ // This should be fixed in the Freq class, but it's too messy to make sense of
+ // This guard only works on WEEKLY, because the others have no fixed time interval
+ // There may still be a bug with the others
+ if (isset($event['RRULE']['INTERVAL']) && $recurring->getFreq() === "WEEKLY") {
+ $replacementList = [];
+
+ foreach($recurrenceTimestamps as $timestamp) {
+ $tmp = new DateTime('now', $event['DTSTART']->getTimezone());
+ $tmp->setTimestamp($timestamp);
+
+ $dayCount = $event['DTSTART']->diff($tmp)->format('%a');
+
+ if ($dayCount % ($event['RRULE']['INTERVAL'] * 7) == 0) {
+ $replacementList[] = $timestamp;
+ }
+ }
+
+ $recurrenceTimestamps = $replacementList;
+ }
+
+ $recurrences = [];
+ foreach ($recurrenceTimestamps as $recurrenceTimestamp) {
+ $tmp = new DateTime('now', $event['DTSTART']->getTimezone());
+ $tmp->setTimestamp($recurrenceTimestamp);
+
+ $recurrenceIDDate = $tmp->format('Ymd');
+ $recurrenceIDDateTime = $tmp->format('Ymd\THis');
+ if (empty($this->data['_RECURRENCE_IDS'][$recurrenceIDDate]) &&
+ empty($this->data['_RECURRENCE_IDS'][$recurrenceIDDateTime])) {
+ $gmtCheck = new DateTime('now', new DateTimeZone('UTC'));
+ $gmtCheck->setTimestamp($recurrenceTimestamp);
+ $recurrenceIDDateTimeZ = $gmtCheck->format('Ymd\THis\Z');
+ if (empty($this->data['_RECURRENCE_IDS'][$recurrenceIDDateTimeZ])) {
+ $recurrences[] = $tmp;
+ }
+ }
+ }
+
+ return $recurrences;
+ }
+
+ private function parseRow($row): array {
+ preg_match('#^([\w-]+);?([\w-]+="[^"]*"|.*?):(.*)$#i', $row, $matches);
+
+ $key = false;
+ $middle = null;
+ $value = null;
+
+ if ($matches) {
+ $key = $matches[1];
+ $middle = $matches[2];
+ $value = $matches[3];
+ $timezone = null;
+
+ if ($key === 'X-WR-TIMEZONE' || $key === 'TZID') {
+ if (preg_match('#(\w+/\w+)$#i', $value, $matches)) {
+ $value = $matches[1];
+ }
+ $value = $this->toTimezone($value);
+ $this->timezone = new DateTimeZone($value);
+ }
+
+ // have some middle part ?
+ if ($middle && preg_match_all('#(?<key>[^=;]+)=(?<value>[^;]+)#', $middle, $matches, PREG_SET_ORDER)) {
+ $middle = [];
+ foreach ($matches as $match) {
+ if ($match['key'] === 'TZID') {
+ $match['value'] = trim($match['value'], "'\"");
+ $match['value'] = $this->toTimezone($match['value']);
+ try {
+ $middle[$match['key']] = $timezone = new DateTimeZone($match['value']);
+ } catch (Exception $e) {
+ $middle[$match['key']] = $match['value'];
+ }
+ } elseif ($match['key'] === 'ENCODING') {
+ if ($match['value'] === 'QUOTED-PRINTABLE') {
+ $value = quoted_printable_decode($value);
+ }
+ } else {
+ $middle[$match['key']] = $match['value'];
+ }
+ }
+ }
+ }
+
+ // process simple dates with timezone
+ if (in_array($key, ['DTSTAMP', 'LAST-MODIFIED', 'CREATED', 'DTSTART', 'DTEND'], true)) {
+ try {
+ $value = new DateTime($value, ($timezone ?: $this->timezone));
+ } catch (Exception $e) {
+ $value = null;
+ }
+ } elseif (in_array($key, ['EXDATE', 'RDATE'])) {
+ $values = [];
+ foreach (explode(',', $value) as $singleValue) {
+ try {
+ $values[] = new DateTime($singleValue, ($timezone ?: $this->timezone));
+ } catch (Exception $e) {
+ // pass
+ }
+ }
+ if (count($values) === 1) {
+ $value = $values[0];
+ } else {
+ $value = $values;
+ }
+ }
+
+ if ($key === 'RRULE' && preg_match_all('#(?<key>[^=;]+)=(?<value>[^;]+)#', $value, $matches, PREG_SET_ORDER)) {
+ $middle = null;
+ $value = [];
+ foreach ($matches as $match) {
+ if (in_array($match['key'], ['UNTIL'])) {
+ try {
+ $value[$match['key']] = new DateTime($match['value'], ($timezone ?: $this->timezone));
+ } catch (Exception $e) {
+ $value[$match['key']] = $match['value'];
+ }
+ } else {
+ $value[$match['key']] = $match['value'];
+ }
+ }
+ }
+
+ //implement 4.3.11 Text ESCAPED-CHAR
+ $text_properties = [
+ 'CALSCALE', 'METHOD', 'PRODID', 'VERSION', 'CATEGORIES', 'CLASS', 'COMMENT', 'DESCRIPTION',
+ 'LOCATION', 'RESOURCES', 'STATUS', 'SUMMARY', 'TRANSP', 'TZID', 'TZNAME', 'CONTACT',
+ 'RELATED-TO', 'UID', 'ACTION', 'REQUEST-STATUS', 'URL',
+ ];
+ if (in_array($key, $text_properties, true) || str_starts_with($key, 'X-')) {
+ if (is_array($value)) {
+ foreach ($value as &$var) {
+ $var = strtr($var, ['\\\\' => '\\', '\\N' => "\n", '\\n' => "\n", '\\;' => ';', '\\,' => ',']);
+ }
+ } else {
+ $value = strtr($value, ['\\\\' => '\\', '\\N' => "\n", '\\n' => "\n", '\\;' => ';', '\\,' => ',']);
+ }
+ }
+
+ if (in_array($key, ['ATTENDEE', 'ORGANIZER'])) {
+ $value = array_merge(is_array($middle) ? $middle : ['middle' => $middle], ['VALUE' => $value]);
+ }
+
+ return [$key, $middle, $value];
+ }
+
+ /**
+ * Process timezone and return correct one...
+ *
+ * @param string $zone
+ * @return mixed|null
+ */
+ private function toTimezone(string $zone): mixed {
+ return $this->windowsTimezones[$zone] ?? $zone;
+ }
+
+ public function isMultipleKey(string $key): ?string {
+ return (['ATTACH' => 'ATTACHMENTS', 'EXDATE' => 'EXDATES', 'RDATE' => 'RDATES', 'ATTENDEE' => 'ATTENDEES'])[$key] ?? null;
+ }
+
+ /**
+ * @param $key
+ * @return string|null
+ */
+ public function isMultipleKeyWithCommaSeparation($key): ?string {
+ return (['X-CATEGORIES' => 'X-CATEGORIES', 'CATEGORIES' => 'CATEGORIES'])[$key] ?? null;
+ }
+
+ public function getAlarms(): array {
+ return $this->data['VALARM'] ?? [];
+ }
+
+ public function getTimezone(): array {
+ return $this->getTimezones();
+ }
+
+ public function getTimezones(): array {
+ return $this->data['VTIMEZONE'] ?? [];
+ }
+
+ /**
+ * Return sorted event list as ArrayObject
+ *
+ * @deprecated use IcalParser::getEvents()->sorted() instead
+ */
+ public function getSortedEvents(): ArrayObject {
+ return $this->getEvents()->sorted();
+ }
+
+ public function getEvents(): EventsList {
+ $events = new EventsList();
+ if (isset($this->data['VEVENT'])) {
+ foreach ($this->data['VEVENT'] as $iValue) {
+ $event = $iValue;
+
+ if (empty($event['RECURRENCES'])) {
+ if (!empty($event['RECURRENCE-ID']) && !empty($event['UID']) && isset($event['SEQUENCE'])) {
+ $modifiedEventUID = $event['UID'];
+ $modifiedEventRecurID = $event['RECURRENCE-ID'];
+ $modifiedEventSeq = (int) $event['SEQUENCE'];
+
+ if (isset($this->data['_RECURRENCE_COUNTERS_BY_UID'][$modifiedEventUID])) {
+ $counter = $this->data['_RECURRENCE_COUNTERS_BY_UID'][$modifiedEventUID];
+
+ $originalEvent = $this->data['VEVENT'][$counter];
+ if (isset($originalEvent['SEQUENCE'])) {
+ $originalEventSeq = (int) $originalEvent['SEQUENCE'];
+ $originalEventFormattedStartDate = $originalEvent['DTSTART']->format('Ymd\THis');
+ if ($modifiedEventRecurID === $originalEventFormattedStartDate && $modifiedEventSeq > $originalEventSeq) {
+ // this modifies the original event
+ $modifiedEvent = array_replace_recursive($originalEvent, $event);
+ $this->data['VEVENT'][$counter] = $modifiedEvent;
+ foreach ($events as $z => $event) {
+ if ($events[$z]['UID'] === $originalEvent['UID'] &&
+ $events[$z]['SEQUENCE'] === $originalEvent['SEQUENCE']) {
+ // replace the original event with the modified event
+ $events[$z] = $modifiedEvent;
+ break;
+ }
+ }
+ $event = null; // don't add this to the $events[] array again
+ } elseif (!empty($originalEvent['RECURRENCES'])) {
+ for ($j = 0; $j < count($originalEvent['RECURRENCES']); $j++) {
+ $recurDate = $originalEvent['RECURRENCES'][$j];
+ $formattedStartDate = $recurDate->format('Ymd\THis');
+ if ($formattedStartDate === $modifiedEventRecurID) {
+ unset($this->data['VEVENT'][$counter]['RECURRENCES'][$j]);
+ $this->data['VEVENT'][$counter]['RECURRENCES'] = array_values($this->data['VEVENT'][$counter]['RECURRENCES']);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!empty($event)) {
+ $events->append($event);
+ }
+ } else {
+ $recurrences = $event['RECURRENCES'];
+ $event['RECURRING'] = true;
+ $event['DTEND'] = !empty($event['DTEND']) ? $event['DTEND'] : $event['DTSTART'];
+ $eventInterval = $event['DTSTART']->diff($event['DTEND']);
+
+ $firstEvent = true;
+ foreach ($recurrences as $j => $recurDate) {
+ $newEvent = $event;
+ if (!$firstEvent) {
+ unset($newEvent['RECURRENCES']);
+ $newEvent['DTSTART'] = $recurDate;
+ $newEvent['DTEND'] = clone($recurDate);
+ $newEvent['DTEND']->add($eventInterval);
+ }
+
+ $newEvent['RECURRENCE_INSTANCE'] = $j;
+ $events->append($newEvent);
+ $firstEvent = false;
+ }
+ }
+ }
+ }
+ return $events;
+ }
+
+ /**
+ * @return \ArrayObject
+ * @deprecated use IcalParser::getEvents->reversed();
+ */
+ public function getReverseSortedEvents(): ArrayObject {
+ return $this->getEvents()->reversed();
+ }
+
+}
diff --git a/includes/composer/vendor/om/icalparser/src/Recurrence.php b/includes/composer/vendor/om/icalparser/src/Recurrence.php
new file mode 100644
index 0000000..7010257
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/src/Recurrence.php
@@ -0,0 +1,234 @@
+<?php
+
+namespace om;
+
+use DateTime;
+use Exception;
+
+/**
+ * Class taken from https://github.com/coopTilleuls/intouch-iCalendar.git (Recurrence.php)
+ *
+ * A wrapper for recurrence rules in iCalendar. Parses the given line and puts the
+ * recurrence rules in the correct field of this object.
+ *
+ * See http://tools.ietf.org/html/rfc2445 for more information. Page 39 and onward contains more
+ * information on the recurrence rules themselves. Page 116 and onward contains
+ * some great examples which were often used for testing.
+ *
+ * @author Steven Oxley
+ * @author Michael Kahn (C) 2013
+ * @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
+ */
+class Recurrence {
+
+ public array $rrule;
+ protected mixed $freq;
+ protected mixed $until;
+ protected mixed $count;
+ protected mixed $interval;
+ protected mixed $bysecond;
+ protected mixed $byminute;
+ protected mixed $byhour;
+ protected mixed $byday;
+ protected mixed $bymonthday;
+ protected mixed $byyearday;
+ protected mixed $byweekno;
+ protected mixed $bymonth;
+ protected mixed $bysetpos;
+ protected mixed $wkst;
+ /**
+ * A list of the properties that can have comma-separated lists for values.
+ *
+ * @var array
+ */
+ protected array $listProperties = [
+ 'bysecond', 'byminute', 'byhour', 'byday', 'bymonthday',
+ 'byyearday', 'byweekno', 'bymonth', 'bysetpos',
+ ];
+
+ /**
+ * Creates a recurrence object with a passed in line. Parses the line.
+ *
+ * @param array $rrule an om\icalparser row array which will be parsed to get the
+ * desired information.
+ */
+ public function __construct(array $rrule) {
+ $this->parseRrule($rrule);
+ }
+
+ /**
+ * Parses an 'RRULE' array and sets the member variables of this object.
+ * Expects a string that looks like this: 'FREQ=WEEKLY;INTERVAL=2;BYDAY=SU,TU,WE'
+ *
+ * @param array $rrule
+ */
+ protected function parseRrule(array $rrule): void {
+ $this->rrule = $rrule;
+ //loop through the properties in the line and set their associated
+ //member variables
+ foreach ($this->rrule as $propertyName => $propertyValue) {
+ //need the lower-case name for setting the member variable
+ $propertyName = strtolower($propertyName);
+ //split up the list of values into an array (if it's a list)
+ if (in_array($propertyName, $this->listProperties, true)) {
+ $propertyValue = explode(',', $propertyValue);
+ }
+ $this->$propertyName = $propertyValue;
+ }
+ }
+
+ /**
+ * Returns the frequency - corresponds to FREQ in RFC 2445.
+ *
+ * @return mixed string if the member has been set, false otherwise
+ */
+ public function getFreq(): mixed {
+ return $this->getMember('freq');
+ }
+
+ /**
+ * Retrieves the desired member variable and returns it (if it's set)
+ *
+ * @param string $member name of the member variable
+ * @return mixed the variable value (if set), false otherwise
+ */
+ protected function getMember(string $member): mixed {
+ return $this->$member ?? false;
+ }
+
+ /**
+ * Returns when the event will go until - corresponds to UNTIL in RFC 2445.
+ *
+ * @return mixed string if the member has been set, false otherwise
+ */
+ public function getUntil(): mixed {
+ return $this->getMember('until');
+ }
+
+ /**
+ * Set the $until member
+ *
+ * @param mixed $ts timestamp (int) / Valid DateTime format (string)
+ * @throws Exception
+ */
+ public function setUntil(mixed $ts): void {
+ if ($ts instanceof DateTime) {
+ $dt = $ts;
+ } elseif (is_int($ts)) {
+ $dt = new DateTime('@' . $ts);
+ } else {
+ $dt = new DateTime($ts);
+ }
+ $this->until = $dt->format('Ymd\THisO');
+ $this->rrule['until'] = $this->until;
+ }
+
+ /**
+ * Returns the count of the times the event will occur (should only appear if 'until'
+ * does not appear) - corresponds to COUNT in RFC 2445.
+ *
+ * @return mixed string if the member has been set, false otherwise
+ */
+ public function getCount(): mixed {
+ return $this->getMember('count');
+ }
+
+ /**
+ * Returns the interval - corresponds to INTERVAL in RFC 2445.
+ *
+ * @return mixed string if the member has been set, false otherwise
+ */
+ public function getInterval(): mixed {
+ return $this->getMember('interval');
+ }
+
+ /**
+ * Returns the bysecond part of the event - corresponds to BYSECOND in RFC 2445.
+ *
+ * @return mixed string if the member has been set, false otherwise
+ */
+ public function getBySecond(): mixed {
+ return $this->getMember('bysecond');
+ }
+
+ /**
+ * Returns the byminute information for the event - corresponds to BYMINUTE in RFC 2445.
+ *
+ * @return mixed string if the member has been set, false otherwise
+ */
+ public function getByMinute(): mixed {
+ return $this->getMember('byminute');
+ }
+
+ /**
+ * Corresponds to BYHOUR in RFC 2445.
+ *
+ * @return mixed string if the member has been set, false otherwise
+ */
+ public function getByHour(): mixed {
+ return $this->getMember('byhour');
+ }
+
+ /**
+ *Corresponds to BYDAY in RFC 2445.
+ *
+ * @return mixed string if the member has been set, false otherwise
+ */
+ public function getByDay(): mixed {
+ return $this->getMember('byday');
+ }
+
+ /**
+ * Corresponds to BYMONTHDAY in RFC 2445.
+ *
+ * @return mixed string if the member has been set, false otherwise
+ */
+ public function getByMonthDay(): mixed {
+ return $this->getMember('bymonthday');
+ }
+
+ /**
+ * Corresponds to BYYEARDAY in RFC 2445.
+ *
+ * @return mixed string if the member has been set, false otherwise
+ */
+ public function getByYearDay(): mixed {
+ return $this->getMember('byyearday');
+ }
+
+ /**
+ * Corresponds to BYWEEKNO in RFC 2445.
+ *
+ * @return mixed string if the member has been set, false otherwise
+ */
+ public function getByWeekNo(): mixed {
+ return $this->getMember('byweekno');
+ }
+
+ /**
+ * Corresponds to BYMONTH in RFC 2445.
+ *
+ * @return mixed string if the member has been set, false otherwise
+ */
+ public function getByMonth(): mixed {
+ return $this->getMember('bymonth');
+ }
+
+ /**
+ * Corresponds to BYSETPOS in RFC 2445.
+ *
+ * @return mixed string if the member has been set, false otherwise
+ */
+ public function getBySetPos(): mixed {
+ return $this->getMember('bysetpos');
+ }
+
+ /**
+ * Corresponds to WKST in RFC 2445.
+ *
+ * @return mixed string if the member has been set, false otherwise
+ */
+ public function getWkst(): mixed {
+ return $this->getMember('wkst');
+ }
+}
diff --git a/includes/composer/vendor/om/icalparser/src/WindowsTimezones.php b/includes/composer/vendor/om/icalparser/src/WindowsTimezones.php
new file mode 100644
index 0000000..9bfa391
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/src/WindowsTimezones.php
@@ -0,0 +1,214 @@
+<?php
+
+/**
+ * List of Windows Timezones
+ */
+return [
+ 'Dateline Standard Time' => 'Etc/GMT+12',
+ '(UTC-12:00) International Date Line West' => 'Etc/GMT+12',
+ 'UTC-11' => 'Etc/GMT+11',
+ '(UTC-11:00) Coordinated Universal Time -11' => 'Etc/GMT+11',
+ 'Hawaiian Standard Time' => 'Pacific/Honolulu',
+ '(UTC-10:00) Hawaii' => 'Pacific/Honolulu',
+ 'Alaskan Standard Time' => 'America/Anchorage',
+ '(UTC-09:00) Alaska' => 'America/Anchorage',
+ 'Pacific Standard Time (Mexico)' => 'America/Santa_Isabel',
+ '(UTC-08:00) Baja California' => 'America/Santa_Isabel',
+ 'Pacific Standard Time' => 'America/Los_Angeles',
+ 'Pacific Time' => 'America/Los_Angeles',
+ '(UTC-08:00) Pacific Time (US and Canada)' => 'America/Los_Angeles',
+ '(UTC-08:00) Pacific Time (US & Canada)' => 'America/Los_Angeles',
+ 'US Mountain Standard Time' => 'America/Phoenix',
+ '(UTC-07:00) Arizona' => 'America/Phoenix',
+ 'Mountain Standard Time (Mexico)' => 'America/Chihuahua',
+ '(UTC-07:00) Chihuahua, La Paz, Mazatlan' => 'America/Chihuahua',
+ 'Mountain Standard Time' => 'America/Denver',
+ 'Mountain Time' => 'America/Denver',
+ '(UTC-07:00) Mountain Time (US and Canada)' => 'America/Denver',
+ '(UTC-07:00) Mountain Time (US & Canada)' => 'America/Denver',
+ 'Central America Standard Time' => 'America/Guatemala',
+ '(UTC-06:00) Central America' => 'America/Guatemala',
+ 'Central Standard Time' => 'America/Chicago',
+ 'Central Time' => 'America/Chicago',
+ '(UTC-06:00) Central Time (US and Canada)' => 'America/Chicago',
+ '(UTC-06:00) Central Time (US & Canada)' => 'America/Chicago',
+ 'Central Standard Time (Mexico)' => 'America/Mexico_City',
+ '(UTC-06:00) Guadalajara, Mexico City, Monterrey' => 'America/Mexico_City',
+ 'Canada Central Standard Time' => 'America/Regina',
+ '(UTC-06:00) Saskatchewan' => 'America/Regina',
+ 'SA Pacific Standard Time' => 'America/Bogota',
+ '(UTC-05:00) Bogota, Lima, Quito' => 'America/Bogota',
+ 'Eastern Standard Time' => 'America/New_York',
+ 'Eastern Time' => 'America/New_York',
+ '(UTC-05:00) Eastern Time (US and Canada)' => 'America/New_York',
+ '(UTC-05:00) Eastern Time (US & Canada)' => 'America/New_York',
+ 'US Eastern Standard Time' => 'America/Indianapolis',
+ '(UTC-05:00) Indiana (East)' => 'America/Indianapolis',
+ 'Venezuela Standard Time' => 'America/Caracas',
+ '(UTC-04:30) Caracas' => 'America/Caracas',
+ 'Paraguay Standard Time' => 'America/Asuncion',
+ '(UTC-04:00) Asuncion' => 'America/Asuncion',
+ 'Atlantic Standard Time' => 'America/Halifax',
+ '(UTC-04:00) Atlantic Time (Canada)' => 'America/Halifax',
+ 'Central Brazilian Standard Time' => 'America/Cuiaba',
+ '(UTC-04:00) Cuiaba' => 'America/Cuiaba',
+ 'SA Western Standard Time' => 'America/La_Paz',
+ '(UTC-04:00) Georgetown, La Paz, Manaus, San Juan' => 'America/La_Paz',
+ 'Pacific SA Standard Time' => 'America/Santiago',
+ '(UTC-04:00) Santiago' => 'America/Santiago',
+ 'Newfoundland Standard Time' => 'America/St_Johns',
+ '(UTC-03:30) Newfoundland' => 'America/St_Johns',
+ 'E. South America Standard Time' => 'America/Sao_Paulo',
+ '(UTC-03:00) Brasilia' => 'America/Sao_Paulo',
+ 'Argentina Standard Time' => 'America/Buenos_Aires',
+ '(UTC-03:00) Buenos Aires' => 'America/Buenos_Aires',
+ 'SA Eastern Standard Time' => 'America/Cayenne',
+ '(UTC-03:00) Cayenne, Fortaleza' => 'America/Cayenne',
+ 'Greenland Standard Time' => 'America/Godthab',
+ '(UTC-03:00) Greenland' => 'America/Godthab',
+ 'Montevideo Standard Time' => 'America/Montevideo',
+ '(UTC-03:00) Montevideo' => 'America/Montevideo',
+ 'Bahia Standard Time' => 'America/Bahia',
+ 'UTC-02' => 'Etc/GMT+2',
+ '(UTC-02:00) Coordinated Universal Time -02' => 'Etc/GMT+2',
+ 'Azores Standard Time' => 'Atlantic/Azores',
+ '(UTC-01:00) Azores' => 'Atlantic/Azores',
+ 'Cape Verde Standard Time' => 'Atlantic/Cape_Verde',
+ '(UTC-01:00) Cabo Verde Is.' => 'Atlantic/Cape_Verde',
+ 'Morocco Standard Time' => 'Africa/Casablanca',
+ '(UTC) Casablanca' => 'Africa/Casablanca',
+ 'UTC' => 'Etc/GMT',
+ 'Microsoft/Utc' => 'Etc/GMT',
+ 'GMT Standard Time' => 'Europe/London',
+ '(UTC) Dublin, Edinburgh, Lisbon, London' => 'Europe/London',
+ 'Greenwich Standard Time' => 'Atlantic/Reykjavik',
+ '(UTC) Monrovia, Reykjavik' => 'Atlantic/Reykjavik',
+ 'W. Europe Standard Time' => 'Europe/Berlin',
+ '(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' => 'Europe/Berlin',
+ '(UTC+01:00) Amsterdam\, Berlin\, Bern\, Rome\, Stockholm\, Vienna' => 'Europe/Berlin',
+ '(UTC+01:00) Amsterdam\, Berlin\, Bern\, Rom\, Stockholm\, Wien' => 'Europe/Berlin',
+ 'Central Europe Standard Time' => 'Europe/Budapest',
+ '(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague' => 'Europe/Budapest',
+ 'Romance Standard Time' => 'Europe/Paris',
+ '(UTC+01:00) Brussels, Copenhagen, Madrid, Paris' => 'Europe/Paris',
+ 'Central European Standard Time' => 'Europe/Warsaw',
+ '(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb' => 'Europe/Warsaw',
+ 'W. Central Africa Standard Time' => 'Africa/Lagos',
+ '(UTC+01:00) West Central Africa' => 'Africa/Lagos',
+ 'Namibia Standard Time' => 'Africa/Windhoek',
+ '(UTC+01:00) Windhoek' => 'Africa/Windhoek',
+ 'GTB Standard Time' => 'Europe/Bucharest',
+ '(UTC+02:00) Athens, Bucharest' => 'Europe/Bucharest',
+ 'Middle East Standard Time' => 'Asia/Beirut',
+ '(UTC+02:00) Beirut' => 'Asia/Beirut',
+ 'Egypt Standard Time' => 'Africa/Cairo',
+ '(UTC+02:00) Cairo' => 'Africa/Cairo',
+ 'Syria Standard Time' => 'Asia/Damascus',
+ '(UTC+02:00) Damascus' => 'Asia/Damascus',
+ 'South Africa Standard Time' => 'Africa/Johannesburg',
+ '(UTC+02:00) Harare, Pretoria' => 'Africa/Johannesburg',
+ 'FLE Standard Time' => 'Europe/Kiev',
+ '(UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius' => 'Europe/Kiev',
+ 'Turkey Standard Time' => 'Europe/Istanbul',
+ '(UTC+02:00) Istanbul' => 'Europe/Istanbul',
+ 'Israel Standard Time' => 'Asia/Jerusalem',
+ '(UTC+02:00) Jerusalem' => 'Asia/Jerusalem',
+ 'Libya Standard Time' => 'Africa/Tripoli',
+ 'Jordan Standard Time' => 'Asia/Amman',
+ '(UTC+02:00) Amman' => 'Asia/Amman',
+ 'Arabic Standard Time' => 'Asia/Baghdad',
+ '(UTC+03:00) Baghdad' => 'Asia/Baghdad',
+ 'Kaliningrad Standard Time' => 'Europe/Kaliningrad',
+ '(UTC+03:00) Kaliningrad' => 'Europe/Kaliningrad',
+ 'Arab Standard Time' => 'Asia/Riyadh',
+ '(UTC+03:00) Kuwait, Riyadh' => 'Asia/Riyadh',
+ 'E. Africa Standard Time' => 'Africa/Nairobi',
+ '(UTC+03:00) Nairobi' => 'Africa/Nairobi',
+ 'Iran Standard Time' => 'Asia/Tehran',
+ '(UTC+03:30) Tehran' => 'Asia/Tehran',
+ 'Arabian Standard Time' => 'Asia/Dubai',
+ '(UTC+04:00) Abu Dhabi, Muscat' => 'Asia/Dubai',
+ 'Azerbaijan Standard Time' => 'Asia/Baku',
+ '(UTC+04:00) Baku' => 'Asia/Baku',
+ 'Russian Standard Time' => 'Europe/Moscow',
+ '(UTC+04:00) Moscow, St. Petersburg, Volgograd' => 'Europe/Moscow',
+ 'Mauritius Standard Time' => 'Indian/Mauritius',
+ '(UTC+04:00) Port Louis' => 'Indian/Mauritius',
+ 'Georgian Standard Time' => 'Asia/Tbilisi',
+ '(UTC+04:00) Tbilisi' => 'Asia/Tbilisi',
+ 'Caucasus Standard Time' => 'Asia/Yerevan',
+ '(UTC+04:00) Yerevan' => 'Asia/Yerevan',
+ 'Afghanistan Standard Time' => 'Asia/Kabul',
+ '(UTC+04:30) Kabul' => 'Asia/Kabul',
+ 'West Asia Standard Time' => 'Asia/Tashkent',
+ '(UTC+05:00) Tashkent' => 'Asia/Tashkent',
+ 'Pakistan Standard Time' => 'Asia/Karachi',
+ '(UTC+05:00) Islamabad, Karachi' => 'Asia/Karachi',
+ 'India Standard Time' => 'Asia/Calcutta',
+ '(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi' => 'Asia/Calcutta',
+ 'Sri Lanka Standard Time' => 'Asia/Colombo',
+ '(UTC+05:30) Sri Jayawardenepura' => 'Asia/Colombo',
+ 'Nepal Standard Time' => 'Asia/Katmandu',
+ '(UTC+05:45) Kathmandu' => 'Asia/Katmandu',
+ 'Central Asia Standard Time' => 'Asia/Almaty',
+ '(UTC+06:00) Astana' => 'Asia/Almaty',
+ 'Bangladesh Standard Time' => 'Asia/Dhaka',
+ '(UTC+06:00) Dhaka' => 'Asia/Dhaka',
+ 'Ekaterinburg Standard Time' => 'Asia/Yekaterinburg',
+ '(UTC+06:00) Ekaterinburg' => 'Asia/Yekaterinburg',
+ 'Myanmar Standard Time' => 'Asia/Rangoon',
+ '(UTC+06:30) Yangon (Rangoon)' => 'Asia/Rangoon',
+ 'SE Asia Standard Time' => 'Asia/Bangkok',
+ '(UTC+07:00) Bangkok, Hanoi, Jakarta' => 'Asia/Bangkok',
+ 'N. Central Asia Standard Time' => 'Asia/Novosibirsk',
+ '(UTC+07:00) Novosibirsk' => 'Asia/Novosibirsk',
+ 'China Standard Time' => 'Asia/Shanghai',
+ '(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi' => 'Asia/Shanghai',
+ 'North Asia Standard Time' => 'Asia/Krasnoyarsk',
+ '(UTC+08:00) Krasnoyarsk' => 'Asia/Krasnoyarsk',
+ 'Singapore Standard Time' => 'Asia/Singapore',
+ '(UTC+08:00) Kuala Lumpur, Singapore' => 'Asia/Singapore',
+ 'W. Australia Standard Time' => 'Australia/Perth',
+ '(UTC+08:00) Perth' => 'Australia/Perth',
+ 'Taipei Standard Time' => 'Asia/Taipei',
+ '(UTC+08:00) Taipei' => 'Asia/Taipei',
+ 'Ulaanbaatar Standard Time' => 'Asia/Ulaanbaatar',
+ '(UTC+08:00) Ulaanbaatar' => 'Asia/Ulaanbaatar',
+ 'North Asia East Standard Time' => 'Asia/Irkutsk',
+ '(UTC+09:00) Irkutsk' => 'Asia/Irkutsk',
+ 'Tokyo Standard Time' => 'Asia/Tokyo',
+ '(UTC+09:00) Osaka, Sapporo, Tokyo' => 'Asia/Tokyo',
+ 'Korea Standard Time' => 'Asia/Seoul',
+ '(UTC+09:00) Seoul' => 'Asia/Seoul',
+ 'Cen. Australia Standard Time' => 'Australia/Adelaide',
+ '(UTC+09:30) Adelaide' => 'Australia/Adelaide',
+ 'AUS Central Standard Time' => 'Australia/Darwin',
+ '(UTC+09:30) Darwin' => 'Australia/Darwin',
+ 'E. Australia Standard Time' => 'Australia/Brisbane',
+ '(UTC+10:00) Brisbane' => 'Australia/Brisbane',
+ 'AUS Eastern Standard Time' => 'Australia/Sydney',
+ '(UTC+10:00) Canberra, Melbourne, Sydney' => 'Australia/Sydney',
+ 'West Pacific Standard Time' => 'Pacific/Port_Moresby',
+ '(UTC+10:00) Guam, Port Moresby' => 'Pacific/Port_Moresby',
+ 'Tasmania Standard Time' => 'Australia/Hobart',
+ '(UTC+10:00) Hobart' => 'Australia/Hobart',
+ 'Yakutsk Standard Time' => 'Asia/Yakutsk',
+ '(UTC+10:00) Yakutsk' => 'Asia/Yakutsk',
+ 'Central Pacific Standard Time' => 'Pacific/Guadalcanal',
+ '(UTC+11:00) Solomon Is., New Caledonia' => 'Pacific/Guadalcanal',
+ 'Vladivostok Standard Time' => 'Asia/Vladivostok',
+ '(UTC+11:00) Vladivostok' => 'Asia/Vladivostok',
+ 'New Zealand Standard Time' => 'Pacific/Auckland',
+ '(UTC+12:00) Auckland, Wellington' => 'Pacific/Auckland',
+ 'UTC+12' => 'Etc/GMT-12',
+ '(UTC+12:00) Coordinated Universal Time +12' => 'Etc/GMT-12',
+ 'Fiji Standard Time' => 'Pacific/Fiji',
+ '(UTC+12:00) Fiji' => 'Pacific/Fiji',
+ 'Magadan Standard Time' => 'Asia/Magadan',
+ '(UTC+12:00) Magadan' => 'Asia/Magadan',
+ 'Tonga Standard Time' => 'Pacific/Tongatapu',
+ '(UTC+13:00) Nuku\'alofa' => 'Pacific/Tongatapu',
+ 'Samoa Standard Time' => 'Pacific/Apia',
+ '(UTC-11:00)Samoa' => 'Pacific/Apia',
+ 'W. Europe Standard Time 1' => 'Europe/Berlin',
+];
diff --git a/includes/composer/vendor/om/icalparser/tests/bootstrap.php b/includes/composer/vendor/om/icalparser/tests/bootstrap.php
new file mode 100644
index 0000000..7e6b798
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/bootstrap.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace tests;
+
+/**
+ * Copyright (c) 2004-2022 Roman Ožana (https://ozana.cz)
+ *
+ * @license BSD-3-Clause
+ * @author Roman Ožana <roman@ozana.cz>
+ */
+
+require_once __DIR__ . '/../vendor/autoload.php';
+
+use Closure;
+use Tester\Environment;
+
+function test($description, Closure $fn): void {
+ printf("• %s%s%s", $description, PHP_EOL, $fn());
+}
+
+Environment::setup();
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/38_weekly_recurring_event_missing_day.ics b/includes/composer/vendor/om/icalparser/tests/cal/38_weekly_recurring_event_missing_day.ics
new file mode 100644
index 0000000..26326c3
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/38_weekly_recurring_event_missing_day.ics
@@ -0,0 +1,59 @@
+BEGIN:VCALENDAR
+PRODID:-//Google Inc//Google Calendar 70.9054//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+METHOD:PUBLISH
+X-WR-CALNAME:Office Opening Hours
+X-WR-TIMEZONE:Europe/London
+BEGIN:VTIMEZONE
+TZID:Europe/London
+X-LIC-LOCATION:Europe/London
+BEGIN:DAYLIGHT
+TZOFFSETFROM:+0000
+TZOFFSETTO:+0100
+TZNAME:BST
+DTSTART:19700329T010000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0000
+TZNAME:GMT
+DTSTART:19701025T020000
+RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTART;TZID=Europe/London:20190401T090000
+DTEND;TZID=Europe/London:20190401T170000
+RRULE:FREQ=WEEKLY;WKST=SU;BYDAY=MO,TU,WE,TH,FR
+DTSTAMP:20190402T174536Z
+UID:1nibcosj8r05bjoia671im7ulg@google.com
+CREATED:20190401T144832Z
+DESCRIPTION:
+LAST-MODIFIED:20190401T145024Z
+LOCATION:
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY:Office Opening Hours
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;TZID=Europe/London:20190225T090000
+DTEND;TZID=Europe/London:20190225T170000
+RRULE:FREQ=WEEKLY;WKST=SU;UNTIL=20190329T235959Z;BYDAY=MO,TU,WE,TH,FR
+DTSTAMP:20190402T174536Z
+UID:7e581hcu1ub3nm0bb6c4o29suj@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=Office
+ Opening Hours;X-NUM-GUESTS=0:mailto:poweredpasture.com_la2jmsbphe5h11351kk
+ scnnqtg@group.calendar.google.com
+CREATED:20190227T164630Z
+DESCRIPTION:
+LAST-MODIFIED:20190401T144725Z
+LOCATION:
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY:Office Opening Hours
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/FrenchHolidays.ics b/includes/composer/vendor/om/icalparser/tests/cal/FrenchHolidays.ics
new file mode 100644
index 0000000..efb0143
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/FrenchHolidays.ics
@@ -0,0 +1,382 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
+BEGIN:VTIMEZONE
+TZID:/mozilla.org/20070129_1/Europe/Paris
+X-LIC-LOCATION:Europe/Paris
+BEGIN:DAYLIGHT
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+TZNAME:CEST
+DTSTART:19700329T020000
+RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+TZNAME:CET
+DTSTART:19701025T030000
+RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+CREATED:20070606T141629Z
+LAST-MODIFIED:20070606T154611Z
+DTSTAMP:20070607T120859Z
+UID:5d1ae55f-3910-4de9-8b65-d652768fb2f2
+SUMMARY:Lundi de Pâques
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070409
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070410
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070606T161310Z
+LAST-MODIFIED:20070606T161327Z
+DTSTAMP:20070607T120859Z
+UID:9e19b119-f077-4ae9-934e-cb62322ca81f
+SUMMARY:Ascension
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20080501
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20080502
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070605T163903Z
+LAST-MODIFIED:20070606T161656Z
+DTSTAMP:20070607T120859Z
+UID:c6a930d6-4ed5-45d8-bb3d-d3587a32b8aa
+SUMMARY:Jour de l'an
+CLASS:PUBLIC
+RRULE:FREQ=YEARLY;INTERVAL=1
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070101
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070102
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070606T141739Z
+LAST-MODIFIED:20070606T161721Z
+DTSTAMP:20070607T120859Z
+UID:f439c81b-fd09-4b40-a629-3a9663dd29ff
+SUMMARY:Fête du Travail
+CLASS:PUBLIC
+RRULE:FREQ=YEARLY;INTERVAL=1
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070501
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070502
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070606T141853Z
+LAST-MODIFIED:20070606T161731Z
+DTSTAMP:20070607T120859Z
+UID:b1c0e1ed-a09f-4fc2-aab1-170d3f661f13
+SUMMARY:Armistice 1945
+RRULE:FREQ=YEARLY;INTERVAL=1
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070508
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070509
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070606T141932Z
+LAST-MODIFIED:20070606T161741Z
+DTSTAMP:20070607T120859Z
+UID:5468e1a0-9dda-4fd2-88c0-dc3e727c1183
+SUMMARY:Ascension
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070517
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070518
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070606T154927Z
+LAST-MODIFIED:20070606T161748Z
+DTSTAMP:20070607T120859Z
+UID:67a89bb6-9ad4-461b-951c-c4c8482d8618
+SUMMARY:Lundi de Pentecôte
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070528
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070529
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070606T142033Z
+LAST-MODIFIED:20070606T161758Z
+DTSTAMP:20070607T120859Z
+UID:00eb3adc-c059-47c8-a1c0-4ca7048051b9
+SUMMARY:Fête Nationale
+RRULE:FREQ=YEARLY;INTERVAL=1
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070714
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070715
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070606T142112Z
+LAST-MODIFIED:20070606T162012Z
+DTSTAMP:20070607T120859Z
+UID:854e5499-9276-4a96-9be3-bcbaa7caafcf
+SUMMARY:Assomption
+RRULE:FREQ=YEARLY;INTERVAL=1
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070815
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070816
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070606T142147Z
+LAST-MODIFIED:20070606T162034Z
+DTSTAMP:20070607T120859Z
+UID:725c8c16-c37a-49f0-9d2e-dc976d6c3ea5
+SUMMARY:Toussaint
+RRULE:FREQ=YEARLY;INTERVAL=1
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20071101
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20071102
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070606T142211Z
+LAST-MODIFIED:20070606T162044Z
+DTSTAMP:20070607T120859Z
+UID:5218f724-6b66-434d-a8c5-138dfed64e07
+SUMMARY:Armistice 1918
+RRULE:FREQ=YEARLY;INTERVAL=1
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20071111
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20071112
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070606T142243Z
+LAST-MODIFIED:20070606T162054Z
+DTSTAMP:20070607T120859Z
+UID:18617cde-2d15-46c6-900f-b3341a7b7f98
+SUMMARY:Noël
+RRULE:FREQ=YEARLY;INTERVAL=1
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20071225
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20071226
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070606T150648Z
+LAST-MODIFIED:20070606T162128Z
+DTSTAMP:20070607T120859Z
+UID:19d50a52-f5a9-4d70-8fd7-a2caa97f6959
+SUMMARY:Lundi de Pâques
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20080324
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20080325
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T105427Z
+LAST-MODIFIED:20070607T105450Z
+DTSTAMP:20070607T120859Z
+UID:18c90b99-edfa-4418-9bc7-992a07384967
+SUMMARY:Lundi de Pâques
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20090413
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20090414
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T105613Z
+LAST-MODIFIED:20070607T105630Z
+DTSTAMP:20070607T120859Z
+UID:b989d129-8f2f-42be-a027-183f6d81eb40
+SUMMARY:Ascension
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20090521
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20090522
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T105907Z
+LAST-MODIFIED:20070607T105928Z
+DTSTAMP:20070607T120859Z
+UID:7c6fe35c-44a5-450b-aea6-dd2baef78f06
+SUMMARY:Lundi de Pentecôte
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20090601
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20090602
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T110046Z
+LAST-MODIFIED:20070607T110100Z
+DTSTAMP:20070607T120859Z
+UID:98c1c381-8632-45ce-9b11-10757face7c4
+SUMMARY:Lundi de Pâques
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20100405
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20100406
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T110121Z
+LAST-MODIFIED:20070607T110135Z
+DTSTAMP:20070607T120859Z
+UID:aaeb1ba5-60f0-4e49-a25a-c0ff949acdd9
+SUMMARY:Ascension
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20100513
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20100514
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T110144Z
+LAST-MODIFIED:20070607T110201Z
+DTSTAMP:20070607T120859Z
+UID:1f68f978-969e-4791-a7e3-cb3011134a34
+SUMMARY:Lundi de Pentecôte
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20100524
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20100525
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T112820Z
+LAST-MODIFIED:20070607T112836Z
+DTSTAMP:20070607T120859Z
+UID:fa0d4b5b-8eeb-4b98-b35c-b6a547b96953
+SUMMARY:Ascension
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20110602
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20110603
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T112846Z
+LAST-MODIFIED:20070607T112906Z
+DTSTAMP:20070607T120859Z
+UID:5be6194a-9fbd-45da-8e3c-568cac66b997
+SUMMARY:Lundi de Pentecôte
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20110613
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20110614
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T112958Z
+LAST-MODIFIED:20070607T113016Z
+DTSTAMP:20070607T120859Z
+UID:4162d242-5468-4007-8814-10286b9589a0
+SUMMARY:Lundi de Pâques
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20120409
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20120410
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T113034Z
+LAST-MODIFIED:20070607T113048Z
+DTSTAMP:20070607T120859Z
+UID:6d368a35-6b6d-4ec1-9f48-97c53b25502e
+SUMMARY:Ascension
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20120517
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20120518
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T113055Z
+LAST-MODIFIED:20070607T113114Z
+DTSTAMP:20070607T120859Z
+UID:e8913a3d-3e84-447d-afb4-f0b082948449
+SUMMARY:Lundi de Pentecôte
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20120528
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20120529
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T113205Z
+LAST-MODIFIED:20070607T113229Z
+DTSTAMP:20070607T120859Z
+UID:ebc79872-a185-4641-bc22-1092d5139efb
+SUMMARY:Lundi de Pâques
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20130401
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20130402
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T113245Z
+LAST-MODIFIED:20070607T113257Z
+DTSTAMP:20070607T120859Z
+UID:9714bd39-429b-433c-ab5c-5fd6a67e658e
+SUMMARY:Ascension
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20130509
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20130510
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T113305Z
+LAST-MODIFIED:20070607T113325Z
+DTSTAMP:20070607T120859Z
+UID:79f5f02f-e92d-417f-9637-9e2a88840583
+SUMMARY:Lundi de Pentecôte
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20130520
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20130521
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T113408Z
+LAST-MODIFIED:20070607T113426Z
+DTSTAMP:20070607T120859Z
+UID:172dcce6-6f95-4c1f-ada9-e8bf2a7e8245
+SUMMARY:Lundi de Pâques
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20140421
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20140422
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T113439Z
+LAST-MODIFIED:20070607T113452Z
+DTSTAMP:20070607T120859Z
+UID:6625ed89-bf9f-478a-9796-c3dc203ab5e6
+SUMMARY:Ascension
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20140529
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20140530
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T113504Z
+LAST-MODIFIED:20070607T113523Z
+DTSTAMP:20070607T120859Z
+UID:7abad826-ab68-447f-81b2-1e04a4c2ef88
+SUMMARY:Lundi de Pentecôte
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20140609
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20140610
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T113606Z
+LAST-MODIFIED:20070607T113622Z
+DTSTAMP:20070607T120859Z
+UID:cf42813f-4a74-422b-aa19-90dcefccc92d
+SUMMARY:Lundi de Pâques
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20150406
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20150407
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T113643Z
+LAST-MODIFIED:20070607T113657Z
+DTSTAMP:20070607T120859Z
+UID:e7d30f2b-d546-4b7e-8780-0754a46e6970
+SUMMARY:Ascension
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20150514
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20150515
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T113706Z
+LAST-MODIFIED:20070607T113726Z
+DTSTAMP:20070607T120859Z
+UID:a8ccc771-4e8b-4594-ae30-33e9ce384ac9
+SUMMARY:Lundi de Pentecôte
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20150525
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20150526
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070606T162350Z
+LAST-MODIFIED:20070607T115133Z
+DTSTAMP:20070607T120859Z
+UID:419f1bff-a820-4d07-bf4e-1a6ec139e3df
+SUMMARY:Lundi de Pentecôte
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20080512
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20080513
+CATEGORIES:Jours fériés
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20070607T112742Z
+LAST-MODIFIED:20070607T115439Z
+DTSTAMP:20070607T120859Z
+UID:570d9fe7-cdc9-4fdf-9794-909edf520383
+SUMMARY:Lundi de Pâques
+DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20110425
+DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20110426
+CATEGORIES:Jours fériés
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/basic.ics b/includes/composer/vendor/om/icalparser/tests/cal/basic.ics
new file mode 100644
index 0000000..0c58974
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/basic.ics
@@ -0,0 +1,616 @@
+BEGIN:VCALENDAR
+PRODID:-//Google Inc//Google Calendar 70.9054//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+METHOD:PUBLISH
+X-WR-TIMEZONE:UTC
+X-WR-CALDESC:České svátky
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20131224
+DTEND;VALUE=DATE:20131225
+DTSTAMP:20140202T201100Z
+UID:h@9bae7e86083abdfdff45712c19ef81bc41f44111@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Štedrý den
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20141224
+DTEND;VALUE=DATE:20141225
+DTSTAMP:20140202T201100Z
+UID:h@26c4829203feb7deebebdf226285322a079b9b7c@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Štedrý den
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20151224
+DTEND;VALUE=DATE:20151225
+DTSTAMP:20140202T201100Z
+UID:h@1c3d67eb7865010f0ee809055f7c465124091d1b@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Štedrý den
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20150406
+DTEND;VALUE=DATE:20150407
+DTSTAMP:20140202T201100Z
+UID:h@e5a21162ed9d250ee5f689058366a09f10cf4ccb@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Velikonoční pondělí
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20140421
+DTEND;VALUE=DATE:20140422
+DTSTAMP:20140202T201100Z
+UID:h@9595a34a188b5376d452c829499a8f8354ec6049@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Velikonoční pondělí
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20150501
+DTEND;VALUE=DATE:20150502
+DTSTAMP:20140202T201100Z
+UID:h@7470a1d77684e63d5aa048babb939885ad1066c5@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Svátek práce
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20140501
+DTEND;VALUE=DATE:20140502
+DTSTAMP:20140202T201100Z
+UID:h@59ea753d474913d8c7ab474eaba701f8ec5c2ba1@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Svátek práce
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20130501
+DTEND;VALUE=DATE:20130502
+DTSTAMP:20140202T201100Z
+UID:h@19480e43102b11f061a7fc231692a6504e6f55de@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Svátek práce
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20140101
+DTEND;VALUE=DATE:20140102
+DTSTAMP:20140202T201100Z
+UID:h@cdcc3450b77b919b7e5c1d0186439b81fe315723@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Nový rok
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20130101
+DTEND;VALUE=DATE:20130102
+DTSTAMP:20140202T201100Z
+UID:h@93d93af5add7e8e8542f9d2be9bf9b189adc7088@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Nový rok
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20150101
+DTEND;VALUE=DATE:20150102
+DTSTAMP:20140202T201100Z
+UID:h@3be21c366071b22c3322e48e898803d89a2873c8@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Nový rok
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20140928
+DTEND;VALUE=DATE:20140929
+DTSTAMP:20140202T201100Z
+UID:h@910a92f331886c719cdab7cae0f8d56387788789@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den české státnosti
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20130928
+DTEND;VALUE=DATE:20130929
+DTSTAMP:20140202T201100Z
+UID:h@4bdee7defc8620b327b23400a9da3be5d0721a10@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den české státnosti
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20150928
+DTEND;VALUE=DATE:20150929
+DTSTAMP:20140202T201100Z
+UID:h@0534aebb7a00356b125529a447b38be655046ca9@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den české státnosti
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20150508
+DTEND;VALUE=DATE:20150509
+DTSTAMP:20140202T201100Z
+UID:h@f234114d721bac5776f1683f23a58ed2ffd53c84@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den vítězství
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20130508
+DTEND;VALUE=DATE:20130509
+DTSTAMP:20140202T201100Z
+UID:h@5b971aece3bf450de53261bafe25898080af2454@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den vítězství
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20140508
+DTEND;VALUE=DATE:20140509
+DTSTAMP:20140202T201100Z
+UID:h@574f6e8b741d1eb0e80ed4caea1441858393ff58@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den vítězství
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20131028
+DTEND;VALUE=DATE:20131029
+DTSTAMP:20140202T201100Z
+UID:h@be894303693f374fad9017da7ac6391c23eca0f6@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den vzniku samostatného československého státu
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20151028
+DTEND;VALUE=DATE:20151029
+DTSTAMP:20140202T201100Z
+UID:h@7e708d1f45342cd36f819e1f0d3d9f9b7eacc02f@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den vzniku samostatného československého státu
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20141028
+DTEND;VALUE=DATE:20141029
+DTSTAMP:20140202T201100Z
+UID:h@4dcdf85aaa09aea21fd49914085b47cdb44ccf0f@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den vzniku samostatného československého státu
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20150706
+DTEND;VALUE=DATE:20150707
+DTSTAMP:20140202T201100Z
+UID:h@d2253851fcafd5063bdbece5367507e338b5eea0@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den upálení mistra Jana Husa
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20140706
+DTEND;VALUE=DATE:20140707
+DTSTAMP:20140202T201100Z
+UID:h@2f980e6bc7f0445430367c33d3e4a27441196199@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den upálení mistra Jana Husa
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20130706
+DTEND;VALUE=DATE:20130707
+DTSTAMP:20140202T201100Z
+UID:h@09c62186abafba1bac59818bde105765a7a02121@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den upálení mistra Jana Husa
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20150705
+DTEND;VALUE=DATE:20150706
+DTSTAMP:20140202T201100Z
+UID:h@df873adcd2b61ea7c1b13761d7561b9fe6faae5c@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den slovanských věrozvěstů Cyrila a Metoděje
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20140705
+DTEND;VALUE=DATE:20140706
+DTSTAMP:20140202T201100Z
+UID:h@cc790a245031c0186f1d6d9ab8560db87c86269d@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den slovanských věrozvěstů Cyrila a Metoděje
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20130705
+DTEND;VALUE=DATE:20130706
+DTSTAMP:20140202T201100Z
+UID:h@4b4d8fc982e6734d799afdd5de90041a009e56b5@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den slovanských věrozvěstů Cyrila a Metoděje
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20150101
+DTEND;VALUE=DATE:20150102
+DTSTAMP:20140202T201100Z
+UID:h@e35dfa2052d4cf25d186e005626c66b794792d35@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den obnovy samostatného českého státu
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20130101
+DTEND;VALUE=DATE:20130102
+DTSTAMP:20140202T201100Z
+UID:h@e13a9a864c4beec0b4f1055fb4e1bb14076b5a9b@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den obnovy samostatného českého státu
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20140101
+DTEND;VALUE=DATE:20140102
+DTSTAMP:20140202T201100Z
+UID:h@a87543a1136d4a980a8037471eb49ea00f087c81@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den obnovy samostatného českého státu
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20141117
+DTEND;VALUE=DATE:20141118
+DTSTAMP:20140202T201100Z
+UID:h@94e14807e30f52a82572a2cd28a51c89a033d09f@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den boje za svobodu a demokracii
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20131117
+DTEND;VALUE=DATE:20131118
+DTSTAMP:20140202T201100Z
+UID:h@873a2a1499e25477876926c6729dc214b2f1f0f9@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den boje za svobodu a demokracii
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20151117
+DTEND;VALUE=DATE:20151118
+DTSTAMP:20140202T201100Z
+UID:h@599f0d6373565c265b3491b084a2c6198b0b00b9@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Den boje za svobodu a demokracii
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20151226
+DTEND;VALUE=DATE:20151227
+DTSTAMP:20140202T201100Z
+UID:h@e65fdbb6f9703e75e775c9f407f55663c80d4764@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:2. svátek vánoční
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20141226
+DTEND;VALUE=DATE:20141227
+DTSTAMP:20140202T201100Z
+UID:h@807088064c072183b2fa351251155bff44f09787@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:2. svátek vánoční
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20131226
+DTEND;VALUE=DATE:20131227
+DTSTAMP:20140202T201100Z
+UID:h@6165f686632ab29cdd7bdb46a4c3578a48045428@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:2. svátek vánoční
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20141225
+DTEND;VALUE=DATE:20141226
+DTSTAMP:20140202T201100Z
+UID:h@e2f049ac27b03ada54a27294becbcd1cc2680d9a@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:1. svátek vánoční
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20131225
+DTEND;VALUE=DATE:20131226
+DTSTAMP:20140202T201100Z
+UID:h@7f26c52a3d0e69b81f2fc35490224899490c0014@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:1. svátek vánoční
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20151225
+DTEND;VALUE=DATE:20151226
+DTSTAMP:20140202T201100Z
+UID:h@3ddadf7953340f067a4e3b07a4621f9343ff1518@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE
+ STS=0:mailto:cdpisorqclhmg8r8dtm6ip31f506esjfelo2sthecdgmopbechgn4bj7dtnmer
+ 355phmur8%40virtual
+CLASS:PUBLIC
+CREATED:20140202T183416Z
+LAST-MODIFIED:20140202T183416Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:1. svátek vánoční
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/blank_description.ics b/includes/composer/vendor/om/icalparser/tests/cal/blank_description.ics
new file mode 100755
index 0000000..568d7e0
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/blank_description.ics
@@ -0,0 +1,51 @@
+BEGIN:VCALENDAR
+PRODID:-//Google Inc//Google Calendar 70.9054//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+X-WR-CALNAME:calmozilla1@gmail.com
+X-WR-TIMEZONE:America/Los_Angeles
+BEGIN:VTIMEZONE
+TZID:America/Los_Angeles
+X-LIC-LOCATION:America/Los_Angeles
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+TZNAME:PDT
+DTSTART:19700308T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+TZNAME:PST
+DTSTART:19701101T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTART;TZID=America/Los_Angeles:20120630T060000
+DTEND;TZID=America/Los_Angeles:20120630T070000
+DTSTAMP:20120724T212411Z
+UID:dn4vrfmfn5p05roahsopg57h48@google.com
+CREATED:20120724T212411Z
+DESCRIPTION:
+LAST-MODIFIED:20120724T212411Z
+LOCATION:
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY:Really long event name thing
+TRANSP:OPAQUE
+BEGIN:VALARM
+ACTION:EMAIL
+DESCRIPTION:This is an event reminder
+SUMMARY:Alarm notification
+ATTENDEE:mailto:calmozilla1@gmail.com
+TRIGGER:-P0DT0H30M0S
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:This is an event reminder
+TRIGGER:-P0DT0H30M0S
+END:VALARM
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/blank_line_end.ics b/includes/composer/vendor/om/icalparser/tests/cal/blank_line_end.ics
new file mode 100755
index 0000000..2c397c2
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/blank_line_end.ics
@@ -0,0 +1,4 @@
+BEGIN:VCALENDAR
+END:VCALENDAR
+
+
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/blank_line_mid.ics b/includes/composer/vendor/om/icalparser/tests/cal/blank_line_mid.ics
new file mode 100755
index 0000000..5e54e2e
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/blank_line_mid.ics
@@ -0,0 +1,4 @@
+BEGIN:VCALENDAR
+COMMENT:This blank line is invalid
+
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/daily_recur.ics b/includes/composer/vendor/om/icalparser/tests/cal/daily_recur.ics
new file mode 100755
index 0000000..9983098
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/daily_recur.ics
@@ -0,0 +1,52 @@
+BEGIN:VCALENDAR
+PRODID:-//Google Inc//Google Calendar 70.9054//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+X-WR-CALNAME:calmozilla1@gmail.com
+X-WR-TIMEZONE:America/Los_Angeles
+BEGIN:VTIMEZONE
+TZID:America/Los_Angeles
+X-LIC-LOCATION:America/Los_Angeles
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+TZNAME:PDT
+DTSTART:19700308T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+TZNAME:PST
+DTSTART:19701101T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTART;TZID=America/Los_Angeles:20120801T050000
+DTEND;TZID=America/Los_Angeles:20120801T060000
+RRULE:FREQ=DAILY
+DTSTAMP:20120803T221236Z
+UID:tgh9qho17b07pk2n2ji3gluans@google.com
+CREATED:20120803T221236Z
+DESCRIPTION:
+LAST-MODIFIED:20120803T221236Z
+LOCATION:
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY:Every day recurring
+TRANSP:OPAQUE
+BEGIN:VALARM
+ACTION:EMAIL
+DESCRIPTION:This is an event reminder
+SUMMARY:Alarm notification
+ATTENDEE:mailto:calmozilla1@gmail.com
+TRIGGER:-P0DT0H30M0S
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:This is an event reminder
+TRIGGER:-P0DT0H30M0S
+END:VALARM
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/daily_recur2.ics b/includes/composer/vendor/om/icalparser/tests/cal/daily_recur2.ics
new file mode 100755
index 0000000..346f54e
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/daily_recur2.ics
@@ -0,0 +1,41 @@
+BEGIN:VCALENDAR
+PRODID:-//Google Inc//Google Calendar 70.9054//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+X-WR-CALNAME:calmozilla1@gmail.com
+X-WR-TIMEZONE:America/Los_Angeles
+BEGIN:VTIMEZONE
+TZID:America/Los_Angeles
+X-LIC-LOCATION:America/Los_Angeles
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+TZNAME:PDT
+DTSTART:19700308T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+TZNAME:PST
+DTSTART:19701101T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170821
+DTEND;VALUE=DATE:20170822
+RRULE:FREQ=WEEKLY;UNTIL=20170911;BYDAY=MO
+DTSTAMP:20170818T191547Z
+UID:37mfdqtlcrrvbil9b3n7vicb1t@google.com
+CLASS:PUBLIC
+CREATED:20170818T175332Z
+DESCRIPTION:
+LAST-MODIFIED:20170818T175332Z
+LOCATION:
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY:Late Start\, SMS
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/day_long_recur_yearly.ics b/includes/composer/vendor/om/icalparser/tests/cal/day_long_recur_yearly.ics
new file mode 100755
index 0000000..0c0aeb5
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/day_long_recur_yearly.ics
@@ -0,0 +1,52 @@
+BEGIN:VCALENDAR
+PRODID:-//Google Inc//Google Calendar 70.9054//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+X-WR-CALNAME:calmozilla1@gmail.com
+X-WR-TIMEZONE:America/Los_Angeles
+BEGIN:VTIMEZONE
+TZID:America/Los_Angeles
+X-LIC-LOCATION:America/Los_Angeles
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+TZNAME:PDT
+DTSTART:19700308T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+TZNAME:PST
+DTSTART:19701101T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20120803
+DTEND;VALUE=DATE:20120804
+RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
+DTSTAMP:20120803T221306Z
+UID:4pfh824gvims850j0gar361t04@google.com
+CREATED:20120803T221306Z
+DESCRIPTION:
+LAST-MODIFIED:20120803T221306Z
+LOCATION:
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY:Day Long Event
+TRANSP:TRANSPARENT
+BEGIN:VALARM
+ACTION:EMAIL
+DESCRIPTION:This is an event reminder
+SUMMARY:Alarm notification
+ATTENDEE:mailto:calmozilla1@gmail.com
+TRIGGER;VALUE=DATE-TIME:20120802T233000Z
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:This is an event reminder
+TRIGGER;VALUE=DATE-TIME:20120802T233000Z
+END:VALARM
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/forced_types.ics b/includes/composer/vendor/om/icalparser/tests/cal/forced_types.ics
new file mode 100755
index 0000000..f213f01
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/forced_types.ics
@@ -0,0 +1,50 @@
+BEGIN:VCALENDAR
+PRODID:-//Google Inc//Google Calendar 70.9054//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+X-WR-CALNAME:calmozilla1@gmail.com
+X-WR-TIMEZONE:America/Los_Angeles
+BEGIN:VTIMEZONE
+TZID:America/Los_Angeles
+X-LIC-LOCATION:America/Los_Angeles
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+TZNAME:PDT
+DTSTART:19700308T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+TZNAME:PST
+DTSTART:19701101T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20120904
+DTEND;VALUE=DATE:20120905
+DTSTAMP:20120905T084734Z
+UID:redgrb1l0aju5edm6h0s102eu4@google.com
+CREATED:20120905T084734Z
+DESCRIPTION:
+LAST-MODIFIED:20120905T084734Z
+LOCATION:
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY:Event
+TRANSP:TRANSPARENT
+BEGIN:VALARM
+ACTION:EMAIL
+DESCRIPTION:This is an event reminder
+SUMMARY:Alarm notification
+ATTENDEE:mailto:calmozilla1@gmail.com
+TRIGGER;VALUE=DATE-TIME:20120903T233000Z
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:This is an event reminder
+END:VALARM
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/google_birthday.ics b/includes/composer/vendor/om/icalparser/tests/cal/google_birthday.ics
new file mode 100755
index 0000000..f9b7c9d
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/google_birthday.ics
@@ -0,0 +1,90 @@
+BEGIN:VCALENDAR
+PRODID:-//Google Inc//Google Calendar 70.9054//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+X-WR-CALNAME:Contacts' birthdays and events
+X-WR-TIMEZONE:America/Los_Angeles
+X-WR-CALDESC:Your contacts' birthdays and anniversaries
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20141210
+DTEND;VALUE=DATE:20141211
+RRULE:FREQ=DAILY;INTERVAL=1;COUNT=1
+RDATE:20131210Z
+RDATE:20121210Z
+DTSTAMP:20121207T183041Z
+UID:2014_BIRTHDAY_79d389868f96182e@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=Contac
+ ts;X-NUM-GUESTS=0:mailto:4dhmurjkc5hn8sq0ctp6utbg5pr2sor1dhimsp31e8n6errfct
+ m6abj3dtmg@virtual
+CLASS:PUBLIC
+CREATED:20121207T183041Z
+LAST-MODIFIED:20121207T183041Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:PErson #2's birthday
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20121210
+DTEND;VALUE=DATE:20121211
+DTSTAMP:20121207T183041Z
+UID:BIRTHDAY_79d389868f96182e@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=Contac
+ ts;X-NUM-GUESTS=0:mailto:4dhmurjkc5hn8sq0ctp6utbg5pr2sor1dhimsp31e8n6errfct
+ m6abj3dtmg@virtual
+X-GOOGLE-CALENDAR-CONTENT-ICON:https://calendar.google.com/googlecalendar/i
+ mages/cake.gif
+X-GOOGLE-CALENDAR-CONTENT-DISPLAY:chip
+RECURRENCE-ID;VALUE=DATE:20121210
+CLASS:PUBLIC
+CREATED:20121207T183041Z
+DESCRIPTION:Today is PErson #2's birthday!
+LAST-MODIFIED:20121207T183041Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:PErson #2's birthday
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20131210
+DTEND;VALUE=DATE:20131211
+DTSTAMP:20121207T183041Z
+UID:BIRTHDAY_79d389868f96182e@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=Contac
+ ts;X-NUM-GUESTS=0:mailto:4dhmurjkc5hn8sq0ctp6utbg5pr2sor1dhimsp31e8n6errfct
+ m6abj3dtmg@virtual
+X-GOOGLE-CALENDAR-CONTENT-ICON:https://calendar.google.com/googlecalendar/i
+ mages/cake.gif
+X-GOOGLE-CALENDAR-CONTENT-DISPLAY:chip
+RECURRENCE-ID;VALUE=DATE:20131210
+CLASS:PUBLIC
+CREATED:20121207T183041Z
+DESCRIPTION:Today is PErson #2's birthday!
+LAST-MODIFIED:20121207T183041Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:PErson #2's birthday
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20141210
+DTEND;VALUE=DATE:20141211
+DTSTAMP:20121207T183041Z
+UID:BIRTHDAY_79d389868f96182e@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=Contac
+ ts;X-NUM-GUESTS=0:mailto:4dhmurjkc5hn8sq0ctp6utbg5pr2sor1dhimsp31e8n6errfct
+ m6abj3dtmg@virtual
+X-GOOGLE-CALENDAR-CONTENT-ICON:https://calendar.google.com/googlecalendar/i
+ mages/cake.gif
+X-GOOGLE-CALENDAR-CONTENT-DISPLAY:chip
+RECURRENCE-ID;VALUE=DATE:20141210
+CLASS:PUBLIC
+CREATED:20121207T183041Z
+DESCRIPTION:Today is PErson #2's birthday!
+LAST-MODIFIED:20121207T183041Z
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:PErson #2's birthday
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/minimal.ics b/includes/composer/vendor/om/icalparser/tests/cal/minimal.ics
new file mode 100755
index 0000000..b97d2ac
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/minimal.ics
@@ -0,0 +1,39 @@
+BEGIN:VCALENDAR
+PRODID:-//Google Inc//Google Calendar 70.9054//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+X-WR-CALNAME:calmozilla1@gmail.com
+X-WR-TIMEZONE:America/Los_Angeles
+BEGIN:VTIMEZONE
+TZID:America/Los_Angeles
+X-LIC-LOCATION:America/Los_Angeles
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+TZNAME:PDT
+DTSTART:19700308T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+TZNAME:PST
+DTSTART:19701101T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTART;TZID=America/Los_Angeles:20120630T060000
+DTEND;TZID=America/Los_Angeles:20120630T070000
+DTSTAMP:20120724T212411Z
+UID:dn4vrfmfn5p05roahsopg57h48@google.com
+CREATED:20120724T212411Z
+DESCRIPTION:
+LAST-MODIFIED:20120724T212411Z
+LOCATION:
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY:Really long event name thing
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/missing-timezone.ics b/includes/composer/vendor/om/icalparser/tests/cal/missing-timezone.ics
new file mode 100644
index 0000000..2579548
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/missing-timezone.ics
@@ -0,0 +1,47 @@
+BEGIN:VCALENDAR
+PRODID;X-RICAL-TZSOURCE=TZINFO:-//Airbnb Inc//Hosting Calendar 0.8.8//EN
+CALSCALE:GREGORIAN
+VERSION:2.0
+BEGIN:VEVENT
+DTEND;VALUE=DATE:20220520
+DTSTART;VALUE=DATE:20220412
+UID:1418fdfasfdasdfsad@airbnb.com
+DESCRIPTION:Reservation URL: https://www.airbnb.com/hosting/reservations/
+ details/HMQHSAR9SE\nPhone Number (Last 4 Digits): 0431
+SUMMARY:Reserved
+END:VEVENT
+BEGIN:VEVENT
+DTEND;VALUE=DATE:20220620
+DTSTART;VALUE=DATE:20220617
+UID:1418fb94e984-dfasdfasdfsdfsdfsd@airbnb.com
+DESCRIPTION:Reservation URL: https://www.airbnb.com/hosting/reservations/
+ details/HMWQXYQSM4\nPhone Number (Last 4 Digits): 2360
+SUMMARY:Reserved
+END:VEVENT
+BEGIN:VEVENT
+DTEND;VALUE=DATE:20220628
+DTSTART;VALUE=DATE:20220625
+UID:1418fb94e984-dafdfdfadfdfadsfasdafsd@airbnb.com
+DESCRIPTION:Reservation URL: https://www.airbnb.com/hosting/reservations/
+ details/HM49HZXKQT\nPhone Number (Last 4 Digits): 1537
+SUMMARY:Reserved
+END:VEVENT
+BEGIN:VEVENT
+DTEND;VALUE=DATE:20220724
+DTSTART;VALUE=DATE:20220723
+UID:6fec1092d3fa-afdfasdfdsfasdfasdfsdfasd@airbnb.com
+SUMMARY:Airbnb (Not available)
+END:VEVENT
+BEGIN:VEVENT
+DTEND;VALUE=DATE:20220807
+DTSTART;VALUE=DATE:20220725
+UID:6fec1092d3fa-afdfsdfsdfasdfadsfsfs@airbnb.com
+SUMMARY:Airbnb (Not available)
+END:VEVENT
+BEGIN:VEVENT
+DTEND;VALUE=DATE:20230512
+DTSTART;VALUE=DATE:20221107
+UID:6fec1092d3fa-afdafdsafsdfdfsdfsd@airbnb.com
+SUMMARY:Airbnb (Not available)
+END:VEVENT
+END:VCALENDAR \ No newline at end of file
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/missing_RRULE_notice.ics b/includes/composer/vendor/om/icalparser/tests/cal/missing_RRULE_notice.ics
new file mode 100644
index 0000000..84ace92
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/missing_RRULE_notice.ics
@@ -0,0 +1,39 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+X-WR-CALNAME:URL
+CALSCALE:GREGORIAN
+BEGIN:VTIMEZONE
+TZID:Europe/Berlin
+TZURL:http://tzurl.org/zoneinfo-outlook/Europe/Berlin
+X-LIC-LOCATION:Europe/Berlin
+BEGIN:DAYLIGHT
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+TZNAME:CEST
+DTSTART:19700329T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+TZNAME:CET
+DTSTART:19701025T030000
+RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:111
+DTSTAMP:20181123T192651Z
+CATEGORIES;LANGUAGE=de-DE:Party
+CONTACT:
+DESCRIPTION:xxx
+DTSTART;TZID=Europe/Berlin:20160415T210000
+DTEND;TZID=Europe/Berlin:20160416T040000
+LOCATION:xxx
+RDATE;TZID=Europe/Berlin:20161216T210000
+RDATE;TZID=Europe/Berlin:20161223T210000
+RDATE;TZID=Europe/Berlin:20161230T210000
+SEQUENCE:0
+SUMMARY:xxx
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/multiline_description.ics b/includes/composer/vendor/om/icalparser/tests/cal/multiline_description.ics
new file mode 100644
index 0000000..b5ff35c
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/multiline_description.ics
@@ -0,0 +1,52 @@
+BEGIN:VCALENDAR
+PRODID:-//Google Inc//Google Calendar 70.9054//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+X-WR-CALNAME:calmozilla1@gmail.com
+X-WR-TIMEZONE:America/Los_Angeles
+BEGIN:VTIMEZONE
+TZID:America/Los_Angeles
+X-LIC-LOCATION:America/Los_Angeles
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+TZNAME:PDT
+DTSTART:19700308T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+TZNAME:PST
+DTSTART:19701101T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTART;TZID=America/Los_Angeles:20120630T060000
+DTEND;TZID=America/Los_Angeles:20120630T070000
+DTSTAMP:20120724T212411Z
+UID:dn4vrfmfn5p05roahsopg57h48@google.com
+CREATED:20120724T212411Z
+DESCRIPTION:Here is a description that spans multiple lines!\n\nThis should be on
+ a new line as well because the description contains newline characters.
+LAST-MODIFIED:20120724T212411Z
+LOCATION:
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY:Really long event name thing
+TRANSP:OPAQUE
+BEGIN:VALARM
+ACTION:EMAIL
+DESCRIPTION:This is an event reminder
+SUMMARY:Alarm notification
+ATTENDEE:mailto:calmozilla1@gmail.com
+TRIGGER:-P0DT0H30M0S
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:This is an event reminder
+TRIGGER:-P0DT0H30M0S
+END:VALARM
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/multiple_attachments.ics b/includes/composer/vendor/om/icalparser/tests/cal/multiple_attachments.ics
new file mode 100755
index 0000000..0c25f82
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/multiple_attachments.ics
@@ -0,0 +1,45 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:Zimbra-Calendar-Provider
+BEGIN:VTIMEZONE
+TZID:America/Los_Angeles
+BEGIN:STANDARD
+DTSTART:19710101T020000
+TZOFFSETTO:-0800
+TZOFFSETFROM:-0700
+RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=11;BYDAY=1SU
+TZNAME:PST
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19710101T020000
+TZOFFSETTO:-0700
+TZOFFSETFROM:-0800
+RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=2SU
+TZNAME:PDT
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:1334F9B7-6136-444E-A58D-472564C6AA73
+SUMMARY:sahaja <> frashed
+DESCRIPTION:weekly 1on1
+ATTACH:http://globe-views.com/dcim/dreams/dog/dog-01.jpg
+ATTACH:http://www.hdanimals.com/static/cache/e9/59/e959f33a6b440f36cad24beca8bf6d1c.jpg
+ATTENDEE;CN=James Lal;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS
+ -ACTION;RSVP=TRUE:mailto:jlal@mozilla.com
+ORGANIZER;CN=Faramarz Rashed:mailto:frashed@mozilla.com
+DTSTART;TZID=America/Los_Angeles:20120326T110000
+DTEND;TZID=America/Los_Angeles:20120326T113000
+STATUS:CONFIRMED
+CLASS:PUBLIC
+TRANSP:OPAQUE
+LAST-MODIFIED:20120326T161522Z
+DTSTAMP:20120730T165637Z
+SEQUENCE:9
+BEGIN:VALARM
+ACTION:DISPLAY
+TRIGGER;RELATED=START:-PT5M
+DESCRIPTION:Reminder
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/multiple_categories.ics b/includes/composer/vendor/om/icalparser/tests/cal/multiple_categories.ics
new file mode 100755
index 0000000..5353845
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/multiple_categories.ics
@@ -0,0 +1,67 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:Zimbra-Calendar-Provider
+BEGIN:VTIMEZONE
+TZID:America/Los_Angeles
+BEGIN:STANDARD
+DTSTART:19710101T020000
+TZOFFSETTO:-0800
+TZOFFSETFROM:-0700
+RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=11;BYDAY=1SU
+TZNAME:PST
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19710101T020000
+TZOFFSETTO:-0700
+TZOFFSETFROM:-0800
+RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=2SU
+TZNAME:PDT
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:1334F9B7-6136-444E-A58D-472564C6AA73
+SUMMARY:sahaja <> frashed
+DESCRIPTION:weekly 1on1
+CATEGORIES:one, two, three
+ATTENDEE;CN=James Lal;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS
+ -ACTION;RSVP=TRUE:mailto:jlal@mozilla.com
+ORGANIZER;CN=Faramarz Rashed:mailto:frashed@mozilla.com
+DTSTART;TZID=America/Los_Angeles:20120326T110000
+DTEND;TZID=America/Los_Angeles:20120326T113000
+STATUS:CONFIRMED
+CLASS:PUBLIC
+TRANSP:OPAQUE
+LAST-MODIFIED:20120326T161522Z
+DTSTAMP:20120730T165637Z
+SEQUENCE:9
+BEGIN:VALARM
+ACTION:DISPLAY
+TRIGGER;RELATED=START:-PT5M
+DESCRIPTION:Reminder
+END:VALARM
+END:VEVENT
+BEGIN:VEVENT
+UID:14556F9B7-6136-444E-A58D-472564C6AA73
+SUMMARY:something something
+DESCRIPTION:weekly 1on1
+CATEGORIES:one
+CATEGORIES:two
+CATEGORIES:three
+ATTENDEE;CN=James Lal;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS
+ -ACTION;RSVP=TRUE:mailto:jlal@mozilla.com
+ORGANIZER;CN=Faramarz Rashed:mailto:frashed@mozilla.com
+DTSTART;TZID=America/Los_Angeles:20120326T110000
+DTEND;TZID=America/Los_Angeles:20120326T113000
+STATUS:CONFIRMED
+CLASS:PUBLIC
+TRANSP:OPAQUE
+LAST-MODIFIED:20120326T161522Z
+DTSTAMP:20120730T165637Z
+SEQUENCE:9
+BEGIN:VALARM
+ACTION:DISPLAY
+TRIGGER;RELATED=START:-PT5M
+DESCRIPTION:Reminder
+END:VALARM
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/multiple_rrules.ics b/includes/composer/vendor/om/icalparser/tests/cal/multiple_rrules.ics
new file mode 100755
index 0000000..339c318
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/multiple_rrules.ics
@@ -0,0 +1,45 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:Zimbra-Calendar-Provider
+BEGIN:VTIMEZONE
+TZID:America/Los_Angeles
+BEGIN:STANDARD
+DTSTART:19710101T020000
+TZOFFSETTO:-0800
+TZOFFSETFROM:-0700
+RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=11;BYDAY=1SU
+TZNAME:PST
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19710101T020000
+TZOFFSETTO:-0700
+TZOFFSETFROM:-0800
+RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=2SU
+TZNAME:PDT
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:1334F9B7-6136-444E-A58D-472564C6AA73
+RRULE:FREQ=WEEKLY;UNTIL=20120730T065959Z
+RRULE:FREQ=MONTHLY;BYDAY=SU;UNTIL=20120730T065959Z
+SUMMARY:sahaja <> frashed
+DESCRIPTION:weekly 1on1
+ATTENDEE;CN=James Lal;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS
+ -ACTION;RSVP=TRUE:mailto:jlal@mozilla.com
+ORGANIZER;CN=Faramarz Rashed:mailto:frashed@mozilla.com
+DTSTART;TZID=America/Los_Angeles:20120326T110000
+DTEND;TZID=America/Los_Angeles:20120326T113000
+STATUS:CONFIRMED
+CLASS:PUBLIC
+TRANSP:OPAQUE
+LAST-MODIFIED:20120326T161522Z
+DTSTAMP:20120730T165637Z
+SEQUENCE:9
+BEGIN:VALARM
+ACTION:DISPLAY
+TRIGGER;RELATED=START:-PT5M
+DESCRIPTION:Reminder
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/parserv2.ics b/includes/composer/vendor/om/icalparser/tests/cal/parserv2.ics
new file mode 100755
index 0000000..11c8301
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/parserv2.ics
@@ -0,0 +1,45 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:Zimbra-Calendar-Provider
+BEGIN:VTIMEZONE
+TZID:America/Los_Angeles
+BEGIN:STANDARD
+DTSTART:19710101T020000
+TZOFFSETTO:-0800
+TZOFFSETFROM:-0700
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
+TZNAME:PST
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19710101T020000
+TZOFFSETTO:-0700
+TZOFFSETFROM:-0800
+RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
+TZNAME:PDT
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:44c10eaa-db0b-4223-8653-cf2b63f26326
+RRULE:FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR
+SUMMARY:Calendar
+DESCRIPTION:desc
+ATTENDEE;CN=XXX;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;RSVP=TRU
+ E:mailto:foo@bar.com
+ATTENDEE;CN=XXXX;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;RSVP=TR
+ UE:mailto:x@bar.com
+ORGANIZER;CN=foobar:mailto:x@bar.com
+DTSTART;TZID=America/Los_Angeles:20120911T103000
+DTEND;TZID=America/Los_Angeles:20120911T110000
+STATUS:CONFIRMED
+CLASS:PUBLIC
+TRANSP:OPAQUE
+LAST-MODIFIED:20120911T184851Z
+DTSTAMP:20120911T184851Z
+SEQUENCE:1
+BEGIN:VALARM
+ACTION:DISPLAY
+TRIGGER;RELATED=START:-PT5M
+DESCRIPTION:Reminder
+END:VALARM
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/readme.md b/includes/composer/vendor/om/icalparser/tests/cal/readme.md
new file mode 100644
index 0000000..4e3ec57
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/readme.md
@@ -0,0 +1,3 @@
+# iCal source
+
+- https://github.com/mozilla-comm/ical.js/tree/master/samples
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/recur_instances.ics b/includes/composer/vendor/om/icalparser/tests/cal/recur_instances.ics
new file mode 100755
index 0000000..d26903f
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/recur_instances.ics
@@ -0,0 +1,92 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:Zimbra-Calendar-Provider
+BEGIN:VTIMEZONE
+TZID:America/Los_Angeles
+BEGIN:STANDARD
+DTSTART:19710101T020000
+TZOFFSETTO:-0800
+TZOFFSETFROM:-0700
+RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=11;BYDAY=1SU
+TZNAME:PST
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19710101T020000
+TZOFFSETTO:-0700
+TZOFFSETFROM:-0800
+RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=2SU
+TZNAME:PDT
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:623c13c0-6c2b-45d6-a12b-c33ad61c4868
+DESCRIPTION:IAM FOO
+RRULE:FREQ=MONTHLY;INTERVAL=1;BYDAY=1TU
+SUMMARY:Crazy Event Thingy!
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=Sahaja
+ Lal;X-NUM-GUESTS=0:mailto:calmozilla1@gmail.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=ja
+ mes@lightsofapollo.com;X-NUM-GUESTS=0:mailto:james@lightsofapollo.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=ia
+ m.revelation@gmail.com;X-NUM-GUESTS=0:mailto:iam.revelation@gmail.com
+LOCATION:PLACE
+ORGANIZER;CN=James Lal:mailto:jlal@mozilla.com
+DTSTART;TZID=America/Los_Angeles:20121002T100000
+DTEND;TZID=America/Los_Angeles:20121002T103000
+STATUS:CONFIRMED
+CLASS:PUBLIC
+TRANSP:OPAQUE
+LAST-MODIFIED:20120912T171506Z
+DTSTAMP:20120912T171506Z
+SEQUENCE:0
+RDATE;TZID=America/Los_Angeles:20121105T100000
+RDATE;TZID=America/Los_Angeles:20121110T100000,20121130T100000
+EXDATE;TZID=America/Los_Angeles:20130402T100000
+EXDATE;TZID=America/Los_Angeles:20121204T100000
+EXDATE;TZID=America/Los_Angeles:20130205T100000
+BEGIN:VALARM
+ACTION:DISPLAY
+TRIGGER;RELATED=START:-PT5M
+DESCRIPTION:Reminder
+END:VALARM
+END:VEVENT
+BEGIN:VEVENT
+UID:623c13c0-6c2b-45d6-a12b-c33ad61c4868
+SUMMARY:Crazy Event Thingy!
+DESCRIPTION:I HAZ CHANGED!
+ORGANIZER;CN=James Lal:mailto:jlal@mozilla.com
+DTSTART;TZID=America/Los_Angeles:20121002T150000
+DTEND;TZID=America/Los_Angeles:20121002T153000
+STATUS:CONFIRMED
+CLASS:PUBLIC
+TRANSP:OPAQUE
+RECURRENCE-ID;TZID=America/Los_Angeles:20121002T100000
+LAST-MODIFIED:20120912T171540Z
+DTSTAMP:20120912T171540Z
+SEQUENCE:1
+BEGIN:VALARM
+ACTION:DISPLAY
+TRIGGER;RELATED=START:-PT5M
+DESCRIPTION:Reminder
+END:VALARM
+END:VEVENT
+BEGIN:VEVENT
+UID:623c13c0-6c2b-45d6-a12b-c33ad61c4868
+SUMMARY:Crazy Event Thingy!
+ORGANIZER;CN=James Lal:mailto:jlal@mozilla.com
+DTSTART;TZID=America/Los_Angeles:20121106T200000
+DTEND;TZID=America/Los_Angeles:20121106T203000
+STATUS:CONFIRMED
+CLASS:PUBLIC
+TRANSP:OPAQUE
+RECURRENCE-ID:20121105T180000Z
+LAST-MODIFIED:20120912T171820Z
+DTSTAMP:20120912T171820Z
+SEQUENCE:1
+BEGIN:VALARM
+ACTION:DISPLAY
+TRIGGER;RELATED=START:-PT5M
+DESCRIPTION:Reminder
+END:VALARM
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/recur_instances_finite.ics b/includes/composer/vendor/om/icalparser/tests/cal/recur_instances_finite.ics
new file mode 100755
index 0000000..e7dc481
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/recur_instances_finite.ics
@@ -0,0 +1,50 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:Zimbra-Calendar-Provider
+BEGIN:VTIMEZONE
+TZID:America/Los_Angeles
+BEGIN:STANDARD
+DTSTART:19710101T020000
+TZOFFSETTO:-0800
+TZOFFSETFROM:-0700
+RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=11;BYDAY=1SU
+TZNAME:PST
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19710101T020000
+TZOFFSETTO:-0700
+TZOFFSETFROM:-0800
+RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=2SU
+TZNAME:PDT
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:623c13c0-6c2b-45d6-a12b-c33ad61c4868
+DESCRIPTION:IAM FOO
+RRULE:FREQ=MONTHLY;INTERVAL=1;BYDAY=1TU;UNTIL=20121231T100000
+SUMMARY:Crazy Event Thingy!
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=Sahaja
+ Lal;X-NUM-GUESTS=0:mailto:calmozilla1@gmail.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=ja
+ mes@lightsofapollo.com;X-NUM-GUESTS=0:mailto:james@lightsofapollo.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=ia
+ m.revelation@gmail.com;X-NUM-GUESTS=0:mailto:iam.revelation@gmail.com
+LOCATION:PLACE
+ORGANIZER;CN=James Lal:mailto:jlal@mozilla.com
+DTSTART;TZID=America/Los_Angeles:20121002T100000
+DTEND;TZID=America/Los_Angeles:20121002T103000
+STATUS:CONFIRMED
+CLASS:PUBLIC
+TRANSP:OPAQUE
+LAST-MODIFIED:20120912T171506Z
+DTSTAMP:20120912T171506Z
+SEQUENCE:0
+RDATE;TZID=America/Los_Angeles:20121110T100000
+RDATE;TZID=America/Los_Angeles:20121105T100000
+BEGIN:VALARM
+ACTION:DISPLAY
+TRIGGER;RELATED=START:-PT5M
+DESCRIPTION:Reminder
+END:VALARM
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/recur_instances_with_modifications.ics b/includes/composer/vendor/om/icalparser/tests/cal/recur_instances_with_modifications.ics
new file mode 100644
index 0000000..dcaef06
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/recur_instances_with_modifications.ics
@@ -0,0 +1,63 @@
+BEGIN:VCALENDAR
+PRODID:-//Google Inc//Google Calendar 70.9054//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+METHOD:PUBLISH
+X-WR-CALNAME:School Board
+X-WR-TIMEZONE:America/Chicago
+X-WR-CALDESC:
+BEGIN:VTIMEZONE
+TZID:America/Chicago
+X-LIC-LOCATION:America/Chicago
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0600
+TZOFFSETTO:-0500
+TZNAME:CDT
+DTSTART:19700308T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0600
+TZNAME:CST
+DTSTART:19701101T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTART;TZID=America/Chicago:20160808T180000
+DTEND;TZID=America/Chicago:20160808T203000
+DTSTAMP:20160718T160226Z
+UID:kpgr4590djmi89l3sv51gmalrc@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=School
+ Board;X-NUM-GUESTS=0:mailto:aliceisd.net_7hatmpq9uek3a1h6if0r0sn5bc@group.
+ calendar.google.com
+RECURRENCE-ID;TZID=America/Chicago:20160808T183000
+CREATED:20150715T183809Z
+DESCRIPTION:
+LAST-MODIFIED:20160718T155538Z
+LOCATION:
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Regular Board Meeting Modified
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;TZID=America/Chicago:20150810T183000
+DTEND;TZID=America/Chicago:20150810T210000
+RRULE:FREQ=MONTHLY;UNTIL=20180709T233000Z;BYDAY=2MO
+DTSTAMP:20160718T160226Z
+UID:kpgr4590djmi89l3sv51gmalrc@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=School
+ Board;X-NUM-GUESTS=0:mailto:aliceisd.net_7hatmpq9uek3a1h6if0r0sn5bc@group.
+ calendar.google.com
+CREATED:20150715T183809Z
+DESCRIPTION:
+LAST-MODIFIED:20160718T155538Z
+LOCATION:
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY:Regular Board Meeting
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/recur_instances_with_modifications_and_interval.ics b/includes/composer/vendor/om/icalparser/tests/cal/recur_instances_with_modifications_and_interval.ics
new file mode 100644
index 0000000..202393e
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/recur_instances_with_modifications_and_interval.ics
@@ -0,0 +1,578 @@
+BEGIN:VCALENDAR
+PRODID:-//Google Inc//Google Calendar 70.9054//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+METHOD:PUBLISH
+X-WR-CALNAME:SB-1213
+X-WR-TIMEZONE:America/Denver
+X-WR-CALDESC:
+BEGIN:VTIMEZONE
+TZID:America/New_York
+X-LIC-LOCATION:America/New_York
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+TZNAME:EDT
+DTSTART:19700308T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+TZNAME:EST
+DTSTART:19701101T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170602
+DTEND;VALUE=DATE:20170603
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170601
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:4
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170531
+DTEND;VALUE=DATE:20170601
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170530
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:4
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170524
+DTEND;VALUE=DATE:20170525
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170524
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:5
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170216
+DTEND;VALUE=DATE:20170217
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170215
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170214
+DTEND;VALUE=DATE:20170215
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170213
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170210
+DTEND;VALUE=DATE:20170211
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170209
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170208
+DTEND;VALUE=DATE:20170209
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170207
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170206
+DTEND;VALUE=DATE:20170207
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170205
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170202
+DTEND;VALUE=DATE:20170203
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170201
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170131
+DTEND;VALUE=DATE:20170201
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170130
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170127
+DTEND;VALUE=DATE:20170128
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170126
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170125
+DTEND;VALUE=DATE:20170126
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170124
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170123
+DTEND;VALUE=DATE:20170124
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170122
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170119
+DTEND;VALUE=DATE:20170120
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170118
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170113
+DTEND;VALUE=DATE:20170114
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170112
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170111
+DTEND;VALUE=DATE:20170112
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170110
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170109
+DTEND;VALUE=DATE:20170110
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170108
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170105
+DTEND;VALUE=DATE:20170106
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170104
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170103
+DTEND;VALUE=DATE:20170104
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170102
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20161122
+DTEND;VALUE=DATE:20161123
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20161121
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20161118
+DTEND;VALUE=DATE:20161119
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20161117
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20161116
+DTEND;VALUE=DATE:20161117
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20161115
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20161114
+DTEND;VALUE=DATE:20161115
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20161113
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20161110
+DTEND;VALUE=DATE:20161111
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20161109
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20160923
+DTEND;VALUE=DATE:20160924
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20160922
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20160921
+DTEND;VALUE=DATE:20160922
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20160920
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20160919
+DTEND;VALUE=DATE:20160920
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20160916
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20160915
+DTEND;VALUE=DATE:20160916
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20160914
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20160913
+DTEND;VALUE=DATE:20160914
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20160912
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20160909
+DTEND;VALUE=DATE:20160910
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20160908
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20160907
+DTEND;VALUE=DATE:20160908
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20160906
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20160817
+DTEND;VALUE=DATE:20160818
+EXDATE;VALUE=DATE:20160821
+EXDATE;VALUE=DATE:20160827
+EXDATE;VALUE=DATE:20160904
+EXDATE;VALUE=DATE:20160910
+EXDATE;VALUE=DATE:20160918
+EXDATE;VALUE=DATE:20160924
+EXDATE;VALUE=DATE:20160926
+EXDATE;VALUE=DATE:20161002
+EXDATE;VALUE=DATE:20161008
+EXDATE;VALUE=DATE:20161016
+EXDATE;VALUE=DATE:20161022
+EXDATE;VALUE=DATE:20161030
+EXDATE;VALUE=DATE:20161105
+EXDATE;VALUE=DATE:20161107
+EXDATE;VALUE=DATE:20161111
+EXDATE;VALUE=DATE:20161119
+EXDATE;VALUE=DATE:20161123
+EXDATE;VALUE=DATE:20161125
+EXDATE;VALUE=DATE:20161127
+EXDATE;VALUE=DATE:20161203
+EXDATE;VALUE=DATE:20161211
+EXDATE;VALUE=DATE:20161217
+EXDATE;VALUE=DATE:20161219
+EXDATE;VALUE=DATE:20161221
+EXDATE;VALUE=DATE:20161223
+EXDATE;VALUE=DATE:20161225
+EXDATE;VALUE=DATE:20161227
+EXDATE;VALUE=DATE:20161229
+EXDATE;VALUE=DATE:20161231
+EXDATE;VALUE=DATE:20170106
+EXDATE;VALUE=DATE:20170114
+EXDATE;VALUE=DATE:20170116
+EXDATE;VALUE=DATE:20170120
+EXDATE;VALUE=DATE:20170128
+EXDATE;VALUE=DATE:20170203
+EXDATE;VALUE=DATE:20170211
+EXDATE;VALUE=DATE:20170217
+EXDATE;VALUE=DATE:20170219
+EXDATE;VALUE=DATE:20170225
+EXDATE;VALUE=DATE:20170305
+EXDATE;VALUE=DATE:20170311
+EXDATE;VALUE=DATE:20170319
+EXDATE;VALUE=DATE:20170325
+EXDATE;VALUE=DATE:20170327
+EXDATE;VALUE=DATE:20170402
+EXDATE;VALUE=DATE:20170408
+EXDATE;VALUE=DATE:20170410
+EXDATE;VALUE=DATE:20170412
+EXDATE;VALUE=DATE:20170414
+EXDATE;VALUE=DATE:20170416
+EXDATE;VALUE=DATE:20170422
+EXDATE;VALUE=DATE:20170430
+EXDATE;VALUE=DATE:20170506
+EXDATE;VALUE=DATE:20170514
+EXDATE;VALUE=DATE:20170520
+EXDATE;VALUE=DATE:20170528
+RRULE:FREQ=DAILY;UNTIL=20170601;INTERVAL=2
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:B
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20170601
+DTEND;VALUE=DATE:20170602
+DTSTAMP:20160829T191852Z
+UID:h9i3hjtisl53goaljdcdb681fc@google.com
+RECURRENCE-ID;VALUE=DATE:20170526
+CREATED:20160829T134437Z
+DESCRIPTION:
+LAST-MODIFIED:20160829T134450Z
+LOCATION:
+SEQUENCE:6
+STATUS:CONFIRMED
+SUMMARY:A
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/recur_instances_with_modifications_to_first_day.ics b/includes/composer/vendor/om/icalparser/tests/cal/recur_instances_with_modifications_to_first_day.ics
new file mode 100755
index 0000000..7dd2939
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/recur_instances_with_modifications_to_first_day.ics
@@ -0,0 +1,57 @@
+BEGIN:VCALENDAR
+PRODID:-//Google Inc//Google Calendar 70.9054//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+METHOD:PUBLISH
+X-WR-CALNAME:G. C. Hawley Middle School
+X-WR-TIMEZONE:America/New_York
+X-WR-CALDESC:This is the website calendar for GC Hawley MS.
+BEGIN:VTIMEZONE
+TZID:America/New_York
+X-LIC-LOCATION:America/New_York
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+TZNAME:EDT
+DTSTART:19700308T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+TZNAME:EST
+DTSTART:19701101T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTART;TZID=America/New_York:20160915T123000
+DTEND;TZID=America/New_York:20160915T160000
+RRULE:FREQ=MONTHLY;UNTIL=20161020T035959Z;BYDAY=3TH
+DTSTAMP:20160913T221241Z
+UID:d28i2lkvhp2qb2v7n6oe9poikc@google.com
+CREATED:20160705T193430Z
+DESCRIPTION:Professional Development for Staff
+LAST-MODIFIED:20160825T003008Z
+LOCATION:
+SEQUENCE:1
+STATUS:CONFIRMED
+SUMMARY:Early Release for Students
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;TZID=America/New_York:20160929T123000
+DTEND;TZID=America/New_York:20160929T160000
+DTSTAMP:20160913T221241Z
+UID:d28i2lkvhp2qb2v7n6oe9poikc@google.com
+RECURRENCE-ID;TZID=America/New_York:20160915T123000
+CREATED:20160705T193430Z
+DESCRIPTION:Professional Development for Staff
+LAST-MODIFIED:20160825T003008Z
+LOCATION:
+SEQUENCE:2
+STATUS:CONFIRMED
+SUMMARY:Early Release for Students
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/rrule_interval.ics b/includes/composer/vendor/om/icalparser/tests/cal/rrule_interval.ics
new file mode 100644
index 0000000..262721a
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/rrule_interval.ics
@@ -0,0 +1,34 @@
+BEGIN:VCALENDAR
+PRODID:-//Google Inc//Google Calendar 70.9054//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+X-WR-CALNAME:calmozilla1@gmail.com
+X-WR-TIMEZONE:America/Los_Angeles
+BEGIN:VTIMEZONE
+TZID:America/Los_Angeles
+X-LIC-LOCATION:America/Los_Angeles
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+TZNAME:PDT
+DTSTART:19700308T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+TZNAME:PST
+DTSTART:19701101T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTART;TZID=America/Los_Angeles:20230131T050000
+DTEND;TZID=America/Los_Angeles:20230131T060000
+RRULE:FREQ=WEEKLY;WKST=MO;UNTIL=20230228T090000;INTERVAL=2;BYDAY=TU
+DTSTAMP:20120803T221236Z
+DESCRIPTION:
+SUMMARY:Every day recurring
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/url.ics b/includes/composer/vendor/om/icalparser/tests/cal/url.ics
new file mode 100644
index 0000000..8ae6620
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/url.ics
@@ -0,0 +1,32 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+X-WR-CALNAME:URL
+CALSCALE:GREGORIAN
+BEGIN:VTIMEZONE
+TZID:Europe/Berlin
+TZURL:http://tzurl.org/zoneinfo-outlook/Europe/Berlin
+X-LIC-LOCATION:Europe/Berlin
+BEGIN:DAYLIGHT
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+TZNAME:CEST
+DTSTART:19700329T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+TZNAME:CET
+DTSTART:19701025T030000
+RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTAMP:20191106T093607Z
+UID:20191106T093607Z-791992399@marudot.com
+DTSTART;VALUE=DATE:20191101
+DTEND;VALUE=DATE:20191102
+SUMMARY:Example event
+URL:https%3A%2F%2Fgithub.com%2FOzzyCzech%2Ficalparser%2F
+END:VEVENT
+END:VCALENDAR \ No newline at end of file
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/utc_negative_zero.ics b/includes/composer/vendor/om/icalparser/tests/cal/utc_negative_zero.ics
new file mode 100755
index 0000000..e61ad9a
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/utc_negative_zero.ics
@@ -0,0 +1,27 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:Zimbra-Calendar-Provider
+BEGIN:VTIMEZONE
+TZID:Etc/GMT
+BEGIN:STANDARD
+DTSTART:19710101T000000
+TZOFFSETTO:-0000
+TZOFFSETFROM:-0000
+TZNAME:GMT
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:d118e997-3683-4552-8fe8-57c641f1f179
+SUMMARY:And another
+ORGANIZER;CN=Sahaja Lal:mailto:calmozilla1@yahoo.com
+DTSTART;TZID=Etc/GMT:20120821T210000
+DTEND;TZID=Etc/GMT:20120821T213000
+STATUS:CONFIRMED
+CLASS:PUBLIC
+X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
+TRANSP:OPAQUE
+X-MICROSOFT-DISALLOW-COUNTER:TRUE
+DTSTAMP:20120817T032509Z
+SEQUENCE:0
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/weird_windows_timezones.ics b/includes/composer/vendor/om/icalparser/tests/cal/weird_windows_timezones.ics
new file mode 100644
index 0000000..5d736a1
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/weird_windows_timezones.ics
@@ -0,0 +1,237 @@
+BEGIN:VCALENDAR
+METHOD:PUBLISH
+PRODID:Microsoft Exchange Server 2010
+VERSION:2.0
+X-WR-CALNAME:High School Counselors Office
+BEGIN:VTIMEZONE
+TZID:(UTC-06:00) Central Time (US & Canada)
+BEGIN:STANDARD
+DTSTART:16010101T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0600
+RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=11
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:16010101T020000
+TZOFFSETFROM:-0600
+TZOFFSETTO:-0500
+RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SU;BYMONTH=3
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VTIMEZONE
+TZID:Eastern Time
+BEGIN:STANDARD
+DTSTART:16010101T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=11
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:16010101T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SU;BYMONTH=3
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VTIMEZONE
+TZID:Greenwich Standard Time
+BEGIN:STANDARD
+DTSTART:16010101T000000
+TZOFFSETFROM:+0000
+TZOFFSETTO:+0000
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:16010101T000000
+TZOFFSETFROM:+0000
+TZOFFSETTO:+0000
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+DESCRIPTION:All seniors MUST make an appointment in the counseling office\,
+ to visit with a counselor and go over their application for graduation.\n
+UID:040000008200E00074C5B7101A82E008000000004A8A50D0FC09D201000000000000000
+ 010000000D27D08FB73761145BC661D80CE521463
+SUMMARY:Senior Appointment Sign-up
+DTSTART;VALUE=DATE:20160914
+DTEND;VALUE=DATE:20160915
+CLASS:PUBLIC
+PRIORITY:5
+DTSTAMP:20160916T193959Z
+TRANSP:OPAQUE
+STATUS:CONFIRMED
+SEQUENCE:0
+LOCATION:
+X-MICROSOFT-CDO-APPT-SEQUENCE:0
+X-MICROSOFT-CDO-BUSYSTATUS:FREE
+X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
+X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
+X-MICROSOFT-CDO-IMPORTANCE:1
+X-MICROSOFT-CDO-INSTTYPE:0
+X-MICROSOFT-DISALLOW-COUNTER:FALSE
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION:Registration for the PSAT test begins in the counseling office.
+ The test is open to any junior or sophomore. The test is $15 and must be
+ paid at the time of registration. Test date is Wednesday\, October 19th.\n
+UID:040000008200E00074C5B7101A82E008000000004B8D8376DB0AD201000000000000000
+ 010000000CF196AE75B9C684C9E51C33A403CA455
+SUMMARY:PSAT Registration
+DTSTART;VALUE=DATE:20160915
+DTEND;VALUE=DATE:20160916
+CLASS:PUBLIC
+PRIORITY:5
+DTSTAMP:20160916T193959Z
+TRANSP:OPAQUE
+STATUS:CONFIRMED
+SEQUENCE:0
+LOCATION:
+X-MICROSOFT-CDO-APPT-SEQUENCE:0
+X-MICROSOFT-CDO-BUSYSTATUS:FREE
+X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
+X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
+X-MICROSOFT-CDO-IMPORTANCE:1
+X-MICROSOFT-CDO-INSTTYPE:0
+X-MICROSOFT-DISALLOW-COUNTER:FALSE
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION:Financial Aid night for all seniors and their parents at 7:00pm
+ in the high school auditorium.\n
+UID:040000008200E00074C5B7101A82E00800000000A3D802C7DB0AD201000000000000000
+ 0100000004690B516982D4841A6A5808F1BF285D2
+SUMMARY:Financial Aid NIght
+DTSTART;TZID="(UTC-06:00) Central Time (US & Canada)":20160919T190000
+DTEND;TZID="(UTC-06:00) Central Time (US & Canada)":20160919T200000
+CLASS:PUBLIC
+PRIORITY:5
+DTSTAMP:20160916T193959Z
+TRANSP:OPAQUE
+STATUS:CONFIRMED
+SEQUENCE:0
+LOCATION:
+X-MICROSOFT-CDO-APPT-SEQUENCE:0
+X-MICROSOFT-CDO-BUSYSTATUS:BUSY
+X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
+X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
+X-MICROSOFT-CDO-IMPORTANCE:1
+X-MICROSOFT-CDO-INSTTYPE:0
+X-MICROSOFT-DISALLOW-COUNTER:FALSE
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION:This is the last day to register for the PSAT test in October!
+ Please see the counseling office if you would like to register.\n
+UID:040000008200E00074C5B7101A82E00800000000DE3BE28BDB0AD201000000000000000
+ 0100000008C413809A1751A4E82CEB8F1BB526C61
+SUMMARY:PSAT Test registration ends
+DTSTART;VALUE=DATE:20160923
+DTEND;VALUE=DATE:20160924
+CLASS:PUBLIC
+PRIORITY:5
+DTSTAMP:20160916T193959Z
+TRANSP:OPAQUE
+STATUS:CONFIRMED
+SEQUENCE:0
+LOCATION:
+X-MICROSOFT-CDO-APPT-SEQUENCE:0
+X-MICROSOFT-CDO-BUSYSTATUS:FREE
+X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
+X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
+X-MICROSOFT-CDO-IMPORTANCE:1
+X-MICROSOFT-CDO-INSTTYPE:0
+X-MICROSOFT-DISALLOW-COUNTER:FALSE
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION:College Application Day for all interested seniors! The Bank of
+ North Dakota is waiving the application fee for any student to apply to a
+ North Dakota college or university. Counselors and college staff will be
+ assisting students with their applications periods 1-3 in the high school
+ library!\n
+UID:040000008200E00074C5B7101A82E0080000000019668644DC0AD201000000000000000
+ 010000000DB1D0B39660D4045A6AC5C232EB0D76B
+SUMMARY:College Application Day
+DTSTART;TZID="(UTC-06:00) Central Time (US & Canada)":20161005T080000
+DTEND;TZID="(UTC-06:00) Central Time (US & Canada)":20161005T081500
+CLASS:PUBLIC
+PRIORITY:5
+DTSTAMP:20160916T193959Z
+TRANSP:OPAQUE
+STATUS:CONFIRMED
+SEQUENCE:0
+LOCATION:
+X-MICROSOFT-CDO-APPT-SEQUENCE:0
+X-MICROSOFT-CDO-BUSYSTATUS:BUSY
+X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
+X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
+X-MICROSOFT-CDO-IMPORTANCE:1
+X-MICROSOFT-CDO-INSTTYPE:0
+X-MICROSOFT-DISALLOW-COUNTER:FALSE
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION:PSAT Test in the high school auditorium for all students that r
+ egistered. Please report to the auditorium at 8:30am with a sharpened #2 p
+ encil and calculator.\n
+UID:040000008200E00074C5B7101A82E00800000000E68A2E67DC0AD201000000000000000
+ 01000000080E5BC118E41944ABC1C94B815AB2235
+SUMMARY:PSAT Test
+DTSTART;VALUE=DATE:20161019
+DTEND;VALUE=DATE:20161020
+CLASS:PUBLIC
+PRIORITY:5
+DTSTAMP:20160916T193959Z
+TRANSP:OPAQUE
+STATUS:CONFIRMED
+SEQUENCE:0
+LOCATION:
+X-MICROSOFT-CDO-APPT-SEQUENCE:0
+X-MICROSOFT-CDO-BUSYSTATUS:FREE
+X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
+X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
+X-MICROSOFT-CDO-IMPORTANCE:1
+X-MICROSOFT-CDO-INSTTYPE:0
+X-MICROSOFT-DISALLOW-COUNTER:FALSE
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION:North Dakota State Assessment for all juniors (periods 2-4).\n
+UID:040000008200E00074C5B7101A82E008000000002EB6D123DD0AD201000000000000000
+ 01000000061F7AF4CD0D2C84B9C6A916D2C633339
+SUMMARY:NDSA Science Assessment
+DTSTART;TZID="(UTC-06:00) Central Time (US & Canada)":20161025T091500
+DTEND;TZID="(UTC-06:00) Central Time (US & Canada)":20161025T114500
+CLASS:PUBLIC
+PRIORITY:5
+DTSTAMP:20160916T193959Z
+TRANSP:OPAQUE
+STATUS:CONFIRMED
+SEQUENCE:0
+LOCATION:
+X-MICROSOFT-CDO-APPT-SEQUENCE:0
+X-MICROSOFT-CDO-BUSYSTATUS:BUSY
+X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
+X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
+X-MICROSOFT-CDO-IMPORTANCE:1
+X-MICROSOFT-CDO-INSTTYPE:0
+X-MICROSOFT-DISALLOW-COUNTER:FALSE
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION:\n
+UID:040000008200E00074C5B7101A82E0080000000052D533E856D6D301000000000000000
+ 010000000568B47E6EF679B4B812809E063BB64E1
+SUMMARY:Test Event
+DTSTART;TZID=Eastern Time:20180419T103000
+DTEND;TZID=Eastern Time:20180419T113000
+CLASS:PUBLIC
+PRIORITY:5
+DTSTAMP:20180417T144309Z
+TRANSP:OPAQUE
+STATUS:CONFIRMED
+SEQUENCE:0
+LOCATION:Boardroom
+X-MICROSOFT-CDO-APPT-SEQUENCE:0
+X-MICROSOFT-CDO-BUSYSTATUS:BUSY
+X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
+X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
+X-MICROSOFT-CDO-IMPORTANCE:1
+X-MICROSOFT-CDO-INSTTYPE:0
+X-MICROSOFT-DONOTFORWARDMEETING:FALSE
+X-MICROSOFT-DISALLOW-COUNTER:FALSE
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/cal/wrong_dates.ics b/includes/composer/vendor/om/icalparser/tests/cal/wrong_dates.ics
new file mode 100644
index 0000000..0468020
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/cal/wrong_dates.ics
@@ -0,0 +1,37 @@
+BEGIN:VCALENDAR
+PRODID:-//Google Inc//Google Calendar 70.9054//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+X-WR-CALNAME:calmozilla1@gmail.com
+X-WR-TIMEZONE:America/Los_Angeles
+BEGIN:VTIMEZONE
+TZID:America/Los_Angeles
+X-LIC-LOCATION:America/Los_Angeles
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:2014929
+DTEND;VALUE=DATE:20140930
+UID:dn4vrfmfn5p05roahsopg57h48@google.com
+CREATED:20120724T212411Z
+DESCRIPTION:
+LAST-MODIFIED:20120724T212411Z
+LOCATION:
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY:Really long event name thing
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20140929
+DTEND;VALUE=DATE:2014930
+UID:dn4vrfmfn5p05roahsopg57h48@google.com
+CREATED:20120724T212411Z
+DESCRIPTION:
+LAST-MODIFIED:20120724T212411Z
+LOCATION:
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY:Really long event name thing
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
diff --git a/includes/composer/vendor/om/icalparser/tests/event.attachements.phpt b/includes/composer/vendor/om/icalparser/tests/event.attachements.phpt
new file mode 100644
index 0000000..1218fb7
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/event.attachements.phpt
@@ -0,0 +1,26 @@
+<?php
+/**
+ * @author PC Drew <pc@soprisapps.com>
+ */
+
+use om\IcalParser;
+use Tester\Assert;
+use Tester\Environment;
+use function tests\test;
+
+require_once __DIR__ . '/bootstrap.php';
+date_default_timezone_set('Europe/Prague');
+
+test('Event with multiple ATTACHMENTS', function () {
+ $cal = new IcalParser();
+ $cal->parseFile(__DIR__ . '/cal/multiple_attachments.ics');
+ $first = $cal->getEvents()->getIterator()->current();
+
+ // Backwards compatibility, there is only ever one key displayed
+ Assert::hasKey('ATTACH', $first);
+ Assert::type('string', $first['ATTACH']);
+
+ // The new key 'ATTACHMENTS' is an array with 1 or more attachments
+ Assert::type('array', $first['ATTACHMENTS']);
+ Assert::count(2, $first['ATTACHMENTS']);
+});
diff --git a/includes/composer/vendor/om/icalparser/tests/event.categories.phpt b/includes/composer/vendor/om/icalparser/tests/event.categories.phpt
new file mode 100644
index 0000000..43c56cb
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/event.categories.phpt
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Copyright (c) 2004-2022 Roman Ožana (https://ozana.cz)
+ *
+ * @license BSD-3-Clause
+ * @author Roman Ožana <roman@ozana.cz>
+ */
+
+use om\IcalParser;
+use Tester\Assert;
+use function tests\test;
+
+require_once __DIR__ . '/bootstrap.php';
+date_default_timezone_set('Europe/Prague');
+
+test('Multiple categories test', function () {
+ $cal = new IcalParser();
+ $cal->parseFile(__DIR__ . '/cal/multiple_categories.ics');
+ $events = $cal->getEvents()->sorted();
+
+ foreach ($events as $event) {
+ Assert::type('array', $event['CATEGORIES']);
+ Assert::same(['one', 'two', 'three'], $event['CATEGORIES']);
+ }
+});
diff --git a/includes/composer/vendor/om/icalparser/tests/event.dates.phpt b/includes/composer/vendor/om/icalparser/tests/event.dates.phpt
new file mode 100644
index 0000000..6af95ae
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/event.dates.phpt
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Copyright (c) 2004-2022 Roman Ožana (https://ozana.cz)
+ *
+ * @license BSD-3-Clause
+ * @author Roman Ožana <roman@ozana.cz>
+ */
+
+use om\IcalParser;
+use Tester\Assert;
+use function tests\test;
+
+require_once __DIR__ . '/bootstrap.php';
+
+test('Events with wrong dates', function () {
+ $cal = new IcalParser();
+ $cal->parseFile(__DIR__ . '/cal/wrong_dates.ics');
+ $events = $cal->getEvents()->sorted();
+ Assert::same('29.9.2014 00:00:00', $events[1]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::same(null, $events[1]['DTEND']);
+
+ Assert::same(null, $events[0]['DTSTART']);
+ Assert::same('30.9.2014 00:00:00', $events[0]['DTEND']->format('j.n.Y H:i:s'));
+});
+
diff --git a/includes/composer/vendor/om/icalparser/tests/event.description.phpt b/includes/composer/vendor/om/icalparser/tests/event.description.phpt
new file mode 100644
index 0000000..8426e32
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/event.description.phpt
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Copyright (c) 2004-2022 Roman Ožana (https://ozana.cz)
+ *
+ * @license BSD-3-Clause
+ * @author Roman Ožana <roman@ozana.cz>
+ */
+
+use om\IcalParser;
+use Tester\Assert;
+use function tests\test;
+
+require_once __DIR__ . '/bootstrap.php';
+date_default_timezone_set('Europe/Prague');
+
+test('Blank description test', function () {
+ $cal = new IcalParser();
+ $results = $cal->parseFile(__DIR__ . '/cal/blank_description.ics');
+ $first = $cal->getEvents()->getIterator()->current();
+
+ Assert::hasKey('DESCRIPTION', $first);
+ Assert::same('', $first['DESCRIPTION']);
+});
+
+test('Multiple lines description', function () {
+ $cal = new IcalParser();
+ $cal->parseFile(__DIR__ . '/cal/multiline_description.ics');
+ $events = $cal->getEvents()->sorted();
+ $first = $events->getIterator()->current();
+
+ Assert::same('30.6.2012 06:00:00', $first['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::same("Here is a description that spans multiple lines!\n\nThis should be on a new line as well because the description contains newline characters.", $first['DESCRIPTION']);
+});
+
diff --git a/includes/composer/vendor/om/icalparser/tests/event.timezones.phpt b/includes/composer/vendor/om/icalparser/tests/event.timezones.phpt
new file mode 100644
index 0000000..fcb708b
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/event.timezones.phpt
@@ -0,0 +1,39 @@
+<?php
+/**
+ * @author Marc Vachette <marc.vachette@gmail.com>
+ */
+
+use om\IcalParser;
+use Tester\Assert;
+use function tests\test;
+
+require_once __DIR__ . '/bootstrap.php';
+date_default_timezone_set('Europe/Paris');
+
+test('Normal time zone', function () {
+ $cal = new IcalParser();
+ $cal->parseFile(__DIR__ . '/cal/blank_description.ics');
+ Assert::same('America/Los_Angeles', $cal->timezone->getName());
+});
+
+test('Negative zero UTC timezone', function () {
+ $cal = new IcalParser();
+ $cal->parseFile(__DIR__ . '/cal/utc_negative_zero.ics');
+ Assert::same('Etc/GMT', $cal->timezone->getName());
+});
+
+/**
+ * Time zone with custom prefixes (Mozilla files tken from here: https://www.mozilla.org/en-US/projects/calendar/holidays/)
+ */
+test('Time zone with custom prefixes', function () {
+ $cal = new IcalParser();
+ $cal->parseFile(__DIR__ . '/cal/FrenchHolidays.ics');
+ Assert::same('Europe/Paris', $cal->timezone->getName());
+});
+
+test('Weird windows timezones', function () {
+ $cal = new IcalParser();
+ $cal->parseFile(__DIR__ . '/cal/weird_windows_timezones.ics');
+ $cal->getEvents()->sorted();
+ Assert::same('Atlantic/Reykjavik', $cal->timezone->getName());
+});
diff --git a/includes/composer/vendor/om/icalparser/tests/event.url.phpt b/includes/composer/vendor/om/icalparser/tests/event.url.phpt
new file mode 100644
index 0000000..b39d3e3
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/event.url.phpt
@@ -0,0 +1,22 @@
+<?php
+/**
+ * Copyright (c) 2004-2022 Roman Ožana (https://ozana.cz)
+ *
+ * @license BSD-3-Clause
+ * @author Roman Ožana <roman@ozana.cz>
+ */
+
+use om\IcalParser;
+use Tester\Assert;
+use function tests\test;
+
+require_once __DIR__ . '/bootstrap.php';
+
+test('URL parsing check', function () {
+ $cal = new IcalParser();
+ $cal->parseFile(__DIR__ . '/cal/url.ics');
+ $first = $cal->getEvents()->getIterator()->current();
+
+ Assert::hasKey('URL', $first);
+ Assert::same($first['URL'], urlencode('https://github.com/OzzyCzech/icalparser/'));
+});
diff --git a/includes/composer/vendor/om/icalparser/tests/events.recurring.phpt b/includes/composer/vendor/om/icalparser/tests/events.recurring.phpt
new file mode 100644
index 0000000..9606992
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/events.recurring.phpt
@@ -0,0 +1,260 @@
+<?php
+/**
+ * @author PC Drew <pc@schoolblocks.com>
+ * @author Roman Ožana <roman@ozana.cz>
+ */
+
+use om\IcalParser;
+use Tester\Assert;
+use function tests\test;
+
+require_once __DIR__ . '/bootstrap.php';
+
+test('Recurring instances finite', function () {
+ $cal = new IcalParser();
+
+ $cal->parseFile(__DIR__ . '/cal/recur_instances_finite.ics');
+ $events = $cal->getEvents()->sorted();
+
+// DTSTART;TZID=America/Los_Angeles:20121002T100000
+// DTEND;TZID=America/Los_Angeles:20121002T103000
+// RRULE:FREQ=MONTHLY;INTERVAL=1;BYDAY=1TU;UNTIL=20121231T100000
+// RDATE;TZID=America/Los_Angeles:20121110T100000
+// RDATE;TZID=America/Los_Angeles:20121105T100000
+ Assert::equal(5, $events->count());
+ Assert::equal('2.10.2012 10:00:00', $events[0]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('5.11.2012 10:00:00', $events[1]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('6.11.2012 10:00:00', $events[2]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('10.11.2012 10:00:00', $events[3]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('4.12.2012 10:00:00', $events[4]['DTSTART']->format('j.n.Y H:i:s'));
+});
+
+test('Recurring instance check', function () {
+ $cal = new IcalParser();
+ $results = $cal->parseFile(__DIR__ . '/cal/recur_instances.ics');
+ $events = $cal->getEvents()->sorted();
+
+ $recurrences = [];
+ foreach ($events as $i => $event) {
+ $recurrences[] = $event['DTSTART'];
+ }
+
+// DTSTART;TZID=America/Los_Angeles:20121002T100000
+// DTEND;TZID=America/Los_Angeles:20121002T103000
+// RRULE:FREQ=MONTHLY;INTERVAL=1;BYDAY=1TU
+// RDATE;TZID=America/Los_Angeles:20121105T100000
+// RDATE;TZID=America/Los_Angeles:20121110T100000,20121130T100000
+// EXDATE;TZID=America/Los_Angeles:20130402T100000
+// EXDATE;TZID=America/Los_Angeles:20121204T100000
+// EXDATE;TZID=America/Los_Angeles:20130205T100000
+// because there is no "UNTIL", we calculate until 3 years from now of repeating events
+ $now = new DateTime('now');
+ $diff = $now->diff(new DateTime('20121002T100000'));
+ $count = ($diff->y + 3) * 12 + $diff->m;
+ Assert::equal($count, count($recurrences));
+ Assert::equal('02.10.2012 15:00:00', $recurrences[0]->format('d.m.Y H:i:s'));
+ Assert::equal('06.11.2012 20:00:00', $recurrences[1]->format('d.m.Y H:i:s'));
+ Assert::equal('10.11.2012 10:00:00', $recurrences[2]->format('d.m.Y H:i:s'));
+ Assert::equal('30.11.2012 10:00:00', $recurrences[3]->format('d.m.Y H:i:s'));
+ Assert::equal('01.01.2013 10:00:00', $recurrences[4]->format('d.m.Y H:i:s'));
+ Assert::equal('05.03.2013 10:00:00', $recurrences[5]->format('d.m.Y H:i:s'));
+ Assert::equal('07.05.2013 10:00:00', $recurrences[6]->format('d.m.Y H:i:s'));
+ Assert::equal('04.06.2013 10:00:00', $recurrences[7]->format('d.m.Y H:i:s'));
+ Assert::equal('02.07.2013 10:00:00', $recurrences[8]->format('d.m.Y H:i:s'));
+ Assert::equal('06.08.2013 10:00:00', $recurrences[9]->format('d.m.Y H:i:s'));
+ Assert::equal('03.09.2013 10:00:00', $recurrences[10]->format('d.m.Y H:i:s'));
+ Assert::equal('01.10.2013 10:00:00', $recurrences[11]->format('d.m.Y H:i:s'));
+ Assert::equal('05.11.2013 10:00:00', $recurrences[12]->format('d.m.Y H:i:s'));
+ Assert::equal('03.12.2013 10:00:00', $recurrences[13]->format('d.m.Y H:i:s'));
+ Assert::equal('07.01.2014 10:00:00', $recurrences[14]->format('d.m.Y H:i:s'));
+ Assert::equal('04.02.2014 10:00:00', $recurrences[15]->format('d.m.Y H:i:s'));
+ Assert::equal('04.03.2014 10:00:00', $recurrences[16]->format('d.m.Y H:i:s'));
+ Assert::equal('01.04.2014 10:00:00', $recurrences[17]->format('d.m.Y H:i:s'));
+ Assert::equal('06.05.2014 10:00:00', $recurrences[18]->format('d.m.Y H:i:s'));
+ Assert::equal('03.06.2014 10:00:00', $recurrences[19]->format('d.m.Y H:i:s'));
+ Assert::equal('01.07.2014 10:00:00', $recurrences[20]->format('d.m.Y H:i:s'));
+ Assert::equal('05.08.2014 10:00:00', $recurrences[21]->format('d.m.Y H:i:s'));
+ Assert::equal('02.09.2014 10:00:00', $recurrences[22]->format('d.m.Y H:i:s'));
+ Assert::equal('07.10.2014 10:00:00', $recurrences[23]->format('d.m.Y H:i:s'));
+ Assert::equal('04.11.2014 10:00:00', $recurrences[24]->format('d.m.Y H:i:s'));
+ Assert::equal('02.12.2014 10:00:00', $recurrences[25]->format('d.m.Y H:i:s'));
+ Assert::equal('06.01.2015 10:00:00', $recurrences[26]->format('d.m.Y H:i:s'));
+ Assert::equal('03.02.2015 10:00:00', $recurrences[27]->format('d.m.Y H:i:s'));
+ Assert::equal('03.03.2015 10:00:00', $recurrences[28]->format('d.m.Y H:i:s'));
+ Assert::equal('07.04.2015 10:00:00', $recurrences[29]->format('d.m.Y H:i:s'));
+ Assert::equal('05.05.2015 10:00:00', $recurrences[30]->format('d.m.Y H:i:s'));
+ Assert::equal('02.06.2015 10:00:00', $recurrences[31]->format('d.m.Y H:i:s'));
+ Assert::equal('07.07.2015 10:00:00', $recurrences[32]->format('d.m.Y H:i:s'));
+ Assert::equal('04.08.2015 10:00:00', $recurrences[33]->format('d.m.Y H:i:s'));
+ Assert::equal('01.09.2015 10:00:00', $recurrences[34]->format('d.m.Y H:i:s'));
+
+ foreach ($events->getIterator()->current()['EXDATES'] as $exDate) {
+ Assert::notContains($exDate, $recurrences);
+ }
+});
+
+test('Recurrent event with modifications at single date', function () {
+ $cal = new IcalParser();
+ $cal->parseFile(__DIR__ . '/cal/recur_instances_with_modifications.ics');
+ $events = $cal->getEvents()->sorted();
+
+ // There should be 36 total events because of the modified event + 35 recurrences
+ Assert::count(36, $events); // 36 events
+
+ // There should be 35 total recurrences because the modified event should've removed 1 recurrence
+ Assert::hasKey('RECURRENCES', $events->offsetGet(1));
+ $recurrences = $events->getIterator()->current()['RECURRENCES'];
+ Assert::count(35, $recurrences);
+
+ // reccurent event don't have RECURRENCES
+ foreach (range(2, 35) as $index) {
+ Assert::hasNotKey('RECURRENCES', $events->offsetGet($index));
+ }
+
+ // the date 8.8.2016 should be modified
+ $modifiedEvent = $events->offsetGet(0);
+ Assert::hasNotKey('RECURRENCES', $modifiedEvent);
+// the 12th entry is the modified event, related to the remaining recurring events
+ Assert::same('8.8.2016', $modifiedEvent['DTSTART']->format('j.n.Y'));
+ Assert::notContains($modifiedEvent['DTSTART'], $recurrences);
+});
+
+test('Recuring instances with modifications and interval', function () {
+ $cal = new IcalParser();
+ $results = $cal->parseFile(__DIR__ . '/cal/recur_instances_with_modifications_and_interval.ics');
+
+// Build the cache of RECURRENCE-IDs and EXDATES first, so that we can properly determine the interval
+ $eventCache = [];
+ foreach ($results['VEVENT'] as $event) {
+ $eventSequence = empty($event['SEQUENCE']) ? "0" : $event['SEQUENCE'];
+ $eventRecurrenceID = empty($event['RECURRENCE-ID']) ? "0" : $event['RECURRENCE-ID'];
+ $eventCache[$event['UID']][$eventRecurrenceID][$eventSequence] = $event;
+ }
+ $trueEvents = [];
+ foreach ($results['VEVENT'] as $event) {
+ if (empty($event['RECURRENCES'])) {
+ $trueEvents[] = $event;
+ } else {
+ $eventUID = $event['UID'];
+ foreach ($event['RECURRENCES'] as $recurrence) {
+ $eventRecurrenceID = $recurrence->format("Ymd");
+ if (empty($eventCache[$eventUID][$eventRecurrenceID])) {
+ $trueEvents[$eventRecurrenceID] = ['DTSTART' => $recurrence];
+ } else {
+ krsort($eventCache[$eventUID][$eventRecurrenceID]);
+ $keys = array_keys($eventCache[$eventUID][$eventRecurrenceID]);
+ $trueEvents[$eventRecurrenceID] = $eventCache[$eventUID][$eventRecurrenceID][$keys[0]];
+ }
+ }
+ }
+ }
+
+ usort(
+ $trueEvents,
+ static function ($a, $b): int {
+ return ($a['DTSTART'] > $b['DTSTART']) ? 1 : -1;
+ }
+ );
+
+ $events = $cal->getEvents()->sorted()->getArrayCopy();
+
+ Assert::false(empty($events[0]['RECURRENCES']));
+ Assert::equal(count($trueEvents), count($events));
+ foreach ($trueEvents as $index => $trueEvent) {
+ Assert::equal($trueEvent['DTSTART']->format("Ymd"), $events[$index]['DTSTART']->format("Ymd"));
+ }
+
+});
+
+test('', function () {
+ $cal = new IcalParser();
+// There is still an issue that needs to be resolved when modifications are made to the initial event that is the
+// base of the recurrences. The below ICS file has a great edge case example: one event, no recurrences in the
+// recurring ruleset, and a modification to the initial event.
+ $results = $cal->parseFile(__DIR__ . '/cal/recur_instances_with_modifications_to_first_day.ics');
+ $events = $cal->getEvents()->sorted()->getArrayCopy();
+ Assert::true(empty($events[0]['RECURRENCES'])); // edited event
+ Assert::true(empty($events[1]['RECURRENCES'])); // recurring event base with no recurrences
+ Assert::equal(1, count($events));
+});
+
+test('', function () {
+ $cal = new IcalParser();
+ $results = $cal->parseFile(__DIR__ . '/cal/daily_recur.ics');
+ $events = $cal->getEvents()->sorted()->getArrayCopy();
+ $period = new DatePeriod(new DateTime('20120801T050000'), new DateInterval('P1D'), new DateTime('20150801T050000'));
+ foreach ($period as $i => $day) {
+ Assert::equal($day->format('j.n.Y H:i:s'), $events[$i]['DTSTART']->format('j.n.Y H:i:s'));
+ }
+});
+
+test('', function () {
+ $cal = new IcalParser();
+ $results = $cal->parseFile(__DIR__ . '/cal/daily_recur2.ics');
+ $events = $cal->getEvents()->sorted()->getArrayCopy();
+
+ Assert::equal(4, count($events));
+ Assert::equal('21.8.2017 00:00:00', $events[0]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('28.8.2017 00:00:00', $events[1]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('4.9.2017 00:00:00', $events[2]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('11.9.2017 00:00:00', $events[3]['DTSTART']->format('j.n.Y H:i:s'));
+});
+
+test('', function () {
+//https://github.com/OzzyCzech/icalparser/issues/38
+ $cal = new IcalParser();
+ $cal->parseFile(__DIR__ . '/cal/38_weekly_recurring_event_missing_day.ics');
+ $events = $cal->getEvents()->sorted()->getArrayCopy();
+
+ //first monday
+ Assert::equal('25.2.2019 09:00:00', $events[0]['DTSTART']->format('j.n.Y H:i:s'));
+ //rest of week
+ Assert::equal('26.2.2019 09:00:00', $events[1]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('27.2.2019 09:00:00', $events[2]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('28.2.2019 09:00:00', $events[3]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('1.3.2019 09:00:00', $events[4]['DTSTART']->format('j.n.Y H:i:s'));
+ //now check the next 4 mondays to make sure they exist as well
+ Assert::equal('4.3.2019 09:00:00', $events[5]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('11.3.2019 09:00:00', $events[10]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('18.3.2019 09:00:00', $events[15]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('25.3.2019 09:00:00', $events[20]['DTSTART']->format('j.n.Y H:i:s'));
+
+ //Last week that works correctly
+ Assert::equal('1.4.2019 09:00:00', $events[25]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('2.4.2019 09:00:00', $events[26]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('3.4.2019 09:00:00', $events[27]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('4.4.2019 09:00:00', $events[28]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('5.4.2019 09:00:00', $events[29]['DTSTART']->format('j.n.Y H:i:s'));
+
+ //This week starts failing
+ Assert::equal('8.4.2019 09:00:00', $events[30]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('9.4.2019 09:00:00', $events[31]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('10.4.2019 09:00:00', $events[32]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('11.4.2019 09:00:00', $events[33]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('12.4.2019 09:00:00', $events[34]['DTSTART']->format('j.n.Y H:i:s'));
+
+ Assert::equal('15.4.2019 09:00:00', $events[35]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('16.4.2019 09:00:00', $events[36]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('17.4.2019 09:00:00', $events[37]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('18.4.2019 09:00:00', $events[38]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('19.4.2019 09:00:00', $events[39]['DTSTART']->format('j.n.Y H:i:s'));
+});
+
+test('Recurring instances bi-weekly', function () {
+// https://github.com/OzzyCzech/icalparser/issues/61
+ $cal = new IcalParser();
+
+ $cal->parseFile(__DIR__ . '/cal/rrule_interval.ics');
+ $events = $cal->getEvents()->sorted();
+
+ var_dump($events[0]['RECURRENCES']);
+
+// DTSTART;TZID=America/Los_Angeles:20230131T050000
+// DTEND;TZID=America/Los_Angeles:20230131T060000
+// RRULE:FREQ=WEEKLY;WKST=MO;UNTIL=20230228T090000;INTERVAL=2;BYDAY=TU
+ Assert::equal(3, count($events[0]['RECURRENCES']));
+ Assert::equal(3, $events->count());
+ Assert::equal('31.1.2023 05:00:00', $events[0]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('14.2.2023 05:00:00', $events[1]['DTSTART']->format('j.n.Y H:i:s'));
+ Assert::equal('28.2.2023 05:00:00', $events[2]['DTSTART']->format('j.n.Y H:i:s'));
+}); \ No newline at end of file
diff --git a/includes/composer/vendor/om/icalparser/tests/events.sorting.phpt b/includes/composer/vendor/om/icalparser/tests/events.sorting.phpt
new file mode 100644
index 0000000..5463396
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/events.sorting.phpt
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Copyright (c) 2004-2022 Roman Ožana (https://ozana.cz)
+ *
+ * @license BSD-3-Clause
+ * @author Roman Ožana <roman@ozana.cz>
+ */
+
+use om\IcalParser;
+use Tester\Assert;
+use function tests\test;
+
+require_once __DIR__ . '/bootstrap.php';
+
+date_default_timezone_set('Europe/Prague');
+
+test('Natural sort order by date', function () {
+ $cal = new IcalParser();
+ $cal->parseFile(__DIR__ . '/cal/basic.ics');
+ $first = $cal->getEvents()->sorted()->getIterator()->current();
+ Assert::same('1.1.2013 00:00:00', $first['DTSTART']->format('j.n.Y H:i:s'));
+});
+
+test('Reverse events sort (parseFile)', function () {
+ $cal = new IcalParser();
+ $cal->parseFile(__DIR__ . '/cal/basic.ics');
+ $first = $cal->getEvents()->reversed()->getIterator()->current();
+ Assert::same('26.12.2015 00:00:00', $first['DTSTART']->format('j.n.Y H:i:s'));
+});
+
+test('Reverse events sort (parseString)', function () {
+ $cal = new IcalParser();
+ $cal->parseString(file_get_contents(__DIR__ . '/cal/basic.ics'));
+ $first = $cal->getEvents()->reversed()->getIterator()->current();
+ Assert::same('26.12.2015 00:00:00', $first['DTSTART']->format('j.n.Y H:i:s'));
+});
diff --git a/includes/composer/vendor/om/icalparser/tests/missing.timezone.phpt b/includes/composer/vendor/om/icalparser/tests/missing.timezone.phpt
new file mode 100644
index 0000000..63eb6c2
--- /dev/null
+++ b/includes/composer/vendor/om/icalparser/tests/missing.timezone.phpt
@@ -0,0 +1,24 @@
+<?php
+
+use om\IcalParser;
+use Tester\Assert;
+use function tests\test;
+
+require_once __DIR__ . '/bootstrap.php';
+date_default_timezone_set('Europe/Prague');
+
+test('Time zone should remain empty', function () {
+ $cal = new IcalParser();
+ $cal->parseFile(__DIR__ . '/cal/missing-timezone.ics');
+
+ Assert::null($cal->timezone);
+});
+
+test('Timezone should be same as current timezone', function () {
+ $cal = new IcalParser();
+ $cal->parseFile(__DIR__ . '/cal/missing-timezone.ics');
+ $dtstart = $cal->getEvents()->reversed()->getIterator()->current()['DTSTART'];
+ /** @var DateTime $dtstart */
+ Assert::same('Europe/Prague', $dtstart->getTimezone()->getName());
+ Assert::same('7.11.2022', $dtstart->format('j.n.Y'));
+}); \ No newline at end of file